diff --git a/.gitignore b/.gitignore index 9fd4c9533b3dcc8ebf3363c3faa523db1f56bc90..0bbae167bf93e9c4ca21160f89ca0c04de2f4404 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,7 @@ modules.order # # RPM spec file (make rpm-pkg) # -/*.spec +/kernel.spec /rpmbuild/ # diff --git a/.mailmap b/.mailmap index a0a6efe871869348ae181c58c846e3ebc01d90a5..c80903efec7594987939ca45df27ed249dc3f013 100644 --- a/.mailmap +++ b/.mailmap @@ -377,6 +377,7 @@ Matthew Wilcox Matthew Wilcox Matthew Wilcox Matthias Fuchs +Matthieu Baerts Matthieu CASTET Matti Vaittinen Matt Ranostay diff --git a/Documentation/ABI/stable/sysfs-driver-dma-idxd b/Documentation/ABI/stable/sysfs-driver-dma-idxd index 534b7a3d59fc599b78c746bb59b853604b4902e2..825e619250bf2ebb77d6c2abc9843414c8b5efd5 100644 --- a/Documentation/ABI/stable/sysfs-driver-dma-idxd +++ b/Documentation/ABI/stable/sysfs-driver-dma-idxd @@ -84,7 +84,7 @@ What: /sys/bus/dsa/devices/dsa/pasid_enabled Date: Oct 27, 2020 KernelVersion: 5.11.0 Contact: dmaengine@vger.kernel.org -Description: To indicate if PASID (process address space identifier) is +Description: To indicate if user PASID (process address space identifier) is enabled or not for this device. What: /sys/bus/dsa/devices/dsa/state diff --git a/Documentation/ABI/testing/sysfs-class-led b/Documentation/ABI/testing/sysfs-class-led index 2e24ac3bd7efa4e64d8dc084462cc0ef92dc9202..b2ff0012c0f2b8df7d843fc50f3568c0ca03f715 100644 --- a/Documentation/ABI/testing/sysfs-class-led +++ b/Documentation/ABI/testing/sysfs-class-led @@ -59,6 +59,15 @@ Description: brightness. Reading this file when no hw brightness change event has happened will return an ENODATA error. +What: /sys/class/leds//color +Date: June 2023 +KernelVersion: 6.5 +Description: + Color of the LED. + + This is a read-only file. Reading this file returns the color + of the LED as a string (e.g: "red", "green", "multicolor"). + What: /sys/class/leds//trigger Date: March 2006 KernelVersion: 2.6.17 diff --git a/Documentation/ABI/testing/sysfs-driver-ufs b/Documentation/ABI/testing/sysfs-driver-ufs index e487f969a15ea937ee17d58959afd0f65fa44af7..0c7efaf62de0c00b5f481b27e547fd24a022959f 100644 --- a/Documentation/ABI/testing/sysfs-driver-ufs +++ b/Documentation/ABI/testing/sysfs-driver-ufs @@ -1437,180 +1437,6 @@ Description: If avail_wb_buff < wb_flush_threshold, it indicates that WriteBooster buffer needs to be flushed, otherwise it is not necessary. -What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/hpb_version -What: /sys/bus/platform/devices/*.ufs/device_descriptor/hpb_version -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the HPB specification version. - The full information about the descriptor can be found in the UFS - HPB (Host Performance Booster) Extension specifications. - Example: version 1.2.3 = 0123h - - The file is read only. - -What: /sys/bus/platform/drivers/ufshcd/*/device_descriptor/hpb_control -What: /sys/bus/platform/devices/*.ufs/device_descriptor/hpb_control -Date: June 2021 -Contact: Daejun Park -Description: This entry shows an indication of the HPB control mode. - 00h: Host control mode - 01h: Device control mode - - The file is read only. - -What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/hpb_region_size -What: /sys/bus/platform/devices/*.ufs/geometry_descriptor/hpb_region_size -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the bHPBRegionSize which can be calculated - as in the following (in bytes): - HPB Region size = 512B * 2^bHPBRegionSize - - The file is read only. - -What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/hpb_number_lu -What: /sys/bus/platform/devices/*.ufs/geometry_descriptor/hpb_number_lu -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the maximum number of HPB LU supported by - the device. - 00h: HPB is not supported by the device. - 01h ~ 20h: Maximum number of HPB LU supported by the device - - The file is read only. - -What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/hpb_subregion_size -What: /sys/bus/platform/devices/*.ufs/geometry_descriptor/hpb_subregion_size -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the bHPBSubRegionSize, which can be - calculated as in the following (in bytes) and shall be a multiple of - logical block size: - HPB Sub-Region size = 512B x 2^bHPBSubRegionSize - bHPBSubRegionSize shall not exceed bHPBRegionSize. - - The file is read only. - -What: /sys/bus/platform/drivers/ufshcd/*/geometry_descriptor/hpb_max_active_regions -What: /sys/bus/platform/devices/*.ufs/geometry_descriptor/hpb_max_active_regions -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the maximum number of active HPB regions that - is supported by the device. - - The file is read only. - -What: /sys/class/scsi_device/*/device/unit_descriptor/hpb_lu_max_active_regions -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the maximum number of HPB regions assigned to - the HPB logical unit. - - The file is read only. - -What: /sys/class/scsi_device/*/device/unit_descriptor/hpb_pinned_region_start_offset -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the start offset of HPB pinned region. - - The file is read only. - -What: /sys/class/scsi_device/*/device/unit_descriptor/hpb_number_pinned_regions -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the number of HPB pinned regions assigned to - the HPB logical unit. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_stats/hit_cnt -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the number of reads that changed to HPB read. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_stats/miss_cnt -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the number of reads that cannot be changed to - HPB read. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_stats/rcmd_noti_cnt -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the number of response UPIUs that has - recommendations for activating sub-regions and/or inactivating region. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_stats/rcmd_active_cnt -Date: June 2021 -Contact: Daejun Park -Description: For the HPB device control mode, this entry shows the number of - active sub-regions recommended by response UPIUs. For the HPB host control - mode, this entry shows the number of active sub-regions recommended by the - HPB host control mode heuristic algorithm. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_stats/rcmd_inactive_cnt -Date: June 2021 -Contact: Daejun Park -Description: For the HPB device control mode, this entry shows the number of - inactive regions recommended by response UPIUs. For the HPB host control - mode, this entry shows the number of inactive regions recommended by the - HPB host control mode heuristic algorithm. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_stats/map_req_cnt -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the number of read buffer commands for - activating sub-regions recommended by response UPIUs. - - The file is read only. - -What: /sys/class/scsi_device/*/device/hpb_params/requeue_timeout_ms -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the requeue timeout threshold for write buffer - command in ms. The value can be changed by writing an integer to - this entry. - -What: /sys/bus/platform/drivers/ufshcd/*/attributes/max_data_size_hpb_single_cmd -What: /sys/bus/platform/devices/*.ufs/attributes/max_data_size_hpb_single_cmd -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the maximum HPB data size for using a single HPB - command. - - === ======== - 00h 4KB - 01h 8KB - 02h 12KB - ... - FFh 1024KB - === ======== - - The file is read only. - -What: /sys/bus/platform/drivers/ufshcd/*/flags/hpb_enable -What: /sys/bus/platform/devices/*.ufs/flags/hpb_enable -Date: June 2021 -Contact: Daejun Park -Description: This entry shows the status of HPB. - - == ============================ - 0 HPB is not enabled. - 1 HPB is enabled - == ============================ - - The file is read only. - Contact: Daniil Lunev What: /sys/bus/platform/drivers/ufshcd/*/capabilities/ What: /sys/bus/platform/devices/*.ufs/capabilities/ @@ -1648,76 +1474,3 @@ Description: Indicates status of Write Booster. The file is read only. -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/activation_thld -Date: February 2021 -Contact: Avri Altman -Description: In host control mode, reads are the major source of activation - trials. Once this threshold hs met, the region is added to the - "to-be-activated" list. Since we reset the read counter upon - write, this include sending a rb command updating the region - ppn as well. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/normalization_factor -Date: February 2021 -Contact: Avri Altman -Description: In host control mode, we think of the regions as "buckets". - Those buckets are being filled with reads, and emptied on write. - We use entries_per_srgn - the amount of blocks in a subregion as - our bucket size. This applies because HPB1.0 only handles - single-block reads. Once the bucket size is crossed, we trigger - a normalization work - not only to avoid overflow, but mainly - because we want to keep those counters normalized, as we are - using those reads as a comparative score, to make various decisions. - The normalization is dividing (shift right) the read counter by - the normalization_factor. If during consecutive normalizations - an active region has exhausted its reads - inactivate it. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_enter -Date: February 2021 -Contact: Avri Altman -Description: Region deactivation is often due to the fact that eviction took - place: A region becomes active at the expense of another. This is - happening when the max-active-regions limit has been crossed. - In host mode, eviction is considered an extreme measure. We - want to verify that the entering region has enough reads, and - the exiting region has much fewer reads. eviction_thld_enter is - the min reads that a region must have in order to be considered - a candidate for evicting another region. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/eviction_thld_exit -Date: February 2021 -Contact: Avri Altman -Description: Same as above for the exiting region. A region is considered to - be a candidate for eviction only if it has fewer reads than - eviction_thld_exit. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_ms -Date: February 2021 -Contact: Avri Altman -Description: In order not to hang on to "cold" regions, we inactivate - a region that has no READ access for a predefined amount of - time - read_timeout_ms. If read_timeout_ms has expired, and the - region is dirty, it is less likely that we can make any use of - HPB reading it so we inactivate it. Still, deactivation has - its overhead, and we may still benefit from HPB reading this - region if it is clean - see read_timeout_expiries. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/read_timeout_expiries -Date: February 2021 -Contact: Avri Altman -Description: If the region read timeout has expired, but the region is clean, - just re-wind its timer for another spin. Do that as long as it - is clean and did not exhaust its read_timeout_expiries threshold. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/timeout_polling_interval_ms -Date: February 2021 -Contact: Avri Altman -Description: The frequency with which the delayed worker that checks the - read_timeouts is awakened. - -What: /sys/class/scsi_device/*/device/hpb_param_sysfs/inflight_map_req -Date: February 2021 -Contact: Avri Altman -Description: In host control mode the host is the originator of map requests. - To avoid flooding the device with map requests, use a simple throttling - mechanism that limits the number of inflight map requests. diff --git a/Documentation/ABI/testing/sysfs-fs-f2fs b/Documentation/ABI/testing/sysfs-fs-f2fs index ad3d76d37c8baf66c32e039864953ffabc12ad3e..36c3cb5479013a30dfe5e73acc2a9f1a37cdde54 100644 --- a/Documentation/ABI/testing/sysfs-fs-f2fs +++ b/Documentation/ABI/testing/sysfs-fs-f2fs @@ -102,9 +102,9 @@ What: /sys/fs/f2fs//max_small_discards Date: November 2013 Contact: "Jaegeuk Kim" Description: Controls the issue rate of discard commands that consist of small - blocks less than 2MB. The candidates to be discarded are cached until - checkpoint is triggered, and issued during the checkpoint. - By default, it is disabled with 0. + blocks less than 2MB. The candidates to be discarded are cached during + checkpoint, and issued by issue_discard thread after checkpoint. + It is enabled by default. What: /sys/fs/f2fs//max_ordered_discard Date: October 2022 diff --git a/Documentation/admin-guide/cgroup-v1/memory.rst b/Documentation/admin-guide/cgroup-v1/memory.rst index 8d3afeede10e46aeddd20b94790302116bbde71b..ff456871bf4b8b74a7e74bea664e0e6fa5e3910b 100644 --- a/Documentation/admin-guide/cgroup-v1/memory.rst +++ b/Documentation/admin-guide/cgroup-v1/memory.rst @@ -92,6 +92,13 @@ Brief summary of control files. memory.oom_control set/show oom controls. memory.numa_stat show the number of memory usage per numa node + memory.kmem.limit_in_bytes Deprecated knob to set and read the kernel + memory hard limit. Kernel hard limit is not + supported since 5.16. Writing any value to + do file will not have any effect same as if + nokmem kernel parameter was specified. + Kernel memory is still charged and reported + by memory.kmem.usage_in_bytes. memory.kmem.usage_in_bytes show current kernel memory allocation memory.kmem.failcnt show the number of kernel memory usage hits limits @@ -195,11 +202,11 @@ are not accounted. We just account pages under usual VM management. RSS pages are accounted at page_fault unless they've already been accounted for earlier. A file page will be accounted for as Page Cache when it's -inserted into inode (radix-tree). While it's mapped into the page tables of +inserted into inode (xarray). While it's mapped into the page tables of processes, duplicate accounting is carefully avoided. An RSS page is unaccounted when it's fully unmapped. A PageCache page is -unaccounted when it's removed from radix-tree. Even if RSS pages are fully +unaccounted when it's removed from xarray. Even if RSS pages are fully unmapped (by kswapd), they may exist as SwapCache in the system until they are really freed. Such SwapCaches are also accounted. A swapped-in page is accounted after adding into swapcache. @@ -907,7 +914,7 @@ experiences some pressure. In this situation, only group C will receive the notification, i.e. groups A and B will not receive it. This is done to avoid excessive "broadcasting" of messages, which disturbs the system and which is especially bad if we are low on memory or thrashing. Group B, will receive -notification only if there are no event listers for group C. +notification only if there are no event listeners for group C. There are three optional modes that specify different propagation behavior: diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 4ef8901911961fff9727df7d0716c3a6a9eccd7b..b26b5274eaaf140ed8ccb617df2eca53a166e8bd 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -1045,7 +1045,7 @@ All time durations are in microseconds. - user_usec - system_usec - and the following three when the controller is enabled: + and the following five when the controller is enabled: - nr_periods - nr_throttled diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b2b1280342250045cd01ae6bbb8e5f407668f73d..0a1731a0f0ef373421c0594886c065461ade1064 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -7076,6 +7076,13 @@ disables both lockup detectors. Default is 10 seconds. + workqueue.unbound_cpus= + [KNL,SMP] Specify to constrain one or some CPUs + to use in unbound workqueues. + Format: + By default, all online CPUs are available for + unbound workqueues. + workqueue.watchdog_thresh= If CONFIG_WQ_WATCHDOG is configured, workqueue can warn stall conditions and dump internal state to @@ -7097,15 +7104,6 @@ threshold repeatedly. They are likely good candidates for using WQ_UNBOUND workqueues instead. - workqueue.disable_numa - By default, all work items queued to unbound - workqueues are affine to the NUMA nodes they're - issued on, which results in better behavior in - general. If NUMA affinity needs to be disabled for - whatever reason, this option can be used. Note - that this also can be controlled per-workqueue for - workqueues visible under /sys/bus/workqueue/. - workqueue.power_efficient Per-cpu workqueues are generally preferred because they show better performance thanks to cache @@ -7121,6 +7119,18 @@ The default value of this parameter is determined by the config option CONFIG_WQ_POWER_EFFICIENT_DEFAULT. + workqueue.default_affinity_scope= + Select the default affinity scope to use for unbound + workqueues. Can be one of "cpu", "smt", "cache", + "numa" and "system". Default is "cache". For more + information, see the Affinity Scopes section in + Documentation/core-api/workqueue.rst. + + This can be changed after boot by writing to the + matching /sys/module/workqueue/parameters file. All + workqueues with the "default" affinity scope will be + updated accordignly. + workqueue.debug_force_rr_cpu Workqueue used to implicitly guarantee that work items queued without explicit CPU specified are put diff --git a/Documentation/admin-guide/perf/alibaba_pmu.rst b/Documentation/admin-guide/perf/alibaba_pmu.rst index 11de998bb4806ac8071db104564a2620b9c77d28..7d840023903f59bb4c9f90db88a79700e49b0bae 100644 --- a/Documentation/admin-guide/perf/alibaba_pmu.rst +++ b/Documentation/admin-guide/perf/alibaba_pmu.rst @@ -88,6 +88,11 @@ data bandwidth:: -e ali_drw_27080/hif_rmw/ \ -e ali_drw_27080/cycle/ -- sleep 10 +Example usage of counting all memory read/write bandwidth by metric:: + + perf stat -M ddr_read_bandwidth.all -- sleep 10 + perf stat -M ddr_write_bandwidth.all -- sleep 10 + The average DRAM bandwidth can be calculated as follows: - Read Bandwidth = perf_hif_rd * DDRC_WIDTH * DDRC_Freq / DDRC_Cycle diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 8019103aac109965b7eecdcd5585e28572af1db3..cf33de56da27dc6bb1ac1e6a1929867584945926 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -450,6 +450,35 @@ this allows system administrators to override the ``IA64_THREAD_UAC_NOPRINT`` ``prctl`` and avoid logs being flooded. +io_uring_disabled +================= + +Prevents all processes from creating new io_uring instances. Enabling this +shrinks the kernel's attack surface. + += ====================================================================== +0 All processes can create io_uring instances as normal. This is the + default setting. +1 io_uring creation is disabled (io_uring_setup() will fail with + -EPERM) for unprivileged processes not in the io_uring_group group. + Existing io_uring instances can still be used. See the + documentation for io_uring_group for more information. +2 io_uring creation is disabled for all processes. io_uring_setup() + always fails with -EPERM. Existing io_uring instances can still be + used. += ====================================================================== + + +io_uring_group +============== + +When io_uring_disabled is set to 1, a process must either be +privileged (CAP_SYS_ADMIN) or be in the io_uring_group group in order +to create an io_uring instance. If io_uring_group is set to -1 (the +default), only processes with the CAP_SYS_ADMIN capability may create +io_uring instances. + + kexec_load_disabled =================== diff --git a/Documentation/arch/arm64/cpu-feature-registers.rst b/Documentation/arch/arm64/cpu-feature-registers.rst index 4e4625f2455fe92552365fc1c088d497bdef5a55..de6d8a4790e2b6cd06f69fe5ebe72eb69c23cb3d 100644 --- a/Documentation/arch/arm64/cpu-feature-registers.rst +++ b/Documentation/arch/arm64/cpu-feature-registers.rst @@ -175,6 +175,8 @@ infrastructure: +------------------------------+---------+---------+ | Name | bits | visible | +------------------------------+---------+---------+ + | SME | [27-24] | y | + +------------------------------+---------+---------+ | MTE | [11-8] | y | +------------------------------+---------+---------+ | SSBS | [7-4] | y | @@ -288,8 +290,18 @@ infrastructure: +------------------------------+---------+---------+ | Name | bits | visible | +------------------------------+---------+---------+ + | CSSC | [55-52] | y | + +------------------------------+---------+---------+ + | RPRFM | [51-48] | y | + +------------------------------+---------+---------+ + | BC | [23-20] | y | + +------------------------------+---------+---------+ | MOPS | [19-16] | y | +------------------------------+---------+---------+ + | APA3 | [15-12] | y | + +------------------------------+---------+---------+ + | GPA3 | [11-8] | y | + +------------------------------+---------+---------+ | RPRES | [7-4] | y | +------------------------------+---------+---------+ | WFXT | [3-0] | y | diff --git a/Documentation/arch/arm64/elf_hwcaps.rst b/Documentation/arch/arm64/elf_hwcaps.rst index 8c8addb4194c93446936e60af3b9e7c744e75575..76ff9d7398fda74afd576f6f6734688cb111eb20 100644 --- a/Documentation/arch/arm64/elf_hwcaps.rst +++ b/Documentation/arch/arm64/elf_hwcaps.rst @@ -305,6 +305,9 @@ HWCAP2_SMEF16F16 HWCAP2_MOPS Functionality implied by ID_AA64ISAR2_EL1.MOPS == 0b0001. +HWCAP2_HBC + Functionality implied by ID_AA64ISAR2_EL1.BC == 0b0001. + 4. Unused AT_HWCAP bits ----------------------- diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst index e96f057ea2a07ed396301938ed0bdefbe6df21b5..f47f63bcf67c91b5b401ff25019aeb170e12a95f 100644 --- a/Documentation/arch/arm64/silicon-errata.rst +++ b/Documentation/arch/arm64/silicon-errata.rst @@ -71,6 +71,8 @@ stable kernels. +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A510 | #2658417 | ARM64_ERRATUM_2658417 | +----------------+-----------------+-----------------+-----------------------------+ +| ARM | Cortex-A520 | #2966298 | ARM64_ERRATUM_2966298 | ++----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A53 | #826319 | ARM64_ERRATUM_826319 | +----------------+-----------------+-----------------+-----------------------------+ | ARM | Cortex-A53 | #827319 | ARM64_ERRATUM_827319 | diff --git a/Documentation/arch/loongarch/introduction.rst b/Documentation/arch/loongarch/introduction.rst index 49135d451ced9a0b9fa5c54a7d52d82880521b32..8c568cfc2107984092f4d82c2076a3bed0c05c4f 100644 --- a/Documentation/arch/loongarch/introduction.rst +++ b/Documentation/arch/loongarch/introduction.rst @@ -381,9 +381,9 @@ Documentation of LoongArch ISA: Documentation of LoongArch ELF psABI: - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.00-CN.pdf (in Chinese) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.01-CN.pdf (in Chinese) - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.00-EN.pdf (in English) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.01-EN.pdf (in English) Linux kernel repository of Loongson and LoongArch: diff --git a/Documentation/bpf/btf.rst b/Documentation/bpf/btf.rst index f32db1f44ae90fc2f2004e1e84145430f5089e96..e43c2fdafcd785f4f259a9b3d79abc9467051ddc 100644 --- a/Documentation/bpf/btf.rst +++ b/Documentation/bpf/btf.rst @@ -726,8 +726,8 @@ same as the one describe in :ref:`BTF_Type_String`. 4.2 .BTF.ext section -------------------- -The .BTF.ext section encodes func_info and line_info which needs loader -manipulation before loading into the kernel. +The .BTF.ext section encodes func_info, line_info and CO-RE relocations +which needs loader manipulation before loading into the kernel. The specification for .BTF.ext section is defined at ``tools/lib/bpf/btf.h`` and ``tools/lib/bpf/btf.c``. @@ -745,15 +745,20 @@ The current header of .BTF.ext section:: __u32 func_info_len; __u32 line_info_off; __u32 line_info_len; + + /* optional part of .BTF.ext header */ + __u32 core_relo_off; + __u32 core_relo_len; }; It is very similar to .BTF section. Instead of type/string section, it -contains func_info and line_info section. See :ref:`BPF_Prog_Load` for details -about func_info and line_info record format. +contains func_info, line_info and core_relo sub-sections. +See :ref:`BPF_Prog_Load` for details about func_info and line_info +record format. The func_info is organized as below.:: - func_info_rec_size + func_info_rec_size /* __u32 value */ btf_ext_info_sec for section #1 /* func_info for section #1 */ btf_ext_info_sec for section #2 /* func_info for section #2 */ ... @@ -773,7 +778,7 @@ Here, num_info must be greater than 0. The line_info is organized as below.:: - line_info_rec_size + line_info_rec_size /* __u32 value */ btf_ext_info_sec for section #1 /* line_info for section #1 */ btf_ext_info_sec for section #2 /* line_info for section #2 */ ... @@ -787,6 +792,20 @@ kernel API, the ``insn_off`` is the instruction offset in the unit of ``struct bpf_insn``. For ELF API, the ``insn_off`` is the byte offset from the beginning of section (``btf_ext_info_sec->sec_name_off``). +The core_relo is organized as below.:: + + core_relo_rec_size /* __u32 value */ + btf_ext_info_sec for section #1 /* core_relo for section #1 */ + btf_ext_info_sec for section #2 /* core_relo for section #2 */ + +``core_relo_rec_size`` specifies the size of ``bpf_core_relo`` +structure when .BTF.ext is generated. All ``bpf_core_relo`` structures +within a single ``btf_ext_info_sec`` describe relocations applied to +section named by ``btf_ext_info_sec->sec_name_off``. + +See :ref:`Documentation/bpf/llvm_reloc.rst ` +for more information on CO-RE relocations. + 4.2 .BTF_ids section -------------------- diff --git a/Documentation/bpf/index.rst b/Documentation/bpf/index.rst index 1ff177b89d66bc19c86b4d8c0faa0c4278d27b91..aeaeb35e6d4a70265e1552b272ca5b2707fabdfc 100644 --- a/Documentation/bpf/index.rst +++ b/Documentation/bpf/index.rst @@ -29,6 +29,7 @@ that goes into great technical depth about the BPF Architecture. bpf_licensing test_debug clang-notes + linux-notes other redirect diff --git a/Documentation/bpf/standardization/linux-notes.rst b/Documentation/bpf/linux-notes.rst similarity index 100% rename from Documentation/bpf/standardization/linux-notes.rst rename to Documentation/bpf/linux-notes.rst diff --git a/Documentation/bpf/llvm_reloc.rst b/Documentation/bpf/llvm_reloc.rst index 450e6403fe3dec0edf983b582b426a420c09075e..44188e219d32d5efe8a79c08bcc0b2b6f5ef00d6 100644 --- a/Documentation/bpf/llvm_reloc.rst +++ b/Documentation/bpf/llvm_reloc.rst @@ -240,3 +240,307 @@ The .BTF/.BTF.ext sections has R_BPF_64_NODYLD32 relocations:: Offset Info Type Symbol's Value Symbol's Name 000000000000002c 0000000200000004 R_BPF_64_NODYLD32 0000000000000000 .text 0000000000000040 0000000200000004 R_BPF_64_NODYLD32 0000000000000000 .text + +.. _btf-co-re-relocations: + +================= +CO-RE Relocations +================= + +From object file point of view CO-RE mechanism is implemented as a set +of CO-RE specific relocation records. These relocation records are not +related to ELF relocations and are encoded in .BTF.ext section. +See :ref:`Documentation/bpf/btf.rst ` for more +information on .BTF.ext structure. + +CO-RE relocations are applied to BPF instructions to update immediate +or offset fields of the instruction at load time with information +relevant for target kernel. + +Field to patch is selected basing on the instruction class: + +* For BPF_ALU, BPF_ALU64, BPF_LD `immediate` field is patched; +* For BPF_LDX, BPF_STX, BPF_ST `offset` field is patched; +* BPF_JMP, BPF_JMP32 instructions **should not** be patched. + +Relocation kinds +================ + +There are several kinds of CO-RE relocations that could be split in +three groups: + +* Field-based - patch instruction with field related information, e.g. + change offset field of the BPF_LDX instruction to reflect offset + of a specific structure field in the target kernel. + +* Type-based - patch instruction with type related information, e.g. + change immediate field of the BPF_ALU move instruction to 0 or 1 to + reflect if specific type is present in the target kernel. + +* Enum-based - patch instruction with enum related information, e.g. + change immediate field of the BPF_LD_IMM64 instruction to reflect + value of a specific enum literal in the target kernel. + +The complete list of relocation kinds is represented by the following enum: + +.. code-block:: c + + enum bpf_core_relo_kind { + BPF_CORE_FIELD_BYTE_OFFSET = 0, /* field byte offset */ + BPF_CORE_FIELD_BYTE_SIZE = 1, /* field size in bytes */ + BPF_CORE_FIELD_EXISTS = 2, /* field existence in target kernel */ + BPF_CORE_FIELD_SIGNED = 3, /* field signedness (0 - unsigned, 1 - signed) */ + BPF_CORE_FIELD_LSHIFT_U64 = 4, /* bitfield-specific left bitshift */ + BPF_CORE_FIELD_RSHIFT_U64 = 5, /* bitfield-specific right bitshift */ + BPF_CORE_TYPE_ID_LOCAL = 6, /* type ID in local BPF object */ + BPF_CORE_TYPE_ID_TARGET = 7, /* type ID in target kernel */ + BPF_CORE_TYPE_EXISTS = 8, /* type existence in target kernel */ + BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */ + BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ + BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */ + BPF_CORE_TYPE_MATCHES = 12, /* type match in target kernel */ + }; + +Notes: + +* ``BPF_CORE_FIELD_LSHIFT_U64`` and ``BPF_CORE_FIELD_RSHIFT_U64`` are + supposed to be used to read bitfield values using the following + algorithm: + + .. code-block:: c + + // To read bitfield ``f`` from ``struct s`` + is_signed = relo(s->f, BPF_CORE_FIELD_SIGNED) + off = relo(s->f, BPF_CORE_FIELD_BYTE_OFFSET) + sz = relo(s->f, BPF_CORE_FIELD_BYTE_SIZE) + l = relo(s->f, BPF_CORE_FIELD_LSHIFT_U64) + r = relo(s->f, BPF_CORE_FIELD_RSHIFT_U64) + // define ``v`` as signed or unsigned integer of size ``sz`` + v = *({s|u} *)((void *)s + off) + v <<= l + v >>= r + +* The ``BPF_CORE_TYPE_MATCHES`` queries matching relation, defined as + follows: + + * for integers: types match if size and signedness match; + * for arrays & pointers: target types are recursively matched; + * for structs & unions: + + * local members need to exist in target with the same name; + + * for each member we recursively check match unless it is already behind a + pointer, in which case we only check matching names and compatible kind; + + * for enums: + + * local variants have to have a match in target by symbolic name (but not + numeric value); + + * size has to match (but enum may match enum64 and vice versa); + + * for function pointers: + + * number and position of arguments in local type has to match target; + * for each argument and the return value we recursively check match. + +CO-RE Relocation Record +======================= + +Relocation record is encoded as the following structure: + +.. code-block:: c + + struct bpf_core_relo { + __u32 insn_off; + __u32 type_id; + __u32 access_str_off; + enum bpf_core_relo_kind kind; + }; + +* ``insn_off`` - instruction offset (in bytes) within a code section + associated with this relocation; + +* ``type_id`` - BTF type ID of the "root" (containing) entity of a + relocatable type or field; + +* ``access_str_off`` - offset into corresponding .BTF string section. + String interpretation depends on specific relocation kind: + + * for field-based relocations, string encodes an accessed field using + a sequence of field and array indices, separated by colon (:). It's + conceptually very close to LLVM's `getelementptr `_ instruction's + arguments for identifying offset to a field. For example, consider the + following C code: + + .. code-block:: c + + struct sample { + int a; + int b; + struct { int c[10]; }; + } __attribute__((preserve_access_index)); + struct sample *s; + + * Access to ``s[0].a`` would be encoded as ``0:0``: + + * ``0``: first element of ``s`` (as if ``s`` is an array); + * ``0``: index of field ``a`` in ``struct sample``. + + * Access to ``s->a`` would be encoded as ``0:0`` as well. + * Access to ``s->b`` would be encoded as ``0:1``: + + * ``0``: first element of ``s``; + * ``1``: index of field ``b`` in ``struct sample``. + + * Access to ``s[1].c[5]`` would be encoded as ``1:2:0:5``: + + * ``1``: second element of ``s``; + * ``2``: index of anonymous structure field in ``struct sample``; + * ``0``: index of field ``c`` in anonymous structure; + * ``5``: access to array element #5. + + * for type-based relocations, string is expected to be just "0"; + + * for enum value-based relocations, string contains an index of enum + value within its enum type; + +* ``kind`` - one of ``enum bpf_core_relo_kind``. + +.. _GEP: https://llvm.org/docs/LangRef.html#getelementptr-instruction + +.. _btf_co_re_relocation_examples: + +CO-RE Relocation Examples +========================= + +For the following C code: + +.. code-block:: c + + struct foo { + int a; + int b; + unsigned c:15; + } __attribute__((preserve_access_index)); + + enum bar { U, V }; + +With the following BTF definitions: + +.. code-block:: + + ... + [2] STRUCT 'foo' size=8 vlen=2 + 'a' type_id=3 bits_offset=0 + 'b' type_id=3 bits_offset=32 + 'c' type_id=4 bits_offset=64 bitfield_size=15 + [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED + [4] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none) + ... + [16] ENUM 'bar' encoding=UNSIGNED size=4 vlen=2 + 'U' val=0 + 'V' val=1 + +Field offset relocations are generated automatically when +``__attribute__((preserve_access_index))`` is used, for example: + +.. code-block:: c + + void alpha(struct foo *s, volatile unsigned long *g) { + *g = s->a; + s->a = 1; + } + + 00 : + 0: r3 = *(s32 *)(r1 + 0x0) + 00: CO-RE [2] struct foo::a (0:0) + 1: *(u64 *)(r2 + 0x0) = r3 + 2: *(u32 *)(r1 + 0x0) = 0x1 + 10: CO-RE [2] struct foo::a (0:0) + 3: exit + + +All relocation kinds could be requested via built-in functions. +E.g. field-based relocations: + +.. code-block:: c + + void bravo(struct foo *s, volatile unsigned long *g) { + *g = __builtin_preserve_field_info(s->b, 0 /* field byte offset */); + *g = __builtin_preserve_field_info(s->b, 1 /* field byte size */); + *g = __builtin_preserve_field_info(s->b, 2 /* field existence */); + *g = __builtin_preserve_field_info(s->b, 3 /* field signedness */); + *g = __builtin_preserve_field_info(s->c, 4 /* bitfield left shift */); + *g = __builtin_preserve_field_info(s->c, 5 /* bitfield right shift */); + } + + 20 : + 4: r1 = 0x4 + 20: CO-RE [2] struct foo::b (0:1) + 5: *(u64 *)(r2 + 0x0) = r1 + 6: r1 = 0x4 + 30: CO-RE [2] struct foo::b (0:1) + 7: *(u64 *)(r2 + 0x0) = r1 + 8: r1 = 0x1 + 40: CO-RE [2] struct foo::b (0:1) + 9: *(u64 *)(r2 + 0x0) = r1 + 10: r1 = 0x1 + 50: CO-RE [2] struct foo::b (0:1) + 11: *(u64 *)(r2 + 0x0) = r1 + 12: r1 = 0x31 + 60: CO-RE [2] struct foo::c (0:2) + 13: *(u64 *)(r2 + 0x0) = r1 + 14: r1 = 0x31 + 70: CO-RE [2] struct foo::c (0:2) + 15: *(u64 *)(r2 + 0x0) = r1 + 16: exit + + +Type-based relocations: + +.. code-block:: c + + void charlie(struct foo *s, volatile unsigned long *g) { + *g = __builtin_preserve_type_info(*s, 0 /* type existence */); + *g = __builtin_preserve_type_info(*s, 1 /* type size */); + *g = __builtin_preserve_type_info(*s, 2 /* type matches */); + *g = __builtin_btf_type_id(*s, 0 /* type id in this object file */); + *g = __builtin_btf_type_id(*s, 1 /* type id in target kernel */); + } + + 88 : + 17: r1 = 0x1 + 88: CO-RE [2] struct foo + 18: *(u64 *)(r2 + 0x0) = r1 + 19: r1 = 0xc + 98: CO-RE [2] struct foo + 20: *(u64 *)(r2 + 0x0) = r1 + 21: r1 = 0x1 + a8: CO-RE [2] struct foo + 22: *(u64 *)(r2 + 0x0) = r1 + 23: r1 = 0x2 ll + b8: CO-RE [2] struct foo + 25: *(u64 *)(r2 + 0x0) = r1 + 26: r1 = 0x2 ll + d0: CO-RE [2] struct foo + 28: *(u64 *)(r2 + 0x0) = r1 + 29: exit + +Enum-based relocations: + +.. code-block:: c + + void delta(struct foo *s, volatile unsigned long *g) { + *g = __builtin_preserve_enum_value(*(enum bar *)U, 0 /* enum literal existence */); + *g = __builtin_preserve_enum_value(*(enum bar *)V, 1 /* enum literal value */); + } + + f0 : + 30: r1 = 0x1 ll + f0: CO-RE [16] enum bar::U = 0 + 32: *(u64 *)(r2 + 0x0) = r1 + 33: r1 = 0x1 ll + 108: CO-RE [16] enum bar::V = 1 + 35: *(u64 *)(r2 + 0x0) = r1 + 36: exit diff --git a/Documentation/bpf/standardization/abi.rst b/Documentation/bpf/standardization/abi.rst new file mode 100644 index 0000000000000000000000000000000000000000..0c2e10eeb89a05a10553a0cec5e826a110128a5f --- /dev/null +++ b/Documentation/bpf/standardization/abi.rst @@ -0,0 +1,25 @@ +.. contents:: +.. sectnum:: + +=================================================== +BPF ABI Recommended Conventions and Guidelines v1.0 +=================================================== + +This is version 1.0 of an informational document containing recommended +conventions and guidelines for producing portable BPF program binaries. + +Registers and calling convention +================================ + +BPF has 10 general purpose registers and a read-only frame pointer register, +all of which are 64-bits wide. + +The BPF calling convention is defined as: + +* R0: return value from function calls, and exit value for BPF programs +* R1 - R5: arguments for function calls +* R6 - R9: callee saved registers that function calls will preserve +* R10: read-only frame pointer to access stack + +R0 - R5 are scratch registers and BPF programs needs to spill/fill them if +necessary across calls. diff --git a/Documentation/bpf/standardization/index.rst b/Documentation/bpf/standardization/index.rst index 09c6ba055fd70148932c0d7ae3201dc13af1e4e7..a50c3baf6345aaba72aeea25768a8713431e4bf7 100644 --- a/Documentation/bpf/standardization/index.rst +++ b/Documentation/bpf/standardization/index.rst @@ -12,7 +12,7 @@ for the working group charter, documents, and more. :maxdepth: 1 instruction-set - linux-notes + abi .. Links: .. _IETF BPF Working Group: https://datatracker.ietf.org/wg/bpf/about/ diff --git a/Documentation/bpf/standardization/instruction-set.rst b/Documentation/bpf/standardization/instruction-set.rst index 4f73e9dc8d9eedea974ead0f46485b572db7de79..c5d53a6e8c79f1a6c2d42b66b53d3b62796fff62 100644 --- a/Documentation/bpf/standardization/instruction-set.rst +++ b/Documentation/bpf/standardization/instruction-set.rst @@ -1,11 +1,11 @@ .. contents:: .. sectnum:: -======================================== -eBPF Instruction Set Specification, v1.0 -======================================== +======================================= +BPF Instruction Set Specification, v1.0 +======================================= -This document specifies version 1.0 of the eBPF instruction set. +This document specifies version 1.0 of the BPF instruction set. Documentation conventions ========================= @@ -97,26 +97,10 @@ Definitions A: 10000110 B: 11111111 10000110 -Registers and calling convention -================================ - -eBPF has 10 general purpose registers and a read-only frame pointer register, -all of which are 64-bits wide. - -The eBPF calling convention is defined as: - -* R0: return value from function calls, and exit value for eBPF programs -* R1 - R5: arguments for function calls -* R6 - R9: callee saved registers that function calls will preserve -* R10: read-only frame pointer to access stack - -R0 - R5 are scratch registers and eBPF programs needs to spill/fill them if -necessary across calls. - Instruction encoding ==================== -eBPF has two instruction encodings: +BPF has two instruction encodings: * the basic instruction encoding, which uses 64 bits to encode an instruction * the wide instruction encoding, which appends a second 64-bit immediate (i.e., @@ -260,7 +244,7 @@ BPF_END 0xd0 0 byte swap operations (see `Byte swap instructions`_ b ========= ===== ======= ========================================================== Underflow and overflow are allowed during arithmetic operations, meaning -the 64-bit or 32-bit value will wrap. If eBPF program execution would +the 64-bit or 32-bit value will wrap. If BPF program execution would result in division by zero, the destination register is instead set to zero. If execution would result in modulo by zero, for ``BPF_ALU64`` the value of the destination register is unchanged whereas for ``BPF_ALU`` the upper @@ -373,7 +357,7 @@ BPF_JNE 0x5 any PC += offset if dst != src BPF_JSGT 0x6 any PC += offset if dst > src signed BPF_JSGE 0x7 any PC += offset if dst >= src signed BPF_CALL 0x8 0x0 call helper function by address see `Helper functions`_ -BPF_CALL 0x8 0x1 call PC += offset see `Program-local functions`_ +BPF_CALL 0x8 0x1 call PC += imm see `Program-local functions`_ BPF_CALL 0x8 0x2 call helper function by BTF ID see `Helper functions`_ BPF_EXIT 0x9 0x0 return BPF_JMP only BPF_JLT 0xa any PC += offset if dst < src unsigned @@ -382,7 +366,7 @@ BPF_JSLT 0xc any PC += offset if dst < src signed BPF_JSLE 0xd any PC += offset if dst <= src signed ======== ===== === =========================================== ========================================= -The eBPF program needs to store the return value into register R0 before doing a +The BPF program needs to store the return value into register R0 before doing a ``BPF_EXIT``. Example: @@ -424,8 +408,8 @@ Program-local functions ~~~~~~~~~~~~~~~~~~~~~~~ Program-local functions are functions exposed by the same BPF program as the caller, and are referenced by offset from the call instruction, similar to -``BPF_JA``. A ``BPF_EXIT`` within the program-local function will return to -the caller. +``BPF_JA``. The offset is encoded in the imm field of the call instruction. +A ``BPF_EXIT`` within the program-local function will return to the caller. Load and store instructions =========================== @@ -502,9 +486,9 @@ Atomic operations Atomic operations are operations that operate on memory and can not be interrupted or corrupted by other access to the same memory region -by other eBPF programs or means outside of this specification. +by other BPF programs or means outside of this specification. -All atomic operations supported by eBPF are encoded as store operations +All atomic operations supported by BPF are encoded as store operations that use the ``BPF_ATOMIC`` mode modifier as follows: * ``BPF_ATOMIC | BPF_W | BPF_STX`` for 32-bit operations @@ -594,7 +578,7 @@ where Maps ~~~~ -Maps are shared memory regions accessible by eBPF programs on some platforms. +Maps are shared memory regions accessible by BPF programs on some platforms. A map can have various semantics as defined in a separate document, and may or may not have a single contiguous memory region, but the 'map_val(map)' is currently only defined for maps that do have a single contiguous memory region. @@ -616,6 +600,6 @@ identified by the given id. Legacy BPF Packet access instructions ------------------------------------- -eBPF previously introduced special instructions for access to packet data that were +BPF previously introduced special instructions for access to packet data that were carried over from classic BPF. However, these instructions are deprecated and should no longer be used. diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst index dfe7e75a71dece99cd691ffe419365152e4a01f7..4451ef501936133998b059d5b0482328845e02ba 100644 --- a/Documentation/core-api/printk-formats.rst +++ b/Documentation/core-api/printk-formats.rst @@ -15,9 +15,10 @@ Integer types If variable is of Type, use printk format specifier: ------------------------------------------------------------ - char %d or %x + signed char %d or %hhx unsigned char %u or %x - short int %d or %x + char %u or %x + short int %d or %hx unsigned short int %u or %x int %d or %x unsigned int %u or %x @@ -27,9 +28,9 @@ Integer types unsigned long long %llu or %llx size_t %zu or %zx ssize_t %zd or %zx - s8 %d or %x + s8 %d or %hhx u8 %u or %x - s16 %d or %x + s16 %d or %hx u16 %u or %x s32 %d or %x u32 %u or %x diff --git a/Documentation/core-api/workqueue.rst b/Documentation/core-api/workqueue.rst index a4c9b9d1905f473dc4d01aa2a5db37c32ad4e4d2..5d7b01aed1fe630e7a1b508ae40f3e02d8c687a4 100644 --- a/Documentation/core-api/workqueue.rst +++ b/Documentation/core-api/workqueue.rst @@ -1,6 +1,6 @@ -==================================== -Concurrency Managed Workqueue (cmwq) -==================================== +========= +Workqueue +========= :Date: September, 2010 :Author: Tejun Heo @@ -25,8 +25,8 @@ there is no work item left on the workqueue the worker becomes idle. When a new work item gets queued, the worker begins executing again. -Why cmwq? -========= +Why Concurrency Managed Workqueue? +================================== In the original wq implementation, a multi threaded (MT) wq had one worker thread per CPU and a single threaded (ST) wq had one worker @@ -220,17 +220,16 @@ resources, scheduled and executed. ``max_active`` -------------- -``@max_active`` determines the maximum number of execution contexts -per CPU which can be assigned to the work items of a wq. For example, -with ``@max_active`` of 16, at most 16 work items of the wq can be -executing at the same time per CPU. +``@max_active`` determines the maximum number of execution contexts per +CPU which can be assigned to the work items of a wq. For example, with +``@max_active`` of 16, at most 16 work items of the wq can be executing +at the same time per CPU. This is always a per-CPU attribute, even for +unbound workqueues. -Currently, for a bound wq, the maximum limit for ``@max_active`` is -512 and the default value used when 0 is specified is 256. For an -unbound wq, the limit is higher of 512 and 4 * -``num_possible_cpus()``. These values are chosen sufficiently high -such that they are not the limiting factor while providing protection -in runaway cases. +The maximum limit for ``@max_active`` is 512 and the default value used +when 0 is specified is 256. These values are chosen sufficiently high +such that they are not the limiting factor while providing protection in +runaway cases. The number of active work items of a wq is usually regulated by the users of the wq, more specifically, by how many work items the users @@ -348,27 +347,346 @@ Guidelines level of locality in wq operations and work item execution. +Affinity Scopes +=============== + +An unbound workqueue groups CPUs according to its affinity scope to improve +cache locality. For example, if a workqueue is using the default affinity +scope of "cache", it will group CPUs according to last level cache +boundaries. A work item queued on the workqueue will be assigned to a worker +on one of the CPUs which share the last level cache with the issuing CPU. +Once started, the worker may or may not be allowed to move outside the scope +depending on the ``affinity_strict`` setting of the scope. + +Workqueue currently supports the following affinity scopes. + +``default`` + Use the scope in module parameter ``workqueue.default_affinity_scope`` + which is always set to one of the scopes below. + +``cpu`` + CPUs are not grouped. A work item issued on one CPU is processed by a + worker on the same CPU. This makes unbound workqueues behave as per-cpu + workqueues without concurrency management. + +``smt`` + CPUs are grouped according to SMT boundaries. This usually means that the + logical threads of each physical CPU core are grouped together. + +``cache`` + CPUs are grouped according to cache boundaries. Which specific cache + boundary is used is determined by the arch code. L3 is used in a lot of + cases. This is the default affinity scope. + +``numa`` + CPUs are grouped according to NUMA bounaries. + +``system`` + All CPUs are put in the same group. Workqueue makes no effort to process a + work item on a CPU close to the issuing CPU. + +The default affinity scope can be changed with the module parameter +``workqueue.default_affinity_scope`` and a specific workqueue's affinity +scope can be changed using ``apply_workqueue_attrs()``. + +If ``WQ_SYSFS`` is set, the workqueue will have the following affinity scope +related interface files under its ``/sys/devices/virtual/WQ_NAME/`` +directory. + +``affinity_scope`` + Read to see the current affinity scope. Write to change. + + When default is the current scope, reading this file will also show the + current effective scope in parentheses, for example, ``default (cache)``. + +``affinity_strict`` + 0 by default indicating that affinity scopes are not strict. When a work + item starts execution, workqueue makes a best-effort attempt to ensure + that the worker is inside its affinity scope, which is called + repatriation. Once started, the scheduler is free to move the worker + anywhere in the system as it sees fit. This enables benefiting from scope + locality while still being able to utilize other CPUs if necessary and + available. + + If set to 1, all workers of the scope are guaranteed always to be in the + scope. This may be useful when crossing affinity scopes has other + implications, for example, in terms of power consumption or workload + isolation. Strict NUMA scope can also be used to match the workqueue + behavior of older kernels. + + +Affinity Scopes and Performance +=============================== + +It'd be ideal if an unbound workqueue's behavior is optimal for vast +majority of use cases without further tuning. Unfortunately, in the current +kernel, there exists a pronounced trade-off between locality and utilization +necessitating explicit configurations when workqueues are heavily used. + +Higher locality leads to higher efficiency where more work is performed for +the same number of consumed CPU cycles. However, higher locality may also +cause lower overall system utilization if the work items are not spread +enough across the affinity scopes by the issuers. The following performance +testing with dm-crypt clearly illustrates this trade-off. + +The tests are run on a CPU with 12-cores/24-threads split across four L3 +caches (AMD Ryzen 9 3900x). CPU clock boost is turned off for consistency. +``/dev/dm-0`` is a dm-crypt device created on NVME SSD (Samsung 990 PRO) and +opened with ``cryptsetup`` with default settings. + + +Scenario 1: Enough issuers and work spread across the machine +------------------------------------------------------------- + +The command used: :: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k --ioengine=libaio \ + --iodepth=64 --runtime=60 --numjobs=24 --time_based --group_reporting \ + --name=iops-test-job --verify=sha512 + +There are 24 issuers, each issuing 64 IOs concurrently. ``--verify=sha512`` +makes ``fio`` generate and read back the content each time which makes +execution locality matter between the issuer and ``kcryptd``. The followings +are the read bandwidths and CPU utilizations depending on different affinity +scope settings on ``kcryptd`` measured over five runs. Bandwidths are in +MiBps, and CPU util in percents. + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - Affinity + - Bandwidth (MiBps) + - CPU util (%) + + * - system + - 1159.40 ±1.34 + - 99.31 ±0.02 + + * - cache + - 1166.40 ±0.89 + - 99.34 ±0.01 + + * - cache (strict) + - 1166.00 ±0.71 + - 99.35 ±0.01 + +With enough issuers spread across the system, there is no downside to +"cache", strict or otherwise. All three configurations saturate the whole +machine but the cache-affine ones outperform by 0.6% thanks to improved +locality. + + +Scenario 2: Fewer issuers, enough work for saturation +----------------------------------------------------- + +The command used: :: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \ + --ioengine=libaio --iodepth=64 --runtime=60 --numjobs=8 \ + --time_based --group_reporting --name=iops-test-job --verify=sha512 + +The only difference from the previous scenario is ``--numjobs=8``. There are +a third of the issuers but is still enough total work to saturate the +system. + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - Affinity + - Bandwidth (MiBps) + - CPU util (%) + + * - system + - 1155.40 ±0.89 + - 97.41 ±0.05 + + * - cache + - 1154.40 ±1.14 + - 96.15 ±0.09 + + * - cache (strict) + - 1112.00 ±4.64 + - 93.26 ±0.35 + +This is more than enough work to saturate the system. Both "system" and +"cache" are nearly saturating the machine but not fully. "cache" is using +less CPU but the better efficiency puts it at the same bandwidth as +"system". + +Eight issuers moving around over four L3 cache scope still allow "cache +(strict)" to mostly saturate the machine but the loss of work conservation +is now starting to hurt with 3.7% bandwidth loss. + + +Scenario 3: Even fewer issuers, not enough work to saturate +----------------------------------------------------------- + +The command used: :: + + $ fio --filename=/dev/dm-0 --direct=1 --rw=randrw --bs=32k \ + --ioengine=libaio --iodepth=64 --runtime=60 --numjobs=4 \ + --time_based --group_reporting --name=iops-test-job --verify=sha512 + +Again, the only difference is ``--numjobs=4``. With the number of issuers +reduced to four, there now isn't enough work to saturate the whole system +and the bandwidth becomes dependent on completion latencies. + +.. list-table:: + :widths: 16 20 20 + :header-rows: 1 + + * - Affinity + - Bandwidth (MiBps) + - CPU util (%) + + * - system + - 993.60 ±1.82 + - 75.49 ±0.06 + + * - cache + - 973.40 ±1.52 + - 74.90 ±0.07 + + * - cache (strict) + - 828.20 ±4.49 + - 66.84 ±0.29 + +Now, the tradeoff between locality and utilization is clearer. "cache" shows +2% bandwidth loss compared to "system" and "cache (struct)" whopping 20%. + + +Conclusion and Recommendations +------------------------------ + +In the above experiments, the efficiency advantage of the "cache" affinity +scope over "system" is, while consistent and noticeable, small. However, the +impact is dependent on the distances between the scopes and may be more +pronounced in processors with more complex topologies. + +While the loss of work-conservation in certain scenarios hurts, it is a lot +better than "cache (strict)" and maximizing workqueue utilization is +unlikely to be the common case anyway. As such, "cache" is the default +affinity scope for unbound pools. + +* As there is no one option which is great for most cases, workqueue usages + that may consume a significant amount of CPU are recommended to configure + the workqueues using ``apply_workqueue_attrs()`` and/or enable + ``WQ_SYSFS``. + +* An unbound workqueue with strict "cpu" affinity scope behaves the same as + ``WQ_CPU_INTENSIVE`` per-cpu workqueue. There is no real advanage to the + latter and an unbound workqueue provides a lot more flexibility. + +* Affinity scopes are introduced in Linux v6.5. To emulate the previous + behavior, use strict "numa" affinity scope. + +* The loss of work-conservation in non-strict affinity scopes is likely + originating from the scheduler. There is no theoretical reason why the + kernel wouldn't be able to do the right thing and maintain + work-conservation in most cases. As such, it is possible that future + scheduler improvements may make most of these tunables unnecessary. + + +Examining Configuration +======================= + +Use tools/workqueue/wq_dump.py to examine unbound CPU affinity +configuration, worker pools and how workqueues map to the pools: :: + + $ tools/workqueue/wq_dump.py + Affinity Scopes + =============== + wq_unbound_cpumask=0000000f + + CPU + nr_pods 4 + pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008 + pod_node [0]=0 [1]=0 [2]=1 [3]=1 + cpu_pod [0]=0 [1]=1 [2]=2 [3]=3 + + SMT + nr_pods 4 + pod_cpus [0]=00000001 [1]=00000002 [2]=00000004 [3]=00000008 + pod_node [0]=0 [1]=0 [2]=1 [3]=1 + cpu_pod [0]=0 [1]=1 [2]=2 [3]=3 + + CACHE (default) + nr_pods 2 + pod_cpus [0]=00000003 [1]=0000000c + pod_node [0]=0 [1]=1 + cpu_pod [0]=0 [1]=0 [2]=1 [3]=1 + + NUMA + nr_pods 2 + pod_cpus [0]=00000003 [1]=0000000c + pod_node [0]=0 [1]=1 + cpu_pod [0]=0 [1]=0 [2]=1 [3]=1 + + SYSTEM + nr_pods 1 + pod_cpus [0]=0000000f + pod_node [0]=-1 + cpu_pod [0]=0 [1]=0 [2]=0 [3]=0 + + Worker Pools + ============ + pool[00] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 0 + pool[01] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 0 + pool[02] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 1 + pool[03] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 1 + pool[04] ref= 1 nice= 0 idle/workers= 4/ 4 cpu= 2 + pool[05] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 2 + pool[06] ref= 1 nice= 0 idle/workers= 3/ 3 cpu= 3 + pool[07] ref= 1 nice=-20 idle/workers= 2/ 2 cpu= 3 + pool[08] ref=42 nice= 0 idle/workers= 6/ 6 cpus=0000000f + pool[09] ref=28 nice= 0 idle/workers= 3/ 3 cpus=00000003 + pool[10] ref=28 nice= 0 idle/workers= 17/ 17 cpus=0000000c + pool[11] ref= 1 nice=-20 idle/workers= 1/ 1 cpus=0000000f + pool[12] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=00000003 + pool[13] ref= 2 nice=-20 idle/workers= 1/ 1 cpus=0000000c + + Workqueue CPU -> pool + ===================== + [ workqueue \ CPU 0 1 2 3 dfl] + events percpu 0 2 4 6 + events_highpri percpu 1 3 5 7 + events_long percpu 0 2 4 6 + events_unbound unbound 9 9 10 10 8 + events_freezable percpu 0 2 4 6 + events_power_efficient percpu 0 2 4 6 + events_freezable_power_ percpu 0 2 4 6 + rcu_gp percpu 0 2 4 6 + rcu_par_gp percpu 0 2 4 6 + slub_flushwq percpu 0 2 4 6 + netns ordered 8 8 8 8 8 + ... + +See the command's help message for more info. + + Monitoring ========== Use tools/workqueue/wq_monitor.py to monitor workqueue operations: :: $ tools/workqueue/wq_monitor.py events - total infl CPUtime CPUhog CMwake mayday rescued + total infl CPUtime CPUhog CMW/RPR mayday rescued events 18545 0 6.1 0 5 - - events_highpri 8 0 0.0 0 0 - - events_long 3 0 0.0 0 0 - - - events_unbound 38306 0 0.1 - - - - + events_unbound 38306 0 0.1 - 7 - - events_freezable 0 0 0.0 0 0 - - events_power_efficient 29598 0 0.2 0 0 - - events_freezable_power_ 10 0 0.0 0 0 - - sock_diag_events 0 0 0.0 0 0 - - - total infl CPUtime CPUhog CMwake mayday rescued + total infl CPUtime CPUhog CMW/RPR mayday rescued events 18548 0 6.1 0 5 - - events_highpri 8 0 0.0 0 0 - - events_long 3 0 0.0 0 0 - - - events_unbound 38322 0 0.1 - - - - + events_unbound 38322 0 0.1 - 7 - - events_freezable 0 0 0.0 0 0 - - events_power_efficient 29603 0 0.2 0 0 - - events_freezable_power_ 10 0 0.0 0 0 - - diff --git a/Documentation/dev-tools/kasan.rst b/Documentation/dev-tools/kasan.rst index f4acf9c2e90f6e3c3416546227db1a734f3b34b2..382818a7197aa2bea74345755967783fd5e90b89 100644 --- a/Documentation/dev-tools/kasan.rst +++ b/Documentation/dev-tools/kasan.rst @@ -41,8 +41,8 @@ Support Architectures ~~~~~~~~~~~~~ -Generic KASAN is supported on x86_64, arm, arm64, powerpc, riscv, s390, and -xtensa, and the tag-based KASAN modes are supported only on arm64. +Generic KASAN is supported on x86_64, arm, arm64, powerpc, riscv, s390, xtensa, +and loongarch, and the tag-based KASAN modes are supported only on arm64. Compilers ~~~~~~~~~ diff --git a/Documentation/devicetree/bindings/ata/pata-common.yaml b/Documentation/devicetree/bindings/ata/pata-common.yaml index 337ddf1113c4d60c8f6b7b394cacf09d4667cd54..4e867dd4d402b2689da486e97eb9729268625ed4 100644 --- a/Documentation/devicetree/bindings/ata/pata-common.yaml +++ b/Documentation/devicetree/bindings/ata/pata-common.yaml @@ -38,6 +38,7 @@ patternProperties: ID number 0 and the slave drive will have ID number 1. The PATA port nodes will be named "ide-port". type: object + additionalProperties: false properties: reg: diff --git a/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml b/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml index b568d0ce438d2117e776c91061bd250647dcd6c3..7e1ffc551046535b8f4b4e11f2a1c4c121e35027 100644 --- a/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml +++ b/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml @@ -73,9 +73,6 @@ patternProperties: "^.*@[0-9a-f]+$": description: Devices attached to the bus type: object - properties: - reg: - maxItems: 1 required: - reg diff --git a/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml b/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d2cbe49f4e15fdc4791c70ae3eaf2589c43bcce7 --- /dev/null +++ b/Documentation/devicetree/bindings/cache/andestech,ax45mp-cache.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright (C) 2023 Renesas Electronics Corp. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cache/andestech,ax45mp-cache.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Andestech AX45MP L2 Cache Controller + +maintainers: + - Lad Prabhakar + +description: + A level-2 cache (L2C) is used to improve the system performance by providing + a large amount of cache line entries and reasonable access delays. The L2C + is shared between cores, and a non-inclusive non-exclusive policy is used. + +select: + properties: + compatible: + contains: + enum: + - andestech,ax45mp-cache + + required: + - compatible + +properties: + compatible: + items: + - const: andestech,ax45mp-cache + - const: cache + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + cache-line-size: + const: 64 + + cache-level: + const: 2 + + cache-sets: + const: 1024 + + cache-size: + enum: [131072, 262144, 524288, 1048576, 2097152] + + cache-unified: true + + next-level-cache: true + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - cache-line-size + - cache-level + - cache-sets + - cache-size + - cache-unified + +examples: + - | + #include + + cache-controller@13400000 { + compatible = "andestech,ax45mp-cache", "cache"; + reg = <0x13400000 0x100000>; + interrupts = <508 IRQ_TYPE_LEVEL_HIGH>; + cache-line-size = <64>; + cache-level = <2>; + cache-sets = <1024>; + cache-size = <262144>; + cache-unified; + }; diff --git a/Documentation/devicetree/bindings/clock/renesas,5p35023.yaml b/Documentation/devicetree/bindings/clock/renesas,5p35023.yaml index 839648e753d4fed700498e2d861d77757d97dd8d..42b6f80613f3cde11c298a2633dde856493fc866 100644 --- a/Documentation/devicetree/bindings/clock/renesas,5p35023.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,5p35023.yaml @@ -37,6 +37,9 @@ properties: maxItems: 1 '#clock-cells': + description: + The index in the assigned-clocks is mapped to the output clock as below + 0 - REF, 1 - SE1, 2 - SE2, 3 - SE3, 4 - DIFF1, 5 - DIFF2. const: 1 clocks: @@ -68,7 +71,7 @@ examples: reg = <0x68>; #clock-cells = <1>; - clocks = <&x1_x2>; + clocks = <&x1>; renesas,settings = [ 80 00 11 19 4c 02 23 7f 83 19 08 a9 5f 25 24 bf @@ -79,8 +82,8 @@ examples: assigned-clocks = <&versa3 0>, <&versa3 1>, <&versa3 2>, <&versa3 3>, <&versa3 4>, <&versa3 5>; - assigned-clock-rates = <12288000>, <25000000>, - <12000000>, <11289600>, - <11289600>, <24000000>; + assigned-clock-rates = <24000000>, <11289600>, + <11289600>, <12000000>, + <25000000>, <12288000>; }; }; diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx6-hdmi.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx6-hdmi.yaml index af7fe9c4d1963b6cd3789236c4cfe1c2ef4b09ca..7979cf07f119978e903c6ea21674f92d85dfd502 100644 --- a/Documentation/devicetree/bindings/display/imx/fsl,imx6-hdmi.yaml +++ b/Documentation/devicetree/bindings/display/imx/fsl,imx6-hdmi.yaml @@ -87,7 +87,7 @@ required: - interrupts - ports -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/dma/atmel-xdma.txt b/Documentation/devicetree/bindings/dma/atmel-xdma.txt index 510b7f25ba24236a7b792c04e3f2ecc66ddab231..76d649b3a25dc0ae1a42e352a4c506ead6d77a74 100644 --- a/Documentation/devicetree/bindings/dma/atmel-xdma.txt +++ b/Documentation/devicetree/bindings/dma/atmel-xdma.txt @@ -3,7 +3,8 @@ * XDMA Controller Required properties: - compatible: Should be "atmel,sama5d4-dma", "microchip,sam9x60-dma" or - "microchip,sama7g5-dma". + "microchip,sama7g5-dma" or + "microchip,sam9x7-dma", "atmel,sama5d4-dma". - reg: Should contain DMA registers location and length. - interrupts: Should contain DMA interrupt. - #dma-cells: Must be <1>, used to represent the number of integer cells in diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt deleted file mode 100644 index b6a8cc0978cd5ff91965b7b3cf93813b80972a3e..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.txt +++ /dev/null @@ -1,83 +0,0 @@ -* BCM2835 DMA controller - -The BCM2835 DMA controller has 16 channels in total. -Only the lower 13 channels have an associated IRQ. -Some arbitrary channels are used by the firmware -(1,3,6,7 in the current firmware version). -The channels 0,2 and 3 have special functionality -and should not be used by the driver. - -Required properties: -- compatible: Should be "brcm,bcm2835-dma". -- reg: Should contain DMA registers location and length. -- interrupts: Should contain the DMA interrupts associated - to the DMA channels in ascending order. -- interrupt-names: Should contain the names of the interrupt - in the form "dmaXX". - Use "dma-shared-all" for the common interrupt line - that is shared by all dma channels. -- #dma-cells: Must be <1>, the cell in the dmas property of the - client device represents the DREQ number. -- brcm,dma-channel-mask: Bit mask representing the channels - not used by the firmware in ascending order, - i.e. first channel corresponds to LSB. - -Example: - -dma: dma@7e007000 { - compatible = "brcm,bcm2835-dma"; - reg = <0x7e007000 0xf00>; - interrupts = <1 16>, - <1 17>, - <1 18>, - <1 19>, - <1 20>, - <1 21>, - <1 22>, - <1 23>, - <1 24>, - <1 25>, - <1 26>, - /* dma channel 11-14 share one irq */ - <1 27>, - <1 27>, - <1 27>, - <1 27>, - /* unused shared irq for all channels */ - <1 28>; - interrupt-names = "dma0", - "dma1", - "dma2", - "dma3", - "dma4", - "dma5", - "dma6", - "dma7", - "dma8", - "dma9", - "dma10", - "dma11", - "dma12", - "dma13", - "dma14", - "dma-shared-all"; - - #dma-cells = <1>; - brcm,dma-channel-mask = <0x7f35>; -}; - - -DMA clients connected to the BCM2835 DMA controller must use the format -described in the dma.txt file, using a two-cell specifier for each channel. - -Example: - -bcm2835_i2s: i2s@7e203000 { - compatible = "brcm,bcm2835-i2s"; - reg = < 0x7e203000 0x24>; - clocks = <&clocks BCM2835_CLOCK_PCM>; - - dmas = <&dma 2>, - <&dma 3>; - dma-names = "tx", "rx"; -}; diff --git a/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c9b9a5490826cd224a044c7440b0fd51f9baae0b --- /dev/null +++ b/Documentation/devicetree/bindings/dma/brcm,bcm2835-dma.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/brcm,bcm2835-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: BCM2835 DMA controller + +maintainers: + - Nicolas Saenz Julienne + +description: + The BCM2835 DMA controller has 16 channels in total. Only the lower + 13 channels have an associated IRQ. Some arbitrary channels are used by the + VideoCore firmware (1,3,6,7 in the current firmware version). The channels + 0, 2 and 3 have special functionality and should not be used by the driver. + +allOf: + - $ref: dma-controller.yaml# + +properties: + compatible: + const: brcm,bcm2835-dma + + reg: + maxItems: 1 + + interrupts: + description: + Should contain the DMA interrupts associated to the DMA channels in + ascending order. + minItems: 1 + maxItems: 16 + + interrupt-names: + minItems: 1 + maxItems: 16 + + '#dma-cells': + description: The single cell represents the DREQ number. + const: 1 + + brcm,dma-channel-mask: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Bitmask of available DMA channels in ascending order that are + not reserved by firmware and are available to the + kernel. i.e. first channel corresponds to LSB. + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - "#dma-cells" + - brcm,dma-channel-mask + +examples: + - | + dma-controller@7e007000 { + compatible = "brcm,bcm2835-dma"; + reg = <0x7e007000 0xf00>; + interrupts = <1 16>, + <1 17>, + <1 18>, + <1 19>, + <1 20>, + <1 21>, + <1 22>, + <1 23>, + <1 24>, + <1 25>, + <1 26>, + /* dma channel 11-14 share one irq */ + <1 27>, + <1 27>, + <1 27>, + <1 27>, + /* unused shared irq for all channels */ + <1 28>; + interrupt-names = "dma0", + "dma1", + "dma2", + "dma3", + "dma4", + "dma5", + "dma6", + "dma7", + "dma8", + "dma9", + "dma10", + "dma11", + "dma12", + "dma13", + "dma14", + "dma-shared-all"; + #dma-cells = <1>; + brcm,dma-channel-mask = <0x7f35>; + }; + +... diff --git a/Documentation/devicetree/bindings/dma/fsl,edma.yaml b/Documentation/devicetree/bindings/dma/fsl,edma.yaml index 5fd8fc6042616b05fa87880bb39876f463ae02fb..437db0c62339facea75e27a057af158c8a01f041 100644 --- a/Documentation/devicetree/bindings/dma/fsl,edma.yaml +++ b/Documentation/devicetree/bindings/dma/fsl,edma.yaml @@ -21,32 +21,41 @@ properties: - enum: - fsl,vf610-edma - fsl,imx7ulp-edma + - fsl,imx8qm-adma + - fsl,imx8qm-edma + - fsl,imx93-edma3 + - fsl,imx93-edma4 - items: - const: fsl,ls1028a-edma - const: fsl,vf610-edma reg: - minItems: 2 + minItems: 1 maxItems: 3 interrupts: - minItems: 2 - maxItems: 17 + minItems: 1 + maxItems: 64 interrupt-names: - minItems: 2 - maxItems: 17 + minItems: 1 + maxItems: 64 "#dma-cells": - const: 2 + enum: + - 2 + - 3 dma-channels: - const: 32 + minItems: 1 + maxItems: 64 clocks: + minItems: 1 maxItems: 2 clock-names: + minItems: 1 maxItems: 2 big-endian: @@ -65,6 +74,29 @@ required: allOf: - $ref: dma-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - fsl,imx8qm-adma + - fsl,imx8qm-edma + - fsl,imx93-edma3 + - fsl,imx93-edma4 + then: + properties: + "#dma-cells": + const: 3 + # It is not necessary to write the interrupt name for each channel. + # instead, you can simply maintain the sequential IRQ numbers as + # defined for the DMA channels. + interrupt-names: false + clock-names: + items: + - const: dma + clocks: + maxItems: 1 + - if: properties: compatible: @@ -72,18 +104,26 @@ allOf: const: fsl,vf610-edma then: properties: + clocks: + minItems: 2 clock-names: items: - const: dmamux0 - const: dmamux1 interrupts: + minItems: 2 maxItems: 2 interrupt-names: items: - const: edma-tx - const: edma-err reg: + minItems: 2 maxItems: 3 + "#dma-cells": + const: 2 + dma-channels: + const: 32 - if: properties: @@ -92,14 +132,22 @@ allOf: const: fsl,imx7ulp-edma then: properties: + clock: + minItems: 2 clock-names: items: - const: dma - const: dmamux0 interrupts: + minItems: 2 maxItems: 17 reg: + minItems: 2 maxItems: 2 + "#dma-cells": + const: 2 + dma-channels: + const: 32 unevaluatedProperties: false @@ -153,3 +201,47 @@ examples: clock-names = "dma", "dmamux0"; clocks = <&pcc2 IMX7ULP_CLK_DMA1>, <&pcc2 IMX7ULP_CLK_DMA_MUX1>; }; + + - | + #include + #include + + dma-controller@44000000 { + compatible = "fsl,imx93-edma3"; + reg = <0x44000000 0x200000>; + #dma-cells = <3>; + dma-channels = <31>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + clocks = <&clk IMX93_CLK_EDMA1_GATE>; + clock-names = "dma"; + }; diff --git a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml index 4f5510b6fa021112a81fa07f5c442f992918eb1d..3ad0d9b1fbc5e4f83dd316d1ad79773c288748ba 100644 --- a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml +++ b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml @@ -15,13 +15,19 @@ allOf: properties: compatible: - enum: - # APQ8064, IPQ8064 and MSM8960 - - qcom,bam-v1.3.0 - # MSM8974, APQ8074 and APQ8084 - - qcom,bam-v1.4.0 - # MSM8916 and SDM845 - - qcom,bam-v1.7.0 + oneOf: + - enum: + # APQ8064, IPQ8064 and MSM8960 + - qcom,bam-v1.3.0 + # MSM8974, APQ8074 and APQ8084 + - qcom,bam-v1.4.0 + # MSM8916, SDM630 + - qcom,bam-v1.7.0 + - items: + - enum: + # SDM845, SM6115, SM8150, SM8250 and QCM2290 + - qcom,bam-v1.7.4 + - const: qcom,bam-v1.7.0 clocks: maxItems: 1 @@ -38,7 +44,7 @@ properties: iommus: minItems: 1 - maxItems: 4 + maxItems: 6 num-channels: $ref: /schemas/types.yaml#/definitions/uint32 @@ -81,6 +87,15 @@ required: - qcom,ee - reg +anyOf: + - required: + - qcom,powered-remotely + - required: + - qcom,controlled-remotely + - required: + - clocks + - clock-names + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt index d1700a5c36bf835abf6753459e57870ed8c9bdc7..590d1948f202c61d20bca4c70d8bb9aa5b6ac0c4 100644 --- a/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_dma.txt @@ -49,6 +49,12 @@ Optional properties for AXI DMA and MCDMA: register as configured in h/w. Takes values {8...26}. If the property is missing or invalid then the default value 23 is used. This is the maximum value that is supported by all IP versions. + +Optional properties for AXI DMA: +- xlnx,axistream-connected: Tells whether DMA is connected to AXI stream IP. +- xlnx,irq-delay: Tells the interrupt delay timeout value. Valid range is from + 0-255. Setting this value to zero disables the delay timer interrupt. + 1 timeout interval = 125 * clock period of SG clock. Optional properties for VDMA: - xlnx,flush-fsync: Tells which channel to Flush on Frame sync. It takes following values: diff --git a/Documentation/devicetree/bindings/i2c/cdns,i2c-r1p10.yaml b/Documentation/devicetree/bindings/i2c/cdns,i2c-r1p10.yaml index ff57c5416ebcb732cb1765738bc3bcdb8ef6ccc9..9f1d35ce1fe8acf7864f47e4a0e1dba0e74342ee 100644 --- a/Documentation/devicetree/bindings/i2c/cdns,i2c-r1p10.yaml +++ b/Documentation/devicetree/bindings/i2c/cdns,i2c-r1p10.yaml @@ -48,6 +48,9 @@ properties: default: 16 enum: [2, 4, 8, 16, 32, 64, 128, 256] + power-domains: + maxItems: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt b/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt deleted file mode 100644 index 548a73cde796f9acc5ac4ee71ce3e0d93bbf9d74..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.txt +++ /dev/null @@ -1,82 +0,0 @@ -GPIO-based I2C Arbitration Using a Challenge & Response Mechanism -================================================================= -This uses GPIO lines and a challenge & response mechanism to arbitrate who is -the master of an I2C bus in a multimaster situation. - -In many cases using GPIOs to arbitrate is not needed and a design can use -the standard I2C multi-master rules. Using GPIOs is generally useful in -the case where there is a device on the bus that has errata and/or bugs -that makes standard multimaster mode not feasible. - -Note that this scheme works well enough but has some downsides: -* It is nonstandard (not using standard I2C multimaster) -* Having two masters on a bus in general makes it relatively hard to debug - problems (hard to tell if i2c issues were caused by one master, another, or - some device on the bus). - - -Algorithm: - -All masters on the bus have a 'bus claim' line which is an output that the -others can see. These are all active low with pull-ups enabled. We'll -describe these lines as: - -- OUR_CLAIM: output from us signaling to other hosts that we want the bus -- THEIR_CLAIMS: output from others signaling that they want the bus - -The basic algorithm is to assert your line when you want the bus, then make -sure that the other side doesn't want it also. A detailed explanation is best -done with an example. - -Let's say we want to claim the bus. We: -1. Assert OUR_CLAIM. -2. Waits a little bit for the other sides to notice (slew time, say 10 - microseconds). -3. Check THEIR_CLAIMS. If none are asserted then the we have the bus and we are - done. -4. Otherwise, wait for a few milliseconds and see if THEIR_CLAIMS are released. -5. If not, back off, release the claim and wait for a few more milliseconds. -6. Go back to 1 (until retry time has expired). - - -Required properties: -- compatible: i2c-arb-gpio-challenge -- our-claim-gpio: The GPIO that we use to claim the bus. -- their-claim-gpios: The GPIOs that the other sides use to claim the bus. - Note that some implementations may only support a single other master. -- I2C arbitration bus node. See i2c-arb.txt in this directory. - -Optional properties: -- slew-delay-us: microseconds to wait for a GPIO to go high. Default is 10 us. -- wait-retry-us: we'll attempt another claim after this many microseconds. - Default is 3000 us. -- wait-free-us: we'll give up after this many microseconds. Default is 50000 us. - - -Example: - i2c@12ca0000 { - compatible = "acme,some-i2c-device"; - #address-cells = <1>; - #size-cells = <0>; - }; - - i2c-arbitrator { - compatible = "i2c-arb-gpio-challenge"; - - i2c-parent = <&{/i2c@12CA0000}>; - - our-claim-gpio = <&gpf0 3 1>; - their-claim-gpios = <&gpe0 4 1>; - slew-delay-us = <10>; - wait-retry-us = <3000>; - wait-free-us = <50000>; - - i2c-arb { - #address-cells = <1>; - #size-cells = <0>; - - i2c@52 { - // Normal I2C device - }; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.yaml b/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b618b5a3433abc80ac7ac9d78f3fdfea3cda97d0 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-arb-gpio-challenge.yaml @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-arb-gpio-challenge.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO-based I2C Arbitration Using a Challenge & Response Mechanism + +maintainers: + - Doug Anderson + - Peter Rosin + +description: | + This uses GPIO lines and a challenge & response mechanism to arbitrate who is + the master of an I2C bus in a multimaster situation. + + In many cases using GPIOs to arbitrate is not needed and a design can use the + standard I2C multi-master rules. Using GPIOs is generally useful in the case + where there is a device on the bus that has errata and/or bugs that makes + standard multimaster mode not feasible. + + Note that this scheme works well enough but has some downsides: + * It is nonstandard (not using standard I2C multimaster) + * Having two masters on a bus in general makes it relatively hard to debug + problems (hard to tell if i2c issues were caused by one master, another, + or some device on the bus). + + Algorithm: + All masters on the bus have a 'bus claim' line which is an output that the + others can see. These are all active low with pull-ups enabled. We'll + describe these lines as: + * OUR_CLAIM: output from us signaling to other hosts that we want the bus + * THEIR_CLAIMS: output from others signaling that they want the bus + + The basic algorithm is to assert your line when you want the bus, then make + sure that the other side doesn't want it also. A detailed explanation is + best done with an example. + + Let's say we want to claim the bus. We: + 1. Assert OUR_CLAIM. + 2. Waits a little bit for the other sides to notice (slew time, say 10 + microseconds). + 3. Check THEIR_CLAIMS. If none are asserted then the we have the bus and we + are done. + 4. Otherwise, wait for a few milliseconds and see if THEIR_CLAIMS are released. + 5. If not, back off, release the claim and wait for a few more milliseconds. + 6. Go back to 1 (until retry time has expired). + +properties: + compatible: + const: i2c-arb-gpio-challenge + + i2c-parent: + $ref: /schemas/types.yaml#/definitions/phandle + description: + The I2C bus that this multiplexer's master-side port is connected to. + + our-claim-gpios: + maxItems: 1 + description: + The GPIO that we use to claim the bus. + + slew-delay-us: + default: 10 + description: + Time to wait for a GPIO to go high. + + their-claim-gpios: + minItems: 1 + maxItems: 8 + description: + The GPIOs that the other sides use to claim the bus. Note that some + implementations may only support a single other master. + + wait-free-us: + default: 50000 + description: + We'll give up after this many microseconds. + + wait-retry-us: + default: 3000 + description: + We'll attempt another claim after this many microseconds. + + i2c-arb: + type: object + $ref: /schemas/i2c/i2c-controller.yaml + unevaluatedProperties: false + description: + I2C arbitration bus node. + +required: + - compatible + - i2c-arb + - our-claim-gpios + - their-claim-gpios + +additionalProperties: false + +examples: + - | + #include + #include + + i2c-arbitrator { + compatible = "i2c-arb-gpio-challenge"; + i2c-parent = <&i2c_4>; + + our-claim-gpios = <&gpf0 3 GPIO_ACTIVE_LOW>; + their-claim-gpios = <&gpe0 4 GPIO_ACTIVE_LOW>; + slew-delay-us = <10>; + wait-retry-us = <3000>; + wait-free-us = <50000>; + + i2c-arb { + #address-cells = <1>; + #size-cells = <0>; + + sbs-battery@b { + compatible = "sbs,sbs-battery"; + reg = <0xb>; + sbs,poll-retry-count = <1>; + }; + + embedded-controller@1e { + compatible = "google,cros-ec-i2c"; + reg = <0x1e>; + interrupts = <6 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&gpx1>; + pinctrl-names = "default"; + pinctrl-0 = <&ec_irq>; + wakeup-source; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-arb.txt b/Documentation/devicetree/bindings/i2c/i2c-arb.txt deleted file mode 100644 index 59abf9277bdc12cc773ad319c686bed03a41ac34..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-arb.txt +++ /dev/null @@ -1,35 +0,0 @@ -Common i2c arbitration bus properties. - -- i2c-arb child node - -Required properties for the i2c-arb child node: -- #address-cells = <1>; -- #size-cells = <0>; - -Optional properties for i2c-arb child node: -- Child nodes conforming to i2c bus binding - - -Example : - - /* - An NXP pca9541 I2C bus master selector at address 0x74 - with a NXP pca8574 GPIO expander attached. - */ - - arb@74 { - compatible = "nxp,pca9541"; - reg = <0x74>; - - i2c-arb { - #address-cells = <1>; - #size-cells = <0>; - - gpio@38 { - compatible = "nxp,pca8574"; - reg = <0x38>; - #gpio-cells = <2>; - gpio-controller; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml index 9f1726d0356b07990fc1018737c877b6f279545a..2d7bb998b0e9d2651daf0efae9dd312f2ddf5ae9 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.yaml @@ -4,21 +4,29 @@ $id: http://devicetree.org/schemas/i2c/i2c-mux-pca954x.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: NXP PCA954x I2C bus switch +title: NXP PCA954x I2C and compatible bus switches maintainers: - Laurent Pinchart description: - The binding supports NXP PCA954x and PCA984x I2C mux/switch devices. - -allOf: - - $ref: /schemas/i2c/i2c-mux.yaml# + The NXP PCA954x and compatible devices are I2C bus + multiplexer/switches that share the same functionality + and register layout. + The devices usually have 4 or 8 child buses, which are + attached to the parent bus by using the SMBus "Send Byte" + command. properties: compatible: oneOf: - enum: + - maxim,max7356 + - maxim,max7357 + - maxim,max7358 + - maxim,max7367 + - maxim,max7368 + - maxim,max7369 - nxp,pca9540 - nxp,pca9542 - nxp,pca9543 @@ -59,10 +67,34 @@ properties: description: if present, overrides i2c-mux-idle-disconnect $ref: /schemas/mux/mux-controller.yaml#/properties/idle-state + vdd-supply: + description: A voltage regulator supplying power to the chip. On PCA9846 + the regulator supplies power to VDD2 (core logic) and optionally to VDD1. + required: - compatible - reg +allOf: + - $ref: /schemas/i2c/i2c-mux.yaml# + - if: + not: + properties: + compatible: + contains: + enum: + - maxim,max7367 + - maxim,max7369 + - nxp,pca9542 + - nxp,pca9543 + - nxp,pca9544 + - nxp,pca9545 + then: + properties: + interrupts: false + "#interrupt-cells": false + interrupt-controller: false + unevaluatedProperties: false examples: @@ -74,11 +106,13 @@ examples: #size-cells = <0>; i2c-mux@74 { - compatible = "nxp,pca9548"; + compatible = "nxp,pca9545"; #address-cells = <1>; #size-cells = <0>; reg = <0x74>; + vdd-supply = <&p3v3>; + interrupt-parent = <&ipic>; interrupts = <17 IRQ_TYPE_LEVEL_LOW>; interrupt-controller; diff --git a/Documentation/devicetree/bindings/i2c/i2c-mxs.yaml b/Documentation/devicetree/bindings/i2c/i2c-mxs.yaml index 21ae7bce038ec2b212de6de5c0445aa0edb0120e..171a41407241908959f2ac647a28b4c2f45b4ce1 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-mxs.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-mxs.yaml @@ -9,6 +9,9 @@ title: Freescale MXS Inter IC (I2C) Controller maintainers: - Shawn Guo +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + properties: compatible: enum: @@ -37,7 +40,7 @@ required: - dmas - dma-names -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/i2c/nxp,pca9541.txt b/Documentation/devicetree/bindings/i2c/nxp,pca9541.txt deleted file mode 100644 index 42bfc09c8918dfcf5800fba7d03bc2360257fd72..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/i2c/nxp,pca9541.txt +++ /dev/null @@ -1,29 +0,0 @@ -* NXP PCA9541 I2C bus master selector - -Required Properties: - - - compatible: Must be "nxp,pca9541" - - - reg: The I2C address of the device. - - The following required properties are defined externally: - - - I2C arbitration bus node. See i2c-arb.txt in this directory. - - -Example: - - i2c-arbitrator@74 { - compatible = "nxp,pca9541"; - reg = <0x74>; - - i2c-arb { - #address-cells = <1>; - #size-cells = <0>; - - eeprom@54 { - compatible = "atmel,24c08"; - reg = <0x54>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/i2c/nxp,pca9541.yaml b/Documentation/devicetree/bindings/i2c/nxp,pca9541.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b65c25c1a435ebff08bb191c64f7970634d3a836 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/nxp,pca9541.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/nxp,pca9541.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PCA9541 I2C bus master selector + +maintainers: + - Peter Rosin + +properties: + compatible: + const: nxp,pca9541 + + reg: + maxItems: 1 + + i2c-arb: + type: object + $ref: /schemas/i2c/i2c-controller.yaml + unevaluatedProperties: false + description: + I2C arbitration bus node. + +required: + - compatible + - reg + - i2c-arb + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + i2c-arbitrator@74 { + compatible = "nxp,pca9541"; + reg = <0x74>; + + i2c-arb { + #address-cells = <1>; + #size-cells = <0>; + + eeprom@54 { + compatible = "atmel,24c08"; + reg = <0x54>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml index ec79b7270437f829be2d8985b2b3271ef354108e..042d4dc636eed0300d15f05bc7cbc74bd8341337 100644 --- a/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml +++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml @@ -269,6 +269,7 @@ examples: port { ov7251_ep: endpoint { data-lanes = <0 1>; + link-frequencies = /bits/ 64 <240000000 319200000>; remote-endpoint = <&csiphy3_ep>; }; }; diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml index fdb4212149e79a86adfa22a366ea297126a3895c..ab69f4115de4f232a26c5a8dacea95f1a76e772f 100644 --- a/Documentation/devicetree/bindings/i3c/i3c.yaml +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml @@ -135,9 +135,10 @@ patternProperties: minimum: 0x1 maximum: 0xff description: | - Dynamic address to be assigned to this device. This property is only - valid if the I3C device has a static address (first cell of the reg - property != 0). + Dynamic address to be assigned to this device. In case static address is + present (first cell of the reg property != 0), this address is assigned + through SETDASA. If static address is not present, this address is assigned + through SETNEWDA after assigning a temporary address via ENTDAA. required: - reg @@ -163,12 +164,18 @@ examples: pagesize = <0x8>; }; - /* I3C device with a static I2C address. */ + /* I3C device with a static I2C address and assigned address. */ thermal_sensor: sensor@68,39200144004 { reg = <0x68 0x392 0x144004>; assigned-address = <0xa>; }; + /* I3C device with only assigned address. */ + pressure_sensor: sensor@0,39200124004 { + reg = <0x0 0x392 0x124000>; + assigned-address = <0xc>; + }; + /* * I3C device without a static I2C address but requiring * resources described in the DT. diff --git a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml index 9ddba7f2e7aa619758c1eb0483cc284ebfd4e096..5b1769c19b1735e3bd70db002581e0693498ec07 100644 --- a/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml +++ b/Documentation/devicetree/bindings/input/azoteq,iqs7222.yaml @@ -4,14 +4,14 @@ $id: http://devicetree.org/schemas/input/azoteq,iqs7222.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Azoteq IQS7222A/B/C Capacitive Touch Controller +title: Azoteq IQS7222A/B/C/D Capacitive Touch Controller maintainers: - Jeff LaBundy description: | - The Azoteq IQS7222A, IQS7222B and IQS7222C are multichannel capacitive touch - controllers that feature additional sensing capabilities. + The Azoteq IQS7222A, IQS7222B, IQS7222C and IQS7222D are multichannel + capacitive touch controllers that feature additional sensing capabilities. Link to datasheets: https://www.azoteq.com/ @@ -21,6 +21,7 @@ properties: - azoteq,iqs7222a - azoteq,iqs7222b - azoteq,iqs7222c + - azoteq,iqs7222d reg: maxItems: 1 @@ -173,6 +174,152 @@ properties: maximum: 3000 description: Specifies the report rate (in ms) during ultra-low-power mode. + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-inverted-x: true + touchscreen-inverted-y: true + touchscreen-swapped-x-y: true + + trackpad: + type: object + description: Represents all channels associated with the trackpad. + + properties: + azoteq,channel-select: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 12 + items: + minimum: 0 + maximum: 13 + description: + Specifies the order of the channels that participate in the trackpad. + Specify 255 to omit a given channel for the purpose of mapping a non- + rectangular trackpad. + + azoteq,num-rows: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 12 + description: Specifies the number of rows that comprise the trackpad. + + azoteq,num-cols: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 12 + description: Specifies the number of columns that comprise the trackpad. + + azoteq,top-speed: + $ref: /schemas/types.yaml#/definitions/uint32 + multipleOf: 4 + minimum: 0 + maximum: 1020 + description: + Specifies the speed (in coordinates traveled per conversion) after + which coordinate filtering is no longer applied. + + azoteq,bottom-speed: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: + Specifies the speed (in coordinates traveled per conversion) after + which coordinate filtering is linearly reduced. + + azoteq,use-prox: + type: boolean + description: + Directs the trackpad to respond to the proximity states of the + selected channels instead of their corresponding touch states. + Note the trackpad cannot report granular coordinates during a + state of proximity. + + patternProperties: + "^azoteq,lower-cal-(x|y)$": + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's lower starting points. + + "^azoteq,upper-cal-(x|y)$": + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's upper starting points. + + "^event-(press|tap|(swipe|flick)-(x|y)-(pos|neg))$": + type: object + $ref: input.yaml# + description: + Represents a press or gesture event reported by the trackpad. Specify + 'linux,code' under the press event to report absolute coordinates. + + properties: + linux,code: true + + azoteq,gesture-angle-tighten: + type: boolean + description: + Limits the tangent of the gesture angle to 0.5 (axial gestures + only). If specified in one direction, the effect is applied in + either direction. + + azoteq,gesture-max-ms: + multipleOf: 16 + minimum: 0 + maximum: 4080 + description: + Specifies the length of time (in ms) within which a tap, swipe + or flick gesture must be completed in order to be acknowledged + by the device. The number specified for any one swipe or flick + gesture applies to all other swipe or flick gestures. + + azoteq,gesture-min-ms: + multipleOf: 16 + minimum: 0 + maximum: 4080 + description: + Specifies the length of time (in ms) for which a tap gesture must + be held in order to be acknowledged by the device. + + azoteq,gesture-dist: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: + Specifies the distance (in coordinates) across which a swipe or + flick gesture must travel in order to be acknowledged by the + device. The number specified for any one swipe or flick gesture + applies to all remaining swipe or flick gestures. + + For tap gestures, this property specifies the distance from the + original point of contact across which the contact is permitted + to travel before the gesture is rejected by the device. + + azoteq,gpio-select: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 3 + items: + minimum: 0 + maximum: 2 + description: | + Specifies one or more GPIO mapped to the event as follows: + 0: GPIO0 + 1: GPIO3 + 2: GPIO4 + + Note that although multiple events can be mapped to a single + GPIO, they must all be of the same type (proximity, touch or + trackpad gesture). + + additionalProperties: false + + required: + - azoteq,channel-select + + additionalProperties: false + patternProperties: "^cycle-[0-9]$": type: object @@ -288,6 +435,10 @@ patternProperties: Activates the reference channel in response to proximity events instead of touch events. + azoteq,counts-filt-enable: + type: boolean + description: Applies counts filtering to the channel. + azoteq,ati-band: $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2, 3] @@ -432,12 +583,12 @@ patternProperties: description: | Specifies one or more GPIO mapped to the event as follows: 0: GPIO0 - 1: GPIO3 (IQS7222C only) - 2: GPIO4 (IQS7222C only) + 1: GPIO3 + 2: GPIO4 Note that although multiple events can be mapped to a single GPIO, they must all be of the same type (proximity, touch or - slider gesture). + slider/trackpad gesture). azoteq,thresh: $ref: /schemas/types.yaml#/definitions/uint32 @@ -521,16 +672,16 @@ patternProperties: minimum: 0 maximum: 65535 description: - Specifies the speed of movement after which coordinate filtering is - no longer applied. + Specifies the speed (in coordinates traveled per conversion) after + which coordinate filtering is no longer applied. azoteq,bottom-speed: $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 255 description: - Specifies the speed of movement after which coordinate filtering is - linearly reduced. + Specifies the speed (in coordinates traveled per conversion) after + which coordinate filtering is linearly reduced. azoteq,bottom-beta: $ref: /schemas/types.yaml#/definitions/uint32 @@ -595,10 +746,10 @@ patternProperties: minimum: 0 maximum: 4080 description: - Specifies the distance across which a swipe or flick gesture must - travel in order to be acknowledged by the device. The number spec- - ified for any one swipe or flick gesture applies to all remaining - swipe or flick gestures. + Specifies the distance (in coordinates) across which a swipe or + flick gesture must travel in order to be acknowledged by the + device. The number specified for any one swipe or flick gesture + applies to all remaining swipe or flick gestures. azoteq,gpio-select: $ref: /schemas/types.yaml#/definitions/uint32-array @@ -610,8 +761,8 @@ patternProperties: description: | Specifies one or more GPIO mapped to the event as follows: 0: GPIO0 - 1: GPIO3 (IQS7222C only) - 2: GPIO4 (IQS7222C only) + 1: GPIO3 + 2: GPIO4 Note that although multiple events can be mapped to a single GPIO, they must all be of the same type (proximity, touch or @@ -629,8 +780,8 @@ patternProperties: description: | Represents a GPIO mapped to one or more events as follows: gpio-0: GPIO0 - gpio-1: GPIO3 (IQS7222C only) - gpio-2: GPIO4 (IQS7222C only) + gpio-1: GPIO3 + gpio-2: GPIO4 allOf: - $ref: ../pinctrl/pincfg-node.yaml# @@ -641,11 +792,53 @@ patternProperties: additionalProperties: false allOf: + - $ref: touchscreen/touchscreen.yaml# + - if: properties: compatible: contains: - const: azoteq,iqs7222b + enum: + - azoteq,iqs7222a + - azoteq,iqs7222b + - azoteq,iqs7222c + + then: + properties: + touchscreen-size-x: false + touchscreen-size-y: false + touchscreen-inverted-x: false + touchscreen-inverted-y: false + touchscreen-swapped-x-y: false + + trackpad: false + + patternProperties: + "^channel-([0-9]|1[0-9])$": + properties: + azoteq,counts-filt-enable: false + + - if: + properties: + compatible: + contains: + enum: + - azoteq,iqs7222b + - azoteq,iqs7222c + + then: + patternProperties: + "^channel-([0-9]|1[0-9])$": + properties: + azoteq,ulp-allow: false + + - if: + properties: + compatible: + contains: + enum: + - azoteq,iqs7222b + - azoteq,iqs7222d then: patternProperties: @@ -657,13 +850,22 @@ allOf: properties: azoteq,ref-select: false + "^slider-[0-1]$": false + + - if: + properties: + compatible: + contains: + const: azoteq,iqs7222b + + then: + patternProperties: + "^channel-([0-9]|1[0-9])$": patternProperties: "^event-(prox|touch)$": properties: azoteq,gpio-select: false - "^slider-[0-1]$": false - "^gpio-[0-2]$": false - if: @@ -704,10 +906,6 @@ allOf: else: patternProperties: - "^channel-([0-9]|1[0-9])$": - properties: - azoteq,ulp-allow: false - "^slider-[0-1]$": patternProperties: "^event-(press|tap|(swipe|flick)-(pos|neg))$": diff --git a/Documentation/devicetree/bindings/input/stmpe-keypad.txt b/Documentation/devicetree/bindings/input/stmpe-keypad.txt deleted file mode 100644 index 12bb771d66d446647722ba3e423aabc3734f77bc..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/input/stmpe-keypad.txt +++ /dev/null @@ -1,41 +0,0 @@ -* STMPE Keypad - -Required properties: - - compatible : "st,stmpe-keypad" - - linux,keymap : See ./matrix-keymap.txt - -Optional properties: - - debounce-interval : Debouncing interval time in milliseconds - - st,scan-count : Scanning cycles elapsed before key data is updated - - st,no-autorepeat : If specified device will not autorepeat - - keypad,num-rows : See ./matrix-keymap.txt - - keypad,num-columns : See ./matrix-keymap.txt - -Example: - - stmpe_keypad { - compatible = "st,stmpe-keypad"; - - debounce-interval = <64>; - st,scan-count = <8>; - st,no-autorepeat; - - linux,keymap = <0x205006b - 0x4010074 - 0x3050072 - 0x1030004 - 0x502006a - 0x500000a - 0x5008b - 0x706001c - 0x405000b - 0x6070003 - 0x3040067 - 0x303006c - 0x60400e7 - 0x602009e - 0x4020073 - 0x5050002 - 0x4030069 - 0x3020008>; - }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8cf371b99f19cd55d16cbd3639ae1ca43f0780b0 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/azoteq,iqs7211.yaml @@ -0,0 +1,769 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/azoteq,iqs7211.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller + +maintainers: + - Jeff LaBundy + +description: | + The Azoteq IQS7210A, IQS7211A and IQS7211E trackpad and touchscreen control- + lers employ projected-capacitance sensing and can track two contacts. + + Link to datasheets: https://www.azoteq.com/ + +properties: + compatible: + enum: + - azoteq,iqs7210a + - azoteq,iqs7211a + - azoteq,iqs7211e + + reg: + maxItems: 1 + + irq-gpios: + maxItems: 1 + description: + Specifies the GPIO connected to the device's active-low RDY output. The + pin doubles as the IQS7211E's active-low MCLR input, in which case this + GPIO must be configured as open-drain. + + reset-gpios: + maxItems: 1 + description: + Specifies the GPIO connected to the device's active-low MCLR input. The + device is temporarily held in hardware reset prior to initialization if + this property is present. + + azoteq,forced-comms: + type: boolean + description: + Enables forced communication; to be used with host adapters that cannot + tolerate clock stretching. + + azoteq,forced-comms-default: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: + Indicates if the device's OTP memory enables (1) or disables (0) forced + communication by default. Specifying this property can expedite startup + time if the default value is known. + + If this property is not specified, communication is not initiated until + the device asserts its RDY pin shortly after exiting hardware reset. At + that point, forced communication is either enabled or disabled based on + the presence or absence of the 'azoteq,forced-comms' property. + + azoteq,rate-active-ms: + minimum: 0 + maximum: 65535 + description: Specifies the report rate (in ms) during active mode. + + azoteq,rate-touch-ms: + minimum: 0 + maximum: 65535 + description: Specifies the report rate (in ms) during idle-touch mode. + + azoteq,rate-idle-ms: + minimum: 0 + maximum: 65535 + description: Specifies the report rate (in ms) during idle mode. + + azoteq,rate-lp1-ms: + minimum: 0 + maximum: 65535 + description: Specifies the report rate (in ms) during low-power mode 1. + + azoteq,rate-lp2-ms: + minimum: 0 + maximum: 65535 + description: Specifies the report rate (in ms) during low-power mode 2. + + azoteq,timeout-active-ms: + multipleOf: 1000 + minimum: 0 + maximum: 65535000 + description: + Specifies the length of time (in ms) to wait for an event before moving + from active mode to idle or idle-touch modes. + + azoteq,timeout-touch-ms: + multipleOf: 1000 + minimum: 0 + maximum: 65535000 + description: + Specifies the length of time (in ms) to wait for an event before moving + from idle-touch mode to idle mode. + + azoteq,timeout-idle-ms: + multipleOf: 1000 + minimum: 0 + maximum: 65535000 + description: + Specifies the length of time (in ms) to wait for an event before moving + from idle mode to low-power mode 1. + + azoteq,timeout-lp1-ms: + multipleOf: 1000 + minimum: 0 + maximum: 65535000 + description: + Specifies the length of time (in ms) to wait for an event before moving + from low-power mode 1 to low-power mode 2. + + azoteq,timeout-lp2-ms: + multipleOf: 1000 + minimum: 0 + maximum: 60000 + description: + Specifies the rate (in ms) at which the trackpad reference values + are updated during low-power modes 1 and 2. + + azoteq,timeout-ati-ms: + multipleOf: 1000 + minimum: 0 + maximum: 60000 + description: + Specifies the delay (in ms) before the automatic tuning implementation + (ATI) is retried in the event it fails to complete. + + azoteq,timeout-comms-ms: + minimum: 0 + maximum: 65535 + description: + Specifies the delay (in ms) before a communication window is closed. + + azoteq,timeout-press-ms: + multipleOf: 1000 + minimum: 0 + maximum: 60000 + description: + Specifies the length of time (in ms) to wait before automatically + releasing a press event. Specify zero to allow the press state to + persist indefinitely. + + azoteq,fosc-freq: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Specifies the device's core clock frequency as follows: + 0: 14 MHz + 1: 18 MHz + + azoteq,fosc-trim: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + description: Specifies the device's core clock frequency trim. + + azoteq,num-contacts: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 2 + default: 0 + description: Specifies the number of contacts reported by the device. + + azoteq,contact-split: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the contact (finger) split factor. + + azoteq,trim-x: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the horizontal trim width. + + azoteq,trim-y: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the vertical trim height. + + trackpad: + type: object + description: Represents all channels associated with the trackpad. + + properties: + azoteq,rx-enable: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 8 + items: + minimum: 0 + maximum: 7 + description: + Specifies the order of the CRx pin(s) associated with the trackpad. + + azoteq,tx-enable: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 12 + items: + minimum: 0 + maximum: 11 + description: + Specifies the order of the CTx pin(s) associated with the trackpad. + + azoteq,channel-select: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 36 + items: + minimum: 0 + maximum: 255 + description: | + Specifies the channels mapped to each cycle in the following order: + Cycle 0, slot 0 + Cycle 0, slot 1 + Cycle 1, slot 0 + Cycle 1, slot 1 + ...and so on. Specify 255 to disable a given slot. + + azoteq,ati-frac-div-fine: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the trackpad's ATI fine fractional divider. + + azoteq,ati-frac-mult-coarse: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + description: Specifies the trackpad's ATI coarse fractional multiplier. + + azoteq,ati-frac-div-coarse: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the trackpad's ATI coarse fractional divider. + + azoteq,ati-comp-div: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the trackpad's ATI compensation divider. + + azoteq,ati-target: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: Specifies the trackpad's ATI target. + + azoteq,touch-enter: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's touch entrance factor. + + azoteq,touch-exit: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's touch exit factor. + + azoteq,thresh: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's stationary touch threshold. + + azoteq,conv-period: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's conversion period. + + azoteq,conv-frac: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the trackpad's conversion frequency fraction. + + patternProperties: + "^event-(tap(-double|-triple)?|hold|palm|swipe-(x|y)-(pos|neg)(-hold)?)$": + type: object + $ref: ../input.yaml# + description: + Represents a gesture event reported by the trackpad. In the case of + axial gestures, the duration or distance specified in one direction + applies to both directions along the same axis. + + properties: + linux,code: true + + azoteq,gesture-max-ms: + minimum: 0 + maximum: 65535 + description: Specifies the maximum duration of tap/swipe gestures. + + azoteq,gesture-mid-ms: + minimum: 0 + maximum: 65535 + description: + Specifies the maximum duration between subsequent tap gestures + (IQS7211E only). + + azoteq,gesture-min-ms: + minimum: 0 + maximum: 65535 + description: Specifies the minimum duration of hold gestures. + + azoteq,gesture-dist: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: + Specifies the minimum (swipe) or maximum (tap and hold) distance + a finger may travel to be considered a gesture. + + azoteq,gesture-dist-rep: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: + Specifies the minimum distance a finger must travel to elicit a + repeated swipe gesture (IQS7211E only). + + azoteq,gesture-angle: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 75 + description: + Specifies the maximum angle (in degrees) a finger may travel to + be considered a swipe gesture. + + azoteq,thresh: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 42 + description: Specifies the palm gesture threshold (IQS7211E only). + + additionalProperties: false + + dependencies: + azoteq,rx-enable: ["azoteq,tx-enable"] + azoteq,tx-enable: ["azoteq,rx-enable"] + azoteq,channel-select: ["azoteq,rx-enable"] + + additionalProperties: false + + alp: + type: object + $ref: ../input.yaml# + description: Represents the alternate low-power channel (ALP). + + properties: + azoteq,rx-enable: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 8 + items: + minimum: 0 + maximum: 7 + description: + Specifies the CRx pin(s) associated with the ALP in no particular + order. + + azoteq,tx-enable: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 12 + items: + minimum: 0 + maximum: 11 + description: + Specifies the CTx pin(s) associated with the ALP in no particular + order. + + azoteq,ati-frac-div-fine: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the ALP's ATI fine fractional divider. + + azoteq,ati-frac-mult-coarse: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + description: Specifies the ALP's ATI coarse fractional multiplier. + + azoteq,ati-frac-div-coarse: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the ALP's ATI coarse fractional divider. + + azoteq,ati-comp-div: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the ALP's ATI compensation divider. + + azoteq,ati-target: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: Specifies the ALP's ATI target. + + azoteq,ati-base: + $ref: /schemas/types.yaml#/definitions/uint32 + multipleOf: 8 + minimum: 0 + maximum: 255 + description: Specifies the ALP's ATI base. + + azoteq,ati-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Specifies the ALP's ATI mode as follows: + 0: Partial + 1: Full + + azoteq,sense-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Specifies the ALP's sensing mode as follows: + 0: Self capacitive + 1: Mutual capacitive + + azoteq,debounce-enter: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the ALP's debounce entrance factor. + + azoteq,debounce-exit: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the ALP's debounce exit factor. + + azoteq,thresh: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: Specifies the ALP's proximity or touch threshold. + + azoteq,conv-period: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the ALP's conversion period. + + azoteq,conv-frac: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the ALP's conversion frequency fraction. + + linux,code: true + + additionalProperties: false + + button: + type: object + description: Represents the inductive or capacitive button. + + properties: + azoteq,ati-frac-div-fine: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the button's ATI fine fractional divider. + + azoteq,ati-frac-mult-coarse: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + description: Specifies the button's ATI coarse fractional multiplier. + + azoteq,ati-frac-div-coarse: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the button's ATI coarse fractional divider. + + azoteq,ati-comp-div: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + description: Specifies the button's ATI compensation divider. + + azoteq,ati-target: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: Specifies the button's ATI target. + + azoteq,ati-base: + $ref: /schemas/types.yaml#/definitions/uint32 + multipleOf: 8 + minimum: 0 + maximum: 255 + description: Specifies the button's ATI base. + + azoteq,ati-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + description: | + Specifies the button's ATI mode as follows: + 0: Partial + 1: Full + + azoteq,sense-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + description: | + Specifies the button's sensing mode as follows: + 0: Self capacitive + 1: Mutual capacitive + 2: Inductive + + azoteq,touch-enter: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the button's touch entrance factor. + + azoteq,touch-exit: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the button's touch exit factor. + + azoteq,debounce-enter: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the button's debounce entrance factor. + + azoteq,debounce-exit: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the button's debounce exit factor. + + azoteq,thresh: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 65535 + description: Specifies the button's proximity threshold. + + azoteq,conv-period: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the button's conversion period. + + azoteq,conv-frac: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + description: Specifies the button's conversion frequency fraction. + + patternProperties: + "^event-(prox|touch)$": + type: object + $ref: ../input.yaml# + description: + Represents a proximity or touch event reported by the button. + + properties: + linux,code: true + + additionalProperties: false + + additionalProperties: false + + wakeup-source: true + + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-inverted-x: true + touchscreen-inverted-y: true + touchscreen-swapped-x-y: true + +dependencies: + touchscreen-size-x: ["azoteq,num-contacts"] + touchscreen-size-y: ["azoteq,num-contacts"] + touchscreen-inverted-x: ["azoteq,num-contacts"] + touchscreen-inverted-y: ["azoteq,num-contacts"] + touchscreen-swapped-x-y: ["azoteq,num-contacts"] + +required: + - compatible + - reg + - irq-gpios + +additionalProperties: false + +allOf: + - $ref: touchscreen.yaml# + + - if: + properties: + compatible: + contains: + const: azoteq,iqs7210a + + then: + properties: + alp: + properties: + azoteq,rx-enable: + maxItems: 4 + items: + minimum: 4 + + else: + properties: + azoteq,timeout-press-ms: false + + alp: + properties: + azoteq,ati-mode: false + + button: false + + - if: + properties: + compatible: + contains: + const: azoteq,iqs7211e + + then: + properties: + reset-gpios: false + + trackpad: + properties: + azoteq,tx-enable: + maxItems: 13 + items: + maximum: 12 + + alp: + properties: + azoteq,tx-enable: + maxItems: 13 + items: + maximum: 12 + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touch@56 { + compatible = "azoteq,iqs7210a"; + reg = <0x56>; + irq-gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + reset-gpios = <&gpio 17 (GPIO_ACTIVE_LOW | + GPIO_PUSH_PULL)>; + azoteq,num-contacts = <2>; + + trackpad { + azoteq,rx-enable = <6>, <5>, <4>, <3>, <2>; + azoteq,tx-enable = <1>, <7>, <8>, <9>, <10>; + }; + + button { + azoteq,sense-mode = <2>; + azoteq,touch-enter = <40>; + azoteq,touch-exit = <36>; + + event-touch { + linux,code = ; + }; + }; + + alp { + azoteq,sense-mode = <1>; + linux,code = ; + }; + }; + }; + + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touch@56 { + compatible = "azoteq,iqs7211e"; + reg = <0x56>; + irq-gpios = <&gpio 4 (GPIO_ACTIVE_LOW | + GPIO_OPEN_DRAIN)>; + + trackpad { + event-tap { + linux,code = ; + }; + + event-tap-double { + linux,code = ; + }; + + event-tap-triple { + linux,code = ; + }; + + event-hold { + linux,code = ; + }; + + event-palm { + linux,code = ; + }; + + event-swipe-x-pos { + linux,code = ; + }; + + event-swipe-x-pos-hold { + linux,code = ; + }; + + event-swipe-x-neg { + linux,code = ; + }; + + event-swipe-x-neg-hold { + linux,code = ; + }; + + event-swipe-y-pos { + linux,code = ; + }; + + event-swipe-y-pos-hold { + linux,code = ; + }; + + event-swipe-y-neg { + linux,code = ; + }; + + event-swipe-y-neg-hold { + linux,code = ; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml index ef4c841387bdd480c856eb729812b7b0372df27b..f2808cb4d99dfdab68755d3211b3e84b38f3b087 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.yaml @@ -93,6 +93,12 @@ properties: minimum: 1 maximum: 255 + threshold: + description: Allows setting the "click"-threshold in the range from 0 to 255. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + touchscreen-size-x: true touchscreen-size-y: true touchscreen-fuzz-x: true diff --git a/Documentation/devicetree/bindings/input/touchscreen/eeti,exc3000.yaml b/Documentation/devicetree/bindings/input/touchscreen/eeti,exc3000.yaml index 007adbc89c143c6fb124fb04c934383b9a1a475a..9dc25d30a0a8911d853ea6747a034ef6326345e9 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/eeti,exc3000.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/eeti,exc3000.yaml @@ -24,6 +24,8 @@ properties: maxItems: 1 reset-gpios: maxItems: 1 + vdd-supply: + description: Power supply regulator for the chip touchscreen-size-x: true touchscreen-size-y: true touchscreen-inverted-x: true diff --git a/Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml b/Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml index fdd02898e2492543bcf3233c31f5feba558843a5..07f9dd6b1c9c44af761bb0cd4de9f3f201f940e0 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/melfas,mms114.yaml @@ -52,6 +52,11 @@ properties: touchscreen-swapped-x-y: true touchscreen-max-pressure: true + linux,keycodes: + description: Keycodes for the touch keys + minItems: 1 + maxItems: 15 + additionalProperties: false required: diff --git a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt b/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt deleted file mode 100644 index 238b51555c0473879424a3fb0903c814681e3e43..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/stmpe.txt +++ /dev/null @@ -1,108 +0,0 @@ -STMPE Touchscreen ----------------- - -Required properties: - - compatible: "st,stmpe-ts" - -Optional properties: -- st,ave-ctrl : Sample average control - 0 -> 1 sample - 1 -> 2 samples - 2 -> 4 samples - 3 -> 8 samples -- st,touch-det-delay : Touch detect interrupt delay (recommended is 3) - 0 -> 10 us - 1 -> 50 us - 2 -> 100 us - 3 -> 500 us - 4 -> 1 ms - 5 -> 5 ms - 6 -> 10 ms - 7 -> 50 ms -- st,settling : Panel driver settling time (recommended is 2) - 0 -> 10 us - 1 -> 100 us - 2 -> 500 us - 3 -> 1 ms - 4 -> 5 ms - 5 -> 10 ms - 6 -> 50 ms - 7 -> 100 ms -- st,fraction-z : Length of the fractional part in z (recommended is 7) - (fraction-z ([0..7]) = Count of the fractional part) -- st,i-drive : current limit value of the touchscreen drivers - 0 -> 20 mA (typical 35mA max) - 1 -> 50 mA (typical 80 mA max) - -Optional properties common with MFD (deprecated): - - st,sample-time : ADC conversion time in number of clock. - 0 -> 36 clocks - 1 -> 44 clocks - 2 -> 56 clocks - 3 -> 64 clocks - 4 -> 80 clocks (recommended) - 5 -> 96 clocks - 6 -> 124 clocks - - st,mod-12b : ADC Bit mode - 0 -> 10bit ADC - 1 -> 12bit ADC - - st,ref-sel : ADC reference source - 0 -> internal - 1 -> external - - st,adc-freq : ADC Clock speed - 0 -> 1.625 MHz - 1 -> 3.25 MHz - 2 || 3 -> 6.5 MHz - -Node should be child node of stmpe node to which it belongs. - -Note that common ADC settings of stmpe_touchscreen (child) will take precedence -over the settings done in MFD. - -Example: - -stmpe811@41 { - compatible = "st,stmpe811"; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_touch_int>; - #address-cells = <1>; - #size-cells = <0>; - reg = <0x41>; - interrupts = <10 IRQ_TYPE_LEVEL_LOW>; - interrupt-parent = <&gpio4>; - interrupt-controller; - id = <0>; - blocks = <0x5>; - irq-trigger = <0x1>; - /* Common ADC settings */ - /* 3.25 MHz ADC clock speed */ - st,adc-freq = <1>; - /* 12-bit ADC */ - st,mod-12b = <1>; - /* internal ADC reference */ - st,ref-sel = <0>; - /* ADC converstion time: 80 clocks */ - st,sample-time = <4>; - - stmpe_touchscreen { - compatible = "st,stmpe-ts"; - reg = <0>; - /* 8 sample average control */ - st,ave-ctrl = <3>; - /* 5 ms touch detect interrupt delay */ - st,touch-det-delay = <5>; - /* 1 ms panel driver settling time */ - st,settling = <3>; - /* 7 length fractional part in z */ - st,fraction-z = <7>; - /* - * 50 mA typical 80 mA max touchscreen drivers - * current limit value - */ - st,i-drive = <1>; - }; - stmpe_adc { - compatible = "st,stmpe-adc"; - st,norequest-mask = <0x0F>; - }; -}; diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml index 2bc38479a41ed50c0357209b67b88d3a42a136f0..0f4a062c9d6fe32259d9349ec4fab4e08f77a5dd 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml @@ -106,6 +106,12 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 maximum: 4096 + dma-noncoherent: + description: + Present if the GIC redistributors permit programming shareability + and cacheability attributes but are connected to a non-coherent + downstream interconnect. + msi-controller: description: Only present if the Message Based Interrupt functionality is @@ -193,6 +199,12 @@ patternProperties: compatible: const: arm,gic-v3-its + dma-noncoherent: + description: + Present if the GIC ITS permits programming shareability and + cacheability attributes but is connected to a non-coherent + downstream interconnect. + msi-controller: true "#msi-cells": diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.yaml b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.yaml index 95033cb514fbd8f63ead74c0db7a5eada84cd056..b417341fc8ae049b91dc715fcb7c619e407fd923 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.yaml @@ -37,6 +37,7 @@ properties: - renesas,intc-ex-r8a77990 # R-Car E3 - renesas,intc-ex-r8a77995 # R-Car D3 - renesas,intc-ex-r8a779a0 # R-Car V3U + - renesas,intc-ex-r8a779f0 # R-Car S4-8 - renesas,intc-ex-r8a779g0 # R-Car V4H - const: renesas,irqc diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml index 33b90e975e33cde70730611424f386ebce47fcf0..2ef3081eaaf36aa653cdf52fd0de9ab918a418c4 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,rzg2l-irqc.yaml @@ -19,20 +19,19 @@ description: | - NMI edge select (NMI is not treated as NMI exception and supports fall edge and stand-up edge detection interrupts) -allOf: - - $ref: /schemas/interrupt-controller.yaml# - properties: compatible: items: - enum: + - renesas,r9a07g043u-irqc # RZ/G2UL - renesas,r9a07g044-irqc # RZ/G2{L,LC} - renesas,r9a07g054-irqc # RZ/V2L - const: renesas,rzg2l-irqc '#interrupt-cells': - description: The first cell should contain external interrupt number (IRQ0-7) and the - second cell is used to specify the flag. + description: The first cell should contain a macro RZG2L_{NMI,IRQX} included in the + include/dt-bindings/interrupt-controller/irqc-rzg2l.h and the second + cell is used to specify the flag. const: 2 '#address-cells': @@ -44,7 +43,96 @@ properties: maxItems: 1 interrupts: - maxItems: 41 + minItems: 41 + items: + - description: NMI interrupt + - description: IRQ0 interrupt + - description: IRQ1 interrupt + - description: IRQ2 interrupt + - description: IRQ3 interrupt + - description: IRQ4 interrupt + - description: IRQ5 interrupt + - description: IRQ6 interrupt + - description: IRQ7 interrupt + - description: GPIO interrupt, TINT0 + - description: GPIO interrupt, TINT1 + - description: GPIO interrupt, TINT2 + - description: GPIO interrupt, TINT3 + - description: GPIO interrupt, TINT4 + - description: GPIO interrupt, TINT5 + - description: GPIO interrupt, TINT6 + - description: GPIO interrupt, TINT7 + - description: GPIO interrupt, TINT8 + - description: GPIO interrupt, TINT9 + - description: GPIO interrupt, TINT10 + - description: GPIO interrupt, TINT11 + - description: GPIO interrupt, TINT12 + - description: GPIO interrupt, TINT13 + - description: GPIO interrupt, TINT14 + - description: GPIO interrupt, TINT15 + - description: GPIO interrupt, TINT16 + - description: GPIO interrupt, TINT17 + - description: GPIO interrupt, TINT18 + - description: GPIO interrupt, TINT19 + - description: GPIO interrupt, TINT20 + - description: GPIO interrupt, TINT21 + - description: GPIO interrupt, TINT22 + - description: GPIO interrupt, TINT23 + - description: GPIO interrupt, TINT24 + - description: GPIO interrupt, TINT25 + - description: GPIO interrupt, TINT26 + - description: GPIO interrupt, TINT27 + - description: GPIO interrupt, TINT28 + - description: GPIO interrupt, TINT29 + - description: GPIO interrupt, TINT30 + - description: GPIO interrupt, TINT31 + - description: Bus error interrupt + + interrupt-names: + minItems: 41 + items: + - const: nmi + - const: irq0 + - const: irq1 + - const: irq2 + - const: irq3 + - const: irq4 + - const: irq5 + - const: irq6 + - const: irq7 + - const: tint0 + - const: tint1 + - const: tint2 + - const: tint3 + - const: tint4 + - const: tint5 + - const: tint6 + - const: tint7 + - const: tint8 + - const: tint9 + - const: tint10 + - const: tint11 + - const: tint12 + - const: tint13 + - const: tint14 + - const: tint15 + - const: tint16 + - const: tint17 + - const: tint18 + - const: tint19 + - const: tint20 + - const: tint21 + - const: tint22 + - const: tint23 + - const: tint24 + - const: tint25 + - const: tint26 + - const: tint27 + - const: tint28 + - const: tint29 + - const: tint30 + - const: tint31 + - const: bus-err clocks: maxItems: 2 @@ -72,6 +160,23 @@ required: - power-domains - resets +allOf: + - $ref: /schemas/interrupt-controller.yaml# + + - if: + properties: + compatible: + contains: + const: renesas,r9a07g043u-irqc + then: + properties: + interrupts: + minItems: 42 + interrupt-names: + minItems: 42 + required: + - interrupt-names + unevaluatedProperties: false examples: @@ -80,55 +185,66 @@ examples: #include irqc: interrupt-controller@110a0000 { - compatible = "renesas,r9a07g044-irqc", "renesas,rzg2l-irqc"; - reg = <0x110a0000 0x10000>; - #interrupt-cells = <2>; - #address-cells = <0>; - interrupt-controller; - interrupts = , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - ; - clocks = <&cpg CPG_MOD R9A07G044_IA55_CLK>, - <&cpg CPG_MOD R9A07G044_IA55_PCLK>; - clock-names = "clk", "pclk"; - power-domains = <&cpg>; - resets = <&cpg R9A07G044_IA55_RESETN>; + compatible = "renesas,r9a07g044-irqc", "renesas,rzg2l-irqc"; + reg = <0x110a0000 0x10000>; + #interrupt-cells = <2>; + #address-cells = <0>; + interrupt-controller; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + interrupt-names = "nmi", + "irq0", "irq1", "irq2", "irq3", + "irq4", "irq5", "irq6", "irq7", + "tint0", "tint1", "tint2", "tint3", + "tint4", "tint5", "tint6", "tint7", + "tint8", "tint9", "tint10", "tint11", + "tint12", "tint13", "tint14", "tint15", + "tint16", "tint17", "tint18", "tint19", + "tint20", "tint21", "tint22", "tint23", + "tint24", "tint25", "tint26", "tint27", + "tint28", "tint29", "tint30", "tint31"; + clocks = <&cpg CPG_MOD R9A07G044_IA55_CLK>, + <&cpg CPG_MOD R9A07G044_IA55_PCLK>; + clock-names = "clk", "pclk"; + power-domains = <&cpg>; + resets = <&cpg R9A07G044_IA55_RESETN>; }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/st,sti-irq-syscfg.txt b/Documentation/devicetree/bindings/interrupt-controller/st,sti-irq-syscfg.txt deleted file mode 100644 index 977d7ed3670eb237770f5ede33a1a363fe15777c..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/interrupt-controller/st,sti-irq-syscfg.txt +++ /dev/null @@ -1,30 +0,0 @@ -STMicroelectronics STi System Configuration Controlled IRQs ------------------------------------------------------------ - -On STi based systems; External, CTI (Core Sight), PMU (Performance Management), -and PL310 L2 Cache IRQs are controlled using System Configuration registers. -This driver is used to unmask them prior to use. - -Required properties: -- compatible : Should be "st,stih407-irq-syscfg" -- st,syscfg : Phandle to Cortex-A9 IRQ system config registers -- st,irq-device : Array of IRQs to enable - should be 2 in length -- st,fiq-device : Array of FIQs to enable - should be 2 in length - -Optional properties: -- st,invert-ext : External IRQs can be inverted at will. This property inverts - these IRQs using bitwise logic. A number of defines have been - provided for convenience: - ST_IRQ_SYSCFG_EXT_1_INV - ST_IRQ_SYSCFG_EXT_2_INV - ST_IRQ_SYSCFG_EXT_3_INV -Example: - -irq-syscfg { - compatible = "st,stih407-irq-syscfg"; - st,syscfg = <&syscfg_cpu>; - st,irq-device = , - ; - st,fiq-device = , - ; -}; diff --git a/Documentation/devicetree/bindings/interrupt-controller/st,stih407-irq-syscfg.yaml b/Documentation/devicetree/bindings/interrupt-controller/st,stih407-irq-syscfg.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2b153d7c5421639b1a2d563460e64c0bb0055f83 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/st,stih407-irq-syscfg.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/st,stih407-irq-syscfg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STi System Configuration Controlled IRQs + +maintainers: + - Patrice Chotard + +description: + On STi based systems; External, CTI (Core Sight), PMU (Performance + Management), and PL310 L2 Cache IRQs are controlled using System + Configuration registers. This device is used to unmask them prior to use. + +properties: + compatible: + const: st,stih407-irq-syscfg + + st,syscfg: + description: Phandle to Cortex-A9 IRQ system config registers + $ref: /schemas/types.yaml#/definitions/phandle + + st,irq-device: + description: Array of IRQs to enable. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Enable the IRQ of the channel one. + - description: Enable the IRQ of the channel two. + + st,fiq-device: + description: Array of FIQs to enable. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: Enable the IRQ of the channel one. + - description: Enable the IRQ of the channel two. + + st,invert-ext: + description: External IRQs can be inverted at will. This property inverts + these three IRQs using bitwise logic, each one being encoded respectively + on the first, second and fourth bit. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 1, 2, 3, 4, 5, 6 ] + +required: + - compatible + - st,syscfg + - st,irq-device + - st,fiq-device + +additionalProperties: false + +examples: + - | + #include + irq-syscfg { + compatible = "st,stih407-irq-syscfg"; + st,syscfg = <&syscfg_cpu>; + st,irq-device = , + ; + st,fiq-device = , + ; + }; +... diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml index 3a31a979709b6588ac990a8e69dd920cd20bc49e..b1b2cf81b42fc1cf2db849fabceb159e368c7ce5 100644 --- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml +++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml @@ -271,6 +271,47 @@ allOf: enum: - qcom,msm8998-smmu-v2 - qcom,sdm630-smmu-v2 + then: + anyOf: + - properties: + clock-names: + items: + - const: bus + clocks: + items: + - description: bus clock required for downstream bus access and for + the smmu ptw + - properties: + clock-names: + items: + - const: iface + - const: mem + - const: mem_iface + clocks: + items: + - description: interface clock required to access smmu's registers + through the TCU's programming interface. + - description: bus clock required for memory access + - description: bus clock required for GPU memory access + - properties: + clock-names: + items: + - const: iface-mm + - const: iface-smmu + - const: bus-smmu + clocks: + items: + - description: interface clock required to access mnoc's registers + through the TCU's programming interface. + - description: interface clock required to access smmu's registers + through the TCU's programming interface. + - description: bus clock required for the smmu ptw + + - if: + properties: + compatible: + contains: + enum: - qcom,sm6375-smmu-v2 then: anyOf: diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml index 5b6395bc10e0cc1ecd13d05ffbf5a67551dbff9b..ea6b0f5f24de7667b74575bc837343c04323c4da 100644 --- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml @@ -78,6 +78,9 @@ properties: - mediatek,mt8173-m4u # generation two - mediatek,mt8183-m4u # generation two - mediatek,mt8186-iommu-mm # generation two + - mediatek,mt8188-iommu-vdo # generation two + - mediatek,mt8188-iommu-vpp # generation two + - mediatek,mt8188-iommu-infra # generation two - mediatek,mt8192-m4u # generation two - mediatek,mt8195-iommu-vdo # generation two - mediatek,mt8195-iommu-vpp # generation two @@ -123,6 +126,7 @@ properties: description: | This is the mtk_m4u_id according to the HW. Specifies the mtk_m4u_id as defined in + dt-binding/memory/mediatek,mt8188-memory-port.h for mt8188, dt-binding/memory/mt2701-larb-port.h for mt2701 and mt7623, dt-binding/memory/mt2712-larb-port.h for mt2712, dt-binding/memory/mt6779-larb-port.h for mt6779, @@ -155,6 +159,8 @@ allOf: - mediatek,mt6795-m4u - mediatek,mt8173-m4u - mediatek,mt8186-iommu-mm + - mediatek,mt8188-iommu-vdo + - mediatek,mt8188-iommu-vpp - mediatek,mt8192-m4u - mediatek,mt8195-iommu-vdo - mediatek,mt8195-iommu-vpp @@ -168,6 +174,8 @@ allOf: compatible: enum: - mediatek,mt8186-iommu-mm + - mediatek,mt8188-iommu-vdo + - mediatek,mt8188-iommu-vpp - mediatek,mt8192-m4u - mediatek,mt8195-iommu-vdo - mediatek,mt8195-iommu-vpp @@ -194,7 +202,9 @@ allOf: properties: compatible: contains: - const: mediatek,mt8195-iommu-infra + enum: + - mediatek,mt8188-iommu-infra + - mediatek,mt8195-iommu-infra then: required: diff --git a/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml b/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml index d9fabdf930d984e24e46fb3490696ddd7b0f9f2d..a74eb899c381ec665932e780769f93a51eb4bbf5 100644 --- a/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml +++ b/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml @@ -17,11 +17,16 @@ description: | properties: compatible: - items: - - enum: - - qcom,msm8916-iommu - - qcom,msm8953-iommu - - const: qcom,msm-iommu-v1 + oneOf: + - items: + - enum: + - qcom,msm8916-iommu + - qcom,msm8953-iommu + - const: qcom,msm-iommu-v1 + - items: + - enum: + - qcom,msm8976-iommu + - const: qcom,msm-iommu-v2 clocks: items: @@ -64,6 +69,8 @@ patternProperties: enum: - qcom,msm-iommu-v1-ns - qcom,msm-iommu-v1-sec + - qcom,msm-iommu-v2-ns + - qcom,msm-iommu-v2-sec interrupts: maxItems: 1 @@ -71,6 +78,11 @@ patternProperties: reg: maxItems: 1 + qcom,ctx-asid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The ASID number associated to the context bank. + required: - compatible - interrupts diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml index 58b492d002464dc2363facacaeea7b1b627dba21..5fb7007f36189b9ee05ae93d94ca922a6e9bd98e 100644 --- a/Documentation/devicetree/bindings/leds/common.yaml +++ b/Documentation/devicetree/bindings/leds/common.yaml @@ -83,8 +83,7 @@ properties: - enum: # LED will act as a back-light, controlled by the framebuffer system - backlight - # LED will turn on (but for leds-gpio see "default-state" property in - # Documentation/devicetree/bindings/leds/leds-gpio.yaml) + # LED will turn on (see also "default-state" property) - default-on # LED "double" flashes at a load average based rate - heartbeat @@ -158,6 +157,18 @@ properties: For flash LED controllers with configurable current this property is mandatory for the LEDs in the non-flash modes (e.g. torch or indicator). + max-brightness: + description: + Normally, the maximum brightness is determined by the hardware, and this + property is not required. This property is used to set a software limit. + It could happen that an LED is made so bright that it gets damaged or + causes damage due to restrictions in a specific system, such as mounting + conditions. + Note that this flag is mainly used for PWM-LEDs, where it is not possible + to map brightness to current. Drivers for other controllers should use + led-max-microamp. + $ref: /schemas/types.yaml#definitions/uint32 + panic-indicator: description: This property specifies that the LED should be used, if at all possible, diff --git a/Documentation/devicetree/bindings/leds/leds-an30259a.txt b/Documentation/devicetree/bindings/leds/leds-an30259a.txt deleted file mode 100644 index cbd833906b2b549c623cb1507f590834696f7a56..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/leds/leds-an30259a.txt +++ /dev/null @@ -1,55 +0,0 @@ -* Panasonic AN30259A 3-channel LED driver - -The AN30259A is a LED controller capable of driving three LEDs independently. It supports -constant current output and sloping current output modes. The chip is connected over I2C. - -Required properties: - - compatible: Must be "panasonic,an30259a". - - reg: I2C slave address. - - #address-cells: Must be 1. - - #size-cells: Must be 0. - -Each LED is represented as a sub-node of the panasonic,an30259a node. - -Required sub-node properties: - - reg: Pin that the LED is connected to. Must be 1, 2, or 3. - -Optional sub-node properties: - - function : - see Documentation/devicetree/bindings/leds/common.txt - - color : - see Documentation/devicetree/bindings/leds/common.txt - - label : - see Documentation/devicetree/bindings/leds/common.txt (deprecated) - - linux,default-trigger : - see Documentation/devicetree/bindings/leds/common.txt - -Example: - -#include - -led-controller@30 { - compatible = "panasonic,an30259a"; - reg = <0x30>; - #address-cells = <1>; - #size-cells = <0>; - - led@1 { - reg = <1>; - linux,default-trigger = "heartbeat"; - function = LED_FUNCTION_INDICATOR; - color = ; - }; - - led@2 { - reg = <2>; - function = LED_FUNCTION_INDICATOR; - color = ; - }; - - led@3 { - reg = <3>; - function = LED_FUNCTION_INDICATOR; - color = ; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-aw2013.yaml b/Documentation/devicetree/bindings/leds/leds-aw2013.yaml index 08f3e1cfc1b1be21f9b0f1bd64fdbaf7fb196afa..26238446f2bdfe1ce251686b80705d051019d826 100644 --- a/Documentation/devicetree/bindings/leds/leds-aw2013.yaml +++ b/Documentation/devicetree/bindings/leds/leds-aw2013.yaml @@ -20,9 +20,20 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + description: Open-drain, low active interrupt pin "INTN". + Used to report completion of operations (power up, LED breath effects). + vcc-supply: description: Regulator providing power to the "VCC" pin. + vio-supply: + description: Regulator providing power for pull-up of the I/O lines. + "VIO1" in the typical application circuit example of the datasheet. + Note that this regulator does not directly connect to AW2013, but is + needed for the correct operation of the interrupt and I2C lines. + "#address-cells": const: 1 @@ -52,6 +63,7 @@ additionalProperties: false examples: - | #include + #include #include i2c { @@ -61,6 +73,7 @@ examples: led-controller@45 { compatible = "awinic,aw2013"; reg = <0x45>; + interrupts = <42 IRQ_TYPE_LEVEL_LOW>; #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/leds/leds-group-multicolor.yaml b/Documentation/devicetree/bindings/leds/leds-group-multicolor.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8ed059a5a724f68389a1d0c4396c85b9ccb2d9af --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-group-multicolor.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/leds-group-multicolor.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Multi-color LED built with monochromatic LEDs + +maintainers: + - Jean-Jacques Hiblot + +description: | + This driver combines several monochromatic LEDs into one multi-color + LED using the multicolor LED class. + +properties: + compatible: + const: leds-group-multicolor + + leds: + description: + An aray of monochromatic leds + $ref: /schemas/types.yaml#/definitions/phandle-array + +required: + - leds + +allOf: + - $ref: leds-class-multicolor.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + + monochromatic-leds { + compatible = "gpio-leds"; + + led0: led-0 { + gpios = <&mcu_pio 0 GPIO_ACTIVE_LOW>; + color = ; + }; + + led1: led-1 { + gpios = <&mcu_pio 1 GPIO_ACTIVE_HIGH>; + color = ; + }; + + led2: led-2 { + gpios = <&mcu_pio 2 GPIO_ACTIVE_HIGH>; + color = ; + }; + }; + + multi-led { + compatible = "leds-group-multicolor"; + color = ; + function = LED_FUNCTION_INDICATOR; + leds = <&led0>, <&led1>, <&led2>; + }; + +... diff --git a/Documentation/devicetree/bindings/leds/nxp,pca953x.yaml b/Documentation/devicetree/bindings/leds/nxp,pca953x.yaml index edf6f55df6851ea2fe894f8c3ce661b67dc4138d..9610bca57dd51c5c696b1fb1aca081cf4badd169 100644 --- a/Documentation/devicetree/bindings/leds/nxp,pca953x.yaml +++ b/Documentation/devicetree/bindings/leds/nxp,pca953x.yaml @@ -29,6 +29,10 @@ properties: gpio-controller: true + gpio-line-names: + minItems: 1 + maxItems: 16 + '#gpio-cells': const: 2 diff --git a/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml b/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml new file mode 100644 index 0000000000000000000000000000000000000000..654915c1f687f3bfe08229410575eeff5376ceea --- /dev/null +++ b/Documentation/devicetree/bindings/leds/nxp,pca995x.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/nxp,pca995x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP PCA995x LED controllers + +maintainers: + - Isai Gaspar + - Marek Vasut + +description: + The NXP PCA9952/PCA9955B are programmable LED controllers connected via I2C + that can drive 16 separate lines. Each of them can be individually switched + on and off, and brightness can be controlled via individual PWM. + + Datasheets are available at + https://www.nxp.com/docs/en/data-sheet/PCA9952_PCA9955.pdf + https://www.nxp.com/docs/en/data-sheet/PCA9955B.pdf + +properties: + compatible: + enum: + - nxp,pca9952 + - nxp,pca9955b + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^led@[0-9a-f]+$": + type: object + $ref: common.yaml# + unevaluatedProperties: false + + properties: + reg: + minimum: 0 + maximum: 15 + + required: + - reg + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@1 { + compatible = "nxp,pca9955b"; + reg = <0x01>; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0x0>; + color = ; + function = LED_FUNCTION_POWER; + }; + + led@2 { + reg = <0x2>; + color = ; + function = LED_FUNCTION_STATUS; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/leds/panasonic,an30259a.yaml b/Documentation/devicetree/bindings/leds/panasonic,an30259a.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e918dceea0828b1be1cb477ef0e640952592d179 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/panasonic,an30259a.yaml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/panasonic,an30259a.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Panasonic AN30259A 3-channel LED controller + +maintainers: + - Iskren Chernev + +description: + The AN30259A is a LED controller capable of driving three LEDs independently. + It supports constant current output and sloping current output modes. The chip + is connected over I2C. + +properties: + compatible: + const: panasonic,an30259a + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^led@[1-3]$": + $ref: common.yaml# + unevaluatedProperties: false + + properties: + reg: + enum: [ 1, 2, 3 ] + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@30 { + compatible = "panasonic,an30259a"; + reg = <0x30>; + #address-cells = <1>; + #size-cells = <0>; + + led@1 { + reg = <1>; + linux,default-trigger = "heartbeat"; + function = LED_FUNCTION_INDICATOR; + color = ; + }; + + led@2 { + reg = <2>; + function = LED_FUNCTION_INDICATOR; + color = ; + }; + + led@3 { + reg = <3>; + function = LED_FUNCTION_INDICATOR; + color = ; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/leds/rohm,bd2606mvv.yaml b/Documentation/devicetree/bindings/leds/rohm,bd2606mvv.yaml index 14700a2e5feaa4a31653266464cb5386316dfd45..44dd91aa239deffe00dc226319c7875eb4b01899 100644 --- a/Documentation/devicetree/bindings/leds/rohm,bd2606mvv.yaml +++ b/Documentation/devicetree/bindings/leds/rohm,bd2606mvv.yaml @@ -35,7 +35,7 @@ properties: description: GPIO pin to enable/disable the device. patternProperties: - "^led@[0-6]$": + "^led@[0-5]$": type: object $ref: common.yaml# unevaluatedProperties: false @@ -43,7 +43,7 @@ patternProperties: properties: reg: minimum: 0 - maximum: 6 + maximum: 5 required: - reg diff --git a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml index 58f0d94c6d7168e03995b4f4e264e512fb9114b8..b7a3ef76cbf4c6cb7a6a72e082eee2179e225720 100644 --- a/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml +++ b/Documentation/devicetree/bindings/leds/rohm,bd71828-leds.yaml @@ -18,8 +18,6 @@ description: | The device has two LED outputs referred as GRNLED and AMBLED in data-sheet. -select: false - properties: compatible: const: rohm,bd71828-leds diff --git a/Documentation/devicetree/bindings/media/i2c/ov5695.txt b/Documentation/devicetree/bindings/media/i2c/ov5695.txt deleted file mode 100644 index 640a63717d96cc5856d4ab567f9a138e112c412b..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov5695.txt +++ /dev/null @@ -1,41 +0,0 @@ -* Omnivision OV5695 MIPI CSI-2 sensor - -Required Properties: -- compatible: shall be "ovti,ov5695" -- clocks: reference to the xvclk input clock -- clock-names: shall be "xvclk" -- avdd-supply: Analog voltage supply, 2.8 volts -- dovdd-supply: Digital I/O voltage supply, 1.8 volts -- dvdd-supply: Digital core voltage supply, 1.2 volts -- reset-gpios: Low active reset gpio - -The device node shall contain one 'port' child node with an -'endpoint' subnode for its digital output video port, -in accordance with the video interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. -The endpoint optional property 'data-lanes' shall be "<1 2>". - -Example: -&i2c7 { - ov5695: camera-sensor@36 { - compatible = "ovti,ov5695"; - reg = <0x36>; - pinctrl-names = "default"; - pinctrl-0 = <&clk_24m_cam>; - - clocks = <&cru SCLK_TESTCLKOUT1>; - clock-names = "xvclk"; - - avdd-supply = <&pp2800_cam>; - dovdd-supply = <&pp1800>; - dvdd-supply = <&pp1250_cam>; - reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; - - port { - wcam_out: endpoint { - remote-endpoint = <&mipi_in_wcam>; - data-lanes = <1 2>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/i2c/ov7251.txt b/Documentation/devicetree/bindings/media/i2c/ov7251.txt deleted file mode 100644 index 8281151f7493e3f5c28006002c80de97dd0b3646..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/media/i2c/ov7251.txt +++ /dev/null @@ -1,52 +0,0 @@ -* Omnivision 1/7.5-Inch B&W VGA CMOS Digital Image Sensor - -The Omnivision OV7251 is a 1/7.5-Inch CMOS active pixel digital image sensor -with an active array size of 640H x 480V. It is programmable through a serial -I2C interface. - -Required Properties: -- compatible: Value should be "ovti,ov7251". -- clocks: Reference to the xclk clock. -- clock-names: Should be "xclk". -- clock-frequency: Frequency of the xclk clock. -- enable-gpios: Chip enable GPIO. Polarity is GPIO_ACTIVE_HIGH. This corresponds - to the hardware pin XSHUTDOWN which is physically active low. -- vdddo-supply: Chip digital IO regulator. -- vdda-supply: Chip analog regulator. -- vddd-supply: Chip digital core regulator. - -The device node shall contain one 'port' child node with a single 'endpoint' -subnode for its digital output video port, in accordance with the video -interface bindings defined in -Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - - &i2c1 { - ... - - ov7251: camera-sensor@60 { - compatible = "ovti,ov7251"; - reg = <0x60>; - - enable-gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>; - pinctrl-names = "default"; - pinctrl-0 = <&camera_bw_default>; - - clocks = <&clks 200>; - clock-names = "xclk"; - clock-frequency = <24000000>; - - vdddo-supply = <&camera_dovdd_1v8>; - vdda-supply = <&camera_avdd_2v8>; - vddd-supply = <&camera_dvdd_1v2>; - - port { - ov7251_ep: endpoint { - clock-lanes = <1>; - data-lanes = <0>; - remote-endpoint = <&csi0_ep>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml index 359dc08440a8e2cf8f98f48083d4ff9a06518e16..6829a4aadd22e31bea67a8f61ebb5c2fe8278745 100644 --- a/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml @@ -5,26 +5,41 @@ $id: http://devicetree.org/schemas/media/i2c/ovti,ov5693.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Omnivision OV5693 CMOS Sensor +title: Omnivision OV5693/OV5695 CMOS Sensors maintainers: - Tommaso Merciai description: | - The Omnivision OV5693 is a high performance, 1/4-inch, 5 megapixel, CMOS - image sensor that delivers 2592x1944 at 30fps. It provides full-frame, + The Omnivision OV5693/OV5695 are high performance, 1/4-inch, 5 megapixel, CMOS + image sensors that deliver 2592x1944 at 30fps. It provides full-frame, sub-sampled, and windowed 10-bit MIPI images in various formats via the Serial Camera Control Bus (SCCB) interface. - OV5693 is controlled via I2C and two-wire Serial Camera Control Bus (SCCB). - The sensor output is available via CSI-2 serial data output (up to 2-lane). + OV5693/OV5695 are controlled via I2C and two-wire Serial Camera Control Bus + (SCCB). The sensor output is available via CSI-2 serial data output (up to + 2-lane). allOf: - $ref: /schemas/media/video-interface-devices.yaml# + - if: + properties: + compatible: + contains: + const: ovti,ov5693 + then: + properties: + port: + properties: + endpoint: + required: + - link-frequencies properties: compatible: - const: ovti,ov5693 + enum: + - ovti,ov5693 + - ovti,ov5695 reg: maxItems: 1 @@ -34,6 +49,9 @@ properties: System input clock (aka XVCLK). From 6 to 27 MHz. maxItems: 1 + clock-names: + const: xvclk + dovdd-supply: description: Digital I/O voltage supply, 1.8V. @@ -72,7 +90,6 @@ properties: required: - data-lanes - - link-frequencies required: - compatible diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov7251.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov7251.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2e5187acbbb89728cbb8a402559d24766198a3da --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov7251.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/ovti,ov7251.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OmniVision OV7251 Image Sensor + +description: + The Omnivision OV7251 is a 1/7.5-Inch CMOS active pixel digital image sensor + with an active array size of 640H x 480V. It is programmable through a serial + I2C interface. + +maintainers: + - Todor Tomov + +properties: + compatible: + const: ovti,ov7251 + + reg: + maxItems: 1 + + clocks: + description: XCLK Input Clock + + clock-names: + const: xclk + + clock-frequency: + description: Frequency of the xclk clock in Hz. + + vdda-supply: + description: Analog voltage supply, 2.8 volts + + vddd-supply: + description: Digital core voltage supply, 1.2 volts + + vdddo-supply: + description: Digital I/O voltage supply, 1.8 volts + + enable-gpios: + maxItems: 1 + description: + Reference to the GPIO connected to the XSHUTDOWN pin, if any. Polarity + is GPIO_ACTIVE_HIGH. + + port: + description: Digital Output Port + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + clock-lanes: + maximum: 1 + + data-lanes: + maxItems: 1 + + link-frequencies: true + + required: + - data-lanes + - link-frequencies + +required: + - compatible + - reg + - clocks + - vdddo-supply + - vdda-supply + - port + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@3c { + compatible = "ovti,ov7251"; + reg = <0x3c>; + clocks = <&clks 1>; + clock-frequency = <24000000>; + vdddo-supply = <&ov7251_vdddo_1v8>; + vdda-supply = <&ov7251_vdda_2v8>; + vddd-supply = <&ov7251_vddd_1v5>; + enable-gpios = <&gpio1 19 GPIO_ACTIVE_HIGH>; + + port { + ov7251_ep: endpoint { + remote-endpoint = <&csi0_ep>; + clock-lanes = <1>; + data-lanes = <0>; + link-frequencies = /bits/ 64 <240000000 319200000>; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml index ffccf5f3c9e34a7fca06b49d96ee4a9b13071d75..642f9b15d3597738e33fd7e5a51636e69d5fe8ae 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml @@ -54,6 +54,7 @@ properties: port: $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false properties: endpoint: diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,tc358746.yaml b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358746.yaml index c5cab549ee8eafb1c03203f70c2c3fb20f24e043..1c476b635b690865cff0882578d72b1db2dc7c50 100644 --- a/Documentation/devicetree/bindings/media/i2c/toshiba,tc358746.yaml +++ b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358746.yaml @@ -69,6 +69,7 @@ properties: properties: port@0: $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: Input port properties: @@ -89,6 +90,7 @@ properties: port@1: $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: Output port properties: diff --git a/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml b/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml index 358019e85d90781e5001170572727f3f9bba4655..326284e151f66e937420af948f58b04df081f1e5 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx7-csi.yaml @@ -59,7 +59,6 @@ allOf: compatible: contains: enum: - - fsl,imx8mq-csi - fsl,imx8mm-csi then: required: diff --git a/Documentation/devicetree/bindings/media/renesas,vin.yaml b/Documentation/devicetree/bindings/media/renesas,vin.yaml index 324703bfb1bded3ad28ded83202b8f5293935dbb..5539d0f8e74d2e827cd78ccf1860de2f770e9c51 100644 --- a/Documentation/devicetree/bindings/media/renesas,vin.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vin.yaml @@ -95,7 +95,7 @@ properties: synchronization is selected. default: 1 - field-active-even: true + field-even-active: true bus-width: true @@ -144,7 +144,7 @@ properties: synchronization is selected. default: 1 - field-active-even: true + field-even-active: true bus-width: true diff --git a/Documentation/devicetree/bindings/media/rockchip-isp1.yaml b/Documentation/devicetree/bindings/media/rockchip-isp1.yaml index 0bad7e640148601b4a518ae65962013c4bd67fa4..e466dff8286d2bbaa2b111ee3c0a26166525ac4c 100644 --- a/Documentation/devicetree/bindings/media/rockchip-isp1.yaml +++ b/Documentation/devicetree/bindings/media/rockchip-isp1.yaml @@ -199,6 +199,7 @@ examples: wcam: camera@36 { compatible = "ovti,ov5695"; reg = <0x36>; + clocks = <&cru SCLK_TESTCLKOUT1>; port { wcam_out: endpoint { diff --git a/Documentation/devicetree/bindings/media/samsung,fimc.yaml b/Documentation/devicetree/bindings/media/samsung,fimc.yaml index 79ff6d83a9fd684c41f5dcd1e758876942fd66d5..b3486c38a05b9ef7cbbcd485fed90f9fd0741670 100644 --- a/Documentation/devicetree/bindings/media/samsung,fimc.yaml +++ b/Documentation/devicetree/bindings/media/samsung,fimc.yaml @@ -57,6 +57,7 @@ properties: patternProperties: "^port@[01]$": $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false description: Camera A and camera B inputs. diff --git a/Documentation/devicetree/bindings/mfd/allwinner,sun6i-a31-prcm.yaml b/Documentation/devicetree/bindings/mfd/allwinner,sun6i-a31-prcm.yaml index cf94176fe1eb60d3f42392246ea4c568d9dd7a63..8789e3639ff7e2b4eec4e67ae1b1f05ec79bcefd 100644 --- a/Documentation/devicetree/bindings/mfd/allwinner,sun6i-a31-prcm.yaml +++ b/Documentation/devicetree/bindings/mfd/allwinner,sun6i-a31-prcm.yaml @@ -34,6 +34,9 @@ patternProperties: - allwinner,sun6i-a31-clock-reset - fixed-factor-clock + required: + - compatible + allOf: - if: properties: @@ -55,25 +58,17 @@ patternProperties: "#clock-cells": const: 0 - # Already checked in the main schema - compatible: true - clocks: maxItems: 2 clock-output-names: maxItems: 1 - phandle: true - required: - "#clock-cells" - - compatible - clocks - clock-output-names - additionalProperties: false - - if: properties: compatible: @@ -85,25 +80,17 @@ patternProperties: "#clock-cells": const: 0 - # Already checked in the main schema - compatible: true - clocks: maxItems: 1 clock-output-names: maxItems: 1 - phandle: true - required: - "#clock-cells" - - compatible - clocks - clock-output-names - additionalProperties: false - - if: properties: compatible: @@ -119,9 +106,6 @@ patternProperties: offset of the bit controlling this particular gate in the register. - # Already checked in the main schema - compatible: true - clocks: maxItems: 1 @@ -129,16 +113,11 @@ patternProperties: minItems: 1 maxItems: 32 - phandle: true - required: - "#clock-cells" - - compatible - clocks - clock-output-names - additionalProperties: false - - if: properties: compatible: @@ -150,9 +129,6 @@ patternProperties: "#clock-cells": const: 0 - # Already checked in the main schema - compatible: true - clocks: maxItems: 4 description: > @@ -162,16 +138,11 @@ patternProperties: clock-output-names: maxItems: 1 - phandle: true - required: - "#clock-cells" - - compatible - clocks - clock-output-names - additionalProperties: false - - if: properties: compatible: @@ -183,16 +154,8 @@ patternProperties: "#reset-cells": const: 1 - # Already checked in the main schema - compatible: true - - phandle: true - required: - "#reset-cells" - - compatible - - additionalProperties: false required: - compatible diff --git a/Documentation/devicetree/bindings/mfd/allwinner,sun8i-a23-prcm.yaml b/Documentation/devicetree/bindings/mfd/allwinner,sun8i-a23-prcm.yaml index 16c80a7eec49c065568616a2d41321316db5963c..e51f8551991177c03b20513ff67142efa2c42ba0 100644 --- a/Documentation/devicetree/bindings/mfd/allwinner,sun8i-a23-prcm.yaml +++ b/Documentation/devicetree/bindings/mfd/allwinner,sun8i-a23-prcm.yaml @@ -57,25 +57,17 @@ patternProperties: "#clock-cells": const: 0 - # Already checked in the main schema - compatible: true - clocks: maxItems: 1 clock-output-names: maxItems: 1 - phandle: true - required: - "#clock-cells" - - compatible - clocks - clock-output-names - additionalProperties: false - - if: properties: compatible: @@ -91,9 +83,6 @@ patternProperties: offset of the bit controlling this particular gate in the register. - # Already checked in the main schema - compatible: true - clocks: maxItems: 1 @@ -101,16 +90,11 @@ patternProperties: minItems: 1 maxItems: 32 - phandle: true - required: - "#clock-cells" - - compatible - clocks - clock-output-names - additionalProperties: false - - if: properties: compatible: @@ -122,34 +106,8 @@ patternProperties: "#reset-cells": const: 1 - # Already checked in the main schema - compatible: true - - phandle: true - required: - "#reset-cells" - - compatible - - additionalProperties: false - - - if: - properties: - compatible: - contains: - const: allwinner,sun8i-a23-codec-analog - - then: - properties: - # Already checked in the main schema - compatible: true - - phandle: true - - required: - - compatible - - additionalProperties: false required: - compatible diff --git a/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt b/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt index 9d837535637b467aad496642d8f764b7106e6842..af692e8833a57c0235c182888cce3c1c20552c70 100644 --- a/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt +++ b/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt @@ -6,6 +6,7 @@ at boot time according to the device tree. Required properties: - compatible: Should be "atmel,sama5d2-flexcom" + or "microchip,sam9x7-flexcom", "atmel,sama5d2-flexcom" - reg: Should be the offset/length value for Flexcom dedicated I/O registers (without USART, TWI or SPI registers). - clocks: Should be the Flexcom peripheral clock from PMC. diff --git a/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt b/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt index e8c525569f10464b8826f89b066a4f688e04f99b..3c989d1760a20d48afc1f90333a0532d1eecb03d 100644 --- a/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt +++ b/Documentation/devicetree/bindings/mfd/atmel-gpbr.txt @@ -6,6 +6,7 @@ Required properties: - compatible: Should be one of the following: "atmel,at91sam9260-gpbr", "syscon" "microchip,sam9x60-gpbr", "syscon" + "microchip,sam9x7-gpbr", "microchip,sam9x60-gpbr", "syscon" - reg: contains offset/length value of the GPBR memory region. diff --git a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt index 5f8880cc757e1d8cea12fadaf798533e173f4dd1..7de696eefaed4b25f773fbe06f77510bd4bddb8c 100644 --- a/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt +++ b/Documentation/devicetree/bindings/mfd/atmel-hlcdc.txt @@ -8,6 +8,7 @@ Required properties: "atmel,sama5d3-hlcdc" "atmel,sama5d4-hlcdc" "microchip,sam9x60-hlcdc" + "microchip,sam9x75-xlcdc" - reg: base address and size of the HLCDC device registers. - clock-names: the name of the 3 clocks requested by the HLCDC device. Should contain "periph_clk", "sys_clk" and "slow_clk". diff --git a/Documentation/devicetree/bindings/mfd/atmel-matrix.txt b/Documentation/devicetree/bindings/mfd/atmel-matrix.txt index 89d05c64fb014de597d4fd3be0792e86edd075b6..6e5f83614e8335afe1427300966691cca68d95ce 100644 --- a/Documentation/devicetree/bindings/mfd/atmel-matrix.txt +++ b/Documentation/devicetree/bindings/mfd/atmel-matrix.txt @@ -14,6 +14,7 @@ Required properties: "atmel,at91sam9x5-matrix", "syscon" "atmel,sama5d3-matrix", "syscon" "microchip,sam9x60-matrix", "syscon" + "microchip,sam9x7-matrix", "atmel,at91sam9x5-matrix", "syscon" - reg: Contains offset/length value of the Bus Matrix memory region. diff --git a/Documentation/devicetree/bindings/mfd/atmel-smc.txt b/Documentation/devicetree/bindings/mfd/atmel-smc.txt index 5696d9fcb5dcd02241659596f86dff5ae251eb95..fd62add38a7917ffede3f84fb82a695671f10417 100644 --- a/Documentation/devicetree/bindings/mfd/atmel-smc.txt +++ b/Documentation/devicetree/bindings/mfd/atmel-smc.txt @@ -10,6 +10,7 @@ Required properties: "atmel,sama5d3-smc", "syscon" "atmel,sama5d2-smc", "syscon" "microchip,sam9x60-smc", "syscon" + "microchip,sam9x7-smc", "atmel,at91sam9260-smc", "syscon" - reg: Contains offset/length value of the SMC memory region. diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml index 9f9a14af875ee43301365609fb8aeaa381153a31..cb480162f967df4b39394f5c63f82dc748b36fe6 100644 --- a/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6318-gpio-sysctl.yaml @@ -35,7 +35,7 @@ patternProperties: "^gpio@[0-9a-f]+$": # Child node type: object - $ref: "../gpio/brcm,bcm63xx-gpio.yaml" + $ref: /schemas/gpio/brcm,bcm63xx-gpio.yaml description: GPIO controller for the SoC GPIOs. This child node definition should follow the bindings specified in @@ -44,7 +44,7 @@ patternProperties: "^pinctrl@[0-9a-f]+$": # Child node type: object - $ref: "../pinctrl/brcm,bcm6318-pinctrl.yaml" + $ref: /schemas/pinctrl/brcm,bcm6318-pinctrl.yaml description: Pin controller for the SoC pins. This child node definition should follow the bindings specified in diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml index 803277dd27250dd50fe7004ac1f8a96502433de4..c14def1b2ad2bb6709c1a4430206470b3296ecd9 100644 --- a/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm63268-gpio-sysctl.yaml @@ -35,7 +35,7 @@ patternProperties: "^gpio@[0-9a-f]+$": # Child node type: object - $ref: "../gpio/brcm,bcm63xx-gpio.yaml" + $ref: /schemas/gpio/brcm,bcm63xx-gpio.yaml description: GPIO controller for the SoC GPIOs. This child node definition should follow the bindings specified in @@ -44,7 +44,7 @@ patternProperties: "^pinctrl@[0-9a-f]+$": # Child node type: object - $ref: "../pinctrl/brcm,bcm63268-pinctrl.yaml" + $ref: /schemas/pinctrl/brcm,bcm63268-pinctrl.yaml description: Pin controller for the SoC pins. This child node definition should follow the bindings specified in diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml index b9a6856ce970bda8c295e398b7fe03f1b434ce62..5f48209ed40f567fe2525d8a4fb1ad9604080f5a 100644 --- a/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6328-gpio-sysctl.yaml @@ -35,7 +35,7 @@ patternProperties: "^gpio@[0-9a-f]+$": # Child node type: object - $ref: "../gpio/brcm,bcm63xx-gpio.yaml" + $ref: /schemas/gpio/brcm,bcm63xx-gpio.yaml description: GPIO controller for the SoC GPIOs. This child node definition should follow the bindings specified in @@ -44,7 +44,7 @@ patternProperties: "^pinctrl@[0-9a-f]+$": # Child node type: object - $ref: "../pinctrl/brcm,bcm6328-pinctrl.yaml" + $ref: /schemas/pinctrl/brcm,bcm6328-pinctrl.yaml description: Pin controller for the SoC pins. This child node definition should follow the bindings specified in diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml index 4651fe4dde079b8f5a77a034d52d4aa66e271c4d..f1f4629565d99f1441ebd64f6c3af8f180f5da51 100644 --- a/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6358-gpio-sysctl.yaml @@ -35,7 +35,7 @@ patternProperties: "^gpio@[0-9a-f]+$": # Child node type: object - $ref: "../gpio/brcm,bcm63xx-gpio.yaml" + $ref: /schemas/gpio/brcm,bcm63xx-gpio.yaml description: GPIO controller for the SoC GPIOs. This child node definition should follow the bindings specified in @@ -44,7 +44,7 @@ patternProperties: "^pinctrl@[0-9a-f]+$": # Child node type: object - $ref: "../pinctrl/brcm,bcm6358-pinctrl.yaml" + $ref: /schemas/pinctrl/brcm,bcm6358-pinctrl.yaml description: Pin controller for the SoC pins. This child node definition should follow the bindings specified in diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml index 0330b621fd3814938430e44fcc880c55a31fdf47..4d594739b382a9cee239a5164d5c5d7ce1b5397f 100644 --- a/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6362-gpio-sysctl.yaml @@ -35,7 +35,7 @@ patternProperties: "^gpio@[0-9a-f]+$": # Child node type: object - $ref: "../gpio/brcm,bcm63xx-gpio.yaml" + $ref: /schemas/gpio/brcm,bcm63xx-gpio.yaml description: GPIO controller for the SoC GPIOs. This child node definition should follow the bindings specified in @@ -44,7 +44,7 @@ patternProperties: "^pinctrl@[0-9a-f]+$": # Child node type: object - $ref: "../pinctrl/brcm,bcm6362-pinctrl.yaml" + $ref: /schemas/pinctrl/brcm,bcm6362-pinctrl.yaml description: Pin controller for the SoC pins. This child node definition should follow the bindings specified in diff --git a/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml b/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml index 82d3e4415bda9634d5e34070f108d8ac73adcb22..aae83d432880ca361ab59f173d94d6312bb05af1 100644 --- a/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml +++ b/Documentation/devicetree/bindings/mfd/brcm,bcm6368-gpio-sysctl.yaml @@ -35,7 +35,7 @@ patternProperties: "^gpio@[0-9a-f]+$": # Child node type: object - $ref: "../gpio/brcm,bcm63xx-gpio.yaml" + $ref: /schemas/gpio/brcm,bcm63xx-gpio.yaml description: GPIO controller for the SoC GPIOs. This child node definition should follow the bindings specified in @@ -44,7 +44,7 @@ patternProperties: "^pinctrl@[0-9a-f]+$": # Child node type: object - $ref: "../pinctrl/brcm,bcm6368-pinctrl.yaml" + $ref: /schemas/pinctrl/brcm,bcm6368-pinctrl.yaml description: Pin controller for the SoC pins. This child node definition should follow the bindings specified in diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77693.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77693.yaml index 1b06a77ec79895cc0174b1e17b20625cf1867982..6a6f222b868ffa06e606e2bd4062efa5d319a6ec 100644 --- a/Documentation/devicetree/bindings/mfd/maxim,max77693.yaml +++ b/Documentation/devicetree/bindings/mfd/maxim,max77693.yaml @@ -37,6 +37,7 @@ properties: max77693-muic: type: object additionalProperties: false + deprecated: true properties: compatible: @@ -45,6 +46,21 @@ properties: required: - compatible + muic: + type: object + additionalProperties: false + + properties: + compatible: + const: maxim,max77693-muic + + connector: + $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + + required: + - compatible + motor-driver: type: object additionalProperties: false @@ -107,6 +123,38 @@ examples: }; }; + muic { + compatible = "maxim,max77693-muic"; + + connector { + compatible = "samsung,usb-connector-11pin", + "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + muic_to_usb: endpoint { + remote-endpoint = <&usb_to_muic>; + }; + }; + + port@3 { + reg = <3>; + + muic_to_mhl: endpoint { + remote-endpoint = <&mhl_to_muic>; + }; + }; + }; + }; + }; + motor-driver { compatible = "maxim,max77693-haptic"; haptic-supply = <&ldo26_reg>; diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml index 8b9a2008a354e0de31a88137ef2f97a5f793ab12..9f03436b1cdcd862b5828f4586147c159d4fc938 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -41,6 +41,7 @@ properties: - qcom,pm660 - qcom,pm660l - qcom,pm7250b + - qcom,pm7550ba - qcom,pm7325 - qcom,pm8004 - qcom,pm8005 @@ -70,6 +71,8 @@ properties: - qcom,pm8994 - qcom,pm8998 - qcom,pma8084 + - qcom,pmc8180 + - qcom,pmc8180c - qcom,pmd9635 - qcom,pmi632 - qcom,pmi8950 @@ -88,6 +91,7 @@ properties: - qcom,pms405 - qcom,pmx55 - qcom,pmx65 + - qcom,pmx75 - qcom,smb2351 - const: qcom,spmi-pmic @@ -127,7 +131,7 @@ patternProperties: "^audio-codec@[0-9a-f]+$": type: object - additionalProperties: true # FIXME qcom,pm8916-wcd-analog-codec binding not converted yet + $ref: /schemas/sound/qcom,pm8916-wcd-analog-codec.yaml# "^charger@[0-9a-f]+$": type: object diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71847-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71847-pmic.yaml index 7ab7b2c7f3e6ccec7ae144dc6f54751952594490..d783cc4e4e86448544b41cbf35a8391d78673338 100644 --- a/Documentation/devicetree/bindings/mfd/rohm,bd71847-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/rohm,bd71847-pmic.yaml @@ -130,7 +130,6 @@ dependencies: examples: - | #include - #include i2c { #address-cells = <1>; diff --git a/Documentation/devicetree/bindings/mfd/st,stmpe.yaml b/Documentation/devicetree/bindings/mfd/st,stmpe.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b77cc3f3075d7904a5fa4a5fee77af79dafaf504 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/st,stmpe.yaml @@ -0,0 +1,297 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/st,stmpe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectonics Port Expander (STMPE) + +description: STMicroelectronics Port Expander (STMPE) is a series of slow + bus controllers for various expanded peripherals such as GPIO, keypad, + touchscreen, ADC, PWM or rotator. It can contain one or several different + peripherals connected to SPI or I2C. + +maintainers: + - Linus Walleij + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - st,stmpe601 + - st,stmpe801 + - st,stmpe811 + - st,stmpe1600 + - st,stmpe1601 + - st,stmpe2401 + - st,stmpe2403 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vcc-supply: true + + vio-supply: true + + reset-gpios: + maxItems: 1 + + wakeup-source: true + + st,autosleep-timeout: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 4, 16, 32, 64, 128, 256, 512, 1024 ] + description: Time idle before going to automatic sleep to save power + + st,sample-time: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3, 4, 5, 6 ] + description: | + Sample time per iteration + 0 = 36 clock ticks + 1 = 44 clock ticks + 2 = 56 clock ticks + 3 = 64 clock ticks + 4 = 80 clock ticks - recommended + 5 = 96 clock ticks + 6 = 124 clock ticks + + st,mod-12b: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: ADC bit mode 0 = 10bit ADC, 1 = 12bit ADC + + st,ref-sel: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: ADC reference source 0 = internal, 1 = external + + st,adc-freq: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3 ] + description: | + ADC clock speed + 0 = 1.625 MHz + 1 = 3.25 MHz + 2, 3 = 6.5 MHz + + adc: + type: object + $ref: /schemas/iio/adc/st,stmpe-adc.yaml# + + gpio: + type: object + $ref: /schemas/gpio/st,stmpe-gpio.yaml# + + keyboard-controller: + type: object + $ref: /schemas/input/matrix-keymap.yaml# + + unevaluatedProperties: false + + properties: + compatible: + const: st,stmpe-keypad + + debounce-interval: + description: Debouncing interval in milliseconds + $ref: /schemas/types.yaml#/definitions/uint32 + + st,no-autorepeat: + description: If present, the keys will not autorepeat when pressed + $ref: /schemas/types.yaml#/definitions/flag + + st,scan-count: + description: Scanning cycles elapsed before key data is updated + $ref: /schemas/types.yaml#/definitions/uint32 + + required: + - compatible + - linux,keymap + + pwm: + type: object + $ref: /schemas/pwm/pwm.yaml# + + unevaluatedProperties: false + + properties: + compatible: + const: st,stmpe-pwm + + "#pwm-cells": + const: 2 + + touchscreen: + type: object + $ref: /schemas/input/touchscreen/touchscreen.yaml# + + unevaluatedProperties: false + + properties: + compatible: + const: st,stmpe-ts + + st,ave-ctrl: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3 ] + description: | + Sample average control + 0 = 1 sample + 1 = 2 samples + 2 = 4 samples + 3 = 8 samples + + st,touch-det-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3, 4, 5, 6, 7 ] + description: | + Touch detection delay + 0 = 10 us + 1 = 50 us + 2 = 100 us + 3 = 500 us - recommended + 4 = 1 ms + 5 = 5 ms + 6 = 10 ms + 7 = 50 ms + + st,settling: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3, 4, 5, 6, 7 ] + description: | + Panel driver settling time + 0 = 10 us + 1 = 100 us + 2 = 500 us - recommended + 3 = 1 ms + 4 = 5 ms + 5 = 10 ms + 6 = 50 ms + 7 = 100 ms + + st,fraction-z: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2, 3, 4, 5, 6, 7 ] + description: Length of the fractional part in z, recommended is 7 + (fraction-z ([0..7]) = Count of the fractional part) + + st,i-drive: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1 ] + description: | + current limit value of the touchscreen drivers + 0 = 20 mA (typical 35 mA max) + 1 = 50 mA (typical 80 mA max) + + required: + - compatible + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + port-expander@43 { + compatible = "st,stmpe2401"; + reg = <0x43>; + reset-gpios = <&gpio 13 GPIO_ACTIVE_LOW>; + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + vcc-supply = <&db8500_vsmps2_reg>; + vio-supply = <&db8500_vsmps2_reg>; + wakeup-source; + st,autosleep-timeout = <1024>; + + gpio { + compatible = "st,stmpe-gpio"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + st,norequest-mask = <0xf0f002>; + }; + + keyboard-controller { + compatible = "st,stmpe-keypad"; + debounce-interval = <64>; + st,scan-count = <8>; + st,no-autorepeat; + keypad,num-rows = <8>; + keypad,num-columns = <8>; + linux,keymap = < + MATRIX_KEY(0x00, 0x00, KEY_1) + MATRIX_KEY(0x00, 0x01, KEY_2) + MATRIX_KEY(0x00, 0x02, KEY_3) + MATRIX_KEY(0x00, 0x03, KEY_4) + MATRIX_KEY(0x00, 0x04, KEY_5) + MATRIX_KEY(0x00, 0x05, KEY_6) + MATRIX_KEY(0x00, 0x06, KEY_7) + MATRIX_KEY(0x00, 0x07, KEY_8) + MATRIX_KEY(0x00, 0x08, KEY_9) + MATRIX_KEY(0x00, 0x09, KEY_0) + >; + }; + + pwm { + compatible = "st,stmpe-pwm"; + #pwm-cells = <2>; + }; + }; + + port-expander@41 { + compatible = "st,stmpe811"; + reg = <0x41>; + interrupts = <10 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&gpio>; + st,adc-freq = <1>; + st,mod-12b = <1>; + st,ref-sel = <0>; + st,sample-time = <4>; + + adc { + compatible = "st,stmpe-adc"; + st,norequest-mask = <0x0f>; + #io-channel-cells = <1>; + }; + + gpio { + compatible = "st,stmpe-gpio"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + pwm { + compatible = "st,stmpe-pwm"; + #pwm-cells = <2>; + }; + + touchscreen { + compatible = "st,stmpe-ts"; + st,ave-ctrl = <3>; + st,touch-det-delay = <5>; + st,settling = <3>; + st,fraction-z = <7>; + st,i-drive = <1>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.yaml b/Documentation/devicetree/bindings/mfd/st,stpmic1.yaml index 97c61097f9e2661eed4eb382ee05ed8c9ba9e160..b17ebeb0a42f16000d1bec520f0f873abdcb1921 100644 --- a/Documentation/devicetree/bindings/mfd/st,stpmic1.yaml +++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.yaml @@ -106,6 +106,7 @@ properties: const: st,stpmic1-regulators ldo3: + $ref: /schemas/regulator/regulator.yaml type: object properties: @@ -128,6 +129,7 @@ properties: additionalProperties: false ldo4: + $ref: /schemas/regulator/regulator.yaml type: object properties: @@ -142,11 +144,14 @@ properties: regulator-name: true regulator-boot-on: true regulator-always-on: true + regulator-min-microvolt: true + regulator-max-microvolt: true regulator-over-current-protection: true additionalProperties: false vref_ddr: + $ref: /schemas/regulator/regulator.yaml type: object properties: @@ -165,6 +170,7 @@ properties: additionalProperties: false boost: + $ref: /schemas/regulator/regulator.yaml type: object properties: @@ -187,10 +193,8 @@ properties: "^(buck[1-4]|ldo[1-6]|vref_ddr|boost|pwr_sw[1-2])-supply$": description: STPMIC1 voltage regulators supplies - "^(buck[1-4]|ldo[1-6]|boost|vref_ddr|pwr_sw[1-2])$": - $ref: ../regulator/regulator.yaml - "^ldo[1-2,5-6]$": + $ref: /schemas/regulator/regulator.yaml type: object properties: @@ -213,6 +217,7 @@ properties: additionalProperties: false "^buck[1-4]$": + $ref: /schemas/regulator/regulator.yaml type: object properties: @@ -237,6 +242,7 @@ properties: additionalProperties: false "^pwr_sw[1-2]$": + $ref: /schemas/regulator/regulator.yaml type: object properties: diff --git a/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml b/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml index a66d58b4d1f2c46674189c8fc8a3f9f19ef8eea9..5e0002f099e45e870af2c3881ddb87fda6cfb98f 100644 --- a/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml +++ b/Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml @@ -72,44 +72,52 @@ properties: main voltage domain for the chip. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_varm: description: The voltage for the ARM Cortex A-9 CPU. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_vmodem: description: The voltage for the modem subsystem. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_vpll: description: The voltage for the phase locked loop clocks. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_vsmps1: description: Also known as VIO12, is a step-down voltage regulator for 1.2V I/O. SMPS means System Management Power Source. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_vsmps2: description: Also known as VIO18, is a step-down voltage regulator for 1.8V I/O. SMPS means System Management Power Source. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_vsmps3: description: This is a step-down voltage regulator for 0.87 thru 1.875V I/O. SMPS means System Management Power Source. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_vrf1: description: RF transceiver voltage regulator. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sva_mmdsp: description: Smart Video Accelerator (SVA) multimedia DSP (MMDSP) @@ -117,18 +125,21 @@ properties: for video encoding and decoding. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sva_mmdsp_ret: description: Smart Video Accelerator (SVA) multimedia DSP (MMDSP) voltage regulator for retention mode. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sva_pipe: description: Smart Video Accelerator (SVA) multimedia DSP (MMDSP) voltage regulator for the data pipe. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sia_mmdsp: description: Smart Image Accelerator (SIA) multimedia DSP (MMDSP) @@ -136,18 +147,21 @@ properties: for image encoding and decoding. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sia_mmdsp_ret: description: Smart Image Accelerator (SIA) multimedia DSP (MMDSP) voltage regulator for retention mode. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sia_pipe: description: Smart Image Accelerator (SIA) multimedia DSP (MMDSP) voltage regulator for the data pipe. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_sga: description: Smart Graphics Accelerator (SGA) voltage regulator. @@ -155,6 +169,7 @@ properties: accelerator block. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_b2r2_mcde: description: Blit Blend Rotate and Rescale (B2R2), and Multi-Channel @@ -162,28 +177,33 @@ properties: blocks. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_esram12: description: Embedded Static RAM (ESRAM) 1 and 2 voltage regulator. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_esram12_ret: description: Embedded Static RAM (ESRAM) 1 and 2 voltage regulator for retention mode. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_esram34: description: Embedded Static RAM (ESRAM) 3 and 4 voltage regulator. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false db8500_esram34_ret: description: Embedded Static RAM (ESRAM) 3 and 4 voltage regulator for retention mode. type: object $ref: ../regulator/regulator.yaml# + unevaluatedProperties: false required: - compatible diff --git a/Documentation/devicetree/bindings/mfd/stmpe.txt b/Documentation/devicetree/bindings/mfd/stmpe.txt deleted file mode 100644 index d4408a417193319950b04a15289a6f2942bcb658..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/mfd/stmpe.txt +++ /dev/null @@ -1,42 +0,0 @@ -* ST Microelectronics STMPE Multi-Functional Device - -STMPE is an MFD device which may expose the following inbuilt devices: gpio, -keypad, touchscreen, adc, pwm, rotator. - -Required properties: - - compatible : "st,stmpe[610|801|811|1600|1601|2401|2403]" - - reg : I2C/SPI address of the device - -Optional properties: - - interrupts : The interrupt outputs from the controller - - interrupt-controller : Marks the device node as an interrupt controller - - wakeup-source : Marks the input device as wakable - - st,autosleep-timeout : Valid entries (ms); 4, 16, 32, 64, 128, 256, 512 and 1024 - - irq-gpio : If present, which GPIO to use for event IRQ - -Optional properties for devices with touch and ADC (STMPE811|STMPE610): - - st,sample-time : ADC conversion time in number of clock. - 0 -> 36 clocks 4 -> 80 clocks (recommended) - 1 -> 44 clocks 5 -> 96 clocks - 2 -> 56 clocks 6 -> 124 clocks - 3 -> 64 clocks - - st,mod-12b : ADC Bit mode - 0 -> 10bit ADC 1 -> 12bit ADC - - st,ref-sel : ADC reference source - 0 -> internal 1 -> external - - st,adc-freq : ADC Clock speed - 0 -> 1.625 MHz 2 || 3 -> 6.5 MHz - 1 -> 3.25 MHz - -Example: - - stmpe1601: stmpe1601@40 { - compatible = "st,stmpe1601"; - reg = <0x40>; - interrupts = <26 0x4>; - interrupt-parent = <&gpio6>; - interrupt-controller; - - wakeup-source; - st,autosleep-timeout = <1024>; - }; diff --git a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml index 787ef488dd5b125b61bac74cce648f92ccd0249e..57b6957c84152c563124929605c60bc04f45ae9d 100644 --- a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml +++ b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml @@ -50,7 +50,7 @@ patternProperties: const: hw nand-ecc-step-size: - const: 1024 + enum: [512, 1024] nand-ecc-strength: enum: [8, 16, 24, 30, 40, 50, 60] @@ -66,6 +66,10 @@ patternProperties: unevaluatedProperties: false + dependencies: + nand-ecc-strength: [nand-ecc-step-size] + nand-ecc-step-size: [nand-ecc-strength] + required: - compatible diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml index 89959e5c47bae24917a7599fe989aa6c33a3c07a..58f0cea160ef54295b3a942d8cf7c7c46c04ec29 100644 --- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml +++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml @@ -43,8 +43,10 @@ properties: - const: jedec,spi-nor - const: jedec,spi-nor description: - Must also include "jedec,spi-nor" for any SPI NOR flash that can be - identified by the JEDEC READ ID opcode (0x9F). + SPI NOR flashes compatible with the JEDEC SFDP standard or which may be + identified with the READ ID opcode (0x9F) do not deserve a specific + compatible. They should instead only be matched against the generic + "jedec,spi-nor" compatible. reg: minItems: 1 @@ -70,6 +72,21 @@ properties: be used on such systems, to denote the absence of a reliable reset mechanism. + no-wp: + type: boolean + description: + The status register write disable (SRWD) bit in status register, combined + with the WP# signal, provides hardware data protection for the device. When + the SRWD bit is set to 1, and the WP# signal is either driven LOW or hard + strapped to LOW, the status register nonvolatile bits become read-only and + the WRITE STATUS REGISTER operation will not execute. The only way to exit + this hardware-protected mode is to drive WP# HIGH. If the WP# signal of the + flash device is not connected or is wrongly tied to GND (that includes internal + pull-downs) then status register permanently becomes read-only as the SRWD bit + cannot be reset. This boolean flag can be used on such systems to avoid setting + the SRWD bit while writing the status register. WP# signal hard strapped to GND + can be a valid use case. + reset-gpios: description: A GPIO line connected to the RESET (active low) signal of the device. diff --git a/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml b/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml index a10729bb18400347b502a1bf4426d7e4fdfe55a9..1ecea848e8b903e7a0d613303c058154b17c48d4 100644 --- a/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml +++ b/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml @@ -16,6 +16,7 @@ properties: - const: marvell,armada-8k-nand-controller - const: marvell,armada370-nand-controller - enum: + - marvell,ac5-nand-controller - marvell,armada370-nand-controller - marvell,pxa3xx-nand-controller - description: legacy bindings diff --git a/Documentation/devicetree/bindings/mtd/nand-controller.yaml b/Documentation/devicetree/bindings/mtd/nand-controller.yaml index 83a4fe4cc29d3f8a920baaa4f8ddb5116cdb7a9b..28167c0cf2719ffdad2159562a8de962f25c2b26 100644 --- a/Documentation/devicetree/bindings/mtd/nand-controller.yaml +++ b/Documentation/devicetree/bindings/mtd/nand-controller.yaml @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- $id: http://devicetree.org/schemas/mtd/nand-controller.yaml# diff --git a/Documentation/devicetree/bindings/mtd/oxnas-nand.txt b/Documentation/devicetree/bindings/mtd/oxnas-nand.txt deleted file mode 100644 index 2ba07fc8b79c91bce24e8246e35bcb930b78b808..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/mtd/oxnas-nand.txt +++ /dev/null @@ -1,41 +0,0 @@ -* Oxford Semiconductor OXNAS NAND Controller - -Please refer to nand-controller.yaml for generic information regarding MTD NAND bindings. - -Required properties: - - compatible: "oxsemi,ox820-nand" - - reg: Base address and length for NAND mapped memory. - -Optional Properties: - - clocks: phandle to the NAND gate clock if needed. - - resets: phandle to the NAND reset control if needed. - -Example: - -nandc: nand-controller@41000000 { - compatible = "oxsemi,ox820-nand"; - reg = <0x41000000 0x100000>; - clocks = <&stdclk CLK_820_NAND>; - resets = <&reset RESET_NAND>; - #address-cells = <1>; - #size-cells = <0>; - - nand@0 { - reg = <0>; - #address-cells = <1>; - #size-cells = <1>; - nand-ecc-mode = "soft"; - nand-ecc-algo = "hamming"; - - partition@0 { - label = "boot"; - reg = <0x00000000 0x00e00000>; - read-only; - }; - - partition@e00000 { - label = "ubi"; - reg = <0x00e00000 0x07200000>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/mtd/partitions/seama.yaml b/Documentation/devicetree/bindings/mtd/partitions/seama.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4c1cbf43e81a6424a29446a3762ed8c4514cf187 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/partitions/seama.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/partitions/seama.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Seattle Image Partitions + +description: The SEAttle iMAge (SEAMA) partition is a type of partition + used for NAND flash devices. This type of flash image is found in some + D-Link routers such as DIR-645, DIR-842, DIR-859, DIR-860L, DIR-885L, + DIR890L and DCH-M225, as well as in WD and NEC routers on the ath79 + (MIPS), Broadcom BCM53xx, and RAMIPS platforms. This partition type + does not have children defined in the device tree, they need to be + detected by software. + +allOf: + - $ref: partition.yaml# + +maintainers: + - Linus Walleij + +properties: + compatible: + const: seama + +required: + - compatible + +unevaluatedProperties: false + +examples: + - | + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + compatible = "seama"; + reg = <0x0 0x800000>; + label = "firmware"; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml index 0972868735fca7522ec629f5a0dbb6c6af55c664..0e07ab61a48d28954f278acb2ac05d430b492ff7 100644 --- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.yaml @@ -12,7 +12,6 @@ maintainers: allOf: - $ref: /schemas/pci/pci-bus.yaml# - - $ref: /schemas/interrupt-controller/msi-controller.yaml# properties: compatible: @@ -34,13 +33,6 @@ properties: description: > Base address and length of the PCIe controller I/O register space - interrupt-map: true - - interrupt-map-mask: true - - "#interrupt-cells": - const: 1 - ranges: minItems: 1 maxItems: 2 @@ -54,16 +46,8 @@ properties: items: - const: pcie-phy - bus-range: true - dma-coherent: true - "#address-cells": true - - "#size-cells": true - - device_type: true - brcm,pcie-ob: type: boolean description: > @@ -78,20 +62,24 @@ properties: msi: type: object + $ref: /schemas/interrupt-controller/msi-controller.yaml# + unevaluatedProperties: false + properties: compatible: items: - const: brcm,iproc-msi - msi-parent: true + interrupts: + maxItems: 4 - msi-controller: true + brcm,pcie-msi-inten: + type: boolean + description: + Needs to be present for some older iProc platforms that require the + interrupt enable registers to be set explicitly to enable MSI - brcm,pcie-msi-inten: - type: boolean - description: > - Needs to be present for some older iProc platforms that require the - interrupt enable registers to be set explicitly to enable MSI + msi-parent: true dependencies: brcm,pcie-ob-axi-offset: ["brcm,pcie-ob"] @@ -117,68 +105,69 @@ unevaluatedProperties: false examples: - | - #include - - bus { - #address-cells = <1>; - #size-cells = <1>; - pcie0: pcie@18012000 { - compatible = "brcm,iproc-pcie"; - reg = <0x18012000 0x1000>; - - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 100 IRQ_TYPE_NONE>; - - linux,pci-domain = <0>; - - bus-range = <0x00 0xff>; - - #address-cells = <3>; - #size-cells = <2>; - device_type = "pci"; - ranges = <0x81000000 0 0 0x28000000 0 0x00010000>, - <0x82000000 0 0x20000000 0x20000000 0 0x04000000>; - - phys = <&phy 0 5>; - phy-names = "pcie-phy"; - - brcm,pcie-ob; - brcm,pcie-ob-axi-offset = <0x00000000>; - - msi-parent = <&msi0>; - - /* iProc event queue based MSI */ - msi0: msi { - compatible = "brcm,iproc-msi"; - msi-controller; - interrupt-parent = <&gic>; - interrupts = , - , - , - ; - }; - }; - - pcie1: pcie@18013000 { - compatible = "brcm,iproc-pcie"; - reg = <0x18013000 0x1000>; - - #interrupt-cells = <1>; - interrupt-map-mask = <0 0 0 0>; - interrupt-map = <0 0 0 0 &gic GIC_SPI 106 IRQ_TYPE_NONE>; - - linux,pci-domain = <1>; - - bus-range = <0x00 0xff>; - - #address-cells = <3>; - #size-cells = <2>; - device_type = "pci"; - ranges = <0x81000000 0 0 0x48000000 0 0x00010000>, - <0x82000000 0 0x40000000 0x40000000 0 0x04000000>; - - phys = <&phy 1 6>; - phy-names = "pcie-phy"; - }; + #include + + gic: interrupt-controller { + interrupt-controller; + #interrupt-cells = <3>; + }; + + pcie@18012000 { + compatible = "brcm,iproc-pcie"; + reg = <0x18012000 0x1000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 100 IRQ_TYPE_NONE>; + + linux,pci-domain = <0>; + + bus-range = <0x00 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0x28000000 0 0x00010000>, + <0x82000000 0 0x20000000 0x20000000 0 0x04000000>; + + phys = <&phy 0 5>; + phy-names = "pcie-phy"; + + brcm,pcie-ob; + brcm,pcie-ob-axi-offset = <0x00000000>; + + msi-parent = <&msi0>; + + /* iProc event queue based MSI */ + msi0: msi { + compatible = "brcm,iproc-msi"; + msi-controller; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + }; + }; + - | + pcie@18013000 { + compatible = "brcm,iproc-pcie"; + reg = <0x18013000 0x1000>; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0>; + interrupt-map = <0 0 0 0 &gic GIC_SPI 106 IRQ_TYPE_NONE>; + + linux,pci-domain = <1>; + + bus-range = <0x00 0xff>; + + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + ranges = <0x81000000 0 0 0x48000000 0 0x00010000>, + <0x82000000 0 0x40000000 0x40000000 0 0x04000000>; + + phys = <&phy 1 6>; + phy-names = "pcie-phy"; }; diff --git a/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml index 230a17f249663a81d14ea1a33f0529f7d22aecee..2bb91542e984e788b20db49204cd97880ba6b224 100644 --- a/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml +++ b/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml @@ -64,7 +64,7 @@ description: | properties: $nodename: - pattern: "^t-phy@[0-9a-f]+$" + pattern: "^t-phy(@[0-9a-f]+)?$" compatible: oneOf: diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2671a048c926c29e5af62b340b49ae33140a4814 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,ipq5332-usb-hsphy.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,ipq5332-usb-hsphy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: M31 USB PHY + +maintainers: + - Sricharan Ramabadhran + - Varadarajan Narayanan + +description: + USB M31 PHY (https://www.m31tech.com) found in Qualcomm + IPQ5018, IPQ5332 SoCs. + +properties: + compatible: + items: + - const: qcom,ipq5332-usb-hsphy + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: cfg_ahb + + resets: + maxItems: 1 + + vdd-supply: + description: + Phandle to 5V regulator supply to PHY digital circuit. + +additionalProperties: false + +examples: + - | + #include + usb-phy@7b000 { + compatible = "qcom,ipq5332-usb-hsphy"; + reg = <0x0007b000 0x12c>; + + clocks = <&gcc GCC_USB0_PHY_CFG_AHB_CLK>; + clock-names = "cfg_ahb"; + + #phy-cells = <0>; + + resets = <&gcc GCC_QUSB2_0_PHY_BCR>; + + vdd-supply = <®ulator_fixed_5p0>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml index 3d42ee3901a170d1e5065727861addc54b7b0128..5073007267ad440a682fb1f32cf4f60a49d7abfd 100644 --- a/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,ipq8074-qmp-pcie-phy.yaml @@ -13,287 +13,79 @@ description: QMP PHY controller supports physical layer functionality for a number of controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB. - Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see - qcom,sc8280xp-qmp-pcie-phy.yaml. - properties: compatible: enum: - qcom,ipq6018-qmp-pcie-phy - qcom,ipq8074-qmp-gen3-pcie-phy - qcom,ipq8074-qmp-pcie-phy - - qcom,msm8998-qmp-pcie-phy - - qcom,sc8180x-qmp-pcie-phy - - qcom,sdm845-qhp-pcie-phy - - qcom,sdm845-qmp-pcie-phy - - qcom,sdx55-qmp-pcie-phy - - qcom,sm8250-qmp-gen3x1-pcie-phy - - qcom,sm8250-qmp-gen3x2-pcie-phy - - qcom,sm8250-qmp-modem-pcie-phy - - qcom,sm8450-qmp-gen3x1-pcie-phy - - qcom,sm8450-qmp-gen4x2-pcie-phy reg: items: - description: serdes - "#address-cells": - enum: [ 1, 2 ] - - "#size-cells": - enum: [ 1, 2 ] - - ranges: true - clocks: - minItems: 2 - maxItems: 4 + maxItems: 3 clock-names: - minItems: 2 - maxItems: 4 + items: + - const: aux + - const: cfg_ahb + - const: pipe resets: - minItems: 1 maxItems: 2 reset-names: - minItems: 1 - maxItems: 2 - - vdda-phy-supply: true - - vdda-pll-supply: true - - vddp-ref-clk-supply: true - -patternProperties: - "^phy@[0-9a-f]+$": - type: object - description: single PHY-provider child node - properties: - reg: - minItems: 3 - maxItems: 6 - - clocks: - items: - - description: PIPE clock - - clock-names: - deprecated: true - items: - - const: pipe0 - - "#clock-cells": - const: 0 - - clock-output-names: - maxItems: 1 + items: + - const: phy + - const: common - "#phy-cells": - const: 0 + "#clock-cells": + const: 0 - required: - - reg - - clocks - - "#clock-cells" - - clock-output-names - - "#phy-cells" + clock-output-names: + maxItems: 1 - additionalProperties: false + "#phy-cells": + const: 0 required: - compatible - reg - - "#address-cells" - - "#size-cells" - - ranges - clocks - clock-names - resets - reset-names + - "#clock-cells" + - clock-output-names + - "#phy-cells" additionalProperties: false -allOf: - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8998-qmp-pcie-phy - then: - properties: - clocks: - maxItems: 3 - clock-names: - items: - - const: aux - - const: cfg_ahb - - const: ref - resets: - maxItems: 2 - reset-names: - items: - - const: phy - - const: common - required: - - vdda-phy-supply - - vdda-pll-supply - - - if: - properties: - compatible: - contains: - enum: - - qcom,ipq6018-qmp-pcie-phy - - qcom,ipq8074-qmp-gen3-pcie-phy - - qcom,ipq8074-qmp-pcie-phy - then: - properties: - clocks: - maxItems: 2 - clock-names: - items: - - const: aux - - const: cfg_ahb - resets: - maxItems: 2 - reset-names: - items: - - const: phy - - const: common - - - if: - properties: - compatible: - contains: - enum: - - qcom,sc8180x-qmp-pcie-phy - - qcom,sdm845-qhp-pcie-phy - - qcom,sdm845-qmp-pcie-phy - - qcom,sdx55-qmp-pcie-phy - - qcom,sm8250-qmp-gen3x1-pcie-phy - - qcom,sm8250-qmp-gen3x2-pcie-phy - - qcom,sm8250-qmp-modem-pcie-phy - - qcom,sm8450-qmp-gen3x1-pcie-phy - - qcom,sm8450-qmp-gen4x2-pcie-phy - then: - properties: - clocks: - maxItems: 4 - clock-names: - items: - - const: aux - - const: cfg_ahb - - const: ref - - const: refgen - resets: - maxItems: 1 - reset-names: - items: - - const: phy - required: - - vdda-phy-supply - - vdda-pll-supply - - - if: - properties: - compatible: - contains: - enum: - - qcom,sc8180x-qmp-pcie-phy - - qcom,sm8250-qmp-gen3x2-pcie-phy - - qcom,sm8250-qmp-modem-pcie-phy - - qcom,sm8450-qmp-gen4x2-pcie-phy - then: - patternProperties: - "^phy@[0-9a-f]+$": - properties: - reg: - items: - - description: TX lane 1 - - description: RX lane 1 - - description: PCS - - description: TX lane 2 - - description: RX lane 2 - - description: PCS_MISC - - - if: - properties: - compatible: - contains: - enum: - - qcom,sdm845-qmp-pcie-phy - - qcom,sdx55-qmp-pcie-phy - - qcom,sm8250-qmp-gen3x1-pcie-phy - - qcom,sm8450-qmp-gen3x1-pcie-phy - then: - patternProperties: - "^phy@[0-9a-f]+$": - properties: - reg: - items: - - description: TX - - description: RX - - description: PCS - - description: PCS_MISC - - - if: - properties: - compatible: - contains: - enum: - - qcom,ipq6018-qmp-pcie-phy - - qcom,ipq8074-qmp-pcie-phy - - qcom,msm8998-qmp-pcie-phy - - qcom,sdm845-qhp-pcie-phy - then: - patternProperties: - "^phy@[0-9a-f]+$": - properties: - reg: - items: - - description: TX - - description: RX - - description: PCS - examples: - | - #include - phy-wrapper@1c0e000 { - compatible = "qcom,sm8250-qmp-gen3x2-pcie-phy"; - reg = <0x01c0e000 0x1c0>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x0 0x01c0e000 0x1000>; - - clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>, - <&gcc GCC_PCIE_1_CFG_AHB_CLK>, - <&gcc GCC_PCIE_WIGIG_CLKREF_EN>, - <&gcc GCC_PCIE1_PHY_REFGEN_CLK>; - clock-names = "aux", "cfg_ahb", "ref", "refgen"; - - resets = <&gcc GCC_PCIE_1_PHY_BCR>; - reset-names = "phy"; + #include + #include - vdda-phy-supply = <&vreg_l10c_0p88>; - vdda-pll-supply = <&vreg_l6b_1p2>; + phy@84000 { + compatible = "qcom,ipq6018-qmp-pcie-phy"; + reg = <0x0 0x00084000 0x0 0x1000>; - phy@200 { - reg = <0x200 0x170>, - <0x400 0x200>, - <0xa00 0x1f0>, - <0x600 0x170>, - <0x800 0x200>, - <0xe00 0xf4>; + clocks = <&gcc GCC_PCIE0_AUX_CLK>, + <&gcc GCC_PCIE0_AHB_CLK>, + <&gcc GCC_PCIE0_PIPE_CLK>; + clock-names = "aux", + "cfg_ahb", + "pipe"; - clocks = <&gcc GCC_PCIE_1_PIPE_CLK>; + clock-output-names = "gcc_pcie0_pipe_clk_src"; + #clock-cells = <0>; - #clock-cells = <0>; - clock-output-names = "pcie_1_pipe_clk"; + #phy-cells = <0>; - #phy-cells = <0>; - }; + resets = <&gcc GCC_PCIE0_PHY_BCR>, + <&gcc GCC_PCIE0PHY_PHY_BCR>; + reset-names = "phy", + "common"; }; diff --git a/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-ufs-phy.yaml deleted file mode 100644 index 881ba543fd465e914a75e23cb93bb5c02bce1dbc..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-ufs-phy.yaml +++ /dev/null @@ -1,228 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/phy/qcom,msm8996-qmp-ufs-phy.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Qualcomm QMP PHY controller (UFS, MSM8996) - -maintainers: - - Vinod Koul - -description: - QMP PHY controller supports physical layer functionality for a number of - controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB. - - Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see - qcom,sc8280xp-qmp-ufs-phy.yaml. - -properties: - compatible: - enum: - - qcom,msm8996-qmp-ufs-phy - - qcom,msm8998-qmp-ufs-phy - - qcom,sc8180x-qmp-ufs-phy - - qcom,sdm845-qmp-ufs-phy - - qcom,sm6115-qmp-ufs-phy - - qcom,sm6350-qmp-ufs-phy - - qcom,sm8150-qmp-ufs-phy - - qcom,sm8250-qmp-ufs-phy - - qcom,sm8350-qmp-ufs-phy - - qcom,sm8450-qmp-ufs-phy - - reg: - items: - - description: serdes - - "#address-cells": - enum: [ 1, 2 ] - - "#size-cells": - enum: [ 1, 2 ] - - ranges: true - - clocks: - minItems: 1 - maxItems: 3 - - clock-names: - minItems: 1 - maxItems: 3 - - power-domains: - maxItems: 1 - - resets: - maxItems: 1 - - reset-names: - items: - - const: ufsphy - - vdda-phy-supply: true - - vdda-pll-supply: true - - vddp-ref-clk-supply: true - -patternProperties: - "^phy@[0-9a-f]+$": - type: object - description: single PHY-provider child node - properties: - reg: - minItems: 3 - maxItems: 6 - - "#clock-cells": - const: 1 - - "#phy-cells": - const: 0 - - required: - - reg - - "#phy-cells" - - additionalProperties: false - -required: - - compatible - - reg - - "#address-cells" - - "#size-cells" - - ranges - - clocks - - clock-names - - resets - - reset-names - - vdda-phy-supply - - vdda-pll-supply - -additionalProperties: false - -allOf: - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8996-qmp-ufs-phy - then: - properties: - clocks: - maxItems: 1 - clock-names: - items: - - const: ref - - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8998-qmp-ufs-phy - - qcom,sc8180x-qmp-ufs-phy - - qcom,sdm845-qmp-ufs-phy - - qcom,sm6115-qmp-ufs-phy - - qcom,sm6350-qmp-ufs-phy - - qcom,sm8150-qmp-ufs-phy - - qcom,sm8250-qmp-ufs-phy - then: - properties: - clocks: - maxItems: 2 - clock-names: - items: - - const: ref - - const: ref_aux - - - if: - properties: - compatible: - contains: - enum: - - qcom,sm8450-qmp-ufs-phy - then: - properties: - clocks: - maxItems: 3 - clock-names: - items: - - const: ref - - const: ref_aux - - const: qref - - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8998-qmp-ufs-phy - - qcom,sc8180x-qmp-ufs-phy - - qcom,sdm845-qmp-ufs-phy - - qcom,sm6350-qmp-ufs-phy - - qcom,sm8150-qmp-ufs-phy - - qcom,sm8250-qmp-ufs-phy - - qcom,sm8350-qmp-ufs-phy - - qcom,sm8450-qmp-ufs-phy - then: - patternProperties: - "^phy@[0-9a-f]+$": - properties: - reg: - items: - - description: TX lane 1 - - description: RX lane 1 - - description: PCS - - description: TX lane 2 - - description: RX lane 2 - - - if: - properties: - compatible: - contains: - enum: - - qcom,msm8996-qmp-ufs-phy - - qcom,sm6115-qmp-ufs-phy - then: - patternProperties: - "^phy@[0-9a-f]+$": - properties: - reg: - items: - - description: TX - - description: RX - - description: PCS - -examples: - - | - #include - #include - - phy-wrapper@1d87000 { - compatible = "qcom,sm8250-qmp-ufs-phy"; - reg = <0x01d87000 0x1c0>; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x0 0x01d87000 0x1000>; - - clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_UFS_PHY_PHY_AUX_CLK>; - clock-names = "ref", "ref_aux"; - - resets = <&ufs_mem_hc 0>; - reset-names = "ufsphy"; - - vdda-phy-supply = <&vreg_l6b>; - vdda-pll-supply = <&vreg_l3b>; - - phy@400 { - reg = <0x400 0x108>, - <0x600 0x1e0>, - <0xc00 0x1dc>, - <0x800 0x108>, - <0xa00 0x1e0>; - #phy-cells = <0>; - }; - }; diff --git a/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-usb3-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-usb3-phy.yaml index 4c96dab5b9e3635c35d83f395c776e7c46094da8..827109d3704150b0d8e9478eb7d7357fc87fb771 100644 --- a/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-usb3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,msm8996-qmp-usb3-phy.yaml @@ -23,25 +23,16 @@ properties: - qcom,ipq8074-qmp-usb3-phy - qcom,msm8996-qmp-usb3-phy - qcom,msm8998-qmp-usb3-phy - - qcom,sc7180-qmp-usb3-phy - - qcom,sc8180x-qmp-usb3-phy - - qcom,sdm845-qmp-usb3-phy - qcom,sdm845-qmp-usb3-uni-phy - qcom,sdx55-qmp-usb3-uni-phy - qcom,sdx65-qmp-usb3-uni-phy - - qcom,sm8150-qmp-usb3-phy - qcom,sm8150-qmp-usb3-uni-phy - - qcom,sm8250-qmp-usb3-phy - qcom,sm8250-qmp-usb3-uni-phy - - qcom,sm8350-qmp-usb3-phy - qcom,sm8350-qmp-usb3-uni-phy - - qcom,sm8450-qmp-usb3-phy reg: - minItems: 1 items: - description: serdes - - description: DP_COM "#address-cells": enum: [ 1, 2 ] @@ -126,28 +117,6 @@ required: additionalProperties: false allOf: - - if: - properties: - compatible: - contains: - enum: - - qcom,sc7180-qmp-usb3-phy - then: - properties: - clocks: - maxItems: 4 - clock-names: - items: - - const: aux - - const: cfg_ahb - - const: ref - - const: com_aux - resets: - maxItems: 1 - reset-names: - items: - - const: phy - - if: properties: compatible: @@ -202,7 +171,6 @@ allOf: compatible: contains: enum: - - qcom,sm8150-qmp-usb3-phy - qcom,sm8150-qmp-usb3-uni-phy - qcom,sm8250-qmp-usb3-uni-phy - qcom,sm8350-qmp-usb3-uni-phy @@ -223,51 +191,6 @@ allOf: - const: phy - const: common - - if: - properties: - compatible: - contains: - enum: - - qcom,sm8250-qmp-usb3-phy - - qcom,sm8350-qmp-usb3-phy - then: - properties: - clocks: - maxItems: 3 - clock-names: - items: - - const: aux - - const: ref_clk_src - - const: com_aux - resets: - maxItems: 2 - reset-names: - items: - - const: phy - - const: common - - - if: - properties: - compatible: - contains: - enum: - - qcom,sdm845-qmp-usb3-phy - - qcom,sm8150-qmp-usb3-phy - - qcom,sm8350-qmp-usb3-phy - - qcom,sm8450-qmp-usb3-phy - then: - patternProperties: - "^phy@[0-9a-f]+$": - properties: - reg: - items: - - description: TX lane 1 - - description: RX lane 1 - - description: PCS - - description: TX lane 2 - - description: RX lane 2 - - description: PCS_MISC - - if: properties: compatible: @@ -293,12 +216,9 @@ allOf: enum: - qcom,ipq6018-qmp-usb3-phy - qcom,ipq8074-qmp-usb3-phy - - qcom,sc7180-qmp-usb3-phy - - qcom,sc8180x-qmp-usb3-phy - qcom,sdx55-qmp-usb3-uni-phy - qcom,sdx65-qmp-usb3-uni-phy - qcom,sm8150-qmp-usb3-uni-phy - - qcom,sm8250-qmp-usb3-phy then: patternProperties: "^phy@[0-9a-f]+$": diff --git a/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-pcie-phy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d05eef0e1ccdfcacbc74cf4cdb3e999107a8a0e6 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,msm8998-qmp-pcie-phy.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/qcom,msm8998-qmp-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QMP PHY controller (PCIe, MSM8998) + +maintainers: + - Vinod Koul + +description: + The QMP PHY controller supports physical layer functionality for a number of + controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB. + +properties: + compatible: + const: qcom,msm8998-qmp-pcie-phy + + reg: + items: + - description: serdes + + clocks: + maxItems: 4 + + clock-names: + items: + - const: aux + - const: cfg_ahb + - const: ref + - const: pipe + + resets: + maxItems: 2 + + reset-names: + items: + - const: phy + - const: common + + vdda-phy-supply: true + + vdda-pll-supply: true + + "#clock-cells": + const: 0 + + clock-output-names: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + - vdda-phy-supply + - vdda-pll-supply + - "#clock-cells" + - clock-output-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + #include + + phy@1c18000 { + compatible = "qcom,msm8998-qmp-pcie-phy"; + reg = <0x01c06000 0x1000>; + + clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_CLKREF_CLK>, + <&gcc GCC_PCIE_0_PIPE_CLK>; + clock-names = "aux", + "cfg_ahb", + "ref", + "pipe"; + + clock-output-names = "pcie_0_pipe_clk_src"; + #clock-cells = <0>; + + #phy-cells = <0>; + + resets = <&gcc GCC_PCIE_0_PHY_BCR>, <&gcc GCC_PCIE_PHY_BCR>; + reset-names = "phy", "common"; + + vdda-phy-supply = <&vreg_l1a_0p875>; + vdda-pll-supply = <&vreg_l2a_1p2>; + }; diff --git a/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml deleted file mode 100644 index d307343388888e96a52b41979aeedee806992b15..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml +++ /dev/null @@ -1,282 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) - -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Qualcomm QMP USB3 DP PHY controller (SC7180) - -description: - The QMP PHY controller supports physical layer functionality for a number of - controllers on Qualcomm chipsets, such as, PCIe, UFS and USB. - - Note that these bindings are for SoCs up to SC8180X. For newer SoCs, see - qcom,sc8280xp-qmp-usb43dp-phy.yaml. - -maintainers: - - Wesley Cheng - -properties: - compatible: - oneOf: - - enum: - - qcom,sc7180-qmp-usb3-dp-phy - - qcom,sc8180x-qmp-usb3-dp-phy - - qcom,sdm845-qmp-usb3-dp-phy - - qcom,sm8250-qmp-usb3-dp-phy - - items: - - enum: - - qcom,sc7280-qmp-usb3-dp-phy - - const: qcom,sm8250-qmp-usb3-dp-phy - - reg: - items: - - description: Address and length of PHY's USB serdes block. - - description: Address and length of the DP_COM control block. - - description: Address and length of PHY's DP serdes block. - - reg-names: - items: - - const: usb - - const: dp_com - - const: dp - - "#address-cells": - enum: [ 1, 2 ] - - "#size-cells": - enum: [ 1, 2 ] - - ranges: true - - clocks: - minItems: 3 - maxItems: 4 - - clock-names: - minItems: 3 - maxItems: 4 - - power-domains: - maxItems: 1 - - orientation-switch: - description: Flag the port as possible handler of orientation switching - type: boolean - - resets: - items: - - description: reset of phy block. - - description: phy common block reset. - - reset-names: - items: - - const: phy - - const: common - - vdda-phy-supply: - description: - Phandle to a regulator supply to PHY core block. - - vdda-pll-supply: - description: - Phandle to 1.8V regulator supply to PHY refclk pll block. - - vddp-ref-clk-supply: - description: - Phandle to a regulator supply to any specific refclk pll block. - -# Required nodes: -patternProperties: - "^usb3-phy@[0-9a-f]+$": - type: object - additionalProperties: false - description: - The USB3 PHY. - - properties: - reg: - items: - - description: Address and length of TX. - - description: Address and length of RX. - - description: Address and length of PCS. - - description: Address and length of TX2. - - description: Address and length of RX2. - - description: Address and length of pcs_misc. - - clocks: - items: - - description: pipe clock - - clock-names: - deprecated: true - items: - - const: pipe0 - - clock-output-names: - items: - - const: usb3_phy_pipe_clk_src - - '#clock-cells': - const: 0 - - '#phy-cells': - const: 0 - - required: - - reg - - clocks - - '#clock-cells' - - '#phy-cells' - - "^dp-phy@[0-9a-f]+$": - type: object - additionalProperties: false - description: - The DP PHY. - - properties: - reg: - items: - - description: Address and length of TX. - - description: Address and length of RX. - - description: Address and length of PCS. - - description: Address and length of TX2. - - description: Address and length of RX2. - - '#clock-cells': - const: 1 - - '#phy-cells': - const: 0 - - required: - - reg - - '#clock-cells' - - '#phy-cells' - -required: - - compatible - - reg - - "#address-cells" - - "#size-cells" - - ranges - - clocks - - clock-names - - resets - - reset-names - - vdda-phy-supply - - vdda-pll-supply - -allOf: - - if: - properties: - compatible: - enum: - - qcom,sc7180-qmp-usb3-dp-phy - - qcom,sdm845-qmp-usb3-dp-phy - then: - properties: - clocks: - items: - - description: Phy aux clock - - description: Phy config clock - - description: 19.2 MHz ref clk - - description: Phy common block aux clock - clock-names: - items: - - const: aux - - const: cfg_ahb - - const: ref - - const: com_aux - - - if: - properties: - compatible: - enum: - - qcom,sc8180x-qmp-usb3-dp-phy - then: - properties: - clocks: - items: - - description: Phy aux clock - - description: 19.2 MHz ref clk - - description: Phy common block aux clock - clock-names: - items: - - const: aux - - const: ref - - const: com_aux - - - if: - properties: - compatible: - enum: - - qcom,sm8250-qmp-usb3-dp-phy - then: - properties: - clocks: - items: - - description: Phy aux clock - - description: Board XO source - - description: Phy common block aux clock - clock-names: - items: - - const: aux - - const: ref_clk_src - - const: com_aux - -additionalProperties: false - -examples: - - | - #include - usb_1_qmpphy: phy-wrapper@88e9000 { - compatible = "qcom,sdm845-qmp-usb3-dp-phy"; - reg = <0x088e9000 0x18c>, - <0x088e8000 0x10>, - <0x088ea000 0x40>; - reg-names = "usb", "dp_com", "dp"; - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x0 0x088e9000 0x2000>; - - clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>, - <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>, - <&gcc GCC_USB3_PRIM_CLKREF_CLK>, - <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>; - clock-names = "aux", "cfg_ahb", "ref", "com_aux"; - - resets = <&gcc GCC_USB3_PHY_PRIM_BCR>, - <&gcc GCC_USB3_DP_PHY_PRIM_BCR>; - reset-names = "phy", "common"; - - vdda-phy-supply = <&vdda_usb2_ss_1p2>; - vdda-pll-supply = <&vdda_usb2_ss_core>; - - orientation-switch; - - usb3-phy@200 { - reg = <0x200 0x128>, - <0x400 0x200>, - <0xc00 0x218>, - <0x600 0x128>, - <0x800 0x200>, - <0xa00 0x100>; - #clock-cells = <0>; - #phy-cells = <0>; - clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; - clock-output-names = "usb3_phy_pipe_clk_src"; - }; - - dp-phy@88ea200 { - reg = <0xa200 0x200>, - <0xa400 0x200>, - <0xaa00 0x200>, - <0xa600 0x200>, - <0xa800 0x200>; - #clock-cells = <1>; - #phy-cells = <0>; - }; - }; diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml index a0407fc795637e773070ade8f84ee051dd1a6fba..2c3d6553a7bac692f688adaf9ac5ff568d862e46 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml @@ -16,11 +16,24 @@ description: properties: compatible: enum: + - qcom,sa8775p-qmp-gen4x2-pcie-phy + - qcom,sa8775p-qmp-gen4x4-pcie-phy + - qcom,sc8180x-qmp-pcie-phy - qcom,sc8280xp-qmp-gen3x1-pcie-phy - qcom,sc8280xp-qmp-gen3x2-pcie-phy - qcom,sc8280xp-qmp-gen3x4-pcie-phy + - qcom,sdm845-qhp-pcie-phy + - qcom,sdm845-qmp-pcie-phy + - qcom,sdx55-qmp-pcie-phy - qcom,sdx65-qmp-gen4x2-pcie-phy + - qcom,sm8150-qmp-gen3x1-pcie-phy + - qcom,sm8150-qmp-gen3x2-pcie-phy + - qcom,sm8250-qmp-gen3x1-pcie-phy + - qcom,sm8250-qmp-gen3x2-pcie-phy + - qcom,sm8250-qmp-modem-pcie-phy - qcom,sm8350-qmp-gen3x1-pcie-phy + - qcom,sm8450-qmp-gen3x1-pcie-phy + - qcom,sm8450-qmp-gen4x2-pcie-phy - qcom,sm8550-qmp-gen3x2-pcie-phy - qcom,sm8550-qmp-gen4x2-pcie-phy @@ -30,7 +43,7 @@ properties: clocks: minItems: 5 - maxItems: 6 + maxItems: 7 clock-names: minItems: 5 @@ -38,9 +51,10 @@ properties: - const: aux - const: cfg_ahb - const: ref - - const: rchng + - enum: [rchng, refgen] - const: pipe - const: pipediv2 + - const: phy_aux power-domains: maxItems: 1 @@ -84,7 +98,6 @@ required: - reg - clocks - clock-names - - power-domains - resets - reset-names - vdda-phy-supply @@ -120,7 +133,18 @@ allOf: compatible: contains: enum: + - qcom,sc8180x-qmp-pcie-phy + - qcom,sdm845-qhp-pcie-phy + - qcom,sdm845-qmp-pcie-phy + - qcom,sdx55-qmp-pcie-phy + - qcom,sm8150-qmp-gen3x1-pcie-phy + - qcom,sm8150-qmp-gen3x2-pcie-phy + - qcom,sm8250-qmp-gen3x1-pcie-phy + - qcom,sm8250-qmp-gen3x2-pcie-phy + - qcom,sm8250-qmp-modem-pcie-phy - qcom,sm8350-qmp-gen3x1-pcie-phy + - qcom,sm8450-qmp-gen3x1-pcie-phy + - qcom,sm8450-qmp-gen3x2-pcie-phy - qcom,sm8550-qmp-gen3x2-pcie-phy - qcom,sm8550-qmp-gen4x2-pcie-phy then: @@ -129,13 +153,36 @@ allOf: maxItems: 5 clock-names: maxItems: 5 - else: + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-qmp-gen3x1-pcie-phy + - qcom,sc8280xp-qmp-gen3x2-pcie-phy + - qcom,sc8280xp-qmp-gen3x4-pcie-phy + then: properties: clocks: minItems: 6 clock-names: minItems: 6 + - if: + properties: + compatible: + contains: + enum: + - qcom,sa8775p-qmp-gen4x2-pcie-phy + - qcom,sa8775p-qmp-gen4x4-pcie-phy + then: + properties: + clocks: + minItems: 7 + clock-names: + minItems: 7 + - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml index a1897a7606df489666db860d2e6784dafa1d74ab..d981d77e82e40afcdecda0d31b54cd833dbe03aa 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml @@ -16,21 +16,31 @@ description: properties: compatible: enum: + - qcom,msm8996-qmp-ufs-phy + - qcom,msm8998-qmp-ufs-phy - qcom,sa8775p-qmp-ufs-phy + - qcom,sc8180x-qmp-ufs-phy - qcom,sc8280xp-qmp-ufs-phy + - qcom,sdm845-qmp-ufs-phy + - qcom,sm6115-qmp-ufs-phy - qcom,sm6125-qmp-ufs-phy + - qcom,sm6350-qmp-ufs-phy - qcom,sm7150-qmp-ufs-phy + - qcom,sm8150-qmp-ufs-phy + - qcom,sm8250-qmp-ufs-phy + - qcom,sm8350-qmp-ufs-phy + - qcom,sm8450-qmp-ufs-phy - qcom,sm8550-qmp-ufs-phy reg: maxItems: 1 clocks: - minItems: 2 + minItems: 1 maxItems: 3 clock-names: - minItems: 2 + minItems: 1 items: - const: ref - const: ref_aux @@ -75,19 +85,51 @@ allOf: contains: enum: - qcom,sa8775p-qmp-ufs-phy + - qcom,sm8450-qmp-ufs-phy then: properties: clocks: minItems: 3 clock-names: minItems: 3 - else: + + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8998-qmp-ufs-phy + - qcom,sc8180x-qmp-ufs-phy + - qcom,sc8280xp-qmp-ufs-phy + - qcom,sdm845-qmp-ufs-phy + - qcom,sm6115-qmp-ufs-phy + - qcom,sm6125-qmp-ufs-phy + - qcom,sm6350-qmp-ufs-phy + - qcom,sm7150-qmp-ufs-phy + - qcom,sm8150-qmp-ufs-phy + - qcom,sm8250-qmp-ufs-phy + - qcom,sm8350-qmp-ufs-phy + - qcom,sm8550-qmp-ufs-phy + then: properties: clocks: maxItems: 2 clock-names: maxItems: 2 + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8996-qmp-ufs-phy + then: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml index ef1c02d8ac88e055176ece483fefc3fe6bab19a1..9af203dc8793f34ee6adf58813c5838fc71200e8 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb43dp-phy.yaml @@ -16,8 +16,14 @@ description: properties: compatible: enum: + - qcom,sc7180-qmp-usb3-dp-phy + - qcom,sc7280-qmp-usb3-dp-phy + - qcom,sc8180x-qmp-usb3-dp-phy - qcom,sc8280xp-qmp-usb43dp-phy + - qcom,sdm845-qmp-usb3-dp-phy - qcom,sm6350-qmp-usb3-dp-phy + - qcom,sm8150-qmp-usb3-dp-phy + - qcom,sm8250-qmp-usb3-dp-phy - qcom,sm8350-qmp-usb3-dp-phy - qcom,sm8450-qmp-usb3-dp-phy - qcom,sm8550-qmp-usb3-dp-phy @@ -26,14 +32,17 @@ properties: maxItems: 1 clocks: - maxItems: 4 + minItems: 4 + maxItems: 5 clock-names: + minItems: 4 items: - const: aux - const: ref - const: com_aux - const: usb3_pipe + - const: cfg_ahb power-domains: maxItems: 1 @@ -85,7 +94,6 @@ required: - reg - clocks - clock-names - - power-domains - resets - reset-names - vdda-phy-supply @@ -93,6 +101,40 @@ required: - "#clock-cells" - "#phy-cells" +allOf: + - if: + properties: + compatible: + enum: + - qcom,sc7180-qmp-usb3-dp-phy + - qcom,sdm845-qmp-usb3-dp-phy + then: + properties: + clocks: + maxItems: 5 + clock-names: + maxItems: 5 + else: + properties: + clocks: + maxItems: 4 + clock-names: + maxItems: 4 + + - if: + properties: + compatible: + enum: + - qcom,sc8280xp-qmp-usb43dp-phy + - qcom,sm6350-qmp-usb3-dp-phy + - qcom,sm8550-qmp-usb3-dp-phy + then: + required: + - power-domains + else: + properties: + power-domains: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml index 083fda530b4847ed7904600da2b5430b0862e4a7..029569d5fcf35f43f91b21ce521ac32193f81345 100644 --- a/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,snps-eusb2-repeater.yaml @@ -15,7 +15,12 @@ description: properties: compatible: - const: qcom,pm8550b-eusb2-repeater + oneOf: + - items: + - enum: + - qcom,pm7550ba-eusb2-repeater + - const: qcom,pm8550b-eusb2-repeater + - const: qcom,pm8550b-eusb2-repeater reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml index 0d6b8c28be07103639bee941ed8ce1cd67635cc3..5254413137c644b2e8e1db4285c2278799de098b 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,inno-usb2phy.yaml @@ -20,6 +20,7 @@ properties: - rockchip,rk3366-usb2phy - rockchip,rk3399-usb2phy - rockchip,rk3568-usb2phy + - rockchip,rk3588-usb2phy - rockchip,rv1108-usb2phy reg: @@ -56,6 +57,14 @@ properties: description: Muxed interrupt for both ports maxItems: 1 + resets: + maxItems: 2 + + reset-names: + items: + - const: phy + - const: apb + rockchip,usbgrf: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -120,15 +129,21 @@ required: - reg - clock-output-names - "#clock-cells" - - host-port - - otg-port + +anyOf: + - required: + - otg-port + - required: + - host-port allOf: - if: properties: compatible: contains: - const: rockchip,rk3568-usb2phy + enum: + - rockchip,rk3568-usb2phy + - rockchip,rk3588-usb2phy then: properties: diff --git a/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml b/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml index 9f2d8d2cc7a5468bb42208a92f6724e250711c7a..c4fbffcde6e4037a2f29f6ad10528b665f19dfef 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,pcie3-phy.yaml @@ -13,19 +13,18 @@ properties: compatible: enum: - rockchip,rk3568-pcie3-phy + - rockchip,rk3588-pcie3-phy reg: maxItems: 1 clocks: - minItems: 3 + minItems: 1 maxItems: 3 clock-names: - items: - - const: refclk_m - - const: refclk_n - - const: pclk + minItems: 1 + maxItems: 3 data-lanes: description: which lanes (by position) should be mapped to which @@ -61,6 +60,30 @@ required: - rockchip,phy-grf - "#phy-cells" +allOf: + - if: + properties: + compatible: + enum: + - rockchip,rk3588-pcie3-phy + then: + properties: + clocks: + maxItems: 1 + clock-names: + items: + - const: pclk + else: + properties: + clocks: + minItems: 3 + + clock-names: + items: + - const: refclk_m + - const: refclk_n + - const: pclk + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml index 5c35e5ceec0bbb9a5ffdfbde65c4707a857286f9..46e64fa293d5010b50146e2c6dfdd3facc923d08 100644 --- a/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml +++ b/Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml @@ -19,6 +19,7 @@ properties: - rockchip,rk3128-dsi-dphy - rockchip,rk3368-dsi-dphy - rockchip,rk3568-dsi-dphy + - rockchip,rv1126-dsi-dphy reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml index 5ba55f9f20cc58cf19434d3d4d04406568d5d291..452e584d98121fb86c0bed983fe11d97d8c0de68 100644 --- a/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml +++ b/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml @@ -29,6 +29,7 @@ properties: - samsung,exynos5420-usbdrd-phy - samsung,exynos5433-usbdrd-phy - samsung,exynos7-usbdrd-phy + - samsung,exynos850-usbdrd-phy clocks: minItems: 2 diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7224cde6fce0167e2d46494f13f1417bb294c473 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-dphy-rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive SoC JH7110 MIPI D-PHY Rx Controller + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + StarFive SoCs contain a MIPI CSI D-PHY based on M31 IP, used to + transfer CSI camera data. + +properties: + compatible: + const: starfive,jh7110-dphy-rx + + reg: + maxItems: 1 + + clocks: + items: + - description: config clock + - description: reference clock + - description: escape mode transmit clock + + clock-names: + items: + - const: cfg + - const: ref + - const: tx + + resets: + items: + - description: DPHY_HW reset + - description: DPHY_B09_ALWAYS_ON reset + + power-domains: + maxItems: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - power-domains + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x19820000 0x10000>; + clocks = <&ispcrg 3>, + <&ispcrg 4>, + <&ispcrg 5>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg 2>, + <&ispcrg 3>; + power-domains = <&aon_syscon 1>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..2e83a6164cd1dec8c2c5b6c2ca8289dbe282920d --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PCIe 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-pcie-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + starfive,sys-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller sys_syscon node. + - description: PHY connect offset of SYS_SYSCONSAIF__SYSCFG register for USB PHY. + description: + The phandle to System Register Controller syscon node and the PHY connect offset + of SYS_SYSCONSAIF__SYSCFG register. Connect PHY to USB3 controller. + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller stg_syscon node. + - description: PHY mode offset of STG_SYSCONSAIF__SYSCFG register. + - description: PHY enable for USB offset of STG_SYSCONSAIF__SYSCFG register. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for PCIe PHY. Total 2 regsisters offset. + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10210000 0x10000>; + #phy-cells = <0>; + starfive,sys-syscon = <&sys_syscon 0x18>; + starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>; + }; diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..269e9f9f12b6266b16aa21d7c9f2e8f79ebbe8c8 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 USB 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: PHY 125m + - description: app 125m + + clock-names: + items: + - const: 125m + - const: app_125m + +required: + - compatible + - reg + - clocks + - clock-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x10200000 0x10000>; + clocks = <&syscrg 95>, + <&stgcrg 6>; + clock-names = "125m", "app_125m"; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.txt b/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.txt deleted file mode 100644 index c42eecfc81ed06daabefb4f97b5d7225be35cba0..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.txt +++ /dev/null @@ -1,21 +0,0 @@ -Broadcom Kona PWM controller device tree bindings - -This controller has 6 channels. - -Required Properties : -- compatible: should contain "brcm,kona-pwm" -- reg: physical base address and length of the controller's registers -- clocks: phandle + clock specifier pair for the external clock -- #pwm-cells: Should be 3. See pwm.yaml in this directory for a - description of the cells format. - -Refer to clocks/clock-bindings.txt for generic clock consumer properties. - -Example: - -pwm: pwm@3e01a000 { - compatible = "brcm,bcm11351-pwm", "brcm,kona-pwm"; - reg = <0x3e01a000 0xc4>; - clocks = <&pwm_clk>; - #pwm-cells = <3>; -}; diff --git a/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.yaml b/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e86c8053b366a2e24da8dd69f71c12d9552e14a3 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/brcm,kona-pwm.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/brcm,kona-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom Kona family PWM controller + +description: + This controller has 6 channels. + +maintainers: + - Florian Fainelli + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + items: + - enum: + - brcm,bcm11351-pwm + - const: brcm,kona-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + '#pwm-cells': + const: 3 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + + pwm@3e01a000 { + compatible = "brcm,bcm11351-pwm", "brcm,kona-pwm"; + reg = <0x3e01a000 0xcc>; + clocks = <&slave_ccu BCM281XX_SLAVE_CCU_PWM>; + #pwm-cells = <3>; + }; +... diff --git a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml index 0c3910f152d1dfbc4a3057520434120f04b74c81..30632efdad8bb2ce6605eb4ab1d8bb881a805f34 100644 --- a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml @@ -20,7 +20,9 @@ properties: - fsl,imx7ulp-cm4 - fsl,imx8mm-cm4 - fsl,imx8mn-cm7 + - fsl,imx8mn-cm7-mmio - fsl,imx8mp-cm7 + - fsl,imx8mp-cm7-mmio - fsl,imx8mq-cm4 - fsl,imx8qm-cm4 - fsl,imx8qxp-cm4 @@ -70,6 +72,11 @@ properties: description: Specify CPU entry address for SCU enabled processor. + fsl,iomuxc-gpr: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to IOMUXC GPR block which provide access to CM7 CPUWAIT bit. + fsl,resource-id: $ref: /schemas/types.yaml#/definitions/uint32 description: @@ -79,6 +86,19 @@ properties: required: - compatible +allOf: + - if: + properties: + compatible: + not: + contains: + enum: + - fsl,imx8mn-cm7-mmio + - fsl,imx8mp-cm7-mmio + then: + properties: + fsl,iomuxc-gpr: false + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml index 643ee787a81fc7f70dc6ac059fc684f1f99937cf..a2b0079de0390605b1892c9ab8e9d12ef80543cb 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.yaml @@ -26,6 +26,7 @@ properties: - qcom,sdm660-adsp-pas - qcom,sdm845-adsp-pas - qcom,sdm845-cdsp-pas + - qcom,sdm845-slpi-pas reg: maxItems: 1 @@ -44,8 +45,13 @@ properties: maxItems: 1 description: Reference to the reserved-memory for the Hexagon core + firmware-name: + maxItems: 1 + description: Firmware name for the Hexagon core + required: - compatible + - memory-region unevaluatedProperties: false @@ -63,6 +69,7 @@ allOf: - qcom,msm8998-adsp-pas - qcom,sdm845-adsp-pas - qcom,sdm845-cdsp-pas + - qcom,sdm845-slpi-pas then: properties: clocks: @@ -104,6 +111,7 @@ allOf: - qcom,msm8998-slpi-pas - qcom,sdm845-adsp-pas - qcom,sdm845-cdsp-pas + - qcom,sdm845-slpi-pas then: properties: interrupts: @@ -157,6 +165,22 @@ allOf: required: - px-supply + - if: + properties: + compatible: + enum: + - qcom,sdm845-slpi-pas + then: + properties: + power-domains: + items: + - description: LCX power domain + - description: LMX power domain + power-domain-names: + items: + - const: lcx + - const: lmx + - if: properties: compatible: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml index c1ac6ca1e759d1be34121c4bec1cfc1c7644ec5a..0643faae2c394f436a44087e0efe5764c20f25b2 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,msm8996-mss-pil.yaml @@ -19,6 +19,7 @@ properties: enum: - qcom,msm8996-mss-pil - qcom,msm8998-mss-pil + - qcom,sdm660-mss-pil - qcom,sdm845-mss-pil reg: @@ -215,13 +216,12 @@ allOf: - description: GCC MSS IFACE clock - description: GCC MSS BUS clock - description: GCC MSS MEM clock - - description: RPMH XO clock + - description: RPM XO clock - description: GCC MSS GPLL0 clock - description: GCC MSS SNOC_AXI clock - description: GCC MSS MNOC_AXI clock - - description: RPMH PNOC clock - - description: GCC MSS PRNG clock - - description: RPMH QDSS clock + - description: RPM PNOC clock + - description: RPM QDSS clock clock-names: items: - const: iface @@ -245,7 +245,9 @@ allOf: - if: properties: compatible: - const: qcom,msm8998-mss-pil + enum: + - qcom,msm8998-mss-pil + - qcom,sdm660-mss-pil then: properties: clocks: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml index 171ef85de193e1fe0a812ec00d9c7e61f53263d4..63a82e7a8bf8885a225d9ece8c8c2f652105f8f1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,pas-common.yaml @@ -82,7 +82,6 @@ required: - clock-names - interrupts - interrupt-names - - memory-region - qcom,smem-states - qcom,smem-state-names diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml index 5efa0e5c04397d3b41849de565f967bef9f2d4a6..eb868a7ff4cd819a3d09178e812481022436fe77 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,qcs404-pas.yaml @@ -42,7 +42,7 @@ properties: smd-edge: false memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core firmware-name: @@ -52,6 +52,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml index 5cefd2c58593fb20a1e90c421af7114e18ff6705..689d5d535331894a5aeadfa26bffc350258bb097 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc7180-pas.yaml @@ -51,7 +51,7 @@ properties: - const: mss memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core qcom,qmp: @@ -67,6 +67,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc8180x-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc8180x-pas.yaml index c1f8dd8d0e4c1adfe9bd4f58cadccfffd345747b..4744a37b2b5d75b6ea5197414720cd9a465b631e 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc8180x-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc8180x-pas.yaml @@ -38,7 +38,7 @@ properties: smd-edge: false memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core firmware-name: @@ -48,6 +48,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml index f6fbc531dc280870877691ebb30548ed733f73d9..96d53baf6e0070896697a76b883ee903d6fc27f6 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sc8280xp-pas.yaml @@ -38,7 +38,7 @@ properties: smd-edge: false memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core firmware-name: @@ -48,6 +48,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml index c66e298462c737cc331a14bf39230311ed44e6e4..5d463272165fdcc7b1ee6600acc1d473b455ff9d 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sdx55-pas.yaml @@ -46,7 +46,7 @@ properties: - const: mss memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core qcom,qmp: @@ -62,6 +62,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml index f5d1fa9f45f13a3968ebb8ad3dc7c3c8c53d3671..02828723591220abf50da6509b60d249dad9c59e 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm6115-pas.yaml @@ -15,10 +15,19 @@ description: properties: compatible: - enum: - - qcom,sm6115-adsp-pas - - qcom,sm6115-cdsp-pas - - qcom,sm6115-mpss-pas + oneOf: + - enum: + - qcom,sm6115-adsp-pas + - qcom,sm6115-cdsp-pas + - qcom,sm6115-mpss-pas + + - items: + - const: qcom,qcm2290-adsp-pas + - const: qcom,sm6115-adsp-pas + + - items: + - const: qcom,qcm2290-mpss-pas + - const: qcom,sm6115-mpss-pas reg: maxItems: 1 @@ -32,7 +41,7 @@ properties: - const: xo memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core smd-edge: false @@ -44,15 +53,17 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# - if: properties: compatible: - enum: - - qcom,sm6115-adsp-pas - - qcom,sm6115-cdsp-pas + contains: + enum: + - qcom,sm6115-adsp-pas + - qcom,sm6115-cdsp-pas then: properties: interrupts: @@ -69,9 +80,10 @@ allOf: - if: properties: compatible: - enum: - - qcom,sm6115-cdsp-pas - - qcom,sm6115-mpss-pas + contains: + enum: + - qcom,sm6115-cdsp-pas + - qcom,sm6115-mpss-pas then: properties: power-domains: @@ -84,8 +96,9 @@ allOf: - if: properties: compatible: - enum: - - qcom,sm6115-adsp-pas + contains: + enum: + - qcom,sm6115-adsp-pas then: properties: power-domains: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml index fee02fa800b5b34068b3af464bbc2bb613db0b31..f7e40fb166da1f5bea65c6241ebfab2118510f4e 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm6350-pas.yaml @@ -36,7 +36,7 @@ properties: description: Reference to the AOSS side-channel message RAM. memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core smd-edge: false @@ -48,6 +48,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml index 2c085ac2c3fb4eb9850c43e8b3b36d7acf80cd7d..238c6e5e67c5698baeaa2effad3e69ef37debf93 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8150-pas.yaml @@ -40,7 +40,7 @@ properties: description: Reference to the AOSS side-channel message RAM. memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core smd-edge: false @@ -52,6 +52,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml index 9a04d192e7e4341f05b49c89ed4e5701983792f0..53cea8e53a311ebc9a8f6491b7b786acaa433af5 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8350-pas.yaml @@ -43,7 +43,7 @@ properties: smd-edge: false memory-region: - minItems: 1 + maxItems: 1 description: Reference to the reserved-memory for the Hexagon core firmware-name: @@ -53,6 +53,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml index fe216aa531ede0b2e9ce409171f4deaaa4d42f5a..58120829fb06f5646f2c1af060bce326f2eec21b 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,sm8550-pas.yaml @@ -53,6 +53,7 @@ properties: required: - compatible - reg + - memory-region allOf: - $ref: /schemas/remoteproc/qcom,pas-common.yaml# diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index 38c0b5213736a463626ab7b1ad69f69e218c0605..97e8441eda1c2bf63f4321115a28c7cfd57e1c71 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -91,6 +91,7 @@ properties: interrupt-controller: type: object + additionalProperties: false description: Describes the CPU's local interrupt controller properties: diff --git a/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml b/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml index 4d2bef15fb7abca85c125a9108b00979213ab05a..c8bb2eef442dd6a77f8ac8a167ec8cd08a1bfa64 100644 --- a/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/atmel,at91rm9200-rtc.yaml @@ -14,13 +14,17 @@ maintainers: properties: compatible: - enum: - - atmel,at91rm9200-rtc - - atmel,at91sam9x5-rtc - - atmel,sama5d4-rtc - - atmel,sama5d2-rtc - - microchip,sam9x60-rtc - - microchip,sama7g5-rtc + oneOf: + - enum: + - atmel,at91rm9200-rtc + - atmel,at91sam9x5-rtc + - atmel,sama5d4-rtc + - atmel,sama5d2-rtc + - microchip,sam9x60-rtc + - microchip,sama7g5-rtc + - items: + - const: microchip,sam9x7-rtc + - const: microchip,sam9x60-rtc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/rtc/intersil,isl12022.yaml b/Documentation/devicetree/bindings/rtc/intersil,isl12022.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c2d1441ef273f0969d60e61ecef44bc849d121e4 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/intersil,isl12022.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/intersil,isl12022.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intersil ISL12022 Real-time Clock + +maintainers: + - Alexandre Belloni + +properties: + compatible: + const: isil,isl12022 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + '#clock-cells': + const: 0 + + isil,battery-trip-levels-microvolt: + description: + The battery voltages at which the first alarm and second alarm + should trigger (normally ~85% and ~75% of nominal V_BAT). + items: + - enum: [2125000, 2295000, 2550000, 2805000, 3060000, 4250000, 4675000] + - enum: [1875000, 2025000, 2250000, 2475000, 2700000, 3750000, 4125000] + +required: + - compatible + - reg + +allOf: + - $ref: rtc.yaml# + # If #clock-cells is present, interrupts must not be present + - if: + required: + - '#clock-cells' + then: + properties: + interrupts: false + +unevaluatedProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@6f { + compatible = "isil,isl12022"; + reg = <0x6f>; + interrupts-extended = <&gpio1 5 IRQ_TYPE_LEVEL_LOW>; + isil,battery-trip-levels-microvolt = <2550000>, <2250000>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/rtc/maxim,ds3231.txt b/Documentation/devicetree/bindings/rtc/maxim,ds3231.txt deleted file mode 100644 index 85be53a421809c0b8e04c6bf1dfa9833ff84a99d..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/rtc/maxim,ds3231.txt +++ /dev/null @@ -1,38 +0,0 @@ -* Maxim DS3231 Real Time Clock - -Required properties: -- compatible: Should contain "maxim,ds3231". -- reg: I2C address for chip. - -Optional property: -- #clock-cells: Should be 1. -- clock-output-names: - overwrite the default clock names "ds3231_clk_sqw" and "ds3231_clk_32khz". - -Each clock is assigned an identifier and client nodes can use this identifier -to specify the clock which they consume. Following indices are allowed: - - 0: square-wave output on the SQW pin - - 1: square-wave output on the 32kHz pin - -- interrupts: rtc alarm/event interrupt. When this property is selected, - clock on the SQW pin cannot be used. - -Example: - -ds3231: ds3231@51 { - compatible = "maxim,ds3231"; - reg = <0x68>; - #clock-cells = <1>; -}; - -device1 { -... - clocks = <&ds3231 0>; -... -}; - -device2 { -... - clocks = <&ds3231 1>; -... -}; diff --git a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml index bcb2300276227eee0d391fe0c6e72c5b1fb98d78..2d9fe5a75b06715d970a72ee66c3f2a4915500f5 100644 --- a/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml +++ b/Documentation/devicetree/bindings/rtc/nxp,pcf2127.yaml @@ -18,6 +18,7 @@ properties: - nxp,pca2129 - nxp,pcf2127 - nxp,pcf2129 + - nxp,pcf2131 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/rtc/st,m48t86.yaml b/Documentation/devicetree/bindings/rtc/st,m48t86.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e3e12fa233806d53b486ebd3577f293572a75571 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/st,m48t86.yaml @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/st,m48t86.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST M48T86 / Dallas DS12887 RTC with SRAM + +maintainers: + - Alexandre Belloni + +allOf: + - $ref: rtc.yaml + +properties: + compatible: + enum: + - st,m48t86 + + reg: + items: + - description: index register + - description: data register + +required: + - compatible + - reg + +unevaluatedProperties: false + +examples: + - | + rtc@10800000 { + compatible = "st,m48t86"; + reg = <0x10800000 0x1>, <0x11700000 0x1>; + }; + +... diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index 9af77f21bb7f487b30233bdb4369f3bd96ca62ca..2a65f31ac5a058a53970d6f31514ca0e71e35600 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -45,8 +45,6 @@ properties: - isil,isl1208 # Intersil ISL1218 Low Power RTC with Battery Backed SRAM - isil,isl1218 - # Intersil ISL12022 Real-time Clock - - isil,isl12022 # Real Time Clock Module with I2C-Bus - microcrystal,rv3029 # Real Time Clock diff --git a/Documentation/devicetree/bindings/soc/loongson/loongson,ls2k-pmc.yaml b/Documentation/devicetree/bindings/soc/loongson/loongson,ls2k-pmc.yaml index da2dcfeebf12ebc8839e30b026774e2c99277ffb..510f6cb0f084f8c8c46a51440fb10126f56acc12 100644 --- a/Documentation/devicetree/bindings/soc/loongson/loongson,ls2k-pmc.yaml +++ b/Documentation/devicetree/bindings/soc/loongson/loongson,ls2k-pmc.yaml @@ -11,11 +11,16 @@ maintainers: properties: compatible: - items: - - enum: - - loongson,ls2k0500-pmc - - loongson,ls2k1000-pmc - - const: syscon + oneOf: + - items: + - const: loongson,ls2k0500-pmc + - const: syscon + - items: + - enum: + - loongson,ls2k1000-pmc + - loongson,ls2k2000-pmc + - const: loongson,ls2k0500-pmc + - const: syscon reg: maxItems: 1 @@ -32,6 +37,18 @@ properties: addition, the PM need according to it to indicate that current SoC whether support Suspend To RAM. + syscon-poweroff: + $ref: /schemas/power/reset/syscon-poweroff.yaml# + type: object + description: + Node for power off method + + syscon-reboot: + $ref: /schemas/power/reset/syscon-reboot.yaml# + type: object + description: + Node for reboot method + required: - compatible - reg @@ -44,9 +61,23 @@ examples: #include power-management@1fe27000 { - compatible = "loongson,ls2k1000-pmc", "syscon"; + compatible = "loongson,ls2k1000-pmc", "loongson,ls2k0500-pmc", "syscon"; reg = <0x1fe27000 0x58>; interrupt-parent = <&liointc1>; interrupts = <11 IRQ_TYPE_LEVEL_LOW>; loongson,suspend-address = <0x0 0x1c000500>; + + syscon-reboot { + compatible = "syscon-reboot"; + offset = <0x30>; + mask = <0x1>; + }; + + syscon-poweroff { + compatible = "syscon-poweroff"; + regmap = <&pmc>; + offset = <0x14>; + mask = <0x3c00>; + value = <0x3c00>; + }; }; diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml index bdde68a1059c87b834ea6a6e51c647ce981693de..a680d7aff2373ca4bb7fad93b6f9667cbc3d2289 100644 --- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml @@ -14,7 +14,13 @@ properties: pattern: "^easrc@.*" compatible: - const: fsl,imx8mn-easrc + oneOf: + - enum: + - fsl,imx8mn-easrc + - items: + - enum: + - fsl,imx8mp-easrc + - const: fsl,imx8mn-easrc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml index 4b99a18c79a0762aa468b1ff8a78b6184f5c82ad..b7e60583563918001b8333b7e473a15a8ddca172 100644 --- a/Documentation/devicetree/bindings/sound/fsl,micfil.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,micfil.yaml @@ -56,6 +56,9 @@ properties: - const: clkext3 minItems: 2 + "#sound-dai-cells": + const: 0 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml index 4f51b2fa82dbda32ea92c2c27492ce9ae56fca05..c3c989ef2a2c81eba73fae3d1ee4bfd18a8e5d29 100644 --- a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml +++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml @@ -26,6 +26,7 @@ properties: - const: rockchip,rk3568-spdif - items: - enum: + - rockchip,rk3128-spdif - rockchip,rk3188-spdif - rockchip,rk3288-spdif - rockchip,rk3308-spdif diff --git a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.yaml b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.yaml index 2f593c7225e5aa8a76c3681771d11a9f10687477..14cac0e6e0a189e2133dd4a8b3247148da920617 100644 --- a/Documentation/devicetree/bindings/spi/fsl-imx-cspi.yaml +++ b/Documentation/devicetree/bindings/spi/fsl-imx-cspi.yaml @@ -22,6 +22,13 @@ properties: - const: fsl,imx35-cspi - const: fsl,imx51-ecspi - const: fsl,imx53-ecspi + - items: + - enum: + - fsl,imx25-cspi + - fsl,imx50-cspi + - fsl,imx51-cspi + - fsl,imx53-cspi + - const: fsl,imx35-cspi - items: - const: fsl,imx8mp-ecspi - const: fsl,imx6ul-ecspi diff --git a/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7538469997f9e1b11142d9f26ab77a3f7d542c76 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/loongson,ls2k-thermal.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Thermal sensors on Loongson-2 SoCs + +maintainers: + - zhanghongchen + - Yinbo Zhu + +properties: + compatible: + oneOf: + - enum: + - loongson,ls2k1000-thermal + - items: + - enum: + - loongson,ls2k2000-thermal + - const: loongson,ls2k1000-thermal + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + #include + thermal: thermal-sensor@1fe01500 { + compatible = "loongson,ls2k1000-thermal"; + reg = <0x1fe01500 0x30>; + interrupt-parent = <&liointc0>; + interrupts = <7 IRQ_TYPE_LEVEL_LOW>; + }; diff --git a/Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt b/Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt deleted file mode 100644 index d191612539e8867129504b569e6c6a82e7514069..0000000000000000000000000000000000000000 --- a/Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt +++ /dev/null @@ -1,17 +0,0 @@ -Oxford Semiconductor OXNAS SoCs Family RPS Timer -================================================ - -Required properties: -- compatible: Should be "oxsemi,ox810se-rps-timer" or "oxsemi,ox820-rps-timer" -- reg : Specifies base physical address and size of the registers. -- interrupts : The interrupts of the two timers -- clocks : The phandle of the timer clock source - -example: - -timer0: timer@200 { - compatible = "oxsemi,ox810se-rps-timer"; - reg = <0x200 0x40>; - clocks = <&rpsclk>; - interrupts = <4 5>; -}; diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index cd58179ae3379ee3f4dd6d3d6bd97d44c9f7de57..430a814f64a5678ed1e8ab4f7d01626cde69cc8a 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -232,7 +232,7 @@ properties: # MEMSIC magnetometer - memsic,mmc35240 # MEMSIC 3-axis accelerometer - - memsic,mx4005 + - memsic,mxc4005 # MEMSIC 2-axis 8-bit digital accelerometer - memsic,mxc6225 # MEMSIC 2-axis 8-bit digital accelerometer diff --git a/Documentation/devicetree/bindings/watchdog/amlogic,meson-gxbb-wdt.yaml b/Documentation/devicetree/bindings/watchdog/amlogic,meson-gxbb-wdt.yaml index f5cc7aa1b93bcec128d786dad19e52a433418216..443e2e7ab4676edef94c94dce01f570a6eadb7aa 100644 --- a/Documentation/devicetree/bindings/watchdog/amlogic,meson-gxbb-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/amlogic,meson-gxbb-wdt.yaml @@ -17,6 +17,7 @@ properties: compatible: enum: - amlogic,meson-gxbb-wdt + - amlogic,t7-wdt reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/watchdog/marvell,cn10624-wdt.yaml b/Documentation/devicetree/bindings/watchdog/marvell,cn10624-wdt.yaml new file mode 100644 index 0000000000000000000000000000000000000000..1b583f232e53e27dcc5e120c8a26b2d47577a0f7 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/marvell,cn10624-wdt.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/marvell,cn10624-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell Global Timer (GTI) system watchdog + +maintainers: + - Bharat Bhushan + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + oneOf: + - enum: + - marvell,cn9670-wdt + - marvell,cn10624-wdt + + - items: + - enum: + - marvell,cn9880-wdt + - marvell,cnf9535-wdt + - const: marvell,cn9670-wdt + + - items: + - enum: + - marvell,cn10308-wdt + - marvell,cnf10518-wdt + - const: marvell,cn10624-wdt + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: refclk + + marvell,wdt-timer-index: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 63 + description: + An SoC have many timers (up to 64), firmware can reserve one or more timer + for some other use case and configures one of the global timer as watchdog + timer. Firmware will update this field with the timer number configured + as watchdog timer. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + soc { + #address-cells = <2>; + #size-cells = <2>; + + watchdog@802000040000 { + compatible = "marvell,cn9670-wdt"; + reg = <0x00008020 0x00040000 0x00000000 0x00020000>; + interrupts = ; + clocks = <&sclk>; + clock-names = "refclk"; + marvell,wdt-timer-index = <63>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml index 6d0fe6abd06ac1d51c67a3df346ba6b234e891d1..5046dfa55f135b569f4ba70b7e36e372904191b3 100644 --- a/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/qcom-wdt.yaml @@ -18,6 +18,7 @@ properties: - items: - enum: - qcom,kpss-wdt-ipq4019 + - qcom,apss-wdt-ipq5018 - qcom,apss-wdt-ipq5332 - qcom,apss-wdt-ipq9574 - qcom,apss-wdt-msm8994 diff --git a/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml b/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml index fc553211e42d8788235c42c04058ad910fef2b62..62ddc284a524ce9b1e7bf8588583ce211f466ac2 100644 --- a/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml +++ b/Documentation/devicetree/bindings/watchdog/ti,rti-wdt.yaml @@ -34,6 +34,20 @@ properties: power-domains: maxItems: 1 + memory-region: + maxItems: 1 + description: + Contains the watchdog reserved memory. It is optional. + In the reserved memory, the specified values, which are + PON_REASON_SOF_NUM(0xBBBBCCCC), PON_REASON_MAGIC_NUM(0xDDDDDDDD), + and PON_REASON_EOF_NUM(0xCCCCBBBB), are pre-stored at the first + 3 * 4 bytes to tell that last boot was caused by watchdog reset. + Once the PON reason is captured by driver(rti_wdt.c), the driver + is supposed to wipe the whole memory region. Surely, if this + property is set, at least 12 bytes reserved memory starting from + specific memory address(0xa220000) should be set. More please + refer to example. + required: - compatible - reg @@ -47,7 +61,18 @@ examples: /* * RTI WDT in main domain on J721e SoC. Assigned clocks are used to * select the source clock for the watchdog, forcing it to tick with - * a 32kHz clock in this case. + * a 32kHz clock in this case. Add a reserved memory(optional) to keep + * the watchdog reset cause persistent, which was be written in 12 bytes + * starting from 0xa2200000 by RTI Watchdog Firmware, then make it + * possible to get watchdog reset cause in driver. + * + * Reserved memory should be defined as follows: + * reserved-memory { + * wdt_reset_memory_region: wdt-memory@a2200000 { + * reg = <0x00 0xa2200000 0x00 0x1000>; + * no-map; + * }; + * } */ #include @@ -58,4 +83,5 @@ examples: power-domains = <&k3_pds 252 TI_SCI_PD_EXCLUSIVE>; assigned-clocks = <&k3_clks 252 1>; assigned-clock-parents = <&k3_clks 252 5>; + memory-region = <&wdt_reset_memory_region>; }; diff --git a/Documentation/driver-api/libata.rst b/Documentation/driver-api/libata.rst index 311af516a3fd9c42a1eff43a2db4c392d0f449b9..5da27a749246edc3b2da24d0f78b5959e5d86f04 100644 --- a/Documentation/driver-api/libata.rst +++ b/Documentation/driver-api/libata.rst @@ -32,22 +32,6 @@ register blocks. :c:type:`struct ata_port_operations ` ---------------------------------------------------------- -Disable ATA port -~~~~~~~~~~~~~~~~ - -:: - - void (*port_disable) (struct ata_port *); - - -Called from :c:func:`ata_bus_probe` error path, as well as when unregistering -from the SCSI module (rmmod, hot unplug). This function should do -whatever needs to be done to take the port out of use. In most cases, -:c:func:`ata_port_disable` can be used as this hook. - -Called from :c:func:`ata_bus_probe` on a failed probe. Called from -:c:func:`ata_scsi_release`. - Post-IDENTIFY device configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -272,14 +256,6 @@ advanced drivers implement their own ``->qc_issue``. Exception and probe handling (EH) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:: - - void (*eng_timeout) (struct ata_port *ap); - void (*phy_reset) (struct ata_port *ap); - - -Deprecated. Use ``->error_handler()`` instead. - :: void (*freeze) (struct ata_port *ap); @@ -364,8 +340,7 @@ SATA phy read/write u32 val); -Read and write standard SATA phy registers. Currently only used if -``->phy_reset`` hook called the :c:func:`sata_phy_reset` helper function. +Read and write standard SATA phy registers. sc_reg is one of SCR_STATUS, SCR_CONTROL, SCR_ERROR, or SCR_ACTIVE. Init and shutdown @@ -536,13 +511,12 @@ to return without deallocating the qc. This leads us to :c:func:`ata_scsi_error` is the current ``transportt->eh_strategy_handler()`` for libata. As discussed above, this will be entered in two cases - -timeout and ATAPI error completion. This function calls low level libata -driver's :c:func:`eng_timeout` callback, the standard callback for which is -:c:func:`ata_eng_timeout`. It checks if a qc is active and calls -:c:func:`ata_qc_timeout` on the qc if so. Actual error handling occurs in -:c:func:`ata_qc_timeout`. +timeout and ATAPI error completion. This function will check if a qc is active +and has not failed yet. Such a qc will be marked with AC_ERR_TIMEOUT such that +EH will know to handle it later. Then it calls low level libata driver's +:c:func:`error_handler` callback. -If EH is invoked for timeout, :c:func:`ata_qc_timeout` stops BMDMA and +When the :c:func:`error_handler` callback is invoked it stops BMDMA and completes the qc. Note that as we're currently in EH, we cannot call scsi_done. As described in SCSI EH doc, a recovered scmd should be either retried with :c:func:`scsi_queue_insert` or finished with diff --git a/Documentation/features/debug/KASAN/arch-support.txt b/Documentation/features/debug/KASAN/arch-support.txt index bf0124fae643a1bbb13d34a539171354642351b1..c4581c2edb28006def42321db12567f20f4e1b5e 100644 --- a/Documentation/features/debug/KASAN/arch-support.txt +++ b/Documentation/features/debug/KASAN/arch-support.txt @@ -13,7 +13,7 @@ | csky: | TODO | | hexagon: | TODO | | ia64: | TODO | - | loongarch: | TODO | + | loongarch: | ok | | m68k: | TODO | | microblaze: | TODO | | mips: | TODO | diff --git a/Documentation/features/debug/kcov/arch-support.txt b/Documentation/features/debug/kcov/arch-support.txt index ffcc9f2b1d74d3930302349b3bcf541bb368c6e8..de84cefbcdd36f028e2eb0f1770dde51551a4652 100644 --- a/Documentation/features/debug/kcov/arch-support.txt +++ b/Documentation/features/debug/kcov/arch-support.txt @@ -13,7 +13,7 @@ | csky: | TODO | | hexagon: | TODO | | ia64: | TODO | - | loongarch: | TODO | + | loongarch: | ok | | m68k: | TODO | | microblaze: | TODO | | mips: | ok | diff --git a/Documentation/features/debug/kgdb/arch-support.txt b/Documentation/features/debug/kgdb/arch-support.txt index 958498f9f2a41f2b6c56f2bd3ead11d88b5ef5ae..5e91ec78c80b759cd3af62c696817e7a607e8b93 100644 --- a/Documentation/features/debug/kgdb/arch-support.txt +++ b/Documentation/features/debug/kgdb/arch-support.txt @@ -13,7 +13,7 @@ | csky: | TODO | | hexagon: | ok | | ia64: | TODO | - | loongarch: | TODO | + | loongarch: | ok | | m68k: | TODO | | microblaze: | ok | | mips: | ok | diff --git a/Documentation/filesystems/btrfs.rst b/Documentation/filesystems/btrfs.rst index 992eddb0e11b8751c125acc45fdd7fbba2cf3962..a81db8f54d689300a49ff78cb06ba783dcdad21d 100644 --- a/Documentation/filesystems/btrfs.rst +++ b/Documentation/filesystems/btrfs.rst @@ -37,7 +37,6 @@ For more information please refer to the documentation site or wiki https://btrfs.readthedocs.io - https://btrfs.wiki.kernel.org that maintains information about administration tasks, frequently asked questions, use cases, mount options, comprehensible changelogs, features, diff --git a/Documentation/filesystems/ceph.rst b/Documentation/filesystems/ceph.rst index 76ce938e702445ac43aadbc2ef65d99771dc8c28..085f309ece600f20eb9faa298e470f9e79ab2d14 100644 --- a/Documentation/filesystems/ceph.rst +++ b/Documentation/filesystems/ceph.rst @@ -57,6 +57,16 @@ a snapshot on any subdirectory (and its nested contents) in the system. Snapshot creation and deletion are as simple as 'mkdir .snap/foo' and 'rmdir .snap/foo'. +Snapshot names have two limitations: + +* They can not start with an underscore ('_'), as these names are reserved + for internal usage by the MDS. +* They can not exceed 240 characters in size. This is because the MDS makes + use of long snapshot names internally, which follow the format: + `__`. Since filenames in general can't have + more than 255 characters, and `` takes 13 characters, the long + snapshot names can take as much as 255 - 1 - 1 - 13 = 240. + Ceph also provides some recursive accounting on directories for nested files and bytes. That is, a 'getfattr -d foo' on any directory in the system will reveal the total number of nested regular files and diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst index 4654ee57c1d5e4ef5d59d9b34cea5651434c5b34..f200d78744952825790e105f8903c44d1601e02e 100644 --- a/Documentation/filesystems/erofs.rst +++ b/Documentation/filesystems/erofs.rst @@ -58,12 +58,14 @@ Here are the main features of EROFS: - Support extended attributes as an option; + - Support a bloom filter that speeds up negative extended attribute lookups; + - Support POSIX.1e ACLs by using extended attributes; - Support transparent data compression as an option: - LZ4 and MicroLZMA algorithms can be used on a per-file basis; In addition, - inplace decompression is also supported to avoid bounce compressed buffers - and page cache thrashing. + LZ4, MicroLZMA and DEFLATE algorithms can be used on a per-file basis; In + addition, inplace decompression is also supported to avoid bounce compressed + buffers and unnecessary page cache thrashing. - Support chunk-based data deduplication and rolling-hash compressed data deduplication; @@ -268,6 +270,38 @@ details.) By the way, chunk-based files are all uncompressed for now. +Long extended attribute name prefixes +------------------------------------- +There are use cases where extended attributes with different values can have +only a few common prefixes (such as overlayfs xattrs). The predefined prefixes +work inefficiently in both image size and runtime performance in such cases. + +The long xattr name prefixes feature is introduced to address this issue. The +overall idea is that, apart from the existing predefined prefixes, the xattr +entry could also refer to user-specified long xattr name prefixes, e.g. +"trusted.overlay.". + +When referring to a long xattr name prefix, the highest bit (bit 7) of +erofs_xattr_entry.e_name_index is set, while the lower bits (bit 0-6) as a whole +represent the index of the referred long name prefix among all long name +prefixes. Therefore, only the trailing part of the name apart from the long +xattr name prefix is stored in erofs_xattr_entry.e_name, which could be empty if +the full xattr name matches exactly as its long xattr name prefix. + +All long xattr prefixes are stored one by one in the packed inode as long as +the packed inode is valid, or in the meta inode otherwise. The +xattr_prefix_count (of the on-disk superblock) indicates the total number of +long xattr name prefixes, while (xattr_prefix_start * 4) indicates the start +offset of long name prefixes in the packed/meta inode. Note that, long extended +attribute name prefixes are disabled if xattr_prefix_count is 0. + +Each long name prefix is stored in the format: ALIGN({__le16 len, data}, 4), +where len represents the total size of the data part. The data part is actually +represented by 'struct erofs_xattr_long_prefix', where base_index represents the +index of the predefined xattr name prefix, e.g. EROFS_XATTR_INDEX_TRUSTED for +"trusted.overlay." long name prefix, while the infix string keeps the string +after stripping the short prefix, e.g. "overlay." for the example above. + Data compression ---------------- EROFS implements fixed-sized output compression which generates fixed-sized diff --git a/Documentation/filesystems/gfs2-glocks.rst b/Documentation/filesystems/gfs2-glocks.rst index bec25c8b3e4b5cbd318ab6b65ef45e45227607fb..8a5842929b60d8bc2b02cc5315d8154f4046645e 100644 --- a/Documentation/filesystems/gfs2-glocks.rst +++ b/Documentation/filesystems/gfs2-glocks.rst @@ -20,8 +20,7 @@ The gl_holders list contains all the queued lock requests (not just the holders) associated with the glock. If there are any held locks, then they will be contiguous entries at the head of the list. Locks are granted in strictly the order that they -are queued, except for those marked LM_FLAG_PRIORITY which are -used only during recovery, and even then only for journal locks. +are queued. There are three lock states that users of the glock layer can request, namely shared (SH), deferred (DF) and exclusive (EX). Those translate diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst index deac4e973ddc12d326ee9e6874ed90456206f5db..4d05b9862451ea8237c583b5f276b1834ee4adb9 100644 --- a/Documentation/filesystems/porting.rst +++ b/Documentation/filesystems/porting.rst @@ -949,3 +949,99 @@ mmap_lock held. All in-tree users have been audited and do not seem to depend on the mmap_lock being held, but out of tree users should verify for themselves. If they do need it, they can return VM_FAULT_RETRY to be called with the mmap_lock held. + +--- + +**mandatory** + +The order of opening block devices and matching or creating superblocks has +changed. + +The old logic opened block devices first and then tried to find a +suitable superblock to reuse based on the block device pointer. + +The new logic tries to find a suitable superblock first based on the device +number, and opening the block device afterwards. + +Since opening block devices cannot happen under s_umount because of lock +ordering requirements s_umount is now dropped while opening block devices and +reacquired before calling fill_super(). + +In the old logic concurrent mounters would find the superblock on the list of +superblocks for the filesystem type. Since the first opener of the block device +would hold s_umount they would wait until the superblock became either born or +was discarded due to initialization failure. + +Since the new logic drops s_umount concurrent mounters could grab s_umount and +would spin. Instead they are now made to wait using an explicit wait-wake +mechanism without having to hold s_umount. + +--- + +**mandatory** + +The holder of a block device is now the superblock. + +The holder of a block device used to be the file_system_type which wasn't +particularly useful. It wasn't possible to go from block device to owning +superblock without matching on the device pointer stored in the superblock. +This mechanism would only work for a single device so the block layer couldn't +find the owning superblock of any additional devices. + +In the old mechanism reusing or creating a superblock for a racing mount(2) and +umount(2) relied on the file_system_type as the holder. This was severly +underdocumented however: + +(1) Any concurrent mounter that managed to grab an active reference on an + existing superblock was made to wait until the superblock either became + ready or until the superblock was removed from the list of superblocks of + the filesystem type. If the superblock is ready the caller would simple + reuse it. + +(2) If the mounter came after deactivate_locked_super() but before + the superblock had been removed from the list of superblocks of the + filesystem type the mounter would wait until the superblock was shutdown, + reuse the block device and allocate a new superblock. + +(3) If the mounter came after deactivate_locked_super() and after + the superblock had been removed from the list of superblocks of the + filesystem type the mounter would reuse the block device and allocate a new + superblock (the bd_holder point may still be set to the filesystem type). + +Because the holder of the block device was the file_system_type any concurrent +mounter could open the block devices of any superblock of the same +file_system_type without risking seeing EBUSY because the block device was +still in use by another superblock. + +Making the superblock the owner of the block device changes this as the holder +is now a unique superblock and thus block devices associated with it cannot be +reused by concurrent mounters. So a concurrent mounter in (2) could suddenly +see EBUSY when trying to open a block device whose holder was a different +superblock. + +The new logic thus waits until the superblock and the devices are shutdown in +->kill_sb(). Removal of the superblock from the list of superblocks of the +filesystem type is now moved to a later point when the devices are closed: + +(1) Any concurrent mounter managing to grab an active reference on an existing + superblock is made to wait until the superblock is either ready or until + the superblock and all devices are shutdown in ->kill_sb(). If the + superblock is ready the caller will simply reuse it. + +(2) If the mounter comes after deactivate_locked_super() but before + the superblock has been removed from the list of superblocks of the + filesystem type the mounter is made to wait until the superblock and the + devices are shut down in ->kill_sb() and the superblock is removed from the + list of superblocks of the filesystem type. The mounter will allocate a new + superblock and grab ownership of the block device (the bd_holder pointer of + the block device will be set to the newly allocated superblock). + +(3) This case is now collapsed into (2) as the superblock is left on the list + of superblocks of the filesystem type until all devices are shutdown in + ->kill_sb(). In other words, if the superblock isn't on the list of + superblock of the filesystem type anymore then it has given up ownership of + all associated block devices (the bd_holder pointer is NULL). + +As this is a VFS level change it has no practical consequences for filesystems +other than that all of them must use one of the provided kill_litter_super(), +kill_anon_super(), or kill_block_super() helpers. diff --git a/Documentation/filesystems/proc.rst b/Documentation/filesystems/proc.rst index 1f9615553b82737610253b2f3afb87376b1f9748..2b59cff8be1798cf3ecb947cf3203ef1d3300ad2 100644 --- a/Documentation/filesystems/proc.rst +++ b/Documentation/filesystems/proc.rst @@ -461,6 +461,7 @@ Memory Area, or VMA) there is a series of lines such as the following:: Private_Dirty: 0 kB Referenced: 892 kB Anonymous: 0 kB + KSM: 0 kB LazyFree: 0 kB AnonHugePages: 0 kB ShmemPmdMapped: 0 kB @@ -501,6 +502,9 @@ accessed. a mapping associated with a file may contain anonymous pages: when MAP_PRIVATE and a page is modified, the file page is replaced by a private anonymous copy. +"KSM" reports how many of the pages are KSM pages. Note that KSM-placed zeropages +are not included, only actual KSM pages. + "LazyFree" shows the amount of memory which is marked by madvise(MADV_FREE). The memory isn't freed immediately with madvise(). It's freed in memory pressure if the memory is clean. Please note that the printed value might diff --git a/Documentation/gpu/amdgpu/driver-misc.rst b/Documentation/gpu/amdgpu/driver-misc.rst index be131e963d87c38e9afdf49353d2aa9b7c3b9463..4321c38fef21f9c8e2e307813f01e56e9e5c167e 100644 --- a/Documentation/gpu/amdgpu/driver-misc.rst +++ b/Documentation/gpu/amdgpu/driver-misc.rst @@ -11,19 +11,19 @@ via sysfs product_name ------------ -.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c :doc: product_name product_number -------------- -.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c - :doc: product_name +.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c + :doc: product_number serial_number ------------- -.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c :doc: serial_number unique_id diff --git a/Documentation/gpu/automated_testing.rst b/Documentation/gpu/automated_testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..469b6fb65c306155d88ff365f9c1e98e880fd8f6 --- /dev/null +++ b/Documentation/gpu/automated_testing.rst @@ -0,0 +1,144 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +========================================= +Automated testing of the DRM subsystem +========================================= + +Introduction +============ + +Making sure that changes to the core or drivers don't introduce regressions can +be very time-consuming when lots of different hardware configurations need to +be tested. Moreover, it isn't practical for each person interested in this +testing to have to acquire and maintain what can be a considerable amount of +hardware. + +Also, it is desirable for developers to check for regressions in their code by +themselves, instead of relying on the maintainers to find them and then +reporting back. + +There are facilities in gitlab.freedesktop.org to automatically test Mesa that +can be used as well for testing the DRM subsystem. This document explains how +people interested in testing it can use this shared infrastructure to save +quite some time and effort. + + +Relevant files +============== + +drivers/gpu/drm/ci/gitlab-ci.yml +-------------------------------- + +This is the root configuration file for GitLab CI. Among other less interesting +bits, it specifies the specific version of the scripts to be used. There are +some variables that can be modified to change the behavior of the pipeline: + +DRM_CI_PROJECT_PATH + Repository that contains the Mesa software infrastructure for CI + +DRM_CI_COMMIT_SHA + A particular revision to use from that repository + +UPSTREAM_REPO + URL to git repository containing the target branch + +TARGET_BRANCH + Branch to which this branch is to be merged into + +IGT_VERSION + Revision of igt-gpu-tools being used, from + https://gitlab.freedesktop.org/drm/igt-gpu-tools + +drivers/gpu/drm/ci/testlist.txt +------------------------------- + +IGT tests to be run on all drivers (unless mentioned in a driver's \*-skips.txt +file, see below). + +drivers/gpu/drm/ci/${DRIVER_NAME}-${HW_REVISION}-fails.txt +---------------------------------------------------------- + +Lists the known failures for a given driver on a specific hardware revision. + +drivers/gpu/drm/ci/${DRIVER_NAME}-${HW_REVISION}-flakes.txt +----------------------------------------------------------- + +Lists the tests that for a given driver on a specific hardware revision are +known to behave unreliably. These tests won't cause a job to fail regardless of +the result. They will still be run. + +drivers/gpu/drm/ci/${DRIVER_NAME}-${HW_REVISION}-skips.txt +----------------------------------------------------------- + +Lists the tests that won't be run for a given driver on a specific hardware +revision. These are usually tests that interfere with the running of the test +list due to hanging the machine, causing OOM, taking too long, etc. + + +How to enable automated testing on your tree +============================================ + +1. Create a Linux tree in https://gitlab.freedesktop.org/ if you don't have one +yet + +2. In your kernel repo's configuration (eg. +https://gitlab.freedesktop.org/janedoe/linux/-/settings/ci_cd), change the +CI/CD configuration file from .gitlab-ci.yml to +drivers/gpu/drm/ci/gitlab-ci.yml. + +3. Next time you push to this repository, you will see a CI pipeline being +created (eg. https://gitlab.freedesktop.org/janedoe/linux/-/pipelines) + +4. The various jobs will be run and when the pipeline is finished, all jobs +should be green unless a regression has been found. + + +How to update test expectations +=============================== + +If your changes to the code fix any tests, you will have to remove one or more +lines from one or more of the files in +drivers/gpu/drm/ci/${DRIVER_NAME}_*_fails.txt, for each of the test platforms +affected by the change. + + +How to expand coverage +====================== + +If your code changes make it possible to run more tests (by solving reliability +issues, for example), you can remove tests from the flakes and/or skips lists, +and then the expected results if there are any known failures. + +If there is a need for updating the version of IGT being used (maybe you have +added more tests to it), update the IGT_VERSION variable at the top of the +gitlab-ci.yml file. + + +How to test your changes to the scripts +======================================= + +For testing changes to the scripts in the drm-ci repo, change the +DRM_CI_PROJECT_PATH and DRM_CI_COMMIT_SHA variables in +drivers/gpu/drm/ci/gitlab-ci.yml to match your fork of the project (eg. +janedoe/drm-ci). This fork needs to be in https://gitlab.freedesktop.org/. + + +How to incorporate external fixes in your testing +================================================= + +Often, regressions in other trees will prevent testing changes local to the +tree under test. These fixes will be automatically merged in during the build +jobs from a branch in the target tree that is named as +${TARGET_BRANCH}-external-fixes. + +If the pipeline is not in a merge request and a branch with the same name +exists in the local tree, commits from that branch will be merged in as well. + + +How to deal with automated testing labs that may be down +======================================================== + +If a hardware farm is down and thus causing pipelines to fail that would +otherwise pass, one can disable all jobs that would be submitted to that farm +by editing the file at +https://gitlab.freedesktop.org/gfx-ci/lab-status/-/blob/main/lab-status.yml. diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst index eee5996acf2c2684cea1914261f2fa8857765a63..e45ff0915246ded8f312b2d52149d7fe687b6e8e 100644 --- a/Documentation/gpu/index.rst +++ b/Documentation/gpu/index.rst @@ -17,6 +17,7 @@ GPU Driver Developer's Guide backlight vga-switcheroo vgaarbiter + automated_testing todo rfc/index diff --git a/Documentation/kbuild/kconfig-language.rst b/Documentation/kbuild/kconfig-language.rst index 858ed5d80defeaf2dc2ec8afaeefa260321315ac..0135905c0aa352f2d83747cc407080d0973d9bd8 100644 --- a/Documentation/kbuild/kconfig-language.rst +++ b/Documentation/kbuild/kconfig-language.rst @@ -573,6 +573,32 @@ above, leading to: bool "Support for foo hardware" depends on ARCH_FOO_VENDOR || COMPILE_TEST +Optional dependencies +~~~~~~~~~~~~~~~~~~~~~ + +Some drivers are able to optionally use a feature from another module +or build cleanly with that module disabled, but cause a link failure +when trying to use that loadable module from a built-in driver. + +The most common way to express this optional dependency in Kconfig logic +uses the slightly counterintuitive:: + + config FOO + tristate "Support for foo hardware" + depends on BAR || !BAR + +This means that there is either a dependency on BAR that disallows +the combination of FOO=y with BAR=m, or BAR is completely disabled. +For a more formalized approach if there are multiple drivers that have +the same dependency, a helper symbol can be used, like:: + + config FOO + tristate "Support for foo hardware" + depends on BAR_OPTIONAL + + config BAR_OPTIONAL + def_tristate BAR || !BAR + Kconfig recursive dependency limitations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/kbuild/kconfig.rst b/Documentation/kbuild/kconfig.rst index 3ee89dfe469760eb48ae48b9a35959700ea84d1a..c946eb44bd138e9784c94b3a1eacf242c7dc06e5 100644 --- a/Documentation/kbuild/kconfig.rst +++ b/Documentation/kbuild/kconfig.rst @@ -56,6 +56,15 @@ KCONFIG_OVERWRITECONFIG If you set KCONFIG_OVERWRITECONFIG in the environment, Kconfig will not break symlinks when .config is a symlink to somewhere else. +KCONFIG_WARN_UNKNOWN_SYMBOLS +---------------------------- +This environment variable makes Kconfig warn about all unrecognized +symbols in the config input. + +KCONFIG_WERROR +-------------- +If set, Kconfig treats warnings as errors. + `CONFIG_` --------- If you set `CONFIG_` in the environment, Kconfig will prefix all symbols @@ -212,6 +221,10 @@ Searching in menuconfig: first (and in alphabetical order), then come all other symbols, sorted in alphabetical order. + In this menu, pressing the key in the (#) prefix will jump + directly to that location. You will be returned to the current + search results after exiting this new menu. + ---------------------------------------------------------------------- User interface options for 'menuconfig' @@ -264,6 +277,10 @@ Searching in nconfig: F8 (SymSearch) searches the configuration symbols for the given string or regular expression (regex). + In the SymSearch, pressing the key in the (#) prefix will + jump directly to that location. You will be returned to the + current search results after exiting this new menu. + NCONFIG_MODE ------------ This mode shows all sub-menus in one large tree. diff --git a/Documentation/kbuild/llvm.rst b/Documentation/kbuild/llvm.rst index c3851fe1900da15e47883e1d7d5292075cfaee48..b1d97fafddcfc990d91d101a0166dd616acfe717 100644 --- a/Documentation/kbuild/llvm.rst +++ b/Documentation/kbuild/llvm.rst @@ -25,50 +25,38 @@ objects `_. Clang is a front-end to LLVM that supports C and the GNU C extensions required by the kernel, and is pronounced "klang," not "see-lang." -Clang ------ - -The compiler used can be swapped out via ``CC=`` command line argument to ``make``. -``CC=`` should be set when selecting a config and during a build. :: - - make CC=clang defconfig - - make CC=clang +Building with LLVM +------------------ -Cross Compiling ---------------- +Invoke ``make`` via:: -A single Clang compiler binary will typically contain all supported backends, -which can help simplify cross compiling. :: - - make ARCH=arm64 CC=clang CROSS_COMPILE=aarch64-linux-gnu- + make LLVM=1 -``CROSS_COMPILE`` is not used to prefix the Clang compiler binary, instead -``CROSS_COMPILE`` is used to set a command line flag: ``--target=``. For -example: :: +to compile for the host target. For cross compiling:: - clang --target=aarch64-linux-gnu foo.c + make LLVM=1 ARCH=arm64 -LLVM Utilities --------------- +The LLVM= argument +------------------ -LLVM has substitutes for GNU binutils utilities. They can be enabled individually. -The full list of supported make variables:: +LLVM has substitutes for GNU binutils utilities. They can be enabled +individually. The full list of supported make variables:: make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \ OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump READELF=llvm-readelf \ HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld -To simplify the above command, Kbuild supports the ``LLVM`` variable:: - - make LLVM=1 +``LLVM=1`` expands to the above. If your LLVM tools are not available in your PATH, you can supply their location using the LLVM variable with a trailing slash:: make LLVM=/path/to/llvm/ -which will use ``/path/to/llvm/clang``, ``/path/to/llvm/ld.lld``, etc. +which will use ``/path/to/llvm/clang``, ``/path/to/llvm/ld.lld``, etc. The +following may also be used:: + + PATH=/path/to/llvm:$PATH make LLVM=1 If your LLVM tools have a version suffix and you want to test with that explicit version rather than the unsuffixed executables like ``LLVM=1``, you @@ -78,31 +66,72 @@ can pass the suffix using the ``LLVM`` variable:: which will use ``clang-14``, ``ld.lld-14``, etc. +To support combinations of out of tree paths with version suffixes, we +recommend:: + + PATH=/path/to/llvm/:$PATH make LLVM=-14 + ``LLVM=0`` is not the same as omitting ``LLVM`` altogether, it will behave like -``LLVM=1``. If you only wish to use certain LLVM utilities, use their respective -make variables. +``LLVM=1``. If you only wish to use certain LLVM utilities, use their +respective make variables. + +The same value used for ``LLVM=`` should be set for each invocation of ``make`` +if configuring and building via distinct commands. ``LLVM=`` should also be set +as an environment variable when running scripts that will eventually run +``make``. -The integrated assembler is enabled by default. You can pass ``LLVM_IAS=0`` to -disable it. +Cross Compiling +--------------- -Omitting CROSS_COMPILE +A single Clang compiler binary (and corresponding LLVM utilities) will +typically contain all supported back ends, which can help simplify cross +compiling especially when ``LLVM=1`` is used. If you use only LLVM tools, +``CROSS_COMPILE`` or target-triple-prefixes become unnecessary. Example:: + + make LLVM=1 ARCH=arm64 + +As an example of mixing LLVM and GNU utilities, for a target like ``ARCH=s390`` +which does not yet have ``ld.lld`` or ``llvm-objcopy`` support, you could +invoke ``make`` via:: + + make LLVM=1 ARCH=s390 LD=s390x-linux-gnu-ld.bfd \ + OBJCOPY=s390x-linux-gnu-objcopy + +This example will invoke ``s390x-linux-gnu-ld.bfd`` as the linker and +``s390x-linux-gnu-objcopy``, so ensure those are reachable in your ``$PATH``. + +``CROSS_COMPILE`` is not used to prefix the Clang compiler binary (or +corresponding LLVM utilities) as is the case for GNU utilities when ``LLVM=1`` +is not set. + +The LLVM_IAS= argument ---------------------- -As explained above, ``CROSS_COMPILE`` is used to set ``--target=``. +Clang can assemble assembler code. You can pass ``LLVM_IAS=0`` to disable this +behavior and have Clang invoke the corresponding non-integrated assembler +instead. Example:: + + make LLVM=1 LLVM_IAS=0 + +``CROSS_COMPILE`` is necessary when cross compiling and ``LLVM_IAS=0`` +is used in order to set ``--prefix=`` for the compiler to find the +corresponding non-integrated assembler (typically, you don't want to use the +system assembler when targeting another architecture). Example:: -If ``CROSS_COMPILE`` is not specified, the ``--target=`` is inferred -from ``ARCH``. + make LLVM=1 ARCH=arm LLVM_IAS=0 CROSS_COMPILE=arm-linux-gnueabi- -That means if you use only LLVM tools, ``CROSS_COMPILE`` becomes unnecessary. -For example, to cross-compile the arm64 kernel:: +Ccache +------ - make ARCH=arm64 LLVM=1 +``ccache`` can be used with ``clang`` to improve subsequent builds, (though +KBUILD_BUILD_TIMESTAMP_ should be set to a deterministic value between builds +in order to avoid 100% cache misses, see Reproducible_builds_ for more info): -If ``LLVM_IAS=0`` is specified, ``CROSS_COMPILE`` is also used to derive -``--prefix=`` to search for the GNU assembler and linker. :: + KBUILD_BUILD_TIMESTAMP='' make LLVM=1 CC="ccache clang" - make ARCH=arm64 LLVM=1 LLVM_IAS=0 CROSS_COMPILE=aarch64-linux-gnu- +.. _KBUILD_BUILD_TIMESTAMP: kbuild.html#kbuild-build-timestamp +.. _Reproducible_builds: reproducible-builds.html#timestamps Supported Architectures ----------------------- @@ -135,14 +164,17 @@ yet. Bug reports are always welcome at the issue tracker below! * - hexagon - Maintained - ``LLVM=1`` + * - loongarch + - Maintained + - ``LLVM=1`` * - mips - Maintained - ``LLVM=1`` * - powerpc - Maintained - - ``CC=clang`` + - ``LLVM=1`` * - riscv - - Maintained + - Supported - ``LLVM=1`` * - s390 - Maintained @@ -171,7 +203,11 @@ Getting Help Getting LLVM ------------- -We provide prebuilt stable versions of LLVM on `kernel.org `_. +We provide prebuilt stable versions of LLVM on `kernel.org +`_. These have been optimized with profile +data for building Linux kernels, which should improve kernel build times +relative to other distributions of LLVM. + Below are links that may be useful for building LLVM from source or procuring it through a distribution's package manager. diff --git a/Documentation/networking/ax25.rst b/Documentation/networking/ax25.rst index f060cfb1445a38a6e0d81c68e5184815976b7fed..605e72c6c8771dbdd8085d89874b2a8bb1008005 100644 --- a/Documentation/networking/ax25.rst +++ b/Documentation/networking/ax25.rst @@ -7,9 +7,9 @@ AX.25 To use the amateur radio protocols within Linux you will need to get a suitable copy of the AX.25 Utilities. More detailed information about AX.25, NET/ROM and ROSE, associated programs and utilities can be -found on http://www.linux-ax25.org. +found on https://linux-ax25.in-berlin.de. -There is an active mailing list for discussing Linux amateur radio matters +There is a mailing list for discussing Linux amateur radio matters called linux-hams@vger.kernel.org. To subscribe to it, send a message to majordomo@vger.kernel.org with the words "subscribe linux-hams" in the body of the message, the subject field is ignored. You don't need to be diff --git a/Documentation/process/embargoed-hardware-issues.rst b/Documentation/process/embargoed-hardware-issues.rst index cb686238f21d1de3bb3483d629237779b240120c..ac7c52f130c9b30978267fdd9d92994f01878260 100644 --- a/Documentation/process/embargoed-hardware-issues.rst +++ b/Documentation/process/embargoed-hardware-issues.rst @@ -251,6 +251,7 @@ an involved disclosed party. The current ambassadors list: IBM Z Christian Borntraeger Intel Tony Luck Qualcomm Trilok Soni + RISC-V Palmer Dabbelt Samsung Javier González Microsoft James Morris diff --git a/Documentation/process/maintainer-netdev.rst b/Documentation/process/maintainer-netdev.rst index c1c732e9748bd918e9c05406f07d0afc051578cd..09dcf6377c272023a6894d5c10dfc3368d6f44aa 100644 --- a/Documentation/process/maintainer-netdev.rst +++ b/Documentation/process/maintainer-netdev.rst @@ -98,7 +98,7 @@ If you aren't subscribed to netdev and/or are simply unsure if repository link above for any new networking-related commits. You may also check the following website for the current status: - https://patchwork.hopto.org/net-next.html + https://netdev.bots.linux.dev/net-next.html The ``net`` tree continues to collect fixes for the vX.Y content, and is fed back to Linus at regular (~weekly) intervals. Meaning that the @@ -120,7 +120,37 @@ queue for netdev: https://patchwork.kernel.org/project/netdevbpf/list/ The "State" field will tell you exactly where things are at with your -patch. Patches are indexed by the ``Message-ID`` header of the emails +patch: + +================== ============================================================= +Patch state Description +================== ============================================================= +New, Under review pending review, patch is in the maintainer’s queue for + review; the two states are used interchangeably (depending on + the exact co-maintainer handling patchwork at the time) +Accepted patch was applied to the appropriate networking tree, this is + usually set automatically by the pw-bot +Needs ACK waiting for an ack from an area expert or testing +Changes requested patch has not passed the review, new revision is expected + with appropriate code and commit message changes +Rejected patch has been rejected and new revision is not expected +Not applicable patch is expected to be applied outside of the networking + subsystem +Awaiting upstream patch should be reviewed and handled by appropriate + sub-maintainer, who will send it on to the networking trees; + patches set to ``Awaiting upstream`` in netdev's patchwork + will usually remain in this state, whether the sub-maintainer + requested changes, accepted or rejected the patch +Deferred patch needs to be reposted later, usually due to dependency + or because it was posted for a closed tree +Superseded new version of the patch was posted, usually set by the + pw-bot +RFC not to be applied, usually not in maintainer’s review queue, + pw-bot can automatically set patches to this state based + on subject tags +================== ============================================================= + +Patches are indexed by the ``Message-ID`` header of the emails which carried them so if you have trouble finding your patch append the value of ``Message-ID`` to the URL above. @@ -155,7 +185,7 @@ must match the MAINTAINERS entry) and a handful of senior reviewers. Bot records its activity here: - https://patchwork.hopto.org/pw-bot.html + https://netdev.bots.linux.dev/pw-bot.html Review timelines ~~~~~~~~~~~~~~~~ diff --git a/Documentation/riscv/hwprobe.rst b/Documentation/riscv/hwprobe.rst index 20eff9650da959dc1e38d756c6e49ebd0d7ae191..a52996b22f75d3e8fea1064313cf36abcd878cf3 100644 --- a/Documentation/riscv/hwprobe.rst +++ b/Documentation/riscv/hwprobe.rst @@ -87,13 +87,12 @@ The following keys are defined: emulated via software, either in or below the kernel. These accesses are always extremely slow. - * :c:macro:`RISCV_HWPROBE_MISALIGNED_SLOW`: Misaligned accesses are supported - in hardware, but are slower than the corresponding aligned accesses - sequences. + * :c:macro:`RISCV_HWPROBE_MISALIGNED_SLOW`: Misaligned accesses are slower + than equivalent byte accesses. Misaligned accesses may be supported + directly in hardware, or trapped and emulated by software. - * :c:macro:`RISCV_HWPROBE_MISALIGNED_FAST`: Misaligned accesses are supported - in hardware and are faster than the corresponding aligned accesses - sequences. + * :c:macro:`RISCV_HWPROBE_MISALIGNED_FAST`: Misaligned accesses are faster + than equivalent byte accesses. * :c:macro:`RISCV_HWPROBE_MISALIGNED_UNSUPPORTED`: Misaligned accesses are not supported at all and will generate a misaligned address fault. diff --git a/Documentation/scsi/scsi_mid_low_api.rst b/Documentation/scsi/scsi_mid_low_api.rst index 6fa3a62795016494280c4a49adac72cc4d4b6af4..022198c513506b2618fcf2803af7082b46604be3 100644 --- a/Documentation/scsi/scsi_mid_low_api.rst +++ b/Documentation/scsi/scsi_mid_low_api.rst @@ -1190,11 +1190,11 @@ Members of interest: - pointer to scsi_device object that this command is associated with. resid - - an LLD should set this signed integer to the requested + - an LLD should set this unsigned integer to the requested transfer length (i.e. 'request_bufflen') less the number of bytes that are actually transferred. 'resid' is preset to 0 so an LLD can ignore it if it cannot detect - underruns (overruns should be rare). If possible an LLD + underruns (overruns should not be reported). An LLD should set 'resid' prior to invoking 'done'. The most interesting case is data transfers from a SCSI target device (e.g. READs) that underrun. diff --git a/Documentation/sound/designs/midi-2.0.rst b/Documentation/sound/designs/midi-2.0.rst index 45987f256b976312644e94522f5565fbf062e255..086487ca7ab11905a8374437d31f12c29f84a402 100644 --- a/Documentation/sound/designs/midi-2.0.rst +++ b/Documentation/sound/designs/midi-2.0.rst @@ -74,8 +74,8 @@ topology based on those information. When the device is older and doesn't respond to the new UMP inquiries, the driver falls back and builds the topology based on Group Terminal Block (GTB) information from the USB descriptor. Some device might be screwed up by the -unexpected UMP command; in such a case, pass `midi2_probe=0` option to -snd-usb-audio driver for skipping the UMP v1.1 inquiries. +unexpected UMP command; in such a case, pass `midi2_ump_probe=0` +option to snd-usb-audio driver for skipping the UMP v1.1 inquiries. When the MIDI 2.0 device is probed, the kernel creates a rawmidi device for each UMP Endpoint of the device. Its device name is diff --git a/Documentation/tools/rtla/rtla-timerlat-hist.rst b/Documentation/tools/rtla/rtla-timerlat-hist.rst index 057db78d4095a1135a66968fdb6c7a2f18846848..03b7f3deb0698fa622121c2c6a2b6a8fec49d967 100644 --- a/Documentation/tools/rtla/rtla-timerlat-hist.rst +++ b/Documentation/tools/rtla/rtla-timerlat-hist.rst @@ -36,11 +36,11 @@ EXAMPLE In the example below, **rtla timerlat hist** is set to run for *10* minutes, in the cpus *0-4*, *skipping zero* only lines. Moreover, **rtla timerlat hist** will change the priority of the *timerlat* threads to run under -*SCHED_DEADLINE* priority, with a *10us* runtime every *1ms* period. The +*SCHED_DEADLINE* priority, with a *100us* runtime every *1ms* period. The *1ms* period is also passed to the *timerlat* tracer. Auto-analysis is disabled to reduce overhead :: - [root@alien ~]# timerlat hist -d 10m -c 0-4 -P d:100us:1ms -p 1ms --no-aa + [root@alien ~]# timerlat hist -d 10m -c 0-4 -P d:100us:1ms -p 1000 --no-aa # RTLA timerlat histogram # Time unit is microseconds (us) # Duration: 0 00:10:00 diff --git a/Documentation/trace/events.rst b/Documentation/trace/events.rst index 15f78e772384b675fa6a6df093cc64fa0c020939..759907c20e7591ce3f03ac191cbc14a29b242f8a 100644 --- a/Documentation/trace/events.rst +++ b/Documentation/trace/events.rst @@ -219,6 +219,20 @@ the function "security_prepare_creds" and less than the end of that function. The ".function" postfix can only be attached to values of size long, and can only be compared with "==" or "!=". +Cpumask fields or scalar fields that encode a CPU number can be filtered using +a user-provided cpumask in cpulist format. The format is as follows:: + + CPUS{$cpulist} + +Operators available to cpumask filtering are: + +& (intersection), ==, != + +For example, this will filter events that have their .target_cpu field present +in the given cpumask:: + + target_cpu & CPUS{17-42} + 5.2 Setting filters ------------------- diff --git a/Documentation/trace/fprobetrace.rst b/Documentation/trace/fprobetrace.rst index 7297f94784594f30cae94a6a92ce03749d7b0592..8e9bebcf0a2e8f0d05edca303d9fbdf718873ce1 100644 --- a/Documentation/trace/fprobetrace.rst +++ b/Documentation/trace/fprobetrace.rst @@ -79,9 +79,9 @@ automatically set by the given name. :: f:fprobes/myprobe vfs_read count=count pos=pos It also chooses the fetch type from BTF information. For example, in the above -example, the ``count`` is unsigned long, and the ``pos`` is a pointer. Thus, both -are converted to 64bit unsigned long, but only ``pos`` has "%Lx" print-format as -below :: +example, the ``count`` is unsigned long, and the ``pos`` is a pointer. Thus, +both are converted to 64bit unsigned long, but only ``pos`` has "%Lx" +print-format as below :: # cat events/fprobes/myprobe/format name: myprobe @@ -105,9 +105,47 @@ is expanded to all function arguments of the function or the tracepoint. :: # cat dynamic_events f:fprobes/myprobe vfs_read file=file buf=buf count=count pos=pos -BTF also affects the ``$retval``. If user doesn't set any type, the retval type is -automatically picked from the BTF. If the function returns ``void``, ``$retval`` -is rejected. +BTF also affects the ``$retval``. If user doesn't set any type, the retval +type is automatically picked from the BTF. If the function returns ``void``, +``$retval`` is rejected. + +You can access the data fields of a data structure using allow operator ``->`` +(for pointer type) and dot operator ``.`` (for data structure type.):: + +# echo 't sched_switch preempt prev_pid=prev->pid next_pid=next->pid' >> dynamic_events + +The field access operators, ``->`` and ``.`` can be combined for accessing deeper +members and other structure members pointed by the member. e.g. ``foo->bar.baz->qux`` +If there is non-name union member, you can directly access it as the C code does. +For example:: + + struct { + union { + int a; + int b; + }; + } *foo; + +To access ``a`` and ``b``, use ``foo->a`` and ``foo->b`` in this case. + +This data field access is available for the return value via ``$retval``, +e.g. ``$retval->name``. + +For these BTF arguments and fields, ``:string`` and ``:ustring`` change the +behavior. If these are used for BTF argument or field, it checks whether +the BTF type of the argument or the data field is ``char *`` or ``char []``, +or not. If not, it rejects applying the string types. Also, with the BTF +support, you don't need a memory dereference operator (``+0(PTR)``) for +accessing the string pointed by a ``PTR``. It automatically adds the memory +dereference operator according to the BTF type. e.g. :: + +# echo 't sched_switch prev->comm:string' >> dynamic_events +# echo 'f getname_flags%return $retval->name:string' >> dynamic_events + +The ``prev->comm`` is an embedded char array in the data structure, and +``$retval->name`` is a char pointer in the data structure. But in both +cases, you can use ``:string`` type to get the string. + Usage examples -------------- @@ -161,10 +199,10 @@ parameters. This means you can access any field values in the task structure pointed by the ``prev`` and ``next`` arguments. For example, usually ``task_struct::start_time`` is not traced, but with this -traceprobe event, you can trace it as below. +traceprobe event, you can trace that field as below. :: - # echo 't sched_switch comm=+1896(next):string start_time=+1728(next):u64' > dynamic_events + # echo 't sched_switch comm=next->comm:string next->start_time' > dynamic_events # head -n 20 trace | tail # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | @@ -176,13 +214,3 @@ traceprobe event, you can trace it as below. -0 [000] d..3. 5606.690317: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="kworker/0:1" usage=1 start_time=137000000 kworker/0:1-14 [000] d..3. 5606.690339: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="swapper/0" usage=2 start_time=0 -0 [000] d..3. 5606.692368: sched_switch: (__probestub_sched_switch+0x4/0x10) comm="kworker/0:1" usage=1 start_time=137000000 - -Currently, to find the offset of a specific field in the data structure, -you need to build kernel with debuginfo and run `perf probe` command with -`-D` option. e.g. -:: - - # perf probe -D "__probestub_sched_switch next->comm:string next->start_time" - p:probe/__probestub_sched_switch __probestub_sched_switch+0 comm=+1896(%cx):string start_time=+1728(%cx):u64 - -And replace the ``%cx`` with the ``next``. diff --git a/Documentation/translations/zh_CN/arch/loongarch/introduction.rst b/Documentation/translations/zh_CN/arch/loongarch/introduction.rst index cba04befc9509f8942eb1e2b1aadc2f80fe17034..59d6bf33050cb831236321d91eab7bbf313655ea 100644 --- a/Documentation/translations/zh_CN/arch/loongarch/introduction.rst +++ b/Documentation/translations/zh_CN/arch/loongarch/introduction.rst @@ -344,9 +344,9 @@ LoongArch指令集架构的文档: LoongArch的ELF psABI文档: - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.00-CN.pdf (中文版) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.01-CN.pdf (中文版) - https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.00-EN.pdf (英文版) + https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v2.01-EN.pdf (英文版) Loongson与LoongArch的Linux内核源码仓库: diff --git a/Documentation/translations/zh_CN/dev-tools/kasan.rst b/Documentation/translations/zh_CN/dev-tools/kasan.rst index 05ef904dbcfb2e154526cb546d22aa65f58b4d90..8fdb20c9665b4b5e8a9afe12fb361f9a1d2315d7 100644 --- a/Documentation/translations/zh_CN/dev-tools/kasan.rst +++ b/Documentation/translations/zh_CN/dev-tools/kasan.rst @@ -42,7 +42,7 @@ KASAN有三种模式: 体系架构 ~~~~~~~~ -在x86_64、arm、arm64、powerpc、riscv、s390和xtensa上支持通用KASAN, +在x86_64、arm、arm64、powerpc、riscv、s390、xtensa和loongarch上支持通用KASAN, 而基于标签的KASAN模式只在arm64上支持。 编译器 diff --git a/Documentation/userspace-api/netlink/intro.rst b/Documentation/userspace-api/netlink/intro.rst index af94e71761ec5b0a68880e6902bbdf2b79aac058..7b1d401210efb4ed5ba0ffa5e7c1e25f413926d0 100644 --- a/Documentation/userspace-api/netlink/intro.rst +++ b/Documentation/userspace-api/netlink/intro.rst @@ -528,6 +528,8 @@ families may, however, require a larger buffer. 32kB buffer is recommended for most efficient handling of dumps (larger buffer fits more dumped objects and therefore fewer recvmsg() calls are needed). +.. _classic_netlink: + Classic Netlink =============== diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 73db30cb60fbd9daa391e7a8540545f330b5f757..21a7578142a18b4ad537acbd654ba510dd16fc9f 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -2259,6 +2259,8 @@ Errors: EINVAL invalid register ID, or no such register or used with VMs in protected virtualization mode on s390 EPERM (arm64) register access not allowed before vcpu finalization + EBUSY (riscv) changing register value not allowed after the vcpu + has run at least once ====== ============================================================ (These error codes are indicative only: do not rely on a specific error @@ -3499,7 +3501,7 @@ VCPU matching underlying host. --------------------- :Capability: basic -:Architectures: arm64, mips +:Architectures: arm64, mips, riscv :Type: vcpu ioctl :Parameters: struct kvm_reg_list (in/out) :Returns: 0 on success; -1 on error diff --git a/MAINTAINERS b/MAINTAINERS index fcbb106aaa57eb467f098400f0bc8ca113b7c396..7608b714653f7b63fe8368663cadffb6a121c818 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -470,7 +470,6 @@ F: drivers/hwmon/adm1029.c ADM8211 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan -W: https://wireless.wiki.kernel.org/ F: drivers/net/wireless/admtek/adm8211.* ADP1653 FLASH CONTROLLER DRIVER @@ -1585,6 +1584,17 @@ F: arch/arm/include/asm/arch_timer.h F: arch/arm64/include/asm/arch_timer.h F: drivers/clocksource/arm_arch_timer.c +ARM GENERIC INTERRUPT CONTROLLER DRIVERS +M: Marc Zyngier +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/interrupt-controller/arm,gic* +F: arch/arm/include/asm/arch_gicv3.h +F: arch/arm64/include/asm/arch_gicv3.h +F: drivers/irqchip/irq-gic*.[ch] +F: include/linux/irqchip/arm-gic*.h +F: include/linux/irqchip/arm-vgic-info.h + ARM HDLCD DRM DRIVER M: Liviu Dudau S: Supported @@ -1662,7 +1672,7 @@ F: arch/arm*/include/asm/perf_event.h F: arch/arm*/kernel/hw_breakpoint.c F: arch/arm*/kernel/perf_* F: drivers/perf/ -F: include/linux/perf/arm_pmu.h +F: include/linux/perf/arm_pmu*.h ARM PORT M: Russell King @@ -1855,7 +1865,7 @@ F: Documentation/devicetree/bindings/phy/amlogic* F: arch/arm/boot/dts/amlogic/ F: arch/arm/mach-meson/ F: arch/arm64/boot/dts/amlogic/ -F: drivers/genpd/amlogic/ +F: drivers/pmdomain/amlogic/ F: drivers/mmc/host/meson* F: drivers/phy/amlogic/ F: drivers/pinctrl/meson/ @@ -1918,7 +1928,7 @@ F: drivers/bluetooth/hci_bcm4377.c F: drivers/clk/clk-apple-nco.c F: drivers/cpufreq/apple-soc-cpufreq.c F: drivers/dma/apple-admac.c -F: drivers/genpd/apple/ +F: drivers/pmdomain/apple/ F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/iommu/apple-dart.c @@ -1963,12 +1973,12 @@ F: drivers/irqchip/irq-aspeed-i2c-ic.c ARM/ASPEED MACHINE SUPPORT M: Joel Stanley -R: Andrew Jeffery +R: Andrew Jeffery L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) S: Supported Q: https://patchwork.ozlabs.org/project/linux-aspeed/list/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/joel/aspeed.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/joel/bmc.git F: Documentation/devicetree/bindings/arm/aspeed/ F: arch/arm/boot/dts/aspeed/ F: arch/arm/mach-aspeed/ @@ -2435,7 +2445,7 @@ F: arch/arm/mach-ux500/ F: drivers/clk/clk-nomadik.c F: drivers/clocksource/clksrc-dbx500-prcmu.c F: drivers/dma/ste_dma40* -F: drivers/genpd/st/ste-ux500-pm-domain.c +F: drivers/pmdomain/st/ste-ux500-pm-domain.c F: drivers/hwspinlock/u8500_hsem.c F: drivers/i2c/busses/i2c-nomadik.c F: drivers/iio/adc/ab8500-gpadc.c @@ -2598,7 +2608,7 @@ F: arch/arm/include/debug/renesas-scif.S F: arch/arm/mach-shmobile/ F: arch/arm64/boot/dts/renesas/ F: arch/riscv/boot/dts/renesas/ -F: drivers/genpd/renesas/ +F: drivers/pmdomain/renesas/ F: drivers/soc/renesas/ F: include/linux/soc/renesas/ K: \brenesas, @@ -3058,7 +3068,7 @@ F: Documentation/devicetree/bindings/peci/peci-aspeed.yaml F: drivers/peci/controller/peci-aspeed.c ASPEED PINCTRL DRIVERS -M: Andrew Jeffery +M: Andrew Jeffery L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) L: openbmc@lists.ozlabs.org (moderated for non-subscribers) L: linux-gpio@vger.kernel.org @@ -3075,7 +3085,7 @@ F: drivers/irqchip/irq-aspeed-scu-ic.c F: include/dt-bindings/interrupt-controller/aspeed-scu-ic.h ASPEED SD/MMC DRIVER -M: Andrew Jeffery +M: Andrew Jeffery L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) L: openbmc@lists.ozlabs.org (moderated for non-subscribers) L: linux-mmc@vger.kernel.org @@ -3344,7 +3354,7 @@ AX.25 NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org S: Maintained -W: http://www.linux-ax25.org/ +W: https://linux-ax25.in-berlin.de F: include/net/ax25.h F: include/uapi/linux/ax25.h F: net/ax25/ @@ -4026,7 +4036,7 @@ F: arch/mips/kernel/*bmips* F: drivers/irqchip/irq-bcm63* F: drivers/irqchip/irq-bcm7* F: drivers/irqchip/irq-brcmstb* -F: drivers/genpd/bcm/bcm63xx-power.c +F: drivers/pmdomain/bcm/bcm63xx-power.c F: include/linux/bcm963xx_nvram.h F: include/linux/bcm963xx_tag.h @@ -4082,7 +4092,7 @@ F: drivers/net/wireless/broadcom/brcm80211/ BROADCOM BRCMSTB GPIO DRIVER M: Doug Berger -M: Florian Fainelli +M: Florian Fainelli R: Broadcom internal kernel review list S: Supported F: Documentation/devicetree/bindings/gpio/brcm,brcmstb-gpio.yaml @@ -4248,7 +4258,7 @@ R: Broadcom internal kernel review list L: linux-pm@vger.kernel.org S: Maintained T: git https://github.com/broadcom/stblinux.git -F: drivers/genpd/bcm/bcm-pmb.c +F: drivers/pmdomain/bcm/bcm-pmb.c F: include/dt-bindings/soc/bcm-pmb.h BROADCOM SPECIFIC AMBA DRIVER (BCMA) @@ -4378,7 +4388,6 @@ M: David Sterba L: linux-btrfs@vger.kernel.org S: Maintained W: https://btrfs.readthedocs.io -W: https://btrfs.wiki.kernel.org/ Q: https://patchwork.kernel.org/project/linux-btrfs/list/ C: irc://irc.libera.chat/btrfs T: git git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git @@ -5255,6 +5264,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup.git F: Documentation/admin-guide/cgroup-v1/cpusets.rst F: include/linux/cpuset.h F: kernel/cgroup/cpuset.c +F: tools/testing/selftests/cgroup/test_cpuset.c +F: tools/testing/selftests/cgroup/test_cpuset_prs.sh CONTROL GROUP - MEMORY RESOURCE CONTROLLER (MEMCG) M: Johannes Weiner @@ -5984,8 +5995,8 @@ F: include/linux/devm-helpers.h DEVICE-MAPPER (LVM) M: Alasdair Kergon M: Mike Snitzer -M: dm-devel@redhat.com -L: dm-devel@redhat.com +M: dm-devel@lists.linux.dev +L: dm-devel@lists.linux.dev S: Maintained W: http://sources.redhat.com/dm Q: http://patchwork.kernel.org/project/dm-devel/list/ @@ -6116,7 +6127,7 @@ F: include/video/udlfb.h DISTRIBUTED LOCK MANAGER (DLM) M: Christine Caulfield M: David Teigland -L: cluster-devel@redhat.com +L: gfs2@lists.linux.dev S: Supported W: http://sources.redhat.com/cluster/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm.git @@ -6644,9 +6655,9 @@ F: Documentation/devicetree/bindings/display/panel/novatek,nt36672a.yaml F: drivers/gpu/drm/panel/panel-novatek-nt36672a.c DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS -M: Ben Skeggs M: Karol Herbst M: Lyude Paul +M: Danilo Krummrich L: dri-devel@lists.freedesktop.org L: nouveau@lists.freedesktop.org S: Supported @@ -7163,6 +7174,14 @@ T: git git://anongit.freedesktop.org/drm/drm-misc F: drivers/gpu/drm/ttm/ F: include/drm/ttm/ +DRM AUTOMATED TESTING +M: Helen Koike +L: dri-devel@lists.freedesktop.org +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: Documentation/gpu/automated_testing.rst +F: drivers/gpu/drm/ci/ + DSBR100 USB FM RADIO DRIVER M: Alexey Klimov L: linux-media@vger.kernel.org @@ -8719,7 +8738,7 @@ M: Ulf Hansson L: linux-pm@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm.git -F: drivers/genpd/ +F: drivers/pmdomain/ GENERIC RESISTIVE TOUCHSCREEN ADC DRIVER M: Eugen Hristev @@ -8772,7 +8791,7 @@ F: scripts/get_maintainer.pl GFS2 FILE SYSTEM M: Bob Peterson M: Andreas Gruenbacher -L: cluster-devel@redhat.com +L: gfs2@lists.linux.dev S: Supported B: https://bugzilla.kernel.org/enter_bug.cgi?product=File%20System&component=gfs2 T: git git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git @@ -8865,7 +8884,7 @@ F: drivers/gpio/gpio-mockup.c F: tools/testing/selftests/gpio/ GPIO REGMAP -R: Michael Walle +M: Michael Walle S: Maintained F: drivers/gpio/gpio-regmap.c F: include/linux/gpio/regmap.h @@ -9522,10 +9541,8 @@ F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml F: drivers/iio/pressure/mprls0025pa.c HOST AP DRIVER -M: Jouni Malinen L: linux-wireless@vger.kernel.org S: Obsolete -W: http://w1.fi/hostap-driver.html F: drivers/net/wireless/intersil/hostap/ HP BIOSCFG DRIVER @@ -11054,7 +11071,7 @@ F: Documentation/devicetree/bindings/sound/irondevice,* F: sound/soc/codecs/sma* IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) -M: Marc Zyngier +M: Thomas Gleixner S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core F: Documentation/core-api/irq/irq-domain.rst @@ -11073,7 +11090,6 @@ F: lib/group_cpus.c IRQCHIP DRIVERS M: Thomas Gleixner -M: Marc Zyngier L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core @@ -11380,6 +11396,7 @@ F: scripts/dummy-tools/ F: scripts/mk* F: scripts/mod/ F: scripts/package/ +F: usr/ KERNEL HARDENING (not covered by other areas) M: Kees Cook @@ -11586,6 +11603,8 @@ F: arch/x86/include/uapi/asm/svm.h F: arch/x86/include/uapi/asm/vmx.h F: arch/x86/kvm/ F: arch/x86/kvm/*/ +F: tools/testing/selftests/kvm/*/x86_64/ +F: tools/testing/selftests/kvm/x86_64/ KERNFS M: Greg Kroah-Hartman @@ -12388,6 +12407,14 @@ S: Maintained F: Documentation/devicetree/bindings/pinctrl/loongson,ls2k-pinctrl.yaml F: drivers/pinctrl/pinctrl-loongson2.c +LOONGSON-2 SOC SERIES THERMAL DRIVER +M: zhanghongchen +M: Yinbo Zhu +L: linux-pm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/thermal/loongson,ls2k-thermal.yaml +F: drivers/thermal/loongson2_thermal.c + LSILOGIC MPT FUSION DRIVERS (FC/SAS/SPI) M: Sathya Prakash M: Sreekanth Reddy @@ -13354,6 +13381,7 @@ L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/iommu/mediatek* F: drivers/iommu/mtk_iommu* +F: include/dt-bindings/memory/mediatek,mt*-port.h F: include/dt-bindings/memory/mt*-port.h MEDIATEK JPEG DRIVER @@ -13595,6 +13623,7 @@ F: drivers/net/ethernet/mellanox/mlxfw/ MELLANOX HARDWARE PLATFORM SUPPORT M: Hans de Goede +M: Ilpo Järvinen M: Mark Gross M: Vadim Pasternak L: platform-driver-x86@vger.kernel.org @@ -13731,6 +13760,7 @@ F: include/linux/memory_hotplug.h F: include/linux/mm.h F: include/linux/mmzone.h F: include/linux/pagewalk.h +F: include/linux/rmap.h F: include/trace/events/ksm.h F: mm/ F: tools/mm/ @@ -14188,6 +14218,7 @@ F: drivers/platform/surface/surface_gpe.c MICROSOFT SURFACE HARDWARE PLATFORM SUPPORT M: Hans de Goede +M: Ilpo Järvinen M: Mark Gross M: Maximilian Luz L: platform-driver-x86@vger.kernel.org @@ -14734,7 +14765,7 @@ NETROM NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org S: Maintained -W: http://www.linux-ax25.org/ +W: https://linux-ax25.in-berlin.de F: include/net/netrom.h F: include/uapi/linux/netrom.h F: net/netrom/ @@ -14923,7 +14954,7 @@ K: macsec K: \bmdo_ NETWORKING [MPTCP] -M: Matthieu Baerts +M: Matthieu Baerts M: Mat Martineau L: netdev@vger.kernel.org L: mptcp@lists.linux.dev @@ -16748,6 +16779,8 @@ L: linux-kernel@vger.kernel.org S: Supported W: https://perf.wiki.kernel.org/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git perf/core +T: git git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools.git perf-tools +T: git git://git.kernel.org/pub/scm/linux/kernel/git/perf/perf-tools-next.git perf-tools-next F: arch/*/events/* F: arch/*/events/*/* F: arch/*/include/asm/perf_event.h @@ -17173,9 +17206,9 @@ F: kernel/sched/psi.c PRINTK M: Petr Mladek -M: Sergey Senozhatsky R: Steven Rostedt R: John Ogness +R: Sergey Senozhatsky S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git F: include/linux/printk.h @@ -17576,6 +17609,7 @@ M: Kalle Valo M: Jeff Johnson L: ath12k@lists.infradead.org S: Supported +W: https://wireless.wiki.kernel.org/en/users/Drivers/ath12k T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git F: drivers/net/wireless/ath/ath12k/ @@ -17655,7 +17689,7 @@ L: linux-pm@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml -F: drivers/genpd/qcom/cpr.c +F: drivers/pmdomain/qcom/cpr.c QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin @@ -18069,7 +18103,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux.git F: Documentation/admin-guide/rtc.rst F: Documentation/devicetree/bindings/rtc/ F: drivers/rtc/ -F: include/linux/platform_data/rtc-* F: include/linux/rtc.h F: include/linux/rtc/ F: include/uapi/linux/rtc.h @@ -18107,8 +18140,6 @@ REALTEK WIRELESS DRIVER (rtlwifi family) M: Ping-Ke Shih L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git F: drivers/net/wireless/realtek/rtlwifi/ REALTEK WIRELESS DRIVER (rtw88) @@ -18584,7 +18615,7 @@ ROSE NETWORK LAYER M: Ralf Baechle L: linux-hams@vger.kernel.org S: Maintained -W: http://www.linux-ax25.org/ +W: https://linux-ax25.in-berlin.de F: include/net/rose.h F: include/uapi/linux/rose.h F: net/rose/ @@ -18636,7 +18667,6 @@ F: drivers/media/dvb-frontends/rtl2832_sdr* RTL8180 WIRELESS DRIVER L: linux-wireless@vger.kernel.org S: Orphan -W: https://wireless.wiki.kernel.org/ F: drivers/net/wireless/realtek/rtl818x/rtl8180/ RTL8187 WIRELESS DRIVER @@ -18644,14 +18674,12 @@ M: Hin-Tak Leung M: Larry Finger L: linux-wireless@vger.kernel.org S: Maintained -W: https://wireless.wiki.kernel.org/ F: drivers/net/wireless/realtek/rtl818x/rtl8187/ RTL8XXXU WIRELESS DRIVER (rtl8xxxu) M: Jes Sorensen L: linux-wireless@vger.kernel.org S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/jes/linux.git rtl8xxxu-devel F: drivers/net/wireless/realtek/rtl8xxxu/ RTRS TRANSPORT DRIVERS @@ -20392,6 +20420,13 @@ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git F: drivers/staging/ +STANDALONE CACHE CONTROLLER DRIVERS +M: Conor Dooley +L: linux-riscv@lists.infradead.org +S: Maintained +T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ +F: drivers/cache + STARFIRE/DURALAN NETWORK DRIVER M: Ion Badulescu S: Odd Fixes @@ -20416,6 +20451,13 @@ S: Maintained F: Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml F: drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c +STARFIVE JH7110 DPHY RX DRIVER +M: Jack Zhu +M: Changhuang Liang +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml +F: drivers/phy/starfive/phy-jh7110-dphy-rx.c + STARFIVE JH7110 MMC/SD/SDIO DRIVER M: William Qiu S: Supported @@ -20476,7 +20518,7 @@ STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen S: Supported F: Documentation/devicetree/bindings/power/starfive* -F: drivers/genpd/starfive/jh71xx-pmu.c +F: drivers/pmdomain/starfive/jh71xx-pmu.c F: include/dt-bindings/power/starfive,jh7110-pmu.h STARFIVE SOC DRIVERS @@ -20499,6 +20541,14 @@ S: Supported F: Documentation/devicetree/bindings/watchdog/starfive* F: drivers/watchdog/starfive-wdt.c +STARFIVE JH71X0 PCIE AND USB PHY DRIVER +M: Minda Chen +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml +F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml +F: drivers/phy/starfive/phy-jh7110-pcie.c +F: drivers/phy/starfive/phy-jh7110-usb.c + STATIC BRANCH/CALL M: Peter Zijlstra M: Josh Poimboeuf @@ -21215,7 +21265,7 @@ F: sound/soc/ti/ TEXAS INSTRUMENTS AUDIO (ASoC/HDA) DRIVERS M: Shenghao Ding M: Kevin Lu -M: Baojun Xu +M: Baojun Xu L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/sound/tas2552.txt @@ -21293,7 +21343,7 @@ F: drivers/irqchip/irq-ti-sci-inta.c F: drivers/irqchip/irq-ti-sci-intr.c F: drivers/reset/reset-ti-sci.c F: drivers/soc/ti/ti_sci_inta_msi.c -F: drivers/genpd/ti/ti_sci_pm_domains.c +F: drivers/pmdomain/ti/ti_sci_pm_domains.c F: include/dt-bindings/soc/ti,sci_pm_domain.h F: include/linux/soc/ti/ti_sci_inta_msi.h F: include/linux/soc/ti/ti_sci_protocol.h @@ -21535,7 +21585,7 @@ L: linux-kernel@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/ti/linux.git -F: drivers/genpd/ti/omap_prm.c +F: drivers/pmdomain/ti/omap_prm.c F: drivers/soc/ti/* TI LM49xxx FAMILY ASoC CODEC DRIVERS @@ -21611,7 +21661,6 @@ L: linux-wireless@vger.kernel.org S: Orphan W: https://wireless.wiki.kernel.org/en/users/Drivers/wl12xx W: https://wireless.wiki.kernel.org/en/users/Drivers/wl1251 -T: git git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx.git F: drivers/net/wireless/ti/ TIMEKEEPING, CLOCKSOURCE CORE, NTP, ALARMTIMER @@ -21778,6 +21827,20 @@ S: Maintained F: Documentation/hwmon/tps546d24.rst F: drivers/hwmon/pmbus/tps546d24.c +TQ SYSTEMS BOARD & DRIVER SUPPORT +L: linux@ew.tq-group.com +S: Supported +W: https://www.tq-group.com/en/products/tq-embedded/ +F: arch/arm/boot/dts/imx*mba*.dts* +F: arch/arm/boot/dts/imx*tqma*.dts* +F: arch/arm/boot/dts/mba*.dtsi +F: arch/arm64/boot/dts/freescale/imx*mba*.dts* +F: arch/arm64/boot/dts/freescale/imx*tqma*.dts* +F: arch/arm64/boot/dts/freescale/mba*.dtsi +F: drivers/gpio/gpio-tqmx86.c +F: drivers/mfd/tqmx86.c +F: drivers/watchdog/tqmx86_wdt.c + TRACING M: Steven Rostedt M: Masami Hiramatsu @@ -23054,9 +23117,9 @@ F: drivers/net/vrf.c VSPRINTF M: Petr Mladek M: Steven Rostedt -M: Sergey Senozhatsky R: Andy Shevchenko R: Rasmus Villemoes +R: Sergey Senozhatsky S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git F: Documentation/core-api/printk-formats.rst @@ -23364,9 +23427,11 @@ F: drivers/platform/x86/x86-android-tablets/ X86 PLATFORM DRIVERS M: Hans de Goede +M: Ilpo Järvinen M: Mark Gross L: platform-driver-x86@vger.kernel.org S: Maintained +Q: https://patchwork.kernel.org/project/platform-driver-x86/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git F: drivers/platform/olpc/ F: drivers/platform/x86/ diff --git a/Makefile b/Makefile index 4f283d915e5440eb986100032a639711e883dd21..88ebf65479644c268b90a5342434f6ebbbfdb025 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 -PATCHLEVEL = 5 +PATCHLEVEL = 6 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc5 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* @@ -280,8 +280,8 @@ no-dot-config-targets := $(clean-targets) \ # Installation targets should not require compiler. Unfortunately, vdso_install # is an exception where build artifacts may be updated. This must be fixed. no-compiler-targets := $(no-dot-config-targets) install dtbs_install \ - headers_install modules_install kernelrelease image_name -no-sync-config-targets := $(no-dot-config-targets) %install kernelrelease \ + headers_install modules_install modules_sign kernelrelease image_name +no-sync-config-targets := $(no-dot-config-targets) %install modules_sign kernelrelease \ image_name single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %.symtypes %/ @@ -510,7 +510,6 @@ LEX = flex YACC = bison AWK = awk INSTALLKERNEL := installkernel -DEPMOD = depmod PERL = perl PYTHON3 = python3 CHECK = sparse @@ -564,14 +563,6 @@ KBUILD_CFLAGS += -funsigned-char KBUILD_CFLAGS += -fno-common KBUILD_CFLAGS += -fno-PIE KBUILD_CFLAGS += -fno-strict-aliasing -KBUILD_CFLAGS += -Wall -KBUILD_CFLAGS += -Wundef -KBUILD_CFLAGS += -Werror=implicit-function-declaration -KBUILD_CFLAGS += -Werror=implicit-int -KBUILD_CFLAGS += -Werror=return-type -KBUILD_CFLAGS += -Werror=strict-prototypes -KBUILD_CFLAGS += -Wno-format-security -KBUILD_CFLAGS += -Wno-trigraphs KBUILD_CPPFLAGS := -D__KERNEL__ KBUILD_RUSTFLAGS := $(rust_common_flags) \ @@ -824,10 +815,6 @@ endif # may-sync-config endif # need-config KBUILD_CFLAGS += -fno-delete-null-pointer-checks -KBUILD_CFLAGS += $(call cc-disable-warning,frame-address,) -KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation) -KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow) -KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) ifdef CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE KBUILD_CFLAGS += -O2 @@ -858,40 +845,15 @@ ifdef CONFIG_READABLE_ASM KBUILD_CFLAGS += -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining endif -ifneq ($(CONFIG_FRAME_WARN),0) -KBUILD_CFLAGS += -Wframe-larger-than=$(CONFIG_FRAME_WARN) -endif - stackp-flags-y := -fno-stack-protector stackp-flags-$(CONFIG_STACKPROTECTOR) := -fstack-protector stackp-flags-$(CONFIG_STACKPROTECTOR_STRONG) := -fstack-protector-strong KBUILD_CFLAGS += $(stackp-flags-y) -KBUILD_CPPFLAGS-$(CONFIG_WERROR) += -Werror -KBUILD_CPPFLAGS += $(KBUILD_CPPFLAGS-y) -KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds - KBUILD_RUSTFLAGS-$(CONFIG_WERROR) += -Dwarnings KBUILD_RUSTFLAGS += $(KBUILD_RUSTFLAGS-y) -ifdef CONFIG_CC_IS_CLANG -# The kernel builds with '-std=gnu11' so use of GNU extensions is acceptable. -KBUILD_CFLAGS += -Wno-gnu -else - -# gcc inanely warns about local variables called 'main' -KBUILD_CFLAGS += -Wno-main -endif - -# These warnings generated too much noise in a regular build. -# Use make W=1 to enable them (see scripts/Makefile.extrawarn) -KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) -KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable) - -# These result in bogus false positives -KBUILD_CFLAGS += $(call cc-disable-warning, dangling-pointer) - ifdef CONFIG_FRAME_POINTER KBUILD_CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls KBUILD_RUSTFLAGS += -Cforce-frame-pointers=y @@ -1026,51 +988,12 @@ endif # arch Makefile may override CC so keep this after arch Makefile is included NOSTDINC_FLAGS += -nostdinc -# Variable Length Arrays (VLAs) should not be used anywhere in the kernel -KBUILD_CFLAGS += -Wvla - -# disable pointer signed / unsigned warnings in gcc 4.0 -KBUILD_CFLAGS += -Wno-pointer-sign - -# In order to make sure new function cast mismatches are not introduced -# in the kernel (to avoid tripping CFI checking), the kernel should be -# globally built with -Wcast-function-type. -KBUILD_CFLAGS += $(call cc-option, -Wcast-function-type) - # To gain proper coverage for CONFIG_UBSAN_BOUNDS and CONFIG_FORTIFY_SOURCE, # the kernel uses only C99 flexible arrays for dynamically sized trailing # arrays. Enforce this for everything that may examine structure sizes and # perform bounds checking. KBUILD_CFLAGS += $(call cc-option, -fstrict-flex-arrays=3) -# disable stringop warnings in gcc 8+ -KBUILD_CFLAGS += $(call cc-disable-warning, stringop-truncation) - -# We'll want to enable this eventually, but it's not going away for 5.7 at least -KBUILD_CFLAGS += $(call cc-disable-warning, stringop-overflow) - -# Another good warning that we'll want to enable eventually -KBUILD_CFLAGS += $(call cc-disable-warning, restrict) - -# Enabled with W=2, disabled by default as noisy -ifdef CONFIG_CC_IS_GCC -KBUILD_CFLAGS += -Wno-maybe-uninitialized -endif - -# The allocators already balk at large sizes, so silence the compiler -# warnings for bounds checks involving those possible values. While -# -Wno-alloc-size-larger-than would normally be used here, earlier versions -# of gcc (<9.1) weirdly don't handle the option correctly when _other_ -# warnings are produced (?!). Using -Walloc-size-larger-than=SIZE_MAX -# doesn't work (as it is documented to), silently resolving to "0" prior to -# version 9.1 (and producing an error more recently). Numeric values larger -# than PTRDIFF_MAX also don't work prior to version 9.1, which are silently -# ignored, continuing to default to PTRDIFF_MAX. So, left with no other -# choice, we must perform a versioned check to disable this warning. -# https://lore.kernel.org/lkml/20210824115859.187f272f@canb.auug.org.au -KBUILD_CFLAGS-$(call gcc-min-version, 90100) += -Wno-alloc-size-larger-than -KBUILD_CFLAGS += $(KBUILD_CFLAGS-y) $(CONFIG_CC_IMPLICIT_FALLTHROUGH) - # disable invalid "can't wrap" optimizations for signed / pointers KBUILD_CFLAGS += -fno-strict-overflow @@ -1082,15 +1005,6 @@ ifdef CONFIG_CC_IS_GCC KBUILD_CFLAGS += -fconserve-stack endif -# Prohibit date/time macros, which would make the build non-deterministic -KBUILD_CFLAGS += -Werror=date-time - -# enforce correct pointer usage -KBUILD_CFLAGS += $(call cc-option,-Werror=incompatible-pointer-types) - -# Require designated initializers for all marked structures -KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init) - # change __FILE__ to the relative path from the srctree KBUILD_CPPFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=) @@ -1548,44 +1462,8 @@ modules: modules_prepare modules_prepare: prepare $(Q)$(MAKE) $(build)=scripts scripts/module.lds -export modules_sign_only := - -ifeq ($(CONFIG_MODULE_SIG),y) -PHONY += modules_sign -modules_sign: modules_install - @: - -# modules_sign is a subset of modules_install. -# 'make modules_install modules_sign' is equivalent to 'make modules_install'. -ifeq ($(filter modules_install,$(MAKECMDGOALS)),) -modules_sign_only := y -endif -endif - endif # CONFIG_MODULES -modinst_pre := -ifneq ($(filter modules_install,$(MAKECMDGOALS)),) -modinst_pre := __modinst_pre -endif - -modules_install: $(modinst_pre) -PHONY += __modinst_pre -__modinst_pre: - @rm -rf $(MODLIB)/kernel - @rm -f $(MODLIB)/source - @mkdir -p $(MODLIB) -ifdef CONFIG_MODULES - @ln -s $(abspath $(srctree)) $(MODLIB)/source - @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ - rm -f $(MODLIB)/build ; \ - ln -s $(CURDIR) $(MODLIB)/build ; \ - fi - @sed 's:^\(.*\)\.o$$:kernel/\1.ko:' modules.order > $(MODLIB)/modules.order -endif - @cp -f modules.builtin $(MODLIB)/ - @cp -f $(objtree)/modules.builtin.modinfo $(MODLIB)/ - ### # Cleaning is done on three levels. # make clean Delete most generated files @@ -1594,7 +1472,7 @@ endif # make distclean Remove editor backup files, patch leftover files and the like # Directories & files removed with 'make clean' -CLEAN_FILES += include/ksym vmlinux.symvers modules-only.symvers \ +CLEAN_FILES += vmlinux.symvers modules-only.symvers \ modules.builtin modules.builtin.modinfo modules.nsdeps \ compile_commands.json .thinlto-cache rust/test rust/doc \ rust-project.json .vmlinux.objs .vmlinux.export.c @@ -1608,7 +1486,7 @@ MRPROPER_FILES += include/config include/generated \ certs/signing_key.pem \ certs/x509.genkey \ vmlinux-gdb.py \ - *.spec rpmbuild \ + kernel.spec rpmbuild \ rust/libmacros.so # clean - Delete most, but leave enough to build external modules @@ -1675,7 +1553,6 @@ help: @echo ' mrproper - Remove all generated files + config + various backup files' @echo ' distclean - mrproper + remove editor backup and patch files' @echo '' - @echo 'Configuration targets:' @$(MAKE) -f $(srctree)/scripts/kconfig/Makefile help @echo '' @echo 'Other generic targets:' @@ -1923,19 +1800,39 @@ help: @echo ' rust-analyzer - generate rust-project.json rust-analyzer support file' @echo '' +ifndef CONFIG_MODULES +modules modules_install: __external_modules_error __external_modules_error: @echo >&2 '***' @echo >&2 '*** The present kernel disabled CONFIG_MODULES.' @echo >&2 '*** You cannot build or install external modules.' @echo >&2 '***' @false +endif endif # KBUILD_EXTMOD # --------------------------------------------------------------------------- # Modules -PHONY += modules modules_install modules_prepare +PHONY += modules modules_install modules_sign modules_prepare + +modules_install: + $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst \ + sign-only=$(if $(filter modules_install,$(MAKECMDGOALS)),,y) + +ifeq ($(CONFIG_MODULE_SIG),y) +# modules_sign is a subset of modules_install. +# 'make modules_install modules_sign' is equivalent to 'make modules_install'. +modules_sign: modules_install + @: +else +modules_sign: + @echo >&2 '***' + @echo >&2 '*** CONFIG_MODULE_SIG is disabled. You cannot sign modules.' + @echo >&2 '***' + @false +endif ifdef CONFIG_MODULES @@ -1953,22 +1850,9 @@ PHONY += modules_check modules_check: $(MODORDER) $(Q)$(CONFIG_SHELL) $(srctree)/scripts/modules-check.sh $< -quiet_cmd_depmod = DEPMOD $(MODLIB) - cmd_depmod = $(CONFIG_SHELL) $(srctree)/scripts/depmod.sh $(DEPMOD) \ - $(KERNELRELEASE) - -modules_install: - $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst - $(call cmd,depmod) - else # CONFIG_MODULES -# Modules not configured -# --------------------------------------------------------------------------- - -PHONY += __external_modules_error - -modules modules_install: __external_modules_error +modules: @: KBUILD_MODULES := @@ -2147,6 +2031,10 @@ kernelversion: image_name: @echo $(KBUILD_IMAGE) +PHONY += run-command +run-command: + $(Q)$(KBUILD_RUN_COMMAND) + quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))) cmd_rmfiles = rm -rf $(rm-files) diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild index dd31e97edae8e5705f83b7f145d21e7fe532a02e..396caece6d6d99c7a428f439322a0a18452e1a42 100644 --- a/arch/alpha/include/asm/Kbuild +++ b/arch/alpha/include/asm/Kbuild @@ -3,6 +3,5 @@ generated-y += syscall_table.h generic-y += agp.h generic-y += asm-offsets.h -generic-y += export.h generic-y += kvm_para.h generic-y += mcs_spinlock.h diff --git a/arch/alpha/lib/callback_srm.S b/arch/alpha/lib/callback_srm.S index b13c4a231f1b7fd088d2338585beb28fd5de8faa..36b63f2951709040dcc88e4e7e96f97247f10262 100644 --- a/arch/alpha/lib/callback_srm.S +++ b/arch/alpha/lib/callback_srm.S @@ -3,8 +3,8 @@ * arch/alpha/lib/callback_srm.S */ +#include #include -#include .text #define HWRPB_CRB_OFFSET 0xc0 diff --git a/arch/alpha/lib/clear_page.S b/arch/alpha/lib/clear_page.S index ce02de7b049347b99c7eed9ee7c7deceb41af895..af70ee309a339e54f4cf237614f82d0ff8e6a3d7 100644 --- a/arch/alpha/lib/clear_page.S +++ b/arch/alpha/lib/clear_page.S @@ -4,7 +4,7 @@ * * Zero an entire page. */ -#include +#include .text .align 4 .global clear_page diff --git a/arch/alpha/lib/clear_user.S b/arch/alpha/lib/clear_user.S index db6c6ca45896c8d3b643cf61df2bf935e8d5a8b7..848eb60a0010c88223a20fea9ceed5cafee9cc1d 100644 --- a/arch/alpha/lib/clear_user.S +++ b/arch/alpha/lib/clear_user.S @@ -10,7 +10,7 @@ * a successful copy). There is also some rather minor exception setup * stuff. */ -#include +#include /* Allow an exception for an insn; exit if we get one. */ #define EX(x,y...) \ diff --git a/arch/alpha/lib/copy_page.S b/arch/alpha/lib/copy_page.S index 5439a30c77d069ac50c7c97ab333e92d5b378ae1..1c444fdad9a5b8753581378342bc9e430239997d 100644 --- a/arch/alpha/lib/copy_page.S +++ b/arch/alpha/lib/copy_page.S @@ -4,7 +4,7 @@ * * Copy an entire page. */ -#include +#include .text .align 4 .global copy_page diff --git a/arch/alpha/lib/copy_user.S b/arch/alpha/lib/copy_user.S index 32ab0344b1853cf2032e76bd8cdbdceaf4454b03..ef18faafcad6ece12bee961bdc3d108bc3c11919 100644 --- a/arch/alpha/lib/copy_user.S +++ b/arch/alpha/lib/copy_user.S @@ -12,7 +12,7 @@ * exception setup stuff.. */ -#include +#include /* Allow an exception for an insn; exit if we get one. */ #define EXI(x,y...) \ diff --git a/arch/alpha/lib/csum_ipv6_magic.S b/arch/alpha/lib/csum_ipv6_magic.S index c7b213ab01abbc35dc32da97c600351b7b1513cc..273c426c3859b79c6ea88a373de1f8abf71735cd 100644 --- a/arch/alpha/lib/csum_ipv6_magic.S +++ b/arch/alpha/lib/csum_ipv6_magic.S @@ -13,7 +13,7 @@ * added by Ivan Kokshaysky */ -#include +#include .globl csum_ipv6_magic .align 4 .ent csum_ipv6_magic diff --git a/arch/alpha/lib/divide.S b/arch/alpha/lib/divide.S index 2b60eb45e50b68993b6225c8c7f23cacedd00651..db01840d76ec7366c8b4a802976d8d318e1f4bb6 100644 --- a/arch/alpha/lib/divide.S +++ b/arch/alpha/lib/divide.S @@ -46,7 +46,7 @@ * $28 - compare status */ -#include +#include #define halt .long 0 /* diff --git a/arch/alpha/lib/ev6-clear_page.S b/arch/alpha/lib/ev6-clear_page.S index 325864c81586daa94d1b04534ed54016fadad6d3..a534d9ff7161c9f30bb70012bdea37f46785ae0b 100644 --- a/arch/alpha/lib/ev6-clear_page.S +++ b/arch/alpha/lib/ev6-clear_page.S @@ -4,7 +4,7 @@ * * Zero an entire page. */ -#include +#include .text .align 4 .global clear_page diff --git a/arch/alpha/lib/ev6-clear_user.S b/arch/alpha/lib/ev6-clear_user.S index 7e644f83cdf2905807b15b6cd788770c1be732d4..af776cc45f91e753ad869afcc97c057c1561a952 100644 --- a/arch/alpha/lib/ev6-clear_user.S +++ b/arch/alpha/lib/ev6-clear_user.S @@ -29,7 +29,7 @@ * want to leave a hole (and we also want to avoid repeating lots of work) */ -#include +#include /* Allow an exception for an insn; exit if we get one. */ #define EX(x,y...) \ 99: x,##y; \ diff --git a/arch/alpha/lib/ev6-copy_page.S b/arch/alpha/lib/ev6-copy_page.S index fd7212c8dcf1848533ed8dc07c2f19c6a7aa673e..36be5113b7b747006d3636d43df4da5342222dbe 100644 --- a/arch/alpha/lib/ev6-copy_page.S +++ b/arch/alpha/lib/ev6-copy_page.S @@ -57,7 +57,7 @@ destination pages are in the dcache, but it is my guess that this is less important than the dcache miss case. */ -#include +#include .text .align 4 .global copy_page diff --git a/arch/alpha/lib/ev6-copy_user.S b/arch/alpha/lib/ev6-copy_user.S index f3e43375439743c54a2e58b2e7928339f50642bb..b9b19710c3648c992609c92011ba4ce79696ad7a 100644 --- a/arch/alpha/lib/ev6-copy_user.S +++ b/arch/alpha/lib/ev6-copy_user.S @@ -23,7 +23,7 @@ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 */ -#include +#include /* Allow an exception for an insn; exit if we get one. */ #define EXI(x,y...) \ 99: x,##y; \ diff --git a/arch/alpha/lib/ev6-csum_ipv6_magic.S b/arch/alpha/lib/ev6-csum_ipv6_magic.S index 9a73f90700a13a63cb3929d9ba3db7daab0250fc..2ee548be98e32dffb1bc2edb5213597e629f083f 100644 --- a/arch/alpha/lib/ev6-csum_ipv6_magic.S +++ b/arch/alpha/lib/ev6-csum_ipv6_magic.S @@ -53,7 +53,7 @@ * may cause additional delay in rare cases (load-load replay traps). */ -#include +#include .globl csum_ipv6_magic .align 4 .ent csum_ipv6_magic diff --git a/arch/alpha/lib/ev6-divide.S b/arch/alpha/lib/ev6-divide.S index 137ff1a07356311d598cd8c254cb8214c8707ec7..b73a6d26362e7441cb219aa7efa7aa8adffb60b0 100644 --- a/arch/alpha/lib/ev6-divide.S +++ b/arch/alpha/lib/ev6-divide.S @@ -56,7 +56,7 @@ * Try not to change the actual algorithm if possible for consistency. */ -#include +#include #define halt .long 0 /* diff --git a/arch/alpha/lib/ev6-memchr.S b/arch/alpha/lib/ev6-memchr.S index 56bf9e14eeeefadf510cbe4d52fba27c0f1f5701..f75ba43e61e378c9d688a97f090aefd4049e3ad1 100644 --- a/arch/alpha/lib/ev6-memchr.S +++ b/arch/alpha/lib/ev6-memchr.S @@ -28,7 +28,7 @@ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 * Try not to change the actual algorithm if possible for consistency. */ -#include +#include .set noreorder .set noat diff --git a/arch/alpha/lib/ev6-memcpy.S b/arch/alpha/lib/ev6-memcpy.S index ffbd056b6eb2905d72d01b6f5bc65d9ac4a06340..3ef43c26c8af6945851a25966b895f5d23b988fb 100644 --- a/arch/alpha/lib/ev6-memcpy.S +++ b/arch/alpha/lib/ev6-memcpy.S @@ -20,7 +20,7 @@ * Temp usage notes: * $1,$2, - scratch */ -#include +#include .set noreorder .set noat diff --git a/arch/alpha/lib/ev6-memset.S b/arch/alpha/lib/ev6-memset.S index 1cfcfbbea6f068fcc69200bfd7a1d4177e1d6b75..89d7809da4ccd41774a9019cd88345cbefccc15c 100644 --- a/arch/alpha/lib/ev6-memset.S +++ b/arch/alpha/lib/ev6-memset.S @@ -27,7 +27,7 @@ * as fixes will need to be made in multiple places. The performance gain * is worth it. */ -#include +#include .set noat .set noreorder .text diff --git a/arch/alpha/lib/ev67-strcat.S b/arch/alpha/lib/ev67-strcat.S index ec3096a9e8d409558f7a29d19868688f7971dc81..f8c7305b11d63518fd2bf9595255ef119fe34211 100644 --- a/arch/alpha/lib/ev67-strcat.S +++ b/arch/alpha/lib/ev67-strcat.S @@ -20,7 +20,7 @@ * string once. */ -#include +#include .text .align 4 diff --git a/arch/alpha/lib/ev67-strchr.S b/arch/alpha/lib/ev67-strchr.S index fbf89e0b6dc3b7bbb70560f1852728c01f3bf26d..97a7cb4753093c097787cab11e3b86829dbc5808 100644 --- a/arch/alpha/lib/ev67-strchr.S +++ b/arch/alpha/lib/ev67-strchr.S @@ -16,7 +16,7 @@ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 * Try not to change the actual algorithm if possible for consistency. */ -#include +#include #include .set noreorder diff --git a/arch/alpha/lib/ev67-strlen.S b/arch/alpha/lib/ev67-strlen.S index b73106ffbbc7c7e32e286a92ec9f2cc276038bfd..3d9078807ab4921da310b5ac57a8263091216a7e 100644 --- a/arch/alpha/lib/ev67-strlen.S +++ b/arch/alpha/lib/ev67-strlen.S @@ -18,7 +18,7 @@ * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1 * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 */ -#include +#include .set noreorder .set noat diff --git a/arch/alpha/lib/ev67-strncat.S b/arch/alpha/lib/ev67-strncat.S index ceb0ca528789aa67026ed17e1e2b6db7245de242..8f313233e3a78ee7aee6aad683e834eeac61216c 100644 --- a/arch/alpha/lib/ev67-strncat.S +++ b/arch/alpha/lib/ev67-strncat.S @@ -21,7 +21,7 @@ * Try not to change the actual algorithm if possible for consistency. */ -#include +#include .text .align 4 diff --git a/arch/alpha/lib/ev67-strrchr.S b/arch/alpha/lib/ev67-strrchr.S index 7f80e398530f564666cfac6f8b1fa8c836b028d4..ae7355f9ec560146a8219ae15fb84548397aadeb 100644 --- a/arch/alpha/lib/ev67-strrchr.S +++ b/arch/alpha/lib/ev67-strrchr.S @@ -19,7 +19,7 @@ * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1 */ -#include +#include #include .set noreorder diff --git a/arch/alpha/lib/memchr.S b/arch/alpha/lib/memchr.S index c13d3eca2e0592736dd17112ff836dcac5c0b7cb..45366e32feeed7d7285d0b8ca8ac5c25d1370671 100644 --- a/arch/alpha/lib/memchr.S +++ b/arch/alpha/lib/memchr.S @@ -31,7 +31,7 @@ For correctness consider that: - only minimum number of quadwords may be accessed - the third argument is an unsigned long */ -#include +#include .set noreorder .set noat diff --git a/arch/alpha/lib/memmove.S b/arch/alpha/lib/memmove.S index 42d1922d0edfca212abebd5bbc674257723ba0ac..3a27689e33902dcaeb23f11e153a48b977210169 100644 --- a/arch/alpha/lib/memmove.S +++ b/arch/alpha/lib/memmove.S @@ -7,7 +7,7 @@ * This is hand-massaged output from the original memcpy.c. We defer to * memcpy whenever possible; the backwards copy loops are not unrolled. */ -#include +#include .set noat .set noreorder .text diff --git a/arch/alpha/lib/memset.S b/arch/alpha/lib/memset.S index 00393e30df259150a5bafe4d0e370987c3fce1e9..9075d69183461cea222555e98a3f0783ab7ccaf4 100644 --- a/arch/alpha/lib/memset.S +++ b/arch/alpha/lib/memset.S @@ -14,7 +14,7 @@ * The scheduling comments are according to the EV5 documentation (and done by * hand, so they might well be incorrect, please do tell me about it..) */ -#include +#include .set noat .set noreorder .text diff --git a/arch/alpha/lib/strcat.S b/arch/alpha/lib/strcat.S index 055877dccd276869895b8a48ad6d6c2590c019e2..62b90ebbcf4430f26f223caee2aa78c8df6d10d4 100644 --- a/arch/alpha/lib/strcat.S +++ b/arch/alpha/lib/strcat.S @@ -5,7 +5,7 @@ * * Append a null-terminated string from SRC to DST. */ -#include +#include .text diff --git a/arch/alpha/lib/strchr.S b/arch/alpha/lib/strchr.S index 17871dd00280489f80b381a2610ca9b547058c9c..68c54ff50dfe4dcdd361f7a21003542673b613ae 100644 --- a/arch/alpha/lib/strchr.S +++ b/arch/alpha/lib/strchr.S @@ -6,7 +6,7 @@ * Return the address of a given character within a null-terminated * string, or null if it is not found. */ -#include +#include #include .set noreorder diff --git a/arch/alpha/lib/strcpy.S b/arch/alpha/lib/strcpy.S index cb74ad23a90df8a79a38ea157ac434bc7a7ac567..d8773ba77525a026140f90e071e14ed18a53033c 100644 --- a/arch/alpha/lib/strcpy.S +++ b/arch/alpha/lib/strcpy.S @@ -6,7 +6,7 @@ * Copy a null-terminated string from SRC to DST. Return a pointer * to the null-terminator in the source. */ -#include +#include .text .align 3 diff --git a/arch/alpha/lib/strlen.S b/arch/alpha/lib/strlen.S index dd882fe4d7e3076469dbdba9d6c3a6135583c75a..4fc6a6ff24cdb7409814a64228021cda122fb102 100644 --- a/arch/alpha/lib/strlen.S +++ b/arch/alpha/lib/strlen.S @@ -12,7 +12,7 @@ * do this instead of the 9 instructions that * binary search needs). */ -#include +#include .set noreorder .set noat diff --git a/arch/alpha/lib/strncat.S b/arch/alpha/lib/strncat.S index 522fee3e26ac148f8d0175f1964d7f586aa2fb64..a913a7c84a398c09a3b147d29c31dc64f7d3d8b5 100644 --- a/arch/alpha/lib/strncat.S +++ b/arch/alpha/lib/strncat.S @@ -10,7 +10,7 @@ * past count, whereas libc may write to count+1. This follows the generic * implementation in lib/string.c and is, IMHO, more sensible. */ -#include +#include .text .align 3 diff --git a/arch/alpha/lib/strncpy.S b/arch/alpha/lib/strncpy.S index cc57fad8b7ca7728c28824dbc506357a562b3b8a..cb90cf022df3a0233e7954793d00f044e2b3f299 100644 --- a/arch/alpha/lib/strncpy.S +++ b/arch/alpha/lib/strncpy.S @@ -11,7 +11,7 @@ * version has cropped that bit o' nastiness as well as assuming that * __stxncpy is in range of a branch. */ -#include +#include .set noat .set noreorder diff --git a/arch/alpha/lib/strrchr.S b/arch/alpha/lib/strrchr.S index 7650ba99b7e2c877cb26d97351fa09bfe50b0e5e..dd8e073b6cf2a966e27e84cb4a300dd43e5771bb 100644 --- a/arch/alpha/lib/strrchr.S +++ b/arch/alpha/lib/strrchr.S @@ -6,7 +6,7 @@ * Return the address of the last occurrence of a given character * within a null-terminated string, or null if it is not found. */ -#include +#include #include .set noreorder diff --git a/arch/alpha/lib/udiv-qrnnd.S b/arch/alpha/lib/udiv-qrnnd.S index b887aa5428e55778dd224367f8f6f0df8113c683..96f05918bffe184dcabc0f2059d186daada93739 100644 --- a/arch/alpha/lib/udiv-qrnnd.S +++ b/arch/alpha/lib/udiv-qrnnd.S @@ -25,7 +25,7 @@ # along with GCC; see the file COPYING. If not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, # MA 02111-1307, USA. -#include +#include .set noreorder .set noat diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig index 6f4995ad98737a79f9f416de165e961cb078e619..3162db540ee96cffc85aa02bbd9d97418ef0098d 100644 --- a/arch/arc/Kconfig +++ b/arch/arc/Kconfig @@ -27,6 +27,8 @@ config ARC select GENERIC_SCHED_CLOCK select GENERIC_SMP_IDLE_THREAD select GENERIC_IOREMAP + select GENERIC_STRNCPY_FROM_USER if MMU + select GENERIC_STRNLEN_USER if MMU select HAVE_ARCH_KGDB select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARC_MMU_V4 @@ -491,11 +493,11 @@ config ARC_KVADDR_SIZE kernel-user gutter) config ARC_CURR_IN_REG - bool "Dedicate Register r25 for current_task pointer" + bool "cache current task pointer in gp" default y help - This reserved Register R25 to point to Current Task in - kernel mode. This saves memory access for each such access + This reserves gp register to point to Current Task in + kernel mode eliding memory access for each access config ARC_EMUL_UNALIGNED diff --git a/arch/arc/Makefile b/arch/arc/Makefile index 329400a1c35598187d62faca9e84f3f286985715..2390dd042e3636442bfd7b123209f1216095fecd 100644 --- a/arch/arc/Makefile +++ b/arch/arc/Makefile @@ -28,14 +28,14 @@ cflags-y += $(tune-mcpu-def-y) endif endif - ifdef CONFIG_ARC_CURR_IN_REG # For a global register definition, make sure it gets passed to every file # We had a customer reported bug where some code built in kernel was NOT using -# any kernel headers, and missing the r25 global register +# any kernel headers, and missing the global register # Can't do unconditionally because of recursive include issues # due to LINUXINCLUDE += -include $(srctree)/arch/arc/include/asm/current.h +cflags-y += -ffixed-gp endif cflags-y += -fsection-anchors @@ -67,7 +67,7 @@ cflags-$(CONFIG_ARC_DW2_UNWIND) += -fasynchronous-unwind-tables $(cfi) # small data is default for elf32 tool-chain. If not usable, disable it # This also allows repurposing GP as scratch reg to gcc reg allocator disable_small_data := y -cflags-$(disable_small_data) += -mno-sdata -fcall-used-gp +cflags-$(disable_small_data) += -mno-sdata cflags-$(CONFIG_CPU_BIG_ENDIAN) += -mbig-endian ldflags-$(CONFIG_CPU_BIG_ENDIAN) += -EB diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h index 2162023195c5aacb61a38301601f0d6da9c31e11..4b13f60fe7cae8ac292c855d33ab74634606ec69 100644 --- a/arch/arc/include/asm/arcregs.h +++ b/arch/arc/include/asm/arcregs.h @@ -23,7 +23,7 @@ #define ARC_REG_ICCM_BUILD 0x78 /* ICCM size (common) */ #define ARC_REG_XY_MEM_BCR 0x79 #define ARC_REG_MAC_BCR 0x7a -#define ARC_REG_MUL_BCR 0x7b +#define ARC_REG_MPY_BCR 0x7b #define ARC_REG_SWAP_BCR 0x7c #define ARC_REG_NORM_BCR 0x7d #define ARC_REG_MIXMAX_BCR 0x7e @@ -177,7 +177,7 @@ struct bcr_isa_arcv2 { #endif }; -struct bcr_uarch_build_arcv2 { +struct bcr_uarch_build { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int pad:8, prod:8, maj:8, min:8; #else @@ -185,6 +185,59 @@ struct bcr_uarch_build_arcv2 { #endif }; +struct bcr_mmu_3 { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ver:8, ways:4, sets:4, res:3, sasid:1, pg_sz:4, + u_itlb:4, u_dtlb:4; +#else + unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, sasid:1, res:3, sets:4, + ways:4, ver:8; +#endif +}; + +struct bcr_mmu_4 { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1, + n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3; +#else + /* DTLB ITLB JES JE JA */ + unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2, + pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8; +#endif +}; + +struct bcr_cache { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; +#else + unsigned int ver:8, config:4, sz:4, line_len:4, pad:12; +#endif +}; + +struct bcr_slc_cfg { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:24, way:2, lsz:2, sz:4; +#else + unsigned int sz:4, lsz:2, way:2, pad:24; +#endif +}; + +struct bcr_clust_cfg { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8; +#else + unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7; +#endif +}; + +struct bcr_volatile { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int start:4, limit:4, pad:22, order:1, disable:1; +#else + unsigned int disable:1, order:1, pad:22, limit:4, start:4; +#endif +}; + struct bcr_mpy { #ifdef CONFIG_CPU_BIG_ENDIAN unsigned int pad:8, x1616:8, dsp:4, cycles:2, type:2, ver:8; @@ -302,48 +355,6 @@ struct bcr_generic { #endif }; -/* - ******************************************************************* - * Generic structures to hold build configuration used at runtime - */ - -struct cpuinfo_arc_mmu { - unsigned int ver:4, pg_sz_k:8, s_pg_sz_m:8, pad:10, sasid:1, pae:1; - unsigned int sets:12, ways:4, u_dtlb:8, u_itlb:8; -}; - -struct cpuinfo_arc_cache { - unsigned int sz_k:14, line_len:8, assoc:4, alias:1, vipt:1, pad:4; -}; - -struct cpuinfo_arc_bpu { - unsigned int ver, full, num_cache, num_pred, ret_stk; -}; - -struct cpuinfo_arc_ccm { - unsigned int base_addr, sz; -}; - -struct cpuinfo_arc { - struct cpuinfo_arc_cache icache, dcache, slc; - struct cpuinfo_arc_mmu mmu; - struct cpuinfo_arc_bpu bpu; - struct bcr_identity core; - struct bcr_isa_arcv2 isa; - const char *release, *name; - unsigned int vec_base; - struct cpuinfo_arc_ccm iccm, dccm; - struct { - unsigned int swap:1, norm:1, minmax:1, barrel:1, crc:1, swape:1, pad1:2, - fpu_sp:1, fpu_dp:1, dual:1, dual_enb:1, pad2:4, - ap_num:4, ap_full:1, smart:1, rtt:1, pad3:1, - timer0:1, timer1:1, rtc:1, gfrc:1, pad4:4; - } extn; - struct bcr_mpy extn_mpy; -}; - -extern struct cpuinfo_arc cpuinfo_arc700[]; - static inline int is_isa_arcv2(void) { return IS_ENABLED(CONFIG_ISA_ARCV2); diff --git a/arch/arc/include/asm/atomic-llsc.h b/arch/arc/include/asm/atomic-llsc.h index 1b0ffaeee16d0e2efb720a91b8a7b3d0eb7d815a..5258cb81a16b4fbf4048d87b99c40d11b276894d 100644 --- a/arch/arc/include/asm/atomic-llsc.h +++ b/arch/arc/include/asm/atomic-llsc.h @@ -18,7 +18,7 @@ static inline void arch_atomic_##op(int i, atomic_t *v) \ : [val] "=&r" (val) /* Early clobber to prevent reg reuse */ \ : [ctr] "r" (&v->counter), /* Not "m": llock only supports reg direct addr mode */ \ [i] "ir" (i) \ - : "cc"); \ + : "cc", "memory"); \ } \ #define ATOMIC_OP_RETURN(op, asm_op) \ @@ -34,7 +34,7 @@ static inline int arch_atomic_##op##_return_relaxed(int i, atomic_t *v) \ : [val] "=&r" (val) \ : [ctr] "r" (&v->counter), \ [i] "ir" (i) \ - : "cc"); \ + : "cc", "memory"); \ \ return val; \ } @@ -56,7 +56,7 @@ static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v) \ [orig] "=&r" (orig) \ : [ctr] "r" (&v->counter), \ [i] "ir" (i) \ - : "cc"); \ + : "cc", "memory"); \ \ return orig; \ } diff --git a/arch/arc/include/asm/atomic64-arcv2.h b/arch/arc/include/asm/atomic64-arcv2.h index 6b6db981967aea95806a1f8de8922dfc6317a5af..9b5791b8547133b1d7ec4c53ab4a2f611857d165 100644 --- a/arch/arc/include/asm/atomic64-arcv2.h +++ b/arch/arc/include/asm/atomic64-arcv2.h @@ -60,7 +60,7 @@ static inline void arch_atomic64_##op(s64 a, atomic64_t *v) \ " bnz 1b \n" \ : "=&r"(val) \ : "r"(&v->counter), "ir"(a) \ - : "cc"); \ + : "cc", "memory"); \ } \ #define ATOMIC64_OP_RETURN(op, op1, op2) \ @@ -77,7 +77,7 @@ static inline s64 arch_atomic64_##op##_return_relaxed(s64 a, atomic64_t *v) \ " bnz 1b \n" \ : [val] "=&r"(val) \ : "r"(&v->counter), "ir"(a) \ - : "cc"); /* memory clobber comes from smp_mb() */ \ + : "cc", "memory"); \ \ return val; \ } @@ -99,7 +99,7 @@ static inline s64 arch_atomic64_fetch_##op##_relaxed(s64 a, atomic64_t *v) \ " bnz 1b \n" \ : "=&r"(orig), "=&r"(val) \ : "r"(&v->counter), "ir"(a) \ - : "cc"); /* memory clobber comes from smp_mb() */ \ + : "cc", "memory"); \ \ return orig; \ } diff --git a/arch/arc/include/asm/current.h b/arch/arc/include/asm/current.h index 9b9bdd3e6538894c7733fa26cf27037b2189a83a..06be89f6f2f052eb65fb76398d84e6fd77cd92c8 100644 --- a/arch/arc/include/asm/current.h +++ b/arch/arc/include/asm/current.h @@ -13,7 +13,7 @@ #ifdef CONFIG_ARC_CURR_IN_REG -register struct task_struct *curr_arc asm("r25"); +register struct task_struct *curr_arc asm("gp"); #define current (curr_arc) #else diff --git a/arch/arc/include/asm/dwarf.h b/arch/arc/include/asm/dwarf.h index 5f4de05bd4eed36cc2492fdb39aa704cb15d71be..a0d5ebe1bc3f5f37280d9ff37ad3df4827af89fb 100644 --- a/arch/arc/include/asm/dwarf.h +++ b/arch/arc/include/asm/dwarf.h @@ -10,23 +10,31 @@ #ifdef ARC_DW2_UNWIND_AS_CFI -#define CFI_STARTPROC .cfi_startproc -#define CFI_ENDPROC .cfi_endproc -#define CFI_DEF_CFA .cfi_def_cfa -#define CFI_REGISTER .cfi_register -#define CFI_REL_OFFSET .cfi_rel_offset -#define CFI_UNDEFINED .cfi_undefined +#define CFI_STARTPROC .cfi_startproc +#define CFI_ENDPROC .cfi_endproc +#define CFI_DEF_CFA .cfi_def_cfa +#define CFI_DEF_CFA_OFFSET .cfi_def_cfa_offset +#define CFI_DEF_CFA_REGISTER .cfi_def_cfa_register +#define CFI_OFFSET .cfi_offset +#define CFI_REL_OFFSET .cfi_rel_offset +#define CFI_REGISTER .cfi_register +#define CFI_RESTORE .cfi_restore +#define CFI_UNDEFINED .cfi_undefined #else #define CFI_IGNORE # -#define CFI_STARTPROC CFI_IGNORE -#define CFI_ENDPROC CFI_IGNORE -#define CFI_DEF_CFA CFI_IGNORE -#define CFI_REGISTER CFI_IGNORE -#define CFI_REL_OFFSET CFI_IGNORE -#define CFI_UNDEFINED CFI_IGNORE +#define CFI_STARTPROC CFI_IGNORE +#define CFI_ENDPROC CFI_IGNORE +#define CFI_DEF_CFA CFI_IGNORE +#define CFI_DEF_CFA_OFFSET CFI_IGNORE +#define CFI_DEF_CFA_REGISTER CFI_IGNORE +#define CFI_OFFSET CFI_IGNORE +#define CFI_REL_OFFSET CFI_IGNORE +#define CFI_REGISTER CFI_IGNORE +#define CFI_RESTORE CFI_IGNORE +#define CFI_UNDEFINED CFI_IGNORE #endif /* !ARC_DW2_UNWIND_AS_CFI */ diff --git a/arch/arc/include/asm/entry-arcv2.h b/arch/arc/include/asm/entry-arcv2.h index 0ff4c061056157b1968eb71a2c50a47f71b700a3..4d13320e0c1b6363a628439bd79bae0042d00a79 100644 --- a/arch/arc/include/asm/entry-arcv2.h +++ b/arch/arc/include/asm/entry-arcv2.h @@ -18,7 +18,6 @@ * | orig_r0 | * | event/ECR | * | bta | - * | user_r25 | * | gp | * | fp | * | sp | @@ -49,14 +48,18 @@ /*------------------------------------------------------------------------*/ .macro INTERRUPT_PROLOGUE - ; (A) Before jumping to Interrupt Vector, hardware micro-ops did following: + ; Before jumping to Interrupt Vector, hardware micro-ops did following: ; 1. SP auto-switched to kernel mode stack ; 2. STATUS32.Z flag set if in U mode at time of interrupt (U:1,K:0) ; 3. Auto save: (mandatory) Push PC and STAT32 on stack ; hardware does even if CONFIG_ARC_IRQ_NO_AUTOSAVE - ; 4. Auto save: (optional) r0-r11, blink, LPE,LPS,LPC, JLI,LDI,EI + ; 4a. Auto save: (optional) r0-r11, blink, LPE,LPS,LPC, JLI,LDI,EI ; - ; (B) Manually saved some regs: r12,r25,r30, sp,fp,gp, ACCL pair + ; Now + ; 4b. If Auto-save (optional) not enabled in hw, manually save them + ; 5. Manually save: r12,r30, sp,fp,gp, ACCL pair + ; + ; At the end, SP points to pt_regs #ifdef CONFIG_ARC_IRQ_NO_AUTOSAVE ; carve pt_regs on stack (case #3), PC/STAT32 already on stack @@ -72,15 +75,16 @@ .endm /*------------------------------------------------------------------------*/ -.macro EXCEPTION_PROLOGUE +.macro EXCEPTION_PROLOGUE_KEEP_AE - ; (A) Before jumping to Exception Vector, hardware micro-ops did following: + ; Before jumping to Exception Vector, hardware micro-ops did following: ; 1. SP auto-switched to kernel mode stack ; 2. STATUS32.Z flag set if in U mode at time of exception (U:1,K:0) ; - ; (B) Manually save the complete reg file below + ; Now manually save rest of reg file + ; At the end, SP points to pt_regs - sub sp, sp, SZ_PT_REGS ; carve pt_regs + sub sp, sp, SZ_PT_REGS ; carve space for pt_regs ; _HARD saves r10 clobbered by _SOFT as scratch hence comes first @@ -100,6 +104,16 @@ ; OUTPUT: r10 has ECR expected by EV_Trap .endm +.macro EXCEPTION_PROLOGUE + + EXCEPTION_PROLOGUE_KEEP_AE ; return ECR in r10 + + lr r0, [efa] + mov r1, sp + + FAKE_RET_FROM_EXCPN ; clobbers r9 +.endm + /*------------------------------------------------------------------------ * This macro saves the registers manually which would normally be autosaved * by hardware on taken interrupts. It is used by @@ -135,10 +149,10 @@ */ .macro __SAVE_REGFILE_SOFT - ST2 gp, fp, PT_r26 ; gp (r26), fp (r27) - - st r12, [sp, PT_sp + 4] - st r30, [sp, PT_sp + 8] + st fp, [sp, PT_fp] ; r27 + st r30, [sp, PT_r30] + st r12, [sp, PT_r12] + st r26, [sp, PT_r26] ; gp ; Saving pt_regs->sp correctly requires some extra work due to the way ; Auto stack switch works @@ -153,30 +167,30 @@ ; ISA requires ADD.nz to have same dest and src reg operands mov.nz r10, sp - add.nz r10, r10, SZ_PT_REGS ; K mode SP + add2.nz r10, r10, SZ_PT_REGS/4 ; K mode SP st r10, [sp, PT_sp] ; SP (pt_regs->sp) -#ifdef CONFIG_ARC_CURR_IN_REG - st r25, [sp, PT_user_r25] - GET_CURR_TASK_ON_CPU r25 -#endif - #ifdef CONFIG_ARC_HAS_ACCL_REGS ST2 r58, r59, PT_r58 #endif /* clobbers r10, r11 registers pair */ DSP_SAVE_REGFILE_IRQ + +#ifdef CONFIG_ARC_CURR_IN_REG + GET_CURR_TASK_ON_CPU gp +#endif + .endm /*------------------------------------------------------------------------*/ .macro __RESTORE_REGFILE_SOFT - LD2 gp, fp, PT_r26 ; gp (r26), fp (r27) - - ld r12, [sp, PT_r12] + ld fp, [sp, PT_fp] ld r30, [sp, PT_r30] + ld r12, [sp, PT_r12] + ld r26, [sp, PT_r26] ; Restore SP (into AUX_USER_SP) only if returning to U mode ; - for K mode, it will be implicitly restored as stack is unwound @@ -188,10 +202,6 @@ sr r10, [AUX_USER_SP] 1: -#ifdef CONFIG_ARC_CURR_IN_REG - ld r25, [sp, PT_user_r25] -#endif - /* clobbers r10, r11 registers pair */ DSP_RESTORE_REGFILE_IRQ @@ -249,7 +259,7 @@ btst r0, STATUS_U_BIT ; Z flag set if K, used in restoring SP - ld r10, [sp, PT_event + 4] + ld r10, [sp, PT_bta] sr r10, [erbta] LD2 r10, r11, PT_ret @@ -264,8 +274,8 @@ .macro FAKE_RET_FROM_EXCPN lr r9, [status32] - bic r9, r9, STATUS_AE_MASK - or r9, r9, STATUS_IE_MASK + bclr r9, r9, STATUS_AE_BIT + bset r9, r9, STATUS_IE_BIT kflag r9 .endm diff --git a/arch/arc/include/asm/entry-compact.h b/arch/arc/include/asm/entry-compact.h index 67ff06e15ceaf42baacf646edb81865e4a925641..a0e760eb35a80e9faefa3d16389b01eac6dcea00 100644 --- a/arch/arc/include/asm/entry-compact.h +++ b/arch/arc/include/asm/entry-compact.h @@ -140,7 +140,7 @@ * * After this it is safe to call the "C" handlers *-------------------------------------------------------------*/ -.macro EXCEPTION_PROLOGUE +.macro EXCEPTION_PROLOGUE_KEEP_AE /* Need at least 1 reg to code the early exception prologue */ PROLOG_FREEUP_REG r9, @ex_saved_reg1 @@ -151,14 +151,6 @@ /* ARC700 doesn't provide auto-stack switching */ SWITCH_TO_KERNEL_STK -#ifdef CONFIG_ARC_CURR_IN_REG - /* Treat r25 as scratch reg (save on stack) and load with "current" */ - PUSH r25 - GET_CURR_TASK_ON_CPU r25 -#else - sub sp, sp, 4 -#endif - st.a r0, [sp, -8] /* orig_r0 needed for syscall (skip ECR slot) */ sub sp, sp, 4 /* skip pt_regs->sp, already saved above */ @@ -178,7 +170,23 @@ PUSHAX erbta lr r10, [ecr] - st r10, [sp, PT_event] /* EV_Trap expects r10 to have ECR */ + st r10, [sp, PT_event] + +#ifdef CONFIG_ARC_CURR_IN_REG + /* gp already saved on stack: now load with "current" */ + GET_CURR_TASK_ON_CPU gp +#endif + ; OUTPUT: r10 has ECR expected by EV_Trap +.endm + +.macro EXCEPTION_PROLOGUE + + EXCEPTION_PROLOGUE_KEEP_AE ; return ECR in r10 + + lr r0, [efa] + mov r1, sp + + FAKE_RET_FROM_EXCPN ; clobbers r9 .endm /*-------------------------------------------------------------- @@ -208,11 +216,8 @@ POP gp RESTORE_R12_TO_R0 -#ifdef CONFIG_ARC_CURR_IN_REG - ld r25, [sp, 12] -#endif ld sp, [sp] /* restore original sp */ - /* orig_r0, ECR, user_r25 skipped automatically */ + /* orig_r0, ECR skipped automatically */ .endm /* Dummy ECR values for Interrupts */ @@ -229,13 +234,6 @@ SWITCH_TO_KERNEL_STK -#ifdef CONFIG_ARC_CURR_IN_REG - /* Treat r25 as scratch reg (save on stack) and load with "current" */ - PUSH r25 - GET_CURR_TASK_ON_CPU r25 -#else - sub sp, sp, 4 -#endif PUSH 0x003\LVL\()abcd /* Dummy ECR */ sub sp, sp, 8 /* skip orig_r0 (not needed) @@ -255,6 +253,10 @@ PUSHAX lp_start PUSHAX bta_l\LVL\() +#ifdef CONFIG_ARC_CURR_IN_REG + /* gp already saved on stack: now load with "current" */ + GET_CURR_TASK_ON_CPU gp +#endif .endm /*-------------------------------------------------------------- @@ -282,11 +284,7 @@ POP gp RESTORE_R12_TO_R0 -#ifdef CONFIG_ARC_CURR_IN_REG - ld r25, [sp, 12] -#endif - ld sp, [sp] /* restore original sp */ - /* orig_r0, ECR, user_r25 skipped automatically */ + ld sp, [sp] /* restore original sp; orig_r0, ECR skipped implicitly */ .endm /* Get thread_info of "current" tsk */ diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h index fcdd59d77f42cd61b5236144f6b1bfefad29367d..49c2e090cb5c1f97786838eb9e3d82200c1d43ed 100644 --- a/arch/arc/include/asm/entry.h +++ b/arch/arc/include/asm/entry.h @@ -13,6 +13,8 @@ #include /* For VMALLOC_START */ #include +#ifdef __ASSEMBLY__ + #ifdef CONFIG_ISA_ARCOMPACT #include /* ISA specific bits */ #else @@ -89,7 +91,7 @@ * Helpers to save/restore callee-saved regs: * used by several macros below *-------------------------------------------------------------*/ -.macro SAVE_R13_TO_R24 +.macro SAVE_R13_TO_R25 PUSH r13 PUSH r14 PUSH r15 @@ -102,9 +104,11 @@ PUSH r22 PUSH r23 PUSH r24 + PUSH r25 .endm -.macro RESTORE_R24_TO_R13 +.macro RESTORE_R25_TO_R13 + POP r25 POP r24 POP r23 POP r22 @@ -119,81 +123,31 @@ POP r13 .endm -/*-------------------------------------------------------------- - * Collect User Mode callee regs as struct callee_regs - needed by - * fork/do_signal/unaligned-access-emulation. - * (By default only scratch regs are saved on entry to kernel) - * - * Special handling for r25 if used for caching Task Pointer. - * It would have been saved in task->thread.user_r25 already, but to keep - * the interface same it is copied into regular r25 placeholder in - * struct callee_regs. - *-------------------------------------------------------------*/ +/* + * save user mode callee regs as struct callee_regs + * - needed by fork/do_signal/unaligned-access-emulation. + */ .macro SAVE_CALLEE_SAVED_USER + SAVE_R13_TO_R25 +.endm - mov r12, sp ; save SP as ref to pt_regs - SAVE_R13_TO_R24 - -#ifdef CONFIG_ARC_CURR_IN_REG - ; Retrieve orig r25 and save it with rest of callee_regs - ld r12, [r12, PT_user_r25] - PUSH r12 -#else - PUSH r25 -#endif - +/* + * restore user mode callee regs as struct callee_regs + * - could have been changed by ptrace tracer or unaligned-access fixup + */ +.macro RESTORE_CALLEE_SAVED_USER + RESTORE_R25_TO_R13 .endm -/*-------------------------------------------------------------- - * Save kernel Mode callee regs at the time of Contect Switch. - * - * Special handling for r25 if used for caching Task Pointer. - * Kernel simply skips saving it since it will be loaded with - * incoming task pointer anyways - *-------------------------------------------------------------*/ +/* + * save/restore kernel mode callee regs at the time of context switch + */ .macro SAVE_CALLEE_SAVED_KERNEL - - SAVE_R13_TO_R24 - -#ifdef CONFIG_ARC_CURR_IN_REG - sub sp, sp, 4 -#else - PUSH r25 -#endif + SAVE_R13_TO_R25 .endm -/*-------------------------------------------------------------- - * Opposite of SAVE_CALLEE_SAVED_KERNEL - *-------------------------------------------------------------*/ .macro RESTORE_CALLEE_SAVED_KERNEL - -#ifdef CONFIG_ARC_CURR_IN_REG - add sp, sp, 4 /* skip usual r25 placeholder */ -#else - POP r25 -#endif - RESTORE_R24_TO_R13 -.endm - -/*-------------------------------------------------------------- - * Opposite of SAVE_CALLEE_SAVED_USER - * - * ptrace tracer or unaligned-access fixup might have changed a user mode - * callee reg which is saved back to usual r25 storage location - *-------------------------------------------------------------*/ -.macro RESTORE_CALLEE_SAVED_USER - -#ifdef CONFIG_ARC_CURR_IN_REG - POP r12 -#else - POP r25 -#endif - RESTORE_R24_TO_R13 - - ; SP is back to start of pt_regs -#ifdef CONFIG_ARC_CURR_IN_REG - st r12, [sp, PT_user_r25] -#endif + RESTORE_R25_TO_R13 .endm /*-------------------------------------------------------------- @@ -229,10 +183,10 @@ #ifdef CONFIG_SMP -/*------------------------------------------------- +/* * Retrieve the current running task on this CPU - * 1. Determine curr CPU id. - * 2. Use it to index into _current_task[ ] + * - loads it from backing _current_task[] (and can't use the + * caching reg for current task */ .macro GET_CURR_TASK_ON_CPU reg GET_CPU_ID \reg @@ -254,7 +208,7 @@ add2 \tmp, @_current_task, \tmp st \tsk, [\tmp] #ifdef CONFIG_ARC_CURR_IN_REG - mov r25, \tsk + mov gp, \tsk #endif .endm @@ -269,21 +223,20 @@ .macro SET_CURR_TASK_ON_CPU tsk, tmp st \tsk, [@_current_task] #ifdef CONFIG_ARC_CURR_IN_REG - mov r25, \tsk + mov gp, \tsk #endif .endm #endif /* SMP / UNI */ -/* ------------------------------------------------------------------ +/* * Get the ptr to some field of Current Task at @off in task struct - * -Uses r25 for Current task ptr if that is enabled + * - Uses current task cached in reg if enabled */ - #ifdef CONFIG_ARC_CURR_IN_REG .macro GET_CURR_TASK_FIELD_PTR off, reg - add \reg, r25, \off + add \reg, gp, \off .endm #else @@ -295,4 +248,23 @@ #endif /* CONFIG_ARC_CURR_IN_REG */ +#else /* !__ASSEMBLY__ */ + +extern void do_signal(struct pt_regs *); +extern void do_notify_resume(struct pt_regs *); +extern int do_privilege_fault(unsigned long, struct pt_regs *); +extern int do_extension_fault(unsigned long, struct pt_regs *); +extern int insterror_is_error(unsigned long, struct pt_regs *); +extern int do_memory_error(unsigned long, struct pt_regs *); +extern int trap_is_brkpt(unsigned long, struct pt_regs *); +extern int do_misaligned_error(unsigned long, struct pt_regs *); +extern int do_trap5_error(unsigned long, struct pt_regs *); +extern int do_misaligned_access(unsigned long, struct pt_regs *, struct callee_regs *); +extern void do_machine_check_fault(unsigned long, struct pt_regs *); +extern void do_non_swi_trap(unsigned long, struct pt_regs *); +extern void do_insterror_or_kprobe(unsigned long, struct pt_regs *); +extern void do_page_fault(unsigned long, struct pt_regs *); + +#endif + #endif /* __ASM_ARC_ENTRY_H */ diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index 0309cb405cfb17139f9eae4050f71360335550f6..c574712ad86589eb858c4d6b0c4c3721af3e904d 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -25,5 +25,6 @@ #include extern void arc_init_IRQ(void); +extern void arch_do_IRQ(unsigned int, struct pt_regs *); #endif diff --git a/arch/arc/include/asm/mmu.h b/arch/arc/include/asm/mmu.h index ca427c30f70ebea546c5f6c76881f7795afc54a0..9febf5bc3de6d0617b3e4b2c0a985231e908963c 100644 --- a/arch/arc/include/asm/mmu.h +++ b/arch/arc/include/asm/mmu.h @@ -14,6 +14,8 @@ typedef struct { unsigned long asid[NR_CPUS]; /* 8 bit MMU PID + Generation cycle */ } mm_context_t; +extern void do_tlb_overlap_fault(unsigned long, unsigned long, struct pt_regs *); + #endif #include diff --git a/arch/arc/include/asm/processor.h b/arch/arc/include/asm/processor.h index fb844fce1ab671552e4dc3a7dd0384da4c6e6abf..d606658e2fe73149ef3afd35cd2e50b08f1d5edd 100644 --- a/arch/arc/include/asm/processor.h +++ b/arch/arc/include/asm/processor.h @@ -22,7 +22,6 @@ * struct thread_info */ struct thread_struct { - unsigned long ksp; /* kernel mode stack pointer */ unsigned long callee_reg; /* pointer to callee regs */ unsigned long fault_address; /* dbls as brkpt holder as well */ #ifdef CONFIG_ARC_DSP_SAVE_RESTORE_REGS @@ -33,9 +32,7 @@ struct thread_struct { #endif }; -#define INIT_THREAD { \ - .ksp = sizeof(init_stack) + (unsigned long) init_stack, \ -} +#define INIT_THREAD { } /* Forward declaration, a strange C thing */ struct task_struct; @@ -56,7 +53,7 @@ struct task_struct; * Where about of Task's sp, fp, blink when it was last seen in kernel mode. * Look in process.c for details of kernel stack layout */ -#define TSK_K_ESP(tsk) (tsk->thread.ksp) +#define TSK_K_ESP(tsk) (task_thread_info(tsk)->ksp) #define TSK_K_REG(tsk, off) (*((unsigned long *)(TSK_K_ESP(tsk) + \ sizeof(struct callee_regs) + off))) diff --git a/arch/arc/include/asm/ptrace.h b/arch/arc/include/asm/ptrace.h index 5869a74c0db2fbe611099bfcbf8874c1c54a52e6..4a2b30fb5a985e588079e574a871c09688e341da 100644 --- a/arch/arc/include/asm/ptrace.h +++ b/arch/arc/include/asm/ptrace.h @@ -12,6 +12,17 @@ #ifndef __ASSEMBLY__ +typedef union { + struct { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned long state:8, vec:8, cause:8, param:8; +#else + unsigned long param:8, cause:8, vec:8, state:8; +#endif + }; + unsigned long full; +} ecr_reg; + /* THE pt_regs: Defines how regs are saved during entry into kernel */ #ifdef CONFIG_ISA_ARCOMPACT @@ -40,23 +51,10 @@ struct pt_regs { * Last word used by Linux for extra state mgmt (syscall-restart) * For interrupts, use artificial ECR values to note current prio-level */ - union { - struct { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned long state:8, ecr_vec:8, - ecr_cause:8, ecr_param:8; -#else - unsigned long ecr_param:8, ecr_cause:8, - ecr_vec:8, state:8; -#endif - }; - unsigned long event; - }; - - unsigned long user_r25; + ecr_reg ecr; }; -#define MAX_REG_OFFSET offsetof(struct pt_regs, user_r25) +#define MAX_REG_OFFSET offsetof(struct pt_regs, ecr) #else @@ -64,28 +62,14 @@ struct pt_regs { unsigned long orig_r0; - union { - struct { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned long state:8, ecr_vec:8, - ecr_cause:8, ecr_param:8; -#else - unsigned long ecr_param:8, ecr_cause:8, - ecr_vec:8, state:8; -#endif - }; - unsigned long event; - }; - - unsigned long bta; /* bta_l1, bta_l2, erbta */ + ecr_reg ecr; /* Exception Cause Reg */ - unsigned long user_r25; + unsigned long bta; /* erbta */ - unsigned long r26; /* gp */ unsigned long fp; - unsigned long sp; /* user/kernel sp depending on where we came from */ - - unsigned long r12, r30; + unsigned long r30; + unsigned long r12; + unsigned long r26; /* gp */ #ifdef CONFIG_ARC_HAS_ACCL_REGS unsigned long r58, r59; /* ACCL/ACCH used by FPU / DSP MPY */ @@ -94,6 +78,8 @@ struct pt_regs { unsigned long DSP_CTRL; #endif + unsigned long sp; /* user/kernel sp depending on entry */ + /*------- Below list auto saved by h/w -----------*/ unsigned long r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11; @@ -134,13 +120,13 @@ struct callee_regs { /* return 1 if PC in delay slot */ #define delay_mode(regs) ((regs->status32 & STATUS_DE_MASK) == STATUS_DE_MASK) -#define in_syscall(regs) ((regs->ecr_vec == ECR_V_TRAP) && !regs->ecr_param) -#define in_brkpt_trap(regs) ((regs->ecr_vec == ECR_V_TRAP) && regs->ecr_param) +#define in_syscall(regs) ((regs->ecr.vec == ECR_V_TRAP) && !regs->ecr.param) +#define in_brkpt_trap(regs) ((regs->ecr.vec == ECR_V_TRAP) && regs->ecr.param) #define STATE_SCALL_RESTARTED 0x01 -#define syscall_wont_restart(reg) (reg->state |= STATE_SCALL_RESTARTED) -#define syscall_restartable(reg) !(reg->state & STATE_SCALL_RESTARTED) +#define syscall_wont_restart(regs) (regs->ecr.state |= STATE_SCALL_RESTARTED) +#define syscall_restartable(regs) !(regs->ecr.state & STATE_SCALL_RESTARTED) #define current_pt_regs() \ ({ \ @@ -181,6 +167,9 @@ static inline unsigned long regs_get_register(struct pt_regs *regs, return *(unsigned long *)((unsigned long)regs + offset); } +extern int syscall_trace_entry(struct pt_regs *); +extern void syscall_trace_exit(struct pt_regs *); + #endif /* !__ASSEMBLY__ */ #endif /* __ASM_PTRACE_H */ diff --git a/arch/arc/include/asm/setup.h b/arch/arc/include/asm/setup.h index 028a8cf76206969eee0c755dc31b8288565511e2..1c6db599e1fcc932204292b1088f7a4ac0931e2c 100644 --- a/arch/arc/include/asm/setup.h +++ b/arch/arc/include/asm/setup.h @@ -35,11 +35,11 @@ long __init arc_get_mem_sz(void); #define IS_AVAIL3(v, v2, s) IS_AVAIL1(v, s), IS_AVAIL1(v, IS_DISABLED_RUN(v2)) extern void arc_mmu_init(void); -extern char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len); -extern void read_decode_mmu_bcr(void); +extern int arc_mmu_mumbojumbo(int cpu_id, char *buf, int len); extern void arc_cache_init(void); -extern char *arc_cache_mumbojumbo(int cpu_id, char *buf, int len); -extern void read_decode_cache_bcr(void); +extern int arc_cache_mumbojumbo(int cpu_id, char *buf, int len); + +extern void __init handle_uboot_args(void); #endif /* __ASMARC_SETUP_H */ diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index d856491606acd94da43db4a8699d0e35b790e793..e0913f52c2cddadf7840282bf43fb430dba7a235 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h @@ -29,6 +29,8 @@ extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); extern void __init smp_init_cpus(void); extern void first_lines_of_secondary(void); extern const char *arc_platform_smp_cpuinfo(void); +extern void arc_platform_smp_wait_to_boot(int); +extern void start_kernel_secondary(void); /* * API expected BY platform smp code (FROM arch smp code) diff --git a/arch/arc/include/asm/thread_info.h b/arch/arc/include/asm/thread_info.h index 6ba7fe417095002f29a1b590bf59e193e904a936..4c530cf131f339c5dbe92ecfce6a4f0f57a17903 100644 --- a/arch/arc/include/asm/thread_info.h +++ b/arch/arc/include/asm/thread_info.h @@ -37,16 +37,16 @@ */ struct thread_info { unsigned long flags; /* low level flags */ + unsigned long ksp; /* kernel mode stack top in __switch_to */ int preempt_count; /* 0 => preemptable, <0 => BUG */ - struct task_struct *task; /* main task structure */ - __u32 cpu; /* current CPU */ + int cpu; /* current CPU */ unsigned long thr_ptr; /* TLS ptr */ + struct task_struct *task; /* main task structure */ }; /* - * macros/functions for gaining access to the thread information structure - * - * preempt_count needs to be 1 initially, until the scheduler is functional. + * initilaize thread_info for any @tsk + * - this is not related to init_task per se */ #define INIT_THREAD_INFO(tsk) \ { \ diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h index 99712471c96a0ce6354b7f3a50bc8ce376a2af0e..1e8809ea000a35ff37673585654ff5ba514542af 100644 --- a/arch/arc/include/asm/uaccess.h +++ b/arch/arc/include/asm/uaccess.h @@ -146,8 +146,9 @@ raw_copy_from_user(void *to, const void __user *from, unsigned long n) if (n == 0) return 0; - /* unaligned */ - if (((unsigned long)to & 0x3) || ((unsigned long)from & 0x3)) { + /* fallback for unaligned access when hardware doesn't support */ + if (!IS_ENABLED(CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS) && + (((unsigned long)to & 0x3) || ((unsigned long)from & 0x3))) { unsigned char tmp; @@ -373,8 +374,9 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) if (n == 0) return 0; - /* unaligned */ - if (((unsigned long)to & 0x3) || ((unsigned long)from & 0x3)) { + /* fallback for unaligned access when hardware doesn't support */ + if (!IS_ENABLED(CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS) && + (((unsigned long)to & 0x3) || ((unsigned long)from & 0x3))) { unsigned char tmp; @@ -584,7 +586,7 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) return res; } -static inline unsigned long __arc_clear_user(void __user *to, unsigned long n) +static inline unsigned long __clear_user(void __user *to, unsigned long n) { long res = n; unsigned char *d_char = to; @@ -626,17 +628,10 @@ static inline unsigned long __arc_clear_user(void __user *to, unsigned long n) return res; } -#ifndef CONFIG_CC_OPTIMIZE_FOR_SIZE - #define INLINE_COPY_TO_USER #define INLINE_COPY_FROM_USER -#define __clear_user(d, n) __arc_clear_user(d, n) -#else -extern unsigned long arc_clear_user_noinline(void __user *to, - unsigned long n); -#define __clear_user(d, n) arc_clear_user_noinline(d, n) -#endif +#define __clear_user __clear_user #include diff --git a/arch/arc/kernel/Makefile b/arch/arc/kernel/Makefile index 0723d888ac446a30c7355b9737b3070bb9900fa2..95fbf9364c6734a8f730befa272ebac0e2ab54a6 100644 --- a/arch/arc/kernel/Makefile +++ b/arch/arc/kernel/Makefile @@ -5,6 +5,8 @@ obj-y := head.o arcksyms.o setup.o irq.o reset.o ptrace.o process.o devtree.o obj-y += signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o +obj-y += ctx_sw_asm.o + obj-$(CONFIG_ISA_ARCOMPACT) += entry-compact.o intc-compact.o obj-$(CONFIG_ISA_ARCV2) += entry-arcv2.o intc-arcv2.o @@ -24,11 +26,4 @@ ifdef CONFIG_ISA_ARCOMPACT CFLAGS_fpu.o += -mdpfp endif -ifdef CONFIG_ARC_DW2_UNWIND -CFLAGS_ctx_sw.o += -fno-omit-frame-pointer -obj-y += ctx_sw.o -else -obj-y += ctx_sw_asm.o -endif - extra-y := vmlinux.lds diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c index 0e884036ab74365a687c8cabfd1ab67fec27bdda..f77deb7991757ea477d8500f80f2d7c6431a4eea 100644 --- a/arch/arc/kernel/asm-offsets.c +++ b/arch/arc/kernel/asm-offsets.c @@ -20,13 +20,13 @@ int main(void) BLANK(); - DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); DEFINE(THREAD_CALLEE_REG, offsetof(struct thread_struct, callee_reg)); DEFINE(THREAD_FAULT_ADDR, offsetof(struct thread_struct, fault_address)); BLANK(); + DEFINE(THREAD_INFO_KSP, offsetof(struct thread_info, ksp)); DEFINE(THREAD_INFO_FLAGS, offsetof(struct thread_info, flags)); DEFINE(THREAD_INFO_PREEMPT_COUNT, offsetof(struct thread_info, preempt_count)); @@ -46,7 +46,8 @@ int main(void) BLANK(); DEFINE(PT_status32, offsetof(struct pt_regs, status32)); - DEFINE(PT_event, offsetof(struct pt_regs, event)); + DEFINE(PT_event, offsetof(struct pt_regs, ecr)); + DEFINE(PT_bta, offsetof(struct pt_regs, bta)); DEFINE(PT_sp, offsetof(struct pt_regs, sp)); DEFINE(PT_r0, offsetof(struct pt_regs, r0)); DEFINE(PT_r1, offsetof(struct pt_regs, r1)); @@ -61,13 +62,9 @@ int main(void) DEFINE(PT_r26, offsetof(struct pt_regs, r26)); DEFINE(PT_ret, offsetof(struct pt_regs, ret)); DEFINE(PT_blink, offsetof(struct pt_regs, blink)); + OFFSET(PT_fp, pt_regs, fp); DEFINE(PT_lpe, offsetof(struct pt_regs, lp_end)); DEFINE(PT_lpc, offsetof(struct pt_regs, lp_count)); - DEFINE(PT_user_r25, offsetof(struct pt_regs, user_r25)); - - DEFINE(SZ_CALLEE_REGS, sizeof(struct callee_regs)); - DEFINE(SZ_PT_REGS, sizeof(struct pt_regs)); - #ifdef CONFIG_ISA_ARCV2 OFFSET(PT_r12, pt_regs, r12); OFFSET(PT_r30, pt_regs, r30); @@ -80,5 +77,8 @@ int main(void) OFFSET(PT_DSP_CTRL, pt_regs, DSP_CTRL); #endif + DEFINE(SZ_CALLEE_REGS, sizeof(struct callee_regs)); + DEFINE(SZ_PT_REGS, sizeof(struct pt_regs)); + return 0; } diff --git a/arch/arc/kernel/ctx_sw.c b/arch/arc/kernel/ctx_sw.c deleted file mode 100644 index 1a76f2d6f694376b259caf12e20883d36eb0c6f8..0000000000000000000000000000000000000000 --- a/arch/arc/kernel/ctx_sw.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) - * - * Vineetg: Aug 2009 - * -"C" version of lowest level context switch asm macro called by schedular - * gcc doesn't generate the dward CFI info for hand written asm, hence can't - * backtrace out of it (e.g. tasks sleeping in kernel). - * So we cheat a bit by writing almost similar code in inline-asm. - * -This is a hacky way of doing things, but there is no other simple way. - * I don't want/intend to extend unwinding code to understand raw asm - */ - -#include -#include -#include - -#define KSP_WORD_OFF ((TASK_THREAD + THREAD_KSP) / 4) - -struct task_struct *__sched -__switch_to(struct task_struct *prev_task, struct task_struct *next_task) -{ - unsigned int tmp; - unsigned int prev = (unsigned int)prev_task; - unsigned int next = (unsigned int)next_task; - - __asm__ __volatile__( - /* FP/BLINK save generated by gcc (standard function prologue */ - "st.a r13, [sp, -4] \n\t" - "st.a r14, [sp, -4] \n\t" - "st.a r15, [sp, -4] \n\t" - "st.a r16, [sp, -4] \n\t" - "st.a r17, [sp, -4] \n\t" - "st.a r18, [sp, -4] \n\t" - "st.a r19, [sp, -4] \n\t" - "st.a r20, [sp, -4] \n\t" - "st.a r21, [sp, -4] \n\t" - "st.a r22, [sp, -4] \n\t" - "st.a r23, [sp, -4] \n\t" - "st.a r24, [sp, -4] \n\t" -#ifndef CONFIG_ARC_CURR_IN_REG - "st.a r25, [sp, -4] \n\t" -#else - "sub sp, sp, 4 \n\t" /* usual r25 placeholder */ -#endif - - /* set ksp of outgoing task in tsk->thread.ksp */ -#if KSP_WORD_OFF <= 255 - "st.as sp, [%3, %1] \n\t" -#else - /* - * Workaround for NR_CPUS=4k - * %1 is bigger than 255 (S9 offset for st.as) - */ - "add2 r24, %3, %1 \n\t" - "st sp, [r24] \n\t" -#endif - - /* - * setup _current_task with incoming tsk. - * optionally, set r25 to that as well - * For SMP extra work to get to &_current_task[cpu] - * (open coded SET_CURR_TASK_ON_CPU) - */ -#ifndef CONFIG_SMP - "st %2, [@_current_task] \n\t" -#else - "lr r24, [identity] \n\t" - "lsr r24, r24, 8 \n\t" - "bmsk r24, r24, 7 \n\t" - "add2 r24, @_current_task, r24 \n\t" - "st %2, [r24] \n\t" -#endif -#ifdef CONFIG_ARC_CURR_IN_REG - "mov r25, %2 \n\t" -#endif - - /* get ksp of incoming task from tsk->thread.ksp */ - "ld.as sp, [%2, %1] \n\t" - - /* start loading it's CALLEE reg file */ - -#ifndef CONFIG_ARC_CURR_IN_REG - "ld.ab r25, [sp, 4] \n\t" -#else - "add sp, sp, 4 \n\t" -#endif - "ld.ab r24, [sp, 4] \n\t" - "ld.ab r23, [sp, 4] \n\t" - "ld.ab r22, [sp, 4] \n\t" - "ld.ab r21, [sp, 4] \n\t" - "ld.ab r20, [sp, 4] \n\t" - "ld.ab r19, [sp, 4] \n\t" - "ld.ab r18, [sp, 4] \n\t" - "ld.ab r17, [sp, 4] \n\t" - "ld.ab r16, [sp, 4] \n\t" - "ld.ab r15, [sp, 4] \n\t" - "ld.ab r14, [sp, 4] \n\t" - "ld.ab r13, [sp, 4] \n\t" - - /* last (ret value) = prev : although for ARC it mov r0, r0 */ - "mov %0, %3 \n\t" - - /* FP/BLINK restore generated by gcc (standard func epilogue */ - - : "=r"(tmp) - : "n"(KSP_WORD_OFF), "r"(next), "r"(prev) - : "blink" - ); - - return (struct task_struct *)tmp; -} diff --git a/arch/arc/kernel/ctx_sw_asm.S b/arch/arc/kernel/ctx_sw_asm.S index 02c4614847611e5e55bbe88f00d102cd73ef0f84..48e1f21976ed1544ce2eea4f7f775c4b3fe88b73 100644 --- a/arch/arc/kernel/ctx_sw_asm.S +++ b/arch/arc/kernel/ctx_sw_asm.S @@ -11,50 +11,54 @@ #include /* For the SAVE_* macros */ #include -#define KSP_WORD_OFF ((TASK_THREAD + THREAD_KSP) / 4) - -;################### Low Level Context Switch ########################## +; IN +; - r0: prev task (also current) +; - r1: next task +; OUT +; - r0: prev task (so r0 not touched) .section .sched.text,"ax",@progbits - .align 4 - .global __switch_to - .type __switch_to, @function -__switch_to: - CFI_STARTPROC - - /* Save regs on kernel mode stack of task */ - st.a blink, [sp, -4] - st.a fp, [sp, -4] - SAVE_CALLEE_SAVED_KERNEL +ENTRY_CFI(__switch_to) - /* Save the now KSP in task->thread.ksp */ -#if KSP_WORD_OFF <= 255 - st.as sp, [r0, KSP_WORD_OFF] -#else - /* Workaround for NR_CPUS=4k as ST.as can only take s9 offset */ - add2 r24, r0, KSP_WORD_OFF - st sp, [r24] -#endif - /* - * Return last task in r0 (return reg) - * On ARC, Return reg = First Arg reg = r0. - * Since we already have last task in r0, - * don't need to do anything special to return it - */ + /* save kernel stack frame regs of @prev task */ + push blink + CFI_DEF_CFA_OFFSET 4 + CFI_OFFSET r31, -4 + + push fp + CFI_DEF_CFA_OFFSET 8 + CFI_OFFSET r27, -8 + + mov fp, sp + CFI_DEF_CFA_REGISTER r27 + + /* kernel mode callee regs of @prev */ + SAVE_CALLEE_SAVED_KERNEL /* - * switch to new task, contained in r1 - * Temp reg r3 is required to get the ptr to store val + * save final SP to @prev->thread_info.ksp + * @prev is "current" so thread_info derived from SP */ - SET_CURR_TASK_ON_CPU r1, r3 + GET_CURR_THR_INFO_FROM_SP r10 + st sp, [r10, THREAD_INFO_KSP] + + /* update @next in _current_task[] and GP register caching it */ + SET_CURR_TASK_ON_CPU r1, r10 - /* reload SP with kernel mode stack pointer in task->thread.ksp */ - ld.as sp, [r1, (TASK_THREAD + THREAD_KSP)/4] + /* load SP from @next->thread_info.ksp */ + ld r10, [r1, TASK_THREAD_INFO] + ld sp, [r10, THREAD_INFO_KSP] - /* restore the registers */ + /* restore callee regs, stack frame regs of @next */ RESTORE_CALLEE_SAVED_KERNEL - ld.ab fp, [sp, 4] - ld.ab blink, [sp, 4] - j [blink] + pop fp + CFI_RESTORE r27 + CFI_DEF_CFA r28, 4 + + pop blink + CFI_RESTORE r31 + CFI_DEF_CFA_OFFSET 0 + + j [blink] END_CFI(__switch_to) diff --git a/arch/arc/kernel/devtree.c b/arch/arc/kernel/devtree.c index 721d465f15809e3cdea6825a81a64be9684aa056..4c9e61457b2f695ccd172384feb8ac94e4a76eea 100644 --- a/arch/arc/kernel/devtree.c +++ b/arch/arc/kernel/devtree.c @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef CONFIG_SERIAL_EARLYCON diff --git a/arch/arc/kernel/entry-arcv2.S b/arch/arc/kernel/entry-arcv2.S index a7e6a2174187714f49a243e28c9b5b31eabeecba..2e49c81c8086b11de360c0e37421b2f9091a9c85 100644 --- a/arch/arc/kernel/entry-arcv2.S +++ b/arch/arc/kernel/entry-arcv2.S @@ -125,11 +125,6 @@ ENTRY(mem_service) EXCEPTION_PROLOGUE - lr r0, [efa] - mov r1, sp - - FAKE_RET_FROM_EXCPN - bl do_memory_error b ret_from_exception END(mem_service) @@ -138,11 +133,6 @@ ENTRY(EV_Misaligned) EXCEPTION_PROLOGUE - lr r0, [efa] ; Faulting Data address - mov r1, sp - - FAKE_RET_FROM_EXCPN - SAVE_CALLEE_SAVED_USER mov r2, sp ; callee_regs @@ -163,11 +153,6 @@ ENTRY(EV_TLBProtV) EXCEPTION_PROLOGUE - lr r0, [efa] ; Faulting Data address - mov r1, sp ; pt_regs - - FAKE_RET_FROM_EXCPN - mov blink, ret_from_exception b do_page_fault diff --git a/arch/arc/kernel/entry-compact.S b/arch/arc/kernel/entry-compact.S index 5cb0cd7e4eabaff15d8e4bc3afa356042615b8fe..774c03cc1d1a04019aa8c4890c9663e321b5657f 100644 --- a/arch/arc/kernel/entry-compact.S +++ b/arch/arc/kernel/entry-compact.S @@ -254,18 +254,7 @@ END(handle_interrupt_level1) ENTRY(EV_TLBProtV) - EXCEPTION_PROLOGUE - - mov r2, r10 ; ECR set into r10 already - lr r0, [efa] ; Faulting Data address (not part of pt_regs saved above) - - ; Exception auto-disables further Intr/exceptions. - ; Re-enable them by pretending to return from exception - ; (so rest of handler executes in pure K mode) - - FAKE_RET_FROM_EXCPN - - mov r1, sp ; Handle to pt_regs + EXCEPTION_PROLOGUE ; ECR returned in r10 ;------ (5) Type of Protection Violation? ---------- ; @@ -273,8 +262,7 @@ ENTRY(EV_TLBProtV) ; -Access Violation : 00_23_(00|01|02|03)_00 ; x r w r+w ; -Unaligned Access : 00_23_04_00 - ; - bbit1 r2, ECR_C_BIT_PROTV_MISALIG_DATA, 4f + bbit1 r10, ECR_C_BIT_PROTV_MISALIG_DATA, 4f ;========= (6a) Access Violation Processing ======== bl do_page_fault @@ -303,9 +291,6 @@ END(EV_TLBProtV) ENTRY(call_do_page_fault) EXCEPTION_PROLOGUE - lr r0, [efa] ; Faulting Data address - mov r1, sp - FAKE_RET_FROM_EXCPN mov blink, ret_from_exception b do_page_fault diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S index 54e91df678dd40859e147e919ea2fb4f09eb9eea..089f6680518f82908664a6f2883ab6d3e1b2c5ca 100644 --- a/arch/arc/kernel/entry.S +++ b/arch/arc/kernel/entry.S @@ -80,11 +80,6 @@ ENTRY(instr_service) EXCEPTION_PROLOGUE - lr r0, [efa] - mov r1, sp - - FAKE_RET_FROM_EXCPN - bl do_insterror_or_kprobe b ret_from_exception END(instr_service) @@ -95,16 +90,15 @@ END(instr_service) ENTRY(EV_MachineCheck) - EXCEPTION_PROLOGUE + EXCEPTION_PROLOGUE_KEEP_AE ; ECR returned in r10 - lr r2, [ecr] lr r0, [efa] mov r1, sp ; MC excpetions disable MMU ARC_MMU_REENABLE r3 - lsr r3, r2, 8 + lsr r3, r10, 8 bmsk r3, r3, 7 brne r3, ECR_C_MCHK_DUP_TLB, 1f @@ -129,11 +123,6 @@ ENTRY(EV_PrivilegeV) EXCEPTION_PROLOGUE - lr r0, [efa] - mov r1, sp - - FAKE_RET_FROM_EXCPN - bl do_privilege_fault b ret_from_exception END(EV_PrivilegeV) @@ -145,11 +134,6 @@ ENTRY(EV_Extension) EXCEPTION_PROLOGUE - lr r0, [efa] - mov r1, sp - - FAKE_RET_FROM_EXCPN - bl do_extension_fault b ret_from_exception END(EV_Extension) @@ -160,20 +144,19 @@ END(EV_Extension) ; syscall Tracing ; --------------------------------------------- tracesys: - ; save EFA in case tracer wants the PC of traced task - ; using ERET won't work since next-PC has already committed + ; safekeep EFA (r12) if syscall tracer wanted PC + ; for traps, ERET is pre-commit so points to next-PC GET_CURR_TASK_FIELD_PTR TASK_THREAD, r11 st r12, [r11, THREAD_FAULT_ADDR] ; thread.fault_address - ; PRE Sys Call Ptrace hook - mov r0, sp ; pt_regs needed - bl @syscall_trace_entry + ; PRE syscall trace hook + mov r0, sp ; pt_regs + bl @syscall_trace_enter ; Tracing code now returns the syscall num (orig or modif) mov r8, r0 ; Do the Sys Call as we normally would. - ; Validate the Sys Call number cmp r8, NR_syscalls - 1 mov.hi r0, -ENOSYS bhi tracesys_exit @@ -190,37 +173,36 @@ tracesys: ld r6, [sp, PT_r6] ld r7, [sp, PT_r7] ld.as r9, [sys_call_table, r8] - jl [r9] ; Entry into Sys Call Handler + jl [r9] tracesys_exit: - st r0, [sp, PT_r0] ; sys call return value in pt_regs + st r0, [sp, PT_r0] - ;POST Sys Call Ptrace Hook + ; POST syscall trace hook mov r0, sp ; pt_regs needed bl @syscall_trace_exit - b ret_from_exception ; NOT ret_from_system_call at is saves r0 which - ; we'd done before calling post hook above + + ; don't call ret_from_system_call as it saves r0, already done above + b ret_from_exception ; --------------------------------------------- ; Breakpoint TRAP ; --------------------------------------------- trap_with_param: mov r0, r12 ; EFA in case ptracer/gdb wants stop_pc - mov r1, sp + mov r1, sp ; pt_regs - ; Save callee regs in case gdb wants to have a look - ; SP will grow up by size of CALLEE Reg-File - ; NOTE: clobbers r12 + ; save callee regs in case tracer/gdb wants to peek SAVE_CALLEE_SAVED_USER - ; save location of saved Callee Regs @ thread_struct->pc + ; safekeep ref to callee regs GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10 st sp, [r10, THREAD_CALLEE_REG] - ; Call the trap handler + ; call the non syscall trap handler bl do_non_swi_trap - ; unwind stack to discard Callee saved Regs + ; unwind stack to discard callee regs DISCARD_CALLEE_SAVED_USER b ret_from_exception @@ -232,37 +214,33 @@ trap_with_param: ENTRY(EV_Trap) - EXCEPTION_PROLOGUE + EXCEPTION_PROLOGUE_KEEP_AE lr r12, [efa] FAKE_RET_FROM_EXCPN - ;============ TRAP 1 :breakpoints - ; Check ECR for trap with arg (PROLOGUE ensures r10 has ECR) + ;============ TRAP N : breakpoints, kprobes etc bmsk.f 0, r10, 7 bnz trap_with_param - ;============ TRAP (no param): syscall top level + ;============ TRAP 0 (no param): syscall - ; If syscall tracing ongoing, invoke pre-post-hooks + ; syscall tracing ongoing, invoke pre-post-hooks around syscall GET_CURR_THR_INFO_FLAGS r10 and.f 0, r10, _TIF_SYSCALL_WORK bnz tracesys ; this never comes back ;============ Normal syscall case - ; syscall num shd not exceed the total system calls avail cmp r8, NR_syscalls - 1 mov.hi r0, -ENOSYS bhi .Lret_from_system_call - ; Offset into the syscall_table and call handler ld.as r9,[sys_call_table, r8] - jl [r9] ; Entry into Sys Call Handler + jl [r9] .Lret_from_system_call: - st r0, [sp, PT_r0] ; sys call return value in pt_regs ; fall through to ret_from_exception @@ -318,7 +296,7 @@ resume_user_mode_begin: ; tracer might call PEEKUSR(CALLEE reg) ; ; NOTE: SP will grow up by size of CALLEE Reg-File - SAVE_CALLEE_SAVED_USER ; clobbers r12 + SAVE_CALLEE_SAVED_USER ; save location of saved Callee Regs @ thread_struct->callee GET_CURR_TASK_FIELD_PTR TASK_THREAD, r10 diff --git a/arch/arc/kernel/intc-arcv2.c b/arch/arc/kernel/intc-arcv2.c index 5cda19d0aa91310070e95f1ec4f83482f27d3863..678898757e4739da5fcffb1df8ae289a783957c2 100644 --- a/arch/arc/kernel/intc-arcv2.c +++ b/arch/arc/kernel/intc-arcv2.c @@ -108,7 +108,7 @@ static void arcv2_irq_unmask(struct irq_data *data) write_aux_reg(AUX_IRQ_ENABLE, 1); } -void arcv2_irq_enable(struct irq_data *data) +static void arcv2_irq_enable(struct irq_data *data) { /* set default priority */ write_aux_reg(AUX_IRQ_SELECT, data->hwirq); diff --git a/arch/arc/kernel/kgdb.c b/arch/arc/kernel/kgdb.c index 345a0000554cbb33313208a79da7d6e763c40bac..4f2b5951454fb31ff3305fffc520e0476533f246 100644 --- a/arch/arc/kernel/kgdb.c +++ b/arch/arc/kernel/kgdb.c @@ -175,7 +175,7 @@ void kgdb_trap(struct pt_regs *regs) * with trap_s 4 (compiled) breakpoints, continuation needs to * start after the breakpoint. */ - if (regs->ecr_param == 3) + if (regs->ecr.param == 3) instruction_pointer(regs) -= BREAK_INSTR_SIZE; kgdb_handle_exception(1, SIGTRAP, 0, regs); diff --git a/arch/arc/kernel/mcip.c b/arch/arc/kernel/mcip.c index f9fdb557c263e06e5da4534513ca66e0d2bdf7e5..55373ca0d28b7ff17b5cf321bfd084f711cd0b4d 100644 --- a/arch/arc/kernel/mcip.c +++ b/arch/arc/kernel/mcip.c @@ -165,8 +165,6 @@ static void mcip_probe_n_setup(void) IS_AVAIL1(mp.idu, "IDU "), IS_AVAIL1(mp.dbg, "DEBUG "), IS_AVAIL1(mp.gfrc, "GFRC")); - - cpuinfo_arc700[0].extn.gfrc = mp.gfrc; } struct plat_smp_ops plat_smp_ops = { diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c index 980b71da2f6105ffd3db9cfd52f1410ce1106dfd..186ceab661eb02e611ffafba8e746a07bddf4437 100644 --- a/arch/arc/kernel/process.c +++ b/arch/arc/kernel/process.c @@ -141,7 +141,7 @@ asmlinkage void ret_from_fork(void); * | unused | * | | * ------------------ - * | r25 | <==== top of Stack (thread.ksp) + * | r25 | <==== top of Stack (thread_info.ksp) * ~ ~ * | --to-- | (CALLEE Regs of kernel mode) * | r13 | @@ -162,7 +162,6 @@ asmlinkage void ret_from_fork(void); * | SP | * | orig_r0 | * | event/ECR | - * | user_r25 | * ------------------ <===== END of PAGE */ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) @@ -182,14 +181,14 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) c_callee = ((struct callee_regs *)childksp) - 1; /* - * __switch_to() uses thread.ksp to start unwinding stack + * __switch_to() uses thread_info.ksp to start unwinding stack * For kernel threads we don't need to create callee regs, the * stack layout nevertheless needs to remain the same. * Also, since __switch_to anyways unwinds callee regs, we use * this to populate kernel thread entry-pt/args into callee regs, * so that ret_from_kernel_thread() becomes simpler. */ - p->thread.ksp = (unsigned long)c_callee; /* THREAD_KSP */ + task_thread_info(p)->ksp = (unsigned long)c_callee; /* THREAD_INFO_KSP */ /* __switch_to expects FP(0), BLINK(return addr) at top */ childksp[0] = 0; /* fp */ @@ -243,16 +242,6 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) */ c_callee->r25 = task_thread_info(p)->thr_ptr; -#ifdef CONFIG_ARC_CURR_IN_REG - /* - * setup usermode thread pointer #2: - * however for this special use of r25 in kernel, __switch_to() sets - * r25 for kernel needs and only in the final return path is usermode - * r25 setup, from pt_regs->user_r25. So set that up as well - */ - c_regs->user_r25 = c_callee->r25; -#endif - return 0; } diff --git a/arch/arc/kernel/ptrace.c b/arch/arc/kernel/ptrace.c index 2abdcd9b09e8cd4c8ab78e89792dd9eac14d7d78..e0c233c178b1aec3ea5e6fe3be9cac851839684c 100644 --- a/arch/arc/kernel/ptrace.c +++ b/arch/arc/kernel/ptrace.c @@ -46,8 +46,7 @@ static const struct pt_regs_offset regoffset_table[] = { REG_OFFSET_NAME(r0), REG_OFFSET_NAME(sp), REG_OFFSET_NAME(orig_r0), - REG_OFFSET_NAME(event), - REG_OFFSET_NAME(user_r25), + REG_OFFSET_NAME(ecr), REG_OFFSET_END, }; @@ -55,9 +54,8 @@ static const struct pt_regs_offset regoffset_table[] = { static const struct pt_regs_offset regoffset_table[] = { REG_OFFSET_NAME(orig_r0), - REG_OFFSET_NAME(event), + REG_OFFSET_NAME(ecr), REG_OFFSET_NAME(bta), - REG_OFFSET_NAME(user_r25), REG_OFFSET_NAME(r26), REG_OFFSET_NAME(fp), REG_OFFSET_NAME(sp), @@ -341,7 +339,7 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -asmlinkage int syscall_trace_entry(struct pt_regs *regs) +asmlinkage int syscall_trace_enter(struct pt_regs *regs) { if (test_thread_flag(TIF_SYSCALL_TRACE)) if (ptrace_report_syscall_entry(regs)) diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 41f07b3e594e0f16f605504c754dc28cc5525a92..4dcf8589b708ac2564b883912ce65ec35c6f0658 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -29,6 +29,7 @@ #include #include #include +#include #define FIX_PTR(x) __asm__ __volatile__(";" : "+r"(x)) @@ -43,19 +44,22 @@ const struct machine_desc *machine_desc; struct task_struct *_current_task[NR_CPUS]; /* For stack switching */ -struct cpuinfo_arc cpuinfo_arc700[NR_CPUS]; +struct cpuinfo_arc { + int arcver; + unsigned int t0:1, t1:1; + struct { + unsigned long base; + unsigned int sz; + } iccm, dccm; +}; + +#ifdef CONFIG_ISA_ARCV2 -static const struct id_to_str arc_legacy_rel[] = { +static const struct id_to_str arc_hs_rel[] = { /* ID.ARCVER, Release */ -#ifdef CONFIG_ISA_ARCOMPACT - { 0x34, "R4.10"}, - { 0x35, "R4.11"}, -#else { 0x51, "R2.0" }, { 0x52, "R2.1" }, { 0x53, "R3.0" }, -#endif - { 0x00, NULL } }; static const struct id_to_str arc_hs_ver54_rel[] = { @@ -66,323 +70,296 @@ static const struct id_to_str arc_hs_ver54_rel[] = { { 3, "R4.00a"}, { 0xFF, NULL } }; +#endif -static void read_decode_ccm_bcr(struct cpuinfo_arc *cpu) +static int +arcompact_mumbojumbo(int c, struct cpuinfo_arc *info, char *buf, int len) { - if (is_isa_arcompact()) { - struct bcr_iccm_arcompact iccm; - struct bcr_dccm_arcompact dccm; + int n = 0; +#ifdef CONFIG_ISA_ARCOMPACT + char *cpu_nm, *isa_nm = "ARCompact"; + struct bcr_fp_arcompact fpu_sp, fpu_dp; + int atomic = 0, be, present; + int bpu_full, bpu_cache, bpu_pred; + struct bcr_bpu_arcompact bpu; + struct bcr_iccm_arcompact iccm; + struct bcr_dccm_arcompact dccm; + struct bcr_generic isa; - READ_BCR(ARC_REG_ICCM_BUILD, iccm); - if (iccm.ver) { - cpu->iccm.sz = 4096 << iccm.sz; /* 8K to 512K */ - cpu->iccm.base_addr = iccm.base << 16; - } + READ_BCR(ARC_REG_ISA_CFG_BCR, isa); - READ_BCR(ARC_REG_DCCM_BUILD, dccm); - if (dccm.ver) { - unsigned long base; - cpu->dccm.sz = 2048 << dccm.sz; /* 2K to 256K */ + if (!isa.ver) /* ISA BCR absent, use Kconfig info */ + atomic = IS_ENABLED(CONFIG_ARC_HAS_LLSC); + else { + /* ARC700_BUILD only has 2 bits of isa info */ + atomic = isa.info & 1; + } - base = read_aux_reg(ARC_REG_DCCM_BASE_BUILD); - cpu->dccm.base_addr = base & ~0xF; - } - } else { - struct bcr_iccm_arcv2 iccm; - struct bcr_dccm_arcv2 dccm; - unsigned long region; - - READ_BCR(ARC_REG_ICCM_BUILD, iccm); - if (iccm.ver) { - cpu->iccm.sz = 256 << iccm.sz00; /* 512B to 16M */ - if (iccm.sz00 == 0xF && iccm.sz01 > 0) - cpu->iccm.sz <<= iccm.sz01; - - region = read_aux_reg(ARC_REG_AUX_ICCM); - cpu->iccm.base_addr = region & 0xF0000000; - } + be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); - READ_BCR(ARC_REG_DCCM_BUILD, dccm); - if (dccm.ver) { - cpu->dccm.sz = 256 << dccm.sz0; - if (dccm.sz0 == 0xF && dccm.sz1 > 0) - cpu->dccm.sz <<= dccm.sz1; + if (info->arcver < 0x34) + cpu_nm = "ARC750"; + else + cpu_nm = "ARC770"; - region = read_aux_reg(ARC_REG_AUX_DCCM); - cpu->dccm.base_addr = region & 0xF0000000; - } - } -} + n += scnprintf(buf + n, len - n, "processor [%d]\t: %s (%s ISA) %s%s%s\n", + c, cpu_nm, isa_nm, + IS_AVAIL2(atomic, "atomic ", CONFIG_ARC_HAS_LLSC), + IS_AVAIL1(be, "[Big-Endian]")); -static void decode_arc_core(struct cpuinfo_arc *cpu) -{ - struct bcr_uarch_build_arcv2 uarch; - const struct id_to_str *tbl; - - if (cpu->core.family < 0x54) { /* includes arc700 */ + READ_BCR(ARC_REG_FP_BCR, fpu_sp); + READ_BCR(ARC_REG_DPFP_BCR, fpu_dp); - for (tbl = &arc_legacy_rel[0]; tbl->id != 0; tbl++) { - if (cpu->core.family == tbl->id) { - cpu->release = tbl->str; - break; - } - } + if (fpu_sp.ver | fpu_dp.ver) + n += scnprintf(buf + n, len - n, "FPU\t\t: %s%s\n", + IS_AVAIL1(fpu_sp.ver, "SP "), + IS_AVAIL1(fpu_dp.ver, "DP ")); - if (is_isa_arcompact()) - cpu->name = "ARC700"; - else if (tbl->str) - cpu->name = "HS38"; - else - cpu->name = cpu->release = "Unknown"; + READ_BCR(ARC_REG_BPU_BCR, bpu); + bpu_full = bpu.fam ? 1 : 0; + bpu_cache = 256 << (bpu.ent - 1); + bpu_pred = 256 << (bpu.ent - 1); - return; + n += scnprintf(buf + n, len - n, + "BPU\t\t: %s%s match, cache:%d, Predict Table:%d\n", + IS_AVAIL1(bpu_full, "full"), + IS_AVAIL1(!bpu_full, "partial"), + bpu_cache, bpu_pred); + + READ_BCR(ARC_REG_ICCM_BUILD, iccm); + if (iccm.ver) { + info->iccm.sz = 4096 << iccm.sz; /* 8K to 512K */ + info->iccm.base = iccm.base << 16; } - /* - * Initial HS cores bumped AUX IDENTITY.ARCVER for each release until - * ARCVER 0x54 which introduced AUX MICRO_ARCH_BUILD and subsequent - * releases only update it. - */ - READ_BCR(ARC_REG_MICRO_ARCH_BCR, uarch); - - if (uarch.prod == 4) { - cpu->name = "HS48"; - cpu->extn.dual = 1; + READ_BCR(ARC_REG_DCCM_BUILD, dccm); + if (dccm.ver) { + unsigned long base; + info->dccm.sz = 2048 << dccm.sz; /* 2K to 256K */ - } else { - cpu->name = "HS38"; + base = read_aux_reg(ARC_REG_DCCM_BASE_BUILD); + info->dccm.base = base & ~0xF; } - for (tbl = &arc_hs_ver54_rel[0]; tbl->id != 0xFF; tbl++) { - if (uarch.maj == tbl->id) { - cpu->release = tbl->str; - break; - } - } + /* ARCompact ISA specific sanity checks */ + present = fpu_dp.ver; /* SP has no arch visible regs */ + CHK_OPT_STRICT(CONFIG_ARC_FPU_SAVE_RESTORE, present); +#endif + return n; + } -static void read_arc_build_cfg_regs(void) +static int arcv2_mumbojumbo(int c, struct cpuinfo_arc *info, char *buf, int len) { - struct bcr_timer timer; - struct bcr_generic bcr; - struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; + int n = 0; +#ifdef CONFIG_ISA_ARCV2 + const char *release, *cpu_nm, *isa_nm = "ARCv2"; + int dual_issue = 0, dual_enb = 0, mpy_opt, present; + int bpu_full, bpu_cache, bpu_pred, bpu_ret_stk; + char mpy_nm[16], lpb_nm[32]; struct bcr_isa_arcv2 isa; - struct bcr_actionpoint ap; - - FIX_PTR(cpu); + struct bcr_mpy mpy; + struct bcr_fp_arcv2 fpu; + struct bcr_bpu_arcv2 bpu; + struct bcr_lpb lpb; + struct bcr_iccm_arcv2 iccm; + struct bcr_dccm_arcv2 dccm; + struct bcr_erp erp; - READ_BCR(AUX_IDENTITY, cpu->core); - decode_arc_core(cpu); - - READ_BCR(ARC_REG_TIMERS_BCR, timer); - cpu->extn.timer0 = timer.t0; - cpu->extn.timer1 = timer.t1; - cpu->extn.rtc = timer.rtc; - - cpu->vec_base = read_aux_reg(AUX_INTR_VEC_BASE); + /* + * Initial HS cores bumped AUX IDENTITY.ARCVER for each release until + * ARCVER 0x54 which introduced AUX MICRO_ARCH_BUILD and subsequent + * releases only update it. + */ - READ_BCR(ARC_REG_MUL_BCR, cpu->extn_mpy); + cpu_nm = "HS38"; - /* Read CCM BCRs for boot reporting even if not enabled in Kconfig */ - read_decode_ccm_bcr(cpu); + if (info->arcver > 0x50 && info->arcver <= 0x53) { + release = arc_hs_rel[info->arcver - 0x51].str; + } else { + const struct id_to_str *tbl; + struct bcr_uarch_build uarch; - read_decode_mmu_bcr(); - read_decode_cache_bcr(); + READ_BCR(ARC_REG_MICRO_ARCH_BCR, uarch); - if (is_isa_arcompact()) { - struct bcr_fp_arcompact sp, dp; - struct bcr_bpu_arcompact bpu; - - READ_BCR(ARC_REG_FP_BCR, sp); - READ_BCR(ARC_REG_DPFP_BCR, dp); - cpu->extn.fpu_sp = sp.ver ? 1 : 0; - cpu->extn.fpu_dp = dp.ver ? 1 : 0; - - READ_BCR(ARC_REG_BPU_BCR, bpu); - cpu->bpu.ver = bpu.ver; - cpu->bpu.full = bpu.fam ? 1 : 0; - if (bpu.ent) { - cpu->bpu.num_cache = 256 << (bpu.ent - 1); - cpu->bpu.num_pred = 256 << (bpu.ent - 1); + for (tbl = &arc_hs_ver54_rel[0]; tbl->id != 0xFF; tbl++) { + if (uarch.maj == tbl->id) { + release = tbl->str; + break; + } } - } else { - struct bcr_fp_arcv2 spdp; - struct bcr_bpu_arcv2 bpu; - - READ_BCR(ARC_REG_FP_V2_BCR, spdp); - cpu->extn.fpu_sp = spdp.sp ? 1 : 0; - cpu->extn.fpu_dp = spdp.dp ? 1 : 0; - - READ_BCR(ARC_REG_BPU_BCR, bpu); - cpu->bpu.ver = bpu.ver; - cpu->bpu.full = bpu.ft; - cpu->bpu.num_cache = 256 << bpu.bce; - cpu->bpu.num_pred = 2048 << bpu.pte; - cpu->bpu.ret_stk = 4 << bpu.rse; - - /* if dual issue hardware, is it enabled ? */ - if (cpu->extn.dual) { + if (uarch.prod == 4) { unsigned int exec_ctrl; + cpu_nm = "HS48"; + dual_issue = 1; + /* if dual issue hardware, is it enabled ? */ READ_BCR(AUX_EXEC_CTRL, exec_ctrl); - cpu->extn.dual_enb = !(exec_ctrl & 1); + dual_enb = !(exec_ctrl & 1); } } - READ_BCR(ARC_REG_AP_BCR, ap); - if (ap.ver) { - cpu->extn.ap_num = 2 << ap.num; - cpu->extn.ap_full = !ap.min; - } - - READ_BCR(ARC_REG_SMART_BCR, bcr); - cpu->extn.smart = bcr.ver ? 1 : 0; - - READ_BCR(ARC_REG_RTT_BCR, bcr); - cpu->extn.rtt = bcr.ver ? 1 : 0; - READ_BCR(ARC_REG_ISA_CFG_BCR, isa); - /* some hacks for lack of feature BCR info in old ARC700 cores */ - if (is_isa_arcompact()) { - if (!isa.ver) /* ISA BCR absent, use Kconfig info */ - cpu->isa.atomic = IS_ENABLED(CONFIG_ARC_HAS_LLSC); - else { - /* ARC700_BUILD only has 2 bits of isa info */ - struct bcr_generic bcr = *(struct bcr_generic *)&isa; - cpu->isa.atomic = bcr.info & 1; - } - - cpu->isa.be = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); + n += scnprintf(buf + n, len - n, "processor [%d]\t: %s %s (%s ISA) %s%s%s\n", + c, cpu_nm, release, isa_nm, + IS_AVAIL1(isa.be, "[Big-Endian]"), + IS_AVAIL3(dual_issue, dual_enb, " Dual-Issue ")); + + READ_BCR(ARC_REG_MPY_BCR, mpy); + mpy_opt = 2; /* stock MPY/MPYH */ + if (mpy.dsp) /* OPT 7-9 */ + mpy_opt = mpy.dsp + 6; + + scnprintf(mpy_nm, 16, "mpy[opt %d] ", mpy_opt); + + READ_BCR(ARC_REG_FP_V2_BCR, fpu); + + n += scnprintf(buf + n, len - n, "ISA Extn\t: %s%s%s%s%s%s%s%s%s%s%s\n", + IS_AVAIL2(isa.atomic, "atomic ", CONFIG_ARC_HAS_LLSC), + IS_AVAIL2(isa.ldd, "ll64 ", CONFIG_ARC_HAS_LL64), + IS_AVAIL2(isa.unalign, "unalign ", CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS), + IS_AVAIL1(mpy.ver, mpy_nm), + IS_AVAIL1(isa.div_rem, "div_rem "), + IS_AVAIL1((fpu.sp | fpu.dp), " FPU:"), + IS_AVAIL1(fpu.sp, " sp"), + IS_AVAIL1(fpu.dp, " dp")); + + READ_BCR(ARC_REG_BPU_BCR, bpu); + bpu_full = bpu.ft; + bpu_cache = 256 << bpu.bce; + bpu_pred = 2048 << bpu.pte; + bpu_ret_stk = 4 << bpu.rse; + + READ_BCR(ARC_REG_LPB_BUILD, lpb); + if (lpb.ver) { + unsigned int ctl; + ctl = read_aux_reg(ARC_REG_LPB_CTRL); + + scnprintf(lpb_nm, sizeof(lpb_nm), " Loop Buffer:%d %s", + lpb.entries, IS_DISABLED_RUN(!ctl)); + } - /* there's no direct way to distinguish 750 vs. 770 */ - if (unlikely(cpu->core.family < 0x34 || cpu->mmu.ver < 3)) - cpu->name = "ARC750"; - } else { - cpu->isa = isa; + n += scnprintf(buf + n, len - n, + "BPU\t\t: %s%s match, cache:%d, Predict Table:%d Return stk: %d%s\n", + IS_AVAIL1(bpu_full, "full"), + IS_AVAIL1(!bpu_full, "partial"), + bpu_cache, bpu_pred, bpu_ret_stk, + lpb_nm); + + READ_BCR(ARC_REG_ICCM_BUILD, iccm); + if (iccm.ver) { + unsigned long base; + info->iccm.sz = 256 << iccm.sz00; /* 512B to 16M */ + if (iccm.sz00 == 0xF && iccm.sz01 > 0) + info->iccm.sz <<= iccm.sz01; + base = read_aux_reg(ARC_REG_AUX_ICCM); + info->iccm.base = base & 0xF0000000; } -} -static char *arc_cpu_mumbojumbo(int cpu_id, char *buf, int len) -{ - struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; - struct bcr_identity *core = &cpu->core; - char mpy_opt[16]; - int n = 0; + READ_BCR(ARC_REG_DCCM_BUILD, dccm); + if (dccm.ver) { + unsigned long base; + info->dccm.sz = 256 << dccm.sz0; + if (dccm.sz0 == 0xF && dccm.sz1 > 0) + info->dccm.sz <<= dccm.sz1; + base = read_aux_reg(ARC_REG_AUX_DCCM); + info->dccm.base = base & 0xF0000000; + } - FIX_PTR(cpu); + /* Error Protection: ECC/Parity */ + READ_BCR(ARC_REG_ERP_BUILD, erp); + if (erp.ver) { + struct ctl_erp ctl; + READ_BCR(ARC_REG_ERP_CTRL, ctl); + /* inverted bits: 0 means enabled */ + n += scnprintf(buf + n, len - n, "Extn [ECC]\t: %s%s%s%s%s%s\n", + IS_AVAIL3(erp.ic, !ctl.dpi, "IC "), + IS_AVAIL3(erp.dc, !ctl.dpd, "DC "), + IS_AVAIL3(erp.mmu, !ctl.mpd, "MMU ")); + } - n += scnprintf(buf + n, len - n, - "\nIDENTITY\t: ARCVER [%#02x] ARCNUM [%#02x] CHIPID [%#4x]\n", - core->family, core->cpu_id, core->chip_id); + /* ARCv2 ISA specific sanity checks */ + present = fpu.sp | fpu.dp | mpy.dsp; /* DSP and/or FPU */ + CHK_OPT_STRICT(CONFIG_ARC_HAS_ACCL_REGS, present); - n += scnprintf(buf + n, len - n, "processor [%d]\t: %s %s (%s ISA) %s%s%s\n", - cpu_id, cpu->name, cpu->release, - is_isa_arcompact() ? "ARCompact" : "ARCv2", - IS_AVAIL1(cpu->isa.be, "[Big-Endian]"), - IS_AVAIL3(cpu->extn.dual, cpu->extn.dual_enb, " Dual-Issue ")); + dsp_config_check(); +#endif + return n; +} - n += scnprintf(buf + n, len - n, "Timers\t\t: %s%s%s%s%s%s\nISA Extn\t: ", - IS_AVAIL1(cpu->extn.timer0, "Timer0 "), - IS_AVAIL1(cpu->extn.timer1, "Timer1 "), - IS_AVAIL2(cpu->extn.rtc, "RTC [UP 64-bit] ", CONFIG_ARC_TIMERS_64BIT), - IS_AVAIL2(cpu->extn.gfrc, "GFRC [SMP 64-bit] ", CONFIG_ARC_TIMERS_64BIT)); +static char *arc_cpu_mumbojumbo(int c, struct cpuinfo_arc *info, char *buf, int len) +{ + struct bcr_identity ident; + struct bcr_timer timer; + struct bcr_generic bcr; + struct mcip_bcr mp; + struct bcr_actionpoint ap; + unsigned long vec_base; + int ap_num, ap_full, smart, rtt, n; - if (cpu->extn_mpy.ver) { - if (is_isa_arcompact()) { - scnprintf(mpy_opt, 16, "mpy"); - } else { + memset(info, 0, sizeof(struct cpuinfo_arc)); - int opt = 2; /* stock MPY/MPYH */ + READ_BCR(AUX_IDENTITY, ident); + info->arcver = ident.family; - if (cpu->extn_mpy.dsp) /* OPT 7-9 */ - opt = cpu->extn_mpy.dsp + 6; + n = scnprintf(buf, len, + "\nIDENTITY\t: ARCVER [%#02x] ARCNUM [%#02x] CHIPID [%#4x]\n", + ident.family, ident.cpu_id, ident.chip_id); - scnprintf(mpy_opt, 16, "mpy[opt %d] ", opt); - } + if (is_isa_arcompact()) { + n += arcompact_mumbojumbo(c, info, buf + n, len - n); + } else if (is_isa_arcv2()){ + n += arcv2_mumbojumbo(c, info, buf + n, len - n); } - n += scnprintf(buf + n, len - n, "%s%s%s%s%s%s%s%s\n", - IS_AVAIL2(cpu->isa.atomic, "atomic ", CONFIG_ARC_HAS_LLSC), - IS_AVAIL2(cpu->isa.ldd, "ll64 ", CONFIG_ARC_HAS_LL64), - IS_AVAIL2(cpu->isa.unalign, "unalign ", CONFIG_ARC_USE_UNALIGNED_MEM_ACCESS), - IS_AVAIL1(cpu->extn_mpy.ver, mpy_opt), - IS_AVAIL1(cpu->isa.div_rem, "div_rem ")); + n += arc_mmu_mumbojumbo(c, buf + n, len - n); + n += arc_cache_mumbojumbo(c, buf + n, len - n); - if (cpu->bpu.ver) { - n += scnprintf(buf + n, len - n, - "BPU\t\t: %s%s match, cache:%d, Predict Table:%d Return stk: %d", - IS_AVAIL1(cpu->bpu.full, "full"), - IS_AVAIL1(!cpu->bpu.full, "partial"), - cpu->bpu.num_cache, cpu->bpu.num_pred, cpu->bpu.ret_stk); - - if (is_isa_arcv2()) { - struct bcr_lpb lpb; - - READ_BCR(ARC_REG_LPB_BUILD, lpb); - if (lpb.ver) { - unsigned int ctl; - ctl = read_aux_reg(ARC_REG_LPB_CTRL); - - n += scnprintf(buf + n, len - n, " Loop Buffer:%d %s", - lpb.entries, - IS_DISABLED_RUN(!ctl)); - } - } - n += scnprintf(buf + n, len - n, "\n"); - } + READ_BCR(ARC_REG_TIMERS_BCR, timer); + info->t0 = timer.t0; + info->t1 = timer.t1; - return buf; -} + READ_BCR(ARC_REG_MCIP_BCR, mp); + vec_base = read_aux_reg(AUX_INTR_VEC_BASE); -static char *arc_extn_mumbojumbo(int cpu_id, char *buf, int len) -{ - int n = 0; - struct cpuinfo_arc *cpu = &cpuinfo_arc700[cpu_id]; + n += scnprintf(buf + n, len - n, + "Timers\t\t: %s%s%s%s%s%s\nVector Table\t: %#lx\n", + IS_AVAIL1(timer.t0, "Timer0 "), + IS_AVAIL1(timer.t1, "Timer1 "), + IS_AVAIL2(timer.rtc, "RTC [UP 64-bit] ", CONFIG_ARC_TIMERS_64BIT), + IS_AVAIL2(mp.gfrc, "GFRC [SMP 64-bit] ", CONFIG_ARC_TIMERS_64BIT), + vec_base); - FIX_PTR(cpu); + READ_BCR(ARC_REG_AP_BCR, ap); + if (ap.ver) { + ap_num = 2 << ap.num; + ap_full = !ap.min; + } - n += scnprintf(buf + n, len - n, "Vector Table\t: %#x\n", cpu->vec_base); + READ_BCR(ARC_REG_SMART_BCR, bcr); + smart = bcr.ver ? 1 : 0; - if (cpu->extn.fpu_sp || cpu->extn.fpu_dp) - n += scnprintf(buf + n, len - n, "FPU\t\t: %s%s\n", - IS_AVAIL1(cpu->extn.fpu_sp, "SP "), - IS_AVAIL1(cpu->extn.fpu_dp, "DP ")); + READ_BCR(ARC_REG_RTT_BCR, bcr); + rtt = bcr.ver ? 1 : 0; - if (cpu->extn.ap_num | cpu->extn.smart | cpu->extn.rtt) { + if (ap.ver | smart | rtt) { n += scnprintf(buf + n, len - n, "DEBUG\t\t: %s%s", - IS_AVAIL1(cpu->extn.smart, "smaRT "), - IS_AVAIL1(cpu->extn.rtt, "RTT ")); - if (cpu->extn.ap_num) { + IS_AVAIL1(smart, "smaRT "), + IS_AVAIL1(rtt, "RTT ")); + if (ap.ver) { n += scnprintf(buf + n, len - n, "ActionPoint %d/%s", - cpu->extn.ap_num, - cpu->extn.ap_full ? "full":"min"); + ap_num, + ap_full ? "full":"min"); } n += scnprintf(buf + n, len - n, "\n"); } - if (cpu->dccm.sz || cpu->iccm.sz) - n += scnprintf(buf + n, len - n, "Extn [CCM]\t: DCCM @ %x, %d KB / ICCM: @ %x, %d KB\n", - cpu->dccm.base_addr, TO_KB(cpu->dccm.sz), - cpu->iccm.base_addr, TO_KB(cpu->iccm.sz)); - - if (is_isa_arcv2()) { - - /* Error Protection: ECC/Parity */ - struct bcr_erp erp; - READ_BCR(ARC_REG_ERP_BUILD, erp); - - if (erp.ver) { - struct ctl_erp ctl; - READ_BCR(ARC_REG_ERP_CTRL, ctl); - - /* inverted bits: 0 means enabled */ - n += scnprintf(buf + n, len - n, "Extn [ECC]\t: %s%s%s%s%s%s\n", - IS_AVAIL3(erp.ic, !ctl.dpi, "IC "), - IS_AVAIL3(erp.dc, !ctl.dpd, "DC "), - IS_AVAIL3(erp.mmu, !ctl.mpd, "MMU ")); - } - } + if (info->dccm.sz || info->iccm.sz) + n += scnprintf(buf + n, len - n, + "Extn [CCM]\t: DCCM @ %lx, %d KB / ICCM: @ %lx, %d KB\n", + info->dccm.base, TO_KB(info->dccm.sz), + info->iccm.base, TO_KB(info->iccm.sz)); return buf; } @@ -401,15 +378,15 @@ void chk_opt_weak(char *opt_name, bool hw_exists, bool opt_ena) panic("Disable %s, hardware NOT present\n", opt_name); } -static void arc_chk_core_config(void) +/* + * ISA agnostic sanity checks + */ +static void arc_chk_core_config(struct cpuinfo_arc *info) { - struct cpuinfo_arc *cpu = &cpuinfo_arc700[smp_processor_id()]; - int present = 0; - - if (!cpu->extn.timer0) + if (!info->t0) panic("Timer0 is not present!\n"); - if (!cpu->extn.timer1) + if (!info->t1) panic("Timer1 is not present!\n"); #ifdef CONFIG_ARC_HAS_DCCM @@ -417,35 +394,17 @@ static void arc_chk_core_config(void) * DCCM can be arbit placed in hardware. * Make sure it's placement/sz matches what Linux is built with */ - if ((unsigned int)__arc_dccm_base != cpu->dccm.base_addr) + if ((unsigned int)__arc_dccm_base != info->dccm.base) panic("Linux built with incorrect DCCM Base address\n"); - if (CONFIG_ARC_DCCM_SZ * SZ_1K != cpu->dccm.sz) + if (CONFIG_ARC_DCCM_SZ * SZ_1K != info->dccm.sz) panic("Linux built with incorrect DCCM Size\n"); #endif #ifdef CONFIG_ARC_HAS_ICCM - if (CONFIG_ARC_ICCM_SZ * SZ_1K != cpu->iccm.sz) + if (CONFIG_ARC_ICCM_SZ * SZ_1K != info->iccm.sz) panic("Linux built with incorrect ICCM Size\n"); #endif - - /* - * FP hardware/software config sanity - * -If hardware present, kernel needs to save/restore FPU state - * -If not, it will crash trying to save/restore the non-existant regs - */ - - if (is_isa_arcompact()) { - /* only DPDP checked since SP has no arch visible regs */ - present = cpu->extn.fpu_dp; - CHK_OPT_STRICT(CONFIG_ARC_FPU_SAVE_RESTORE, present); - } else { - /* Accumulator Low:High pair (r58:59) present if DSP MPY or FPU */ - present = cpu->extn_mpy.dsp | cpu->extn.fpu_sp | cpu->extn.fpu_dp; - CHK_OPT_STRICT(CONFIG_ARC_HAS_ACCL_REGS, present); - - dsp_config_check(); - } } /* @@ -456,21 +415,19 @@ static void arc_chk_core_config(void) void setup_processor(void) { + struct cpuinfo_arc info; + int c = smp_processor_id(); char str[512]; - int cpu_id = smp_processor_id(); - read_arc_build_cfg_regs(); - arc_init_IRQ(); + pr_info("%s", arc_cpu_mumbojumbo(c, &info, str, sizeof(str))); + pr_info("%s", arc_platform_smp_cpuinfo()); - pr_info("%s", arc_cpu_mumbojumbo(cpu_id, str, sizeof(str))); + arc_chk_core_config(&info); + arc_init_IRQ(); arc_mmu_init(); arc_cache_init(); - pr_info("%s", arc_extn_mumbojumbo(cpu_id, str, sizeof(str))); - pr_info("%s", arc_platform_smp_cpuinfo()); - - arc_chk_core_config(); } static inline bool uboot_arg_invalid(unsigned long addr) @@ -617,6 +574,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) char *str; int cpu_id = ptr_to_cpu(v); struct device *cpu_dev = get_cpu_device(cpu_id); + struct cpuinfo_arc info; struct clk *cpu_clk; unsigned long freq = 0; @@ -629,7 +587,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) if (!str) goto done; - seq_printf(m, arc_cpu_mumbojumbo(cpu_id, str, PAGE_SIZE)); + seq_printf(m, arc_cpu_mumbojumbo(cpu_id, &info, str, PAGE_SIZE)); cpu_clk = clk_get(cpu_dev, NULL); if (IS_ERR(cpu_clk)) { @@ -646,9 +604,6 @@ static int show_cpuinfo(struct seq_file *m, void *v) loops_per_jiffy / (500000 / HZ), (loops_per_jiffy / (5000 / HZ)) % 100); - seq_printf(m, arc_mmu_mumbojumbo(cpu_id, str, PAGE_SIZE)); - seq_printf(m, arc_cache_mumbojumbo(cpu_id, str, PAGE_SIZE)); - seq_printf(m, arc_extn_mumbojumbo(cpu_id, str, PAGE_SIZE)); seq_printf(m, arc_platform_smp_cpuinfo()); free_page((unsigned long)str); diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index 3c1590c27fae3b3e72d37d01a25c5ca691c29bf3..0b3bb529d24632bfd19bff87535502b2a288228c 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -53,6 +53,7 @@ #include #include +#include struct rt_sigframe { struct siginfo info; diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index 409cfa4675b40d535b74a31f45707d0bab228ade..8d9b188caa27bcd965cd3f94a6a4a4739657c97d 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -23,9 +23,10 @@ #include #include -#include -#include #include +#include +#include +#include #ifndef CONFIG_ARC_HAS_LLSC arch_spinlock_t smp_atomic_ops_lock = __ARCH_SPIN_LOCK_UNLOCKED; @@ -351,7 +352,7 @@ static inline int __do_IPI(unsigned long msg) * arch-common ISR to handle for inter-processor interrupts * Has hooks for platform specific IPI */ -irqreturn_t do_IPI(int irq, void *dev_id) +static irqreturn_t do_IPI(int irq, void *dev_id) { unsigned long pending; unsigned long __maybe_unused copy; diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c index 5372dc04e784789291f0f7a8ee38e624c4fb30b8..ea99c066ef2502ad519c741f98efe0b963e96c9d 100644 --- a/arch/arc/kernel/stacktrace.c +++ b/arch/arc/kernel/stacktrace.c @@ -29,6 +29,7 @@ #include #include +#include #include /*------------------------------------------------------------------------- diff --git a/arch/arc/kernel/traps.c b/arch/arc/kernel/traps.c index 6b83e3f2b41c3eb5b20ce1241e777c48dfa40f58..9b9570b79362ee08931044931bccbd965526c786 100644 --- a/arch/arc/kernel/traps.c +++ b/arch/arc/kernel/traps.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -109,9 +110,7 @@ void do_machine_check_fault(unsigned long address, struct pt_regs *regs) */ void do_non_swi_trap(unsigned long address, struct pt_regs *regs) { - unsigned int param = regs->ecr_param; - - switch (param) { + switch (regs->ecr.param) { case 1: trap_is_brkpt(address, regs); break; diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c index 7654c2e42dc0a63b76f5c16788f4ca512fcd491e..d5b3ed2c58f5030ab8022ac260b7ce8e172c2fa7 100644 --- a/arch/arc/kernel/troubleshoot.c +++ b/arch/arc/kernel/troubleshoot.c @@ -115,8 +115,8 @@ static void show_ecr_verbose(struct pt_regs *regs) /* For Data fault, this is data address not instruction addr */ address = current->thread.fault_address; - vec = regs->ecr_vec; - cause_code = regs->ecr_cause; + vec = regs->ecr.vec; + cause_code = regs->ecr.cause; /* For DTLB Miss or ProtV, display the memory involved too */ if (vec == ECR_V_DTLB_MISS) { @@ -154,7 +154,7 @@ static void show_ecr_verbose(struct pt_regs *regs) pr_cont("Misaligned r/w from 0x%08lx\n", address); #endif } else if (vec == ECR_V_TRAP) { - if (regs->ecr_param == 5) + if (regs->ecr.param == 5) pr_cont("gcc generated __builtin_trap\n"); } else { pr_cont("Check Programmer's Manual\n"); @@ -184,9 +184,10 @@ void show_regs(struct pt_regs *regs) if (user_mode(regs)) show_faulting_vma(regs->ret); /* faulting code, not data */ - pr_info("ECR: 0x%08lx EFA: 0x%08lx ERET: 0x%08lx\nSTAT: 0x%08lx", - regs->event, current->thread.fault_address, regs->ret, - regs->status32); + pr_info("ECR: 0x%08lx EFA: 0x%08lx ERET: 0x%08lx\n", + regs->ecr.full, current->thread.fault_address, regs->ret); + + pr_info("STAT32: 0x%08lx", regs->status32); #define STS_BIT(r, bit) r->status32 & STATUS_##bit##_MASK ? #bit" " : "" diff --git a/arch/arc/lib/memset-archs.S b/arch/arc/lib/memset-archs.S index d2e09fece5bcffdb556c02bbd7d91781e869a229..d0a5cec4cdcaf5eceba40781e764c880eb87aa02 100644 --- a/arch/arc/lib/memset-archs.S +++ b/arch/arc/lib/memset-archs.S @@ -36,12 +36,13 @@ #endif ENTRY_CFI(memset) - PREFETCHW_INSTR r0, 0 ; Prefetch the first write location mov.f 0, r2 ;;; if size is zero jz.d [blink] mov r3, r0 ; don't clobber ret val + PREFETCHW_INSTR r0, 0 ; Prefetch the first write location + ;;; if length < 8 brls.d.nt r2, 8, .Lsmallchunk mov.f lp_count,r2 diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c index 3c16ee942a5c5ae8d8f97a85173fa638cdc85df3..f7e05c14663750db8890ff4464ef1bed6a537568 100644 --- a/arch/arc/mm/cache.c +++ b/arch/arc/mm/cache.c @@ -28,6 +28,10 @@ int slc_enable = 1, ioc_enable = 1; unsigned long perip_base = ARC_UNCACHED_ADDR_SPACE; /* legacy value for boot */ unsigned long perip_end = 0xFFFFFFFF; /* legacy value */ +static struct cpuinfo_arc_cache { + unsigned int sz_k, line_len, colors; +} ic_info, dc_info, slc_info; + void (*_cache_line_loop_ic_fn)(phys_addr_t paddr, unsigned long vaddr, unsigned long sz, const int op, const int full_page); @@ -35,78 +39,24 @@ void (*__dma_cache_wback_inv)(phys_addr_t start, unsigned long sz); void (*__dma_cache_inv)(phys_addr_t start, unsigned long sz); void (*__dma_cache_wback)(phys_addr_t start, unsigned long sz); -char *arc_cache_mumbojumbo(int c, char *buf, int len) -{ - int n = 0; - struct cpuinfo_arc_cache *p; - -#define PR_CACHE(p, cfg, str) \ - if (!(p)->line_len) \ - n += scnprintf(buf + n, len - n, str"\t\t: N/A\n"); \ - else \ - n += scnprintf(buf + n, len - n, \ - str"\t\t: %uK, %dway/set, %uB Line, %s%s%s\n", \ - (p)->sz_k, (p)->assoc, (p)->line_len, \ - (p)->vipt ? "VIPT" : "PIPT", \ - (p)->alias ? " aliasing" : "", \ - IS_USED_CFG(cfg)); - - PR_CACHE(&cpuinfo_arc700[c].icache, CONFIG_ARC_HAS_ICACHE, "I-Cache"); - PR_CACHE(&cpuinfo_arc700[c].dcache, CONFIG_ARC_HAS_DCACHE, "D-Cache"); - - p = &cpuinfo_arc700[c].slc; - if (p->line_len) - n += scnprintf(buf + n, len - n, - "SLC\t\t: %uK, %uB Line%s\n", - p->sz_k, p->line_len, IS_USED_RUN(slc_enable)); - - n += scnprintf(buf + n, len - n, "Peripherals\t: %#lx%s%s\n", - perip_base, - IS_AVAIL3(ioc_exists, ioc_enable, ", IO-Coherency (per-device) ")); - - return buf; -} - -/* - * Read the Cache Build Confuration Registers, Decode them and save into - * the cpuinfo structure for later use. - * No Validation done here, simply read/convert the BCRs - */ -static void read_decode_cache_bcr_arcv2(int cpu) +static int read_decode_cache_bcr_arcv2(int c, char *buf, int len) { - struct cpuinfo_arc_cache *p_slc = &cpuinfo_arc700[cpu].slc; + struct cpuinfo_arc_cache *p_slc = &slc_info; + struct bcr_identity ident; struct bcr_generic sbcr; - - struct bcr_slc_cfg { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int pad:24, way:2, lsz:2, sz:4; -#else - unsigned int sz:4, lsz:2, way:2, pad:24; -#endif - } slc_cfg; - - struct bcr_clust_cfg { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int pad:7, c:1, num_entries:8, num_cores:8, ver:8; -#else - unsigned int ver:8, num_cores:8, num_entries:8, c:1, pad:7; -#endif - } cbcr; - - struct bcr_volatile { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int start:4, limit:4, pad:22, order:1, disable:1; -#else - unsigned int disable:1, order:1, pad:22, limit:4, start:4; -#endif - } vol; - + struct bcr_clust_cfg cbcr; + struct bcr_volatile vol; + int n = 0; READ_BCR(ARC_REG_SLC_BCR, sbcr); if (sbcr.ver) { + struct bcr_slc_cfg slc_cfg; READ_BCR(ARC_REG_SLC_CFG, slc_cfg); p_slc->sz_k = 128 << slc_cfg.sz; l2_line_sz = p_slc->line_len = (slc_cfg.lsz == 0) ? 128 : 64; + n += scnprintf(buf + n, len - n, + "SLC\t\t: %uK, %uB Line%s\n", + p_slc->sz_k, p_slc->line_len, IS_USED_RUN(slc_enable)); } READ_BCR(ARC_REG_CLUSTER_BCR, cbcr); @@ -129,70 +79,83 @@ static void read_decode_cache_bcr_arcv2(int cpu) ioc_enable = 0; } + READ_BCR(AUX_IDENTITY, ident); + /* HS 2.0 didn't have AUX_VOL */ - if (cpuinfo_arc700[cpu].core.family > 0x51) { + if (ident.family > 0x51) { READ_BCR(AUX_VOL, vol); perip_base = vol.start << 28; /* HS 3.0 has limit and strict-ordering fields */ - if (cpuinfo_arc700[cpu].core.family > 0x52) + if (ident.family > 0x52) perip_end = (vol.limit << 28) - 1; } + + n += scnprintf(buf + n, len - n, "Peripherals\t: %#lx%s%s\n", + perip_base, + IS_AVAIL3(ioc_exists, ioc_enable, ", IO-Coherency (per-device) ")); + + return n; } -void read_decode_cache_bcr(void) +int arc_cache_mumbojumbo(int c, char *buf, int len) { - struct cpuinfo_arc_cache *p_ic, *p_dc; - unsigned int cpu = smp_processor_id(); - struct bcr_cache { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int pad:12, line_len:4, sz:4, config:4, ver:8; -#else - unsigned int ver:8, config:4, sz:4, line_len:4, pad:12; -#endif - } ibcr, dbcr; + struct cpuinfo_arc_cache *p_ic = &ic_info, *p_dc = &dc_info; + struct bcr_cache ibcr, dbcr; + int vipt, assoc; + int n = 0; - p_ic = &cpuinfo_arc700[cpu].icache; READ_BCR(ARC_REG_IC_BCR, ibcr); - if (!ibcr.ver) goto dc_chk; - if (ibcr.ver <= 3) { + if (is_isa_arcompact() && (ibcr.ver <= 3)) { BUG_ON(ibcr.config != 3); - p_ic->assoc = 2; /* Fixed to 2w set assoc */ - } else if (ibcr.ver >= 4) { - p_ic->assoc = 1 << ibcr.config; /* 1,2,4,8 */ + assoc = 2; /* Fixed to 2w set assoc */ + } else if (is_isa_arcv2() && (ibcr.ver >= 4)) { + assoc = 1 << ibcr.config; /* 1,2,4,8 */ } p_ic->line_len = 8 << ibcr.line_len; p_ic->sz_k = 1 << (ibcr.sz - 1); - p_ic->vipt = 1; - p_ic->alias = p_ic->sz_k/p_ic->assoc/TO_KB(PAGE_SIZE) > 1; + p_ic->colors = p_ic->sz_k/assoc/TO_KB(PAGE_SIZE); + + n += scnprintf(buf + n, len - n, + "I-Cache\t\t: %uK, %dway/set, %uB Line, VIPT%s%s\n", + p_ic->sz_k, assoc, p_ic->line_len, + p_ic->colors > 1 ? " aliasing" : "", + IS_USED_CFG(CONFIG_ARC_HAS_ICACHE)); dc_chk: - p_dc = &cpuinfo_arc700[cpu].dcache; READ_BCR(ARC_REG_DC_BCR, dbcr); - if (!dbcr.ver) goto slc_chk; - if (dbcr.ver <= 3) { + if (is_isa_arcompact() && (dbcr.ver <= 3)) { BUG_ON(dbcr.config != 2); - p_dc->assoc = 4; /* Fixed to 4w set assoc */ - p_dc->vipt = 1; - p_dc->alias = p_dc->sz_k/p_dc->assoc/TO_KB(PAGE_SIZE) > 1; - } else if (dbcr.ver >= 4) { - p_dc->assoc = 1 << dbcr.config; /* 1,2,4,8 */ - p_dc->vipt = 0; - p_dc->alias = 0; /* PIPT so can't VIPT alias */ + vipt = 1; + assoc = 4; /* Fixed to 4w set assoc */ + p_dc->colors = p_dc->sz_k/assoc/TO_KB(PAGE_SIZE); + } else if (is_isa_arcv2() && (dbcr.ver >= 4)) { + vipt = 0; + assoc = 1 << dbcr.config; /* 1,2,4,8 */ + p_dc->colors = 1; /* PIPT so can't VIPT alias */ } p_dc->line_len = 16 << dbcr.line_len; p_dc->sz_k = 1 << (dbcr.sz - 1); + n += scnprintf(buf + n, len - n, + "D-Cache\t\t: %uK, %dway/set, %uB Line, %s%s%s\n", + p_dc->sz_k, assoc, p_dc->line_len, + vipt ? "VIPT" : "PIPT", + p_dc->colors > 1 ? " aliasing" : "", + IS_USED_CFG(CONFIG_ARC_HAS_DCACHE)); + slc_chk: if (is_isa_arcv2()) - read_decode_cache_bcr_arcv2(cpu); + n += read_decode_cache_bcr_arcv2(c, buf + n, len - n); + + return n; } /* @@ -581,7 +544,7 @@ static void __ic_line_inv_vaddr(phys_addr_t paddr, unsigned long vaddr, #endif /* CONFIG_ARC_HAS_ICACHE */ -noinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op) +static noinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op) { #ifdef CONFIG_ISA_ARCV2 /* @@ -644,7 +607,7 @@ noinline void slc_op_rgn(phys_addr_t paddr, unsigned long sz, const int op) #endif } -noinline void slc_op_line(phys_addr_t paddr, unsigned long sz, const int op) +static __maybe_unused noinline void slc_op_line(phys_addr_t paddr, unsigned long sz, const int op) { #ifdef CONFIG_ISA_ARCV2 /* @@ -1082,7 +1045,7 @@ SYSCALL_DEFINE3(cacheflush, uint32_t, start, uint32_t, sz, uint32_t, flags) * 3. All Caches need to be disabled when setting up IOC to elide any in-flight * Coherency transactions */ -noinline void __init arc_ioc_setup(void) +static noinline void __init arc_ioc_setup(void) { unsigned int ioc_base, mem_sz; @@ -1144,12 +1107,10 @@ noinline void __init arc_ioc_setup(void) * one core suffices for all * - IOC setup / dma callbacks only need to be done once */ -void __init arc_cache_init_master(void) +static noinline void __init arc_cache_init_master(void) { - unsigned int __maybe_unused cpu = smp_processor_id(); - if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) { - struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache; + struct cpuinfo_arc_cache *ic = &ic_info; if (!ic->line_len) panic("cache support enabled but non-existent cache\n"); @@ -1162,14 +1123,14 @@ void __init arc_cache_init_master(void) * In MMU v4 (HS38x) the aliasing icache config uses IVIL/PTAG * pair to provide vaddr/paddr respectively, just as in MMU v3 */ - if (is_isa_arcv2() && ic->alias) + if (is_isa_arcv2() && ic->colors > 1) _cache_line_loop_ic_fn = __cache_line_loop_v3; else _cache_line_loop_ic_fn = __cache_line_loop; } if (IS_ENABLED(CONFIG_ARC_HAS_DCACHE)) { - struct cpuinfo_arc_cache *dc = &cpuinfo_arc700[cpu].dcache; + struct cpuinfo_arc_cache *dc = &dc_info; if (!dc->line_len) panic("cache support enabled but non-existent cache\n"); @@ -1181,14 +1142,13 @@ void __init arc_cache_init_master(void) /* check for D-Cache aliasing on ARCompact: ARCv2 has PIPT */ if (is_isa_arcompact()) { int handled = IS_ENABLED(CONFIG_ARC_CACHE_VIPT_ALIASING); - int num_colors = dc->sz_k/dc->assoc/TO_KB(PAGE_SIZE); - if (dc->alias) { + if (dc->colors > 1) { if (!handled) panic("Enable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); - if (CACHE_COLORS_NUM != num_colors) + if (CACHE_COLORS_NUM != dc->colors) panic("CACHE_COLORS_NUM not optimized for config\n"); - } else if (!dc->alias && handled) { + } else if (handled && dc->colors == 1) { panic("Disable CONFIG_ARC_CACHE_VIPT_ALIASING\n"); } } @@ -1231,9 +1191,6 @@ void __init arc_cache_init_master(void) void __ref arc_cache_init(void) { unsigned int __maybe_unused cpu = smp_processor_id(); - char str[256]; - - pr_info("%s", arc_cache_mumbojumbo(0, str, sizeof(str))); if (!cpu) arc_cache_init_master(); diff --git a/arch/arc/mm/extable.c b/arch/arc/mm/extable.c index 4e14c4244ea2301a9ad3603e5c98c54a72433615..88fa3a4d4906dd4c6f0280d4e1728688cd25e9df 100644 --- a/arch/arc/mm/extable.c +++ b/arch/arc/mm/extable.c @@ -22,14 +22,3 @@ int fixup_exception(struct pt_regs *regs) return 0; } - -#ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE - -unsigned long arc_clear_user_noinline(void __user *to, - unsigned long n) -{ - return __arc_clear_user(to, n); -} -EXPORT_SYMBOL(arc_clear_user_noinline); - -#endif diff --git a/arch/arc/mm/fault.c b/arch/arc/mm/fault.c index f59e722d147f91972ac8cf42b1f77d1a12cca80b..95119a5e7761da9c1bcb815b96f9754f2a9c5e48 100644 --- a/arch/arc/mm/fault.c +++ b/arch/arc/mm/fault.c @@ -13,6 +13,7 @@ #include #include #include +#include #include /* @@ -99,10 +100,10 @@ void do_page_fault(unsigned long address, struct pt_regs *regs) if (faulthandler_disabled() || !mm) goto no_context; - if (regs->ecr_cause & ECR_C_PROTV_STORE) /* ST/EX */ + if (regs->ecr.cause & ECR_C_PROTV_STORE) /* ST/EX */ write = 1; - else if ((regs->ecr_vec == ECR_V_PROTV) && - (regs->ecr_cause == ECR_C_PROTV_INST_FETCH)) + else if ((regs->ecr.vec == ECR_V_PROTV) && + (regs->ecr.cause == ECR_C_PROTV_INST_FETCH)) exec = 1; flags = FAULT_FLAG_DEFAULT; diff --git a/arch/arc/mm/init.c b/arch/arc/mm/init.c index 9f64d729c9f87cc70c9c140439591f323b9bb60f..6a71b23f1383cbcf04882f129f50de98efddb0f8 100644 --- a/arch/arc/mm/init.c +++ b/arch/arc/mm/init.c @@ -15,6 +15,7 @@ #include #include #include +#include #include pgd_t swapper_pg_dir[PTRS_PER_PGD] __aligned(PAGE_SIZE); diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c index 6f40f37e655010654e5490dbb2a37206b85029db..e536b2dcd4b0249d75446b62722bb04b200a9f04 100644 --- a/arch/arc/mm/tlb.c +++ b/arch/arc/mm/tlb.c @@ -18,7 +18,9 @@ /* A copy of the ASID from the PID reg is kept in asid_cache */ DEFINE_PER_CPU(unsigned int, asid_cache) = MM_CTXT_FIRST_CYCLE; -static int __read_mostly pae_exists; +static struct cpuinfo_arc_mmu { + unsigned int ver, pg_sz_k, s_pg_sz_m, pae, sets, ways; +} mmuinfo; /* * Utility Routine to erase a J-TLB entry @@ -131,7 +133,7 @@ static void tlb_entry_insert(unsigned int pd0, phys_addr_t pd1) noinline void local_flush_tlb_all(void) { - struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + struct cpuinfo_arc_mmu *mmu = &mmuinfo; unsigned long flags; unsigned int entry; int num_tlb = mmu->sets * mmu->ways; @@ -389,7 +391,7 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end) /* * Routine to create a TLB entry */ -void create_tlb(struct vm_area_struct *vma, unsigned long vaddr, pte_t *ptep) +static void create_tlb(struct vm_area_struct *vma, unsigned long vaddr, pte_t *ptep) { unsigned long flags; unsigned int asid_or_sasid, rwx; @@ -564,89 +566,64 @@ void local_flush_pmd_tlb_range(struct vm_area_struct *vma, unsigned long start, * the cpuinfo structure for later use. * No Validation is done here, simply read/convert the BCRs */ -void read_decode_mmu_bcr(void) +int arc_mmu_mumbojumbo(int c, char *buf, int len) { - struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; - unsigned int tmp; - struct bcr_mmu_3 { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int ver:8, ways:4, sets:4, res:3, sasid:1, pg_sz:4, - u_itlb:4, u_dtlb:4; -#else - unsigned int u_dtlb:4, u_itlb:4, pg_sz:4, sasid:1, res:3, sets:4, - ways:4, ver:8; -#endif - } *mmu3; - - struct bcr_mmu_4 { -#ifdef CONFIG_CPU_BIG_ENDIAN - unsigned int ver:8, sasid:1, sz1:4, sz0:4, res:2, pae:1, - n_ways:2, n_entry:2, n_super:2, u_itlb:3, u_dtlb:3; -#else - /* DTLB ITLB JES JE JA */ - unsigned int u_dtlb:3, u_itlb:3, n_super:2, n_entry:2, n_ways:2, - pae:1, res:2, sz0:4, sz1:4, sasid:1, ver:8; -#endif - } *mmu4; + struct cpuinfo_arc_mmu *mmu = &mmuinfo; + unsigned int bcr, u_dtlb, u_itlb, sasid; + struct bcr_mmu_3 *mmu3; + struct bcr_mmu_4 *mmu4; + char super_pg[64] = ""; + int n = 0; - tmp = read_aux_reg(ARC_REG_MMU_BCR); - mmu->ver = (tmp >> 24); + bcr = read_aux_reg(ARC_REG_MMU_BCR); + mmu->ver = (bcr >> 24); if (is_isa_arcompact() && mmu->ver == 3) { - mmu3 = (struct bcr_mmu_3 *)&tmp; + mmu3 = (struct bcr_mmu_3 *)&bcr; mmu->pg_sz_k = 1 << (mmu3->pg_sz - 1); mmu->sets = 1 << mmu3->sets; mmu->ways = 1 << mmu3->ways; - mmu->u_dtlb = mmu3->u_dtlb; - mmu->u_itlb = mmu3->u_itlb; - mmu->sasid = mmu3->sasid; + u_dtlb = mmu3->u_dtlb; + u_itlb = mmu3->u_itlb; + sasid = mmu3->sasid; } else { - mmu4 = (struct bcr_mmu_4 *)&tmp; + mmu4 = (struct bcr_mmu_4 *)&bcr; mmu->pg_sz_k = 1 << (mmu4->sz0 - 1); mmu->s_pg_sz_m = 1 << (mmu4->sz1 - 11); mmu->sets = 64 << mmu4->n_entry; mmu->ways = mmu4->n_ways * 2; - mmu->u_dtlb = mmu4->u_dtlb * 4; - mmu->u_itlb = mmu4->u_itlb * 4; - mmu->sasid = mmu4->sasid; - pae_exists = mmu->pae = mmu4->pae; + u_dtlb = mmu4->u_dtlb * 4; + u_itlb = mmu4->u_itlb * 4; + sasid = mmu4->sasid; + mmu->pae = mmu4->pae; } -} -char *arc_mmu_mumbojumbo(int cpu_id, char *buf, int len) -{ - int n = 0; - struct cpuinfo_arc_mmu *p_mmu = &cpuinfo_arc700[cpu_id].mmu; - char super_pg[64] = ""; - - if (p_mmu->s_pg_sz_m) - scnprintf(super_pg, 64, "%dM Super Page %s", - p_mmu->s_pg_sz_m, - IS_USED_CFG(CONFIG_TRANSPARENT_HUGEPAGE)); + if (mmu->s_pg_sz_m) + scnprintf(super_pg, 64, "/%dM%s", + mmu->s_pg_sz_m, + IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) ? " (THP enabled)":""); n += scnprintf(buf + n, len - n, - "MMU [v%x]\t: %dk PAGE, %s, swalk %d lvl, JTLB %d (%dx%d), uDTLB %d, uITLB %d%s%s\n", - p_mmu->ver, p_mmu->pg_sz_k, super_pg, CONFIG_PGTABLE_LEVELS, - p_mmu->sets * p_mmu->ways, p_mmu->sets, p_mmu->ways, - p_mmu->u_dtlb, p_mmu->u_itlb, - IS_AVAIL2(p_mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40)); - - return buf; + "MMU [v%x]\t: %dk%s, swalk %d lvl, JTLB %dx%d, uDTLB %d, uITLB %d%s%s%s\n", + mmu->ver, mmu->pg_sz_k, super_pg, CONFIG_PGTABLE_LEVELS, + mmu->sets, mmu->ways, + u_dtlb, u_itlb, + IS_AVAIL1(sasid, ", SASID"), + IS_AVAIL2(mmu->pae, ", PAE40 ", CONFIG_ARC_HAS_PAE40)); + + return n; } int pae40_exist_but_not_enab(void) { - return pae_exists && !is_pae40_enabled(); + return mmuinfo.pae && !is_pae40_enabled(); } void arc_mmu_init(void) { - struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; - char str[256]; + struct cpuinfo_arc_mmu *mmu = &mmuinfo; int compat = 0; - pr_info("%s", arc_mmu_mumbojumbo(0, str, sizeof(str))); - /* * Can't be done in processor.h due to header include dependencies */ @@ -723,7 +700,7 @@ volatile int dup_pd_silent; /* Be silent abt it or complain (default) */ void do_tlb_overlap_fault(unsigned long cause, unsigned long address, struct pt_regs *regs) { - struct cpuinfo_arc_mmu *mmu = &cpuinfo_arc700[smp_processor_id()].mmu; + struct cpuinfo_arc_mmu *mmu = &mmuinfo; unsigned long flags; int set, n_ways = mmu->ways; diff --git a/arch/arc/plat-axs10x/axs10x.c b/arch/arc/plat-axs10x/axs10x.c index b821df7b0089d50c4da80e50218b898d2296960f..1feb990a56bcfd79c70c96fd860dd36764e6dfcb 100644 --- a/arch/arc/plat-axs10x/axs10x.c +++ b/arch/arc/plat-axs10x/axs10x.c @@ -6,7 +6,6 @@ */ #include -#include #include #include diff --git a/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi b/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi index 091ba310053ebc6fb99424c136b7794efc4721b4..d2d516d113baa7a33b3dba06ccede72623369e80 100644 --- a/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi +++ b/arch/arm/boot/dts/ti/omap/motorola-mapphone-common.dtsi @@ -614,12 +614,12 @@ /* Configure pwm clock source for timers 8 & 9 */ &timer8 { assigned-clocks = <&abe_clkctrl OMAP4_TIMER8_CLKCTRL 24>; - assigned-clock-parents = <&sys_clkin_ck>; + assigned-clock-parents = <&sys_32k_ck>; }; &timer9 { assigned-clocks = <&l4_per_clkctrl OMAP4_TIMER9_CLKCTRL 24>; - assigned-clock-parents = <&sys_clkin_ck>; + assigned-clock-parents = <&sys_32k_ck>; }; /* @@ -640,6 +640,7 @@ &uart3 { interrupts-extended = <&wakeupgen GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH &omap4_pmx_core 0x17c>; + overrun-throttle-ms = <500>; }; &uart4 { diff --git a/arch/arm/boot/dts/ti/omap/omap3-cpu-thermal.dtsi b/arch/arm/boot/dts/ti/omap/omap3-cpu-thermal.dtsi index 0da759f8e2c2df351c44a7c3f4e87ef5a6a6fcf0..7dd2340bc5e4556a9c840209a1695ccc2279a024 100644 --- a/arch/arm/boot/dts/ti/omap/omap3-cpu-thermal.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap3-cpu-thermal.dtsi @@ -12,8 +12,7 @@ cpu_thermal: cpu-thermal { polling-delay = <1000>; /* milliseconds */ coefficients = <0 20000>; - /* sensor ID */ - thermal-sensors = <&bandgap 0>; + thermal-sensors = <&bandgap>; cpu_trips: trips { cpu_alert0: cpu_alert { diff --git a/arch/arm/boot/dts/ti/omap/omap4-cpu-thermal.dtsi b/arch/arm/boot/dts/ti/omap/omap4-cpu-thermal.dtsi index 801b4f10350c126c53baa248cd4699cdde6f7bcd..d484ec1e4fd86b7c8f28a0de47942fcb3bafe439 100644 --- a/arch/arm/boot/dts/ti/omap/omap4-cpu-thermal.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap4-cpu-thermal.dtsi @@ -12,7 +12,10 @@ cpu_thermal: cpu_thermal { polling-delay-passive = <250>; /* milliseconds */ polling-delay = <1000>; /* milliseconds */ - /* sensor ID */ + /* + * See 44xx files for single sensor addressing, omap5 and dra7 need + * also sensor ID for addressing. + */ thermal-sensors = <&bandgap 0>; cpu_trips: trips { diff --git a/arch/arm/boot/dts/ti/omap/omap443x.dtsi b/arch/arm/boot/dts/ti/omap/omap443x.dtsi index 238aceb799f89584c5d527e869e090ed0e781f5d..2104170fe2cd7080b683c362f36693249bcefb43 100644 --- a/arch/arm/boot/dts/ti/omap/omap443x.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap443x.dtsi @@ -69,6 +69,7 @@ }; &cpu_thermal { + thermal-sensors = <&bandgap>; coefficients = <0 20000>; }; diff --git a/arch/arm/boot/dts/ti/omap/omap4460.dtsi b/arch/arm/boot/dts/ti/omap/omap4460.dtsi index 1b27a862ae810a5e7100e7632f66e324e6c3655e..a6764750d4476c0910a6eddf5c47268a0daebaba 100644 --- a/arch/arm/boot/dts/ti/omap/omap4460.dtsi +++ b/arch/arm/boot/dts/ti/omap/omap4460.dtsi @@ -79,6 +79,7 @@ }; &cpu_thermal { + thermal-sensors = <&bandgap>; coefficients = <348 (-9301)>; }; diff --git a/arch/arm/configs/dram_0x00000000.config b/arch/arm/configs/dram_0x00000000.config index db96dcb420cebec5b5f4594e78506177bc3d3af3..8803a0f58343c8f2f41bc9fde54cdcb068df803e 100644 --- a/arch/arm/configs/dram_0x00000000.config +++ b/arch/arm/configs/dram_0x00000000.config @@ -1 +1,2 @@ +# Help: DRAM base at 0x00000000 CONFIG_DRAM_BASE=0x00000000 diff --git a/arch/arm/configs/dram_0xc0000000.config b/arch/arm/configs/dram_0xc0000000.config index 343d5333d973d502515c55a1015fb4c78298face..aab8f864686b52c5bb24824c2db94667d7f4ada3 100644 --- a/arch/arm/configs/dram_0xc0000000.config +++ b/arch/arm/configs/dram_0xc0000000.config @@ -1 +1,2 @@ +# Help: DRAM base at 0xc0000000 CONFIG_DRAM_BASE=0xc0000000 diff --git a/arch/arm/configs/dram_0xd0000000.config b/arch/arm/configs/dram_0xd0000000.config index 61ba7045f8a139ef5321224e92b226f8725bb53b..4aabce4ea3d46505a327f34f7bc6c1845fe0d2fa 100644 --- a/arch/arm/configs/dram_0xd0000000.config +++ b/arch/arm/configs/dram_0xd0000000.config @@ -1 +1,2 @@ +# Help: DRAM base at 0xd0000000 CONFIG_DRAM_BASE=0xd0000000 diff --git a/arch/arm/configs/lpae.config b/arch/arm/configs/lpae.config index a6d6f7ab3c01a130d9086af74290fdd37bd3bc82..1ab94da8345d32906bae6ba2bb24ce9a04e751b0 100644 --- a/arch/arm/configs/lpae.config +++ b/arch/arm/configs/lpae.config @@ -1,2 +1,3 @@ +# Help: Enable Large Physical Address Extension mode CONFIG_ARM_LPAE=y CONFIG_VMSPLIT_2G=y diff --git a/arch/arm/include/asm/arm_pmuv3.h b/arch/arm/include/asm/arm_pmuv3.h index f3cd04ff022df106f17ce676ede60d647cb3cf20..72529f5e2bed9598196b6bcb2d559269a2fc0809 100644 --- a/arch/arm/include/asm/arm_pmuv3.h +++ b/arch/arm/include/asm/arm_pmuv3.h @@ -227,6 +227,8 @@ static inline bool kvm_set_pmuserenr(u64 val) return false; } +static inline void kvm_vcpu_pmu_resync_el0(void) {} + /* PMU Version in DFR Register */ #define ARMV8_PMU_DFR_VER_NI 0 #define ARMV8_PMU_DFR_VER_V3P4 0x5 diff --git a/arch/arm/include/asm/hardware/locomo.h b/arch/arm/include/asm/hardware/locomo.h index 246a3de25931db6d65259933fee7d50169a81f2f..aaaedafef7cce6ce0caefc726cf229aa510e15c2 100644 --- a/arch/arm/include/asm/hardware/locomo.h +++ b/arch/arm/include/asm/hardware/locomo.h @@ -195,7 +195,7 @@ struct locomo_driver { #define LOCOMO_DRIVER_NAME(_ldev) ((_ldev)->dev.driver->name) -void locomo_lcd_power(struct locomo_dev *, int, unsigned int); +extern void locomolcd_power(int on); int locomo_driver_register(struct locomo_driver *); void locomo_driver_unregister(struct locomo_driver *); diff --git a/arch/arm/include/asm/ide.h b/arch/arm/include/asm/ide.h deleted file mode 100644 index a81e0b0d6747aa2fc56066fe972d1d82df2d714f..0000000000000000000000000000000000000000 --- a/arch/arm/include/asm/ide.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * arch/arm/include/asm/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the ARM architecture specific IDE code. - */ - -#ifndef __ASMARM_IDE_H -#define __ASMARM_IDE_H - -#ifdef __KERNEL__ - -#define __ide_mm_insw(port,addr,len) readsw(port,addr,len) -#define __ide_mm_insl(port,addr,len) readsl(port,addr,len) -#define __ide_mm_outsw(port,addr,len) writesw(port,addr,len) -#define __ide_mm_outsl(port,addr,len) writesl(port,addr,len) - -#endif /* __KERNEL__ */ - -#endif /* __ASMARM_IDE_H */ diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c index f57802f3ee3adc62d234f91e0c6d771a8c0c6195..37b168119fe43e2e960a4e6aea7b50c4a4c2d25c 100644 --- a/arch/arm/mach-omap2/pm44xx.c +++ b/arch/arm/mach-omap2/pm44xx.c @@ -99,7 +99,7 @@ static int omap4_pm_suspend(void) * possible causes. * http://www.spinics.net/lists/arm-kernel/msg218641.html */ - pr_warn("A possible cause could be an old bootloader - try u-boot >= v2012.07\n"); + pr_debug("A possible cause could be an old bootloader - try u-boot >= v2012.07\n"); } else { pr_info("Successfully put all powerdomains to target state\n"); } @@ -257,7 +257,7 @@ int __init omap4_pm_init(void) * http://www.spinics.net/lists/arm-kernel/msg218641.html */ if (cpu_is_omap44xx()) - pr_warn("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n"); + pr_debug("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n"); ret = pwrdm_for_each(pwrdms_setup, NULL); if (ret) { diff --git a/arch/arm/mach-sa1100/include/mach/collie.h b/arch/arm/mach-sa1100/include/mach/collie.h index b7bc23ffd3c696284d0d75423e747d7e3879f771..c95273c9567bb944c153c39417dbcf624c97fa3b 100644 --- a/arch/arm/mach-sa1100/include/mach/collie.h +++ b/arch/arm/mach-sa1100/include/mach/collie.h @@ -16,8 +16,6 @@ #include "hardware.h" /* Gives GPIO_MAX */ -extern void locomolcd_power(int on); - #define COLLIE_SCOOP_GPIO_BASE (GPIO_MAX + 1) #define COLLIE_GPIO_CHARGE_ON (COLLIE_SCOOP_GPIO_BASE + 0) #define COLLIE_SCP_DIAG_BOOT1 SCOOP_GPCR_PA12 diff --git a/arch/arm/mm/cache-uniphier.c b/arch/arm/mm/cache-uniphier.c index ff288145850432984ba116cc39bf79b2a7e2cea7..84a2f17ff32d09dd282ad12e0d8be5fe01510d70 100644 --- a/arch/arm/mm/cache-uniphier.c +++ b/arch/arm/mm/cache-uniphier.c @@ -58,11 +58,13 @@ ((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_RANGE) /** - * uniphier_cache_data - UniPhier outer cache specific data + * struct uniphier_cache_data - UniPhier outer cache specific data * * @ctrl_base: virtual base address of control registers * @rev_base: virtual base address of revision registers * @op_base: virtual base address of operation registers + * @way_ctrl_base: virtual address of the way control registers for this + * SoC revision * @way_mask: each bit specifies if the way is present * @nsets: number of associativity sets * @line_size: line size in bytes diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 7d59765aef220b429afdf7a8d3a2a6ec0923c0b2..c392e18f1e43178fd00a177033baba203e25e931 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -207,7 +207,7 @@ static void xen_power_off(void) static irqreturn_t xen_arm_callback(int irq, void *arg) { - xen_hvm_evtchn_do_upcall(); + xen_evtchn_do_upcall(); return IRQ_HANDLED; } diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index b10515c0200b4044cf37a98cbf83b743664c1b65..78f20e6327120ea439ebad77de7611eea59cca8d 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1037,6 +1037,19 @@ config ARM64_ERRATUM_2645198 If unsure, say Y. +config ARM64_ERRATUM_2966298 + bool "Cortex-A520: 2966298: workaround for speculatively executed unprivileged load" + default y + help + This option adds the workaround for ARM Cortex-A520 erratum 2966298. + + On an affected Cortex-A520 core, a speculatively executed unprivileged + load might leak data from a privileged level via a cache side channel. + + Work around this problem by executing a TLBI before returning to EL0. + + If unsure, say Y. + config CAVIUM_ERRATUM_22375 bool "Cavium erratum 22375, 24313" default y diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile index c6872b7e9471d1aec3e0794abfe854a78a4078a0..89aee6c925760d2839cd99c446131b120827678b 100644 --- a/arch/arm64/boot/dts/freescale/Makefile +++ b/arch/arm64/boot/dts/freescale/Makefile @@ -66,6 +66,7 @@ dtb-$(CONFIG_ARCH_MXC) += imx8mm-mx8menlo.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-nitrogen-r2.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-phg.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-phyboard-polis-rdk.dtb +dtb-$(CONFIG_ARCH_MXC) += imx8mm-prt8mm.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-tqma8mqml-mba8mx.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-var-som-symphony.dtb dtb-$(CONFIG_ARCH_MXC) += imx8mm-venice-gw71xx-0x.dtb diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi index e31ab8b4f54f3f626463f126ecede8824c284e81..a882c86ec3132b88479a13720ee7cb92cca7aac1 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi @@ -26,7 +26,7 @@ port { hdmi_connector_in: endpoint { - remote-endpoint = <&adv7533_out>; + remote-endpoint = <&adv7535_out>; }; }; }; @@ -72,6 +72,13 @@ enable-active-high; }; + reg_vddext_3v3: regulator-vddext-3v3 { + compatible = "regulator-fixed"; + regulator-name = "VDDEXT_3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + backlight: backlight { compatible = "pwm-backlight"; pwms = <&pwm1 0 5000000 0>; @@ -317,15 +324,16 @@ hdmi@3d { compatible = "adi,adv7535"; - reg = <0x3d>, <0x3c>, <0x3e>, <0x3f>; - reg-names = "main", "cec", "edid", "packet"; + reg = <0x3d>; + interrupt-parent = <&gpio1>; + interrupts = <9 IRQ_TYPE_EDGE_FALLING>; adi,dsi-lanes = <4>; - - adi,input-depth = <8>; - adi,input-colorspace = "rgb"; - adi,input-clock = "1x"; - adi,input-style = <1>; - adi,input-justification = "evenly"; + avdd-supply = <&buck5_reg>; + dvdd-supply = <&buck5_reg>; + pvdd-supply = <&buck5_reg>; + a2vdd-supply = <&buck5_reg>; + v3p3-supply = <®_vddext_3v3>; + v1p2-supply = <&buck5_reg>; ports { #address-cells = <1>; @@ -334,7 +342,7 @@ port@0 { reg = <0>; - adv7533_in: endpoint { + adv7535_in: endpoint { remote-endpoint = <&dsi_out>; }; }; @@ -342,7 +350,7 @@ port@1 { reg = <1>; - adv7533_out: endpoint { + adv7535_out: endpoint { remote-endpoint = <&hdmi_connector_in>; }; }; @@ -448,7 +456,7 @@ reg = <1>; dsi_out: endpoint { - remote-endpoint = <&adv7533_in>; + remote-endpoint = <&adv7535_in>; data-lanes = <1 2 3 4>; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts b/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts index 06e91297fb163b8a8488f3f2c0bf419bcf90ba5d..acd265d8b58ed9c8399d63794caacc1f229732d1 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts +++ b/arch/arm64/boot/dts/freescale/imx8mp-beacon-kit.dts @@ -381,9 +381,10 @@ &sai3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_sai3>; - assigned-clocks = <&clk IMX8MP_CLK_SAI3>; + assigned-clocks = <&clk IMX8MP_CLK_SAI3>, + <&clk IMX8MP_AUDIO_PLL2> ; assigned-clock-parents = <&clk IMX8MP_AUDIO_PLL2_OUT>; - assigned-clock-rates = <12288000>; + assigned-clock-rates = <12288000>, <361267200>; fsl,sai-mclk-direction-output; status = "okay"; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi index 6f2f50e1639c372e2355b41611b54a703355e380..83d907294fbc73ede28c54672ed05d96c9a8ff2d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi @@ -790,6 +790,12 @@ reg = ; clocks = <&clk IMX8MP_CLK_AUDIO_ROOT>, <&clk IMX8MP_CLK_AUDIO_AXI>; + assigned-clocks = <&clk IMX8MP_CLK_AUDIO_AHB>, + <&clk IMX8MP_CLK_AUDIO_AXI_SRC>; + assigned-clock-parents = <&clk IMX8MP_SYS_PLL1_800M>, + <&clk IMX8MP_SYS_PLL1_800M>; + assigned-clock-rates = <400000000>, + <600000000>; }; pgc_gpu2d: power-domain@6 { diff --git a/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi b/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi index 1c71c08becde8a939b22c35d51a8212321569220..f6e422dc2663e99702e3a305d7213e6547aa79e5 100644 --- a/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi +++ b/arch/arm64/boot/dts/freescale/imx93-tqma9352.dtsi @@ -81,7 +81,7 @@ &gpio1 { pmic-irq-hog { gpio-hog; - gpios = <2 GPIO_ACTIVE_LOW>; + gpios = <3 GPIO_ACTIVE_LOW>; input; line-name = "PMIC_IRQ#"; }; diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi index 4dbbf8fdab75857d7a9f889c3222900331c81174..a9e52b50c8c48bfc2162c17e98afaec96bd4ca47 100644 --- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi @@ -2957,7 +2957,7 @@ clock-names = "merge","merge_async"; power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>; mediatek,gce-client-reg = <&gce0 SUBSYS_1c10XXXX 0xc000 0x1000>; - mediatek,merge-mute = <1>; + mediatek,merge-mute; resets = <&vdosys1 MT8195_VDOSYS1_SW0_RST_B_MERGE0_DL_ASYNC>; }; @@ -2970,7 +2970,7 @@ clock-names = "merge","merge_async"; power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>; mediatek,gce-client-reg = <&gce0 SUBSYS_1c10XXXX 0xd000 0x1000>; - mediatek,merge-mute = <1>; + mediatek,merge-mute; resets = <&vdosys1 MT8195_VDOSYS1_SW0_RST_B_MERGE1_DL_ASYNC>; }; @@ -2983,7 +2983,7 @@ clock-names = "merge","merge_async"; power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>; mediatek,gce-client-reg = <&gce0 SUBSYS_1c10XXXX 0xe000 0x1000>; - mediatek,merge-mute = <1>; + mediatek,merge-mute; resets = <&vdosys1 MT8195_VDOSYS1_SW0_RST_B_MERGE2_DL_ASYNC>; }; @@ -2996,7 +2996,7 @@ clock-names = "merge","merge_async"; power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>; mediatek,gce-client-reg = <&gce0 SUBSYS_1c10XXXX 0xf000 0x1000>; - mediatek,merge-mute = <1>; + mediatek,merge-mute; resets = <&vdosys1 MT8195_VDOSYS1_SW0_RST_B_MERGE3_DL_ASYNC>; }; @@ -3009,7 +3009,7 @@ clock-names = "merge","merge_async"; power-domains = <&spm MT8195_POWER_DOMAIN_VDOSYS1>; mediatek,gce-client-reg = <&gce0 SUBSYS_1c11XXXX 0x0000 0x1000>; - mediatek,merge-fifo-en = <1>; + mediatek,merge-fifo-en; resets = <&vdosys1 MT8195_VDOSYS1_SW0_RST_B_MERGE4_DL_ASYNC>; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index a7c3020a5de491bf8027f4789eab9d411c371781..06c53000bb74d47634a5460f2e18f244154e9f8a 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -3958,7 +3958,7 @@ pdc: interrupt-controller@b220000 { compatible = "qcom,sm8150-pdc", "qcom,pdc"; - reg = <0 0x0b220000 0 0x400>; + reg = <0 0x0b220000 0 0x30000>; qcom,pdc-ranges = <0 480 94>, <94 609 31>, <125 63 1>; #interrupt-cells = <2>; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 5315789f48682ae684e319361cecef5528a35aec..a789119e6483b50000e6a3a6a1aeff5fb6eabe6d 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -636,6 +636,7 @@ CONFIG_POWER_RESET_MSM=y CONFIG_POWER_RESET_QCOM_PON=m CONFIG_POWER_RESET_XGENE=y CONFIG_POWER_RESET_SYSCON=y +CONFIG_POWER_RESET_SYSCON_POWEROFF=y CONFIG_SYSCON_REBOOT_MODE=y CONFIG_NVMEM_REBOOT_MODE=m CONFIG_BATTERY_SBS=m @@ -1175,7 +1176,6 @@ CONFIG_COMMON_CLK_S2MPS11=y CONFIG_COMMON_CLK_PWM=y CONFIG_COMMON_CLK_RS9_PCIE=y CONFIG_COMMON_CLK_VC5=y -CONFIG_COMMON_CLK_NPCM8XX=y CONFIG_COMMON_CLK_BD718XX=m CONFIG_CLK_RASPBERRYPI=m CONFIG_CLK_IMX8MM=y diff --git a/arch/arm64/configs/virt.config b/arch/arm64/configs/virt.config index 6865d54e68f8880eb86705b1ce8fc1c446200d24..c47c36f8f67b7c4b4c9e548d5f15f1486bf9c8d2 100644 --- a/arch/arm64/configs/virt.config +++ b/arch/arm64/configs/virt.config @@ -1,3 +1,4 @@ +# Help: Virtualization guest # # Base options for platforms # diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 4d537d56eb847569d286f232186567ff253610ca..6792a1f83f2ad400c98efa360f91ae0e802b1c7c 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -9,6 +9,7 @@ #ifndef _ASM_ACPI_H #define _ASM_ACPI_H +#include #include #include #include @@ -44,6 +45,24 @@ #define ACPI_MADT_GICC_TRBE (offsetof(struct acpi_madt_generic_interrupt, \ trbe_interrupt) + sizeof(u16)) +/* + * Arm® Functional Fixed Hardware Specification Version 1.2. + * Table 2: Arm Architecture context loss flags + */ +#define CPUIDLE_CORE_CTXT BIT(0) /* Core context Lost */ + +static inline unsigned int arch_get_idle_state_flags(u32 arch_flags) +{ + if (arch_flags & CPUIDLE_CORE_CTXT) + return CPUIDLE_FLAG_TIMER_STOP; + + return 0; +} +#define arch_get_idle_state_flags arch_get_idle_state_flags + +#define CPUIDLE_TRACE_CTXT BIT(1) /* Trace context loss */ +#define CPUIDLE_GICR_CTXT BIT(2) /* GICR */ +#define CPUIDLE_GICD_CTXT BIT(3) /* GICD */ /* Basic configuration for ACPI */ #ifdef CONFIG_ACPI diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 96e50227f940ecbde381ccc422d9d8c1433e5e2c..5bba393760557d0b390ff39927de19d861af006d 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -663,7 +663,7 @@ static inline bool supports_clearbhb(int scope) isar2 = read_sanitised_ftr_reg(SYS_ID_AA64ISAR2_EL1); return cpuid_feature_extract_unsigned_field(isar2, - ID_AA64ISAR2_EL1_BC_SHIFT); + ID_AA64ISAR2_EL1_CLRBHB_SHIFT); } const struct cpumask *system_32bit_el0_cpumask(void); diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 5f6f84837a490375c8175626e3fec40057bb56d9..74d00feb62f03e7e2ea609be70a008989623bba4 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -79,6 +79,7 @@ #define ARM_CPU_PART_CORTEX_A78AE 0xD42 #define ARM_CPU_PART_CORTEX_X1 0xD44 #define ARM_CPU_PART_CORTEX_A510 0xD46 +#define ARM_CPU_PART_CORTEX_A520 0xD80 #define ARM_CPU_PART_CORTEX_A710 0xD47 #define ARM_CPU_PART_CORTEX_A715 0xD4D #define ARM_CPU_PART_CORTEX_X2 0xD48 @@ -148,6 +149,7 @@ #define MIDR_CORTEX_A78AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78AE) #define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) #define MIDR_CORTEX_A510 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A510) +#define MIDR_CORTEX_A520 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A520) #define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) #define MIDR_CORTEX_A715 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A715) #define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h index f482b994c608ccf3cb33d867c2db038196f9fde6..bcd5622aa09686a9d256af624b2a92abc3e6fee0 100644 --- a/arch/arm64/include/asm/efi.h +++ b/arch/arm64/include/asm/efi.h @@ -156,4 +156,6 @@ static inline void efi_capsule_flush_cache_range(void *addr, int size) efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f); +void efi_icache_sync(unsigned long start, unsigned long end); + #endif /* _ASM_EFI_H */ diff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h index f43a38ac17799d26317ab985daccd35978b2b2f3..2ddc33d93b13b28c39d84d4253150d32c61888bd 100644 --- a/arch/arm64/include/asm/hugetlb.h +++ b/arch/arm64/include/asm/hugetlb.h @@ -28,7 +28,7 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags); #define arch_make_huge_pte arch_make_huge_pte #define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT extern void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pte); + pte_t *ptep, pte_t pte, unsigned long sz); #define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS extern int huge_ptep_set_access_flags(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index 58e5eb27da68d67418ab54c177ac033a264a4aad..5882b2415596416403b8560fef9f3f540d9ed73a 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -18,10 +18,19 @@ #define HCR_DCT (UL(1) << 57) #define HCR_ATA_SHIFT 56 #define HCR_ATA (UL(1) << HCR_ATA_SHIFT) +#define HCR_TTLBOS (UL(1) << 55) +#define HCR_TTLBIS (UL(1) << 54) +#define HCR_ENSCXT (UL(1) << 53) +#define HCR_TOCU (UL(1) << 52) #define HCR_AMVOFFEN (UL(1) << 51) +#define HCR_TICAB (UL(1) << 50) #define HCR_TID4 (UL(1) << 49) #define HCR_FIEN (UL(1) << 47) #define HCR_FWB (UL(1) << 46) +#define HCR_NV2 (UL(1) << 45) +#define HCR_AT (UL(1) << 44) +#define HCR_NV1 (UL(1) << 43) +#define HCR_NV (UL(1) << 42) #define HCR_API (UL(1) << 41) #define HCR_APK (UL(1) << 40) #define HCR_TEA (UL(1) << 37) @@ -89,7 +98,6 @@ HCR_BSU_IS | HCR_FB | HCR_TACR | \ HCR_AMO | HCR_SWIO | HCR_TIDCP | HCR_RW | HCR_TLOR | \ HCR_FMO | HCR_IMO | HCR_PTW | HCR_TID3) -#define HCR_VIRT_EXCP_MASK (HCR_VSE | HCR_VI | HCR_VF) #define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA) #define HCR_HOST_NVHE_PROTECTED_FLAGS (HCR_HOST_NVHE_FLAGS | HCR_TSC) #define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H) @@ -324,6 +332,47 @@ BIT(18) | \ GENMASK(16, 15)) +/* + * FGT register definitions + * + * RES0 and polarity masks as of DDI0487J.a, to be updated as needed. + * We're not using the generated masks as they are usually ahead of + * the published ARM ARM, which we use as a reference. + * + * Once we get to a point where the two describe the same thing, we'll + * merge the definitions. One day. + */ +#define __HFGRTR_EL2_RES0 (GENMASK(63, 56) | GENMASK(53, 51)) +#define __HFGRTR_EL2_MASK GENMASK(49, 0) +#define __HFGRTR_EL2_nMASK (GENMASK(55, 54) | BIT(50)) + +#define __HFGWTR_EL2_RES0 (GENMASK(63, 56) | GENMASK(53, 51) | \ + BIT(46) | BIT(42) | BIT(40) | BIT(28) | \ + GENMASK(26, 25) | BIT(21) | BIT(18) | \ + GENMASK(15, 14) | GENMASK(10, 9) | BIT(2)) +#define __HFGWTR_EL2_MASK GENMASK(49, 0) +#define __HFGWTR_EL2_nMASK (GENMASK(55, 54) | BIT(50)) + +#define __HFGITR_EL2_RES0 GENMASK(63, 57) +#define __HFGITR_EL2_MASK GENMASK(54, 0) +#define __HFGITR_EL2_nMASK GENMASK(56, 55) + +#define __HDFGRTR_EL2_RES0 (BIT(49) | BIT(42) | GENMASK(39, 38) | \ + GENMASK(21, 20) | BIT(8)) +#define __HDFGRTR_EL2_MASK ~__HDFGRTR_EL2_nMASK +#define __HDFGRTR_EL2_nMASK GENMASK(62, 59) + +#define __HDFGWTR_EL2_RES0 (BIT(63) | GENMASK(59, 58) | BIT(51) | BIT(47) | \ + BIT(43) | GENMASK(40, 38) | BIT(34) | BIT(30) | \ + BIT(22) | BIT(9) | BIT(6)) +#define __HDFGWTR_EL2_MASK ~__HDFGWTR_EL2_nMASK +#define __HDFGWTR_EL2_nMASK GENMASK(62, 60) + +/* Similar definitions for HCRX_EL2 */ +#define __HCRX_EL2_RES0 (GENMASK(63, 16) | GENMASK(13, 12)) +#define __HCRX_EL2_MASK (0) +#define __HCRX_EL2_nMASK (GENMASK(15, 14) | GENMASK(4, 0)) + /* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */ #define HPFAR_MASK (~UL(0xf)) /* diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 24e28bb2d95b609dfb5ef3e25ffdadb4e12251fe..24b5e6b23417b2799f7e51085c063b98d6837de9 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -70,6 +70,7 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa, __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_ipa_nsh, __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid, + __KVM_HOST_SMCCC_FUNC___kvm_tlb_flush_vmid_range, __KVM_HOST_SMCCC_FUNC___kvm_flush_cpu_context, __KVM_HOST_SMCCC_FUNC___kvm_timer_set_cntvoff, __KVM_HOST_SMCCC_FUNC___vgic_v3_read_vmcr, @@ -229,6 +230,8 @@ extern void __kvm_tlb_flush_vmid_ipa(struct kvm_s2_mmu *mmu, phys_addr_t ipa, extern void __kvm_tlb_flush_vmid_ipa_nsh(struct kvm_s2_mmu *mmu, phys_addr_t ipa, int level); +extern void __kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, + phys_addr_t start, unsigned long pages); extern void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu); extern void __kvm_timer_set_cntvoff(u64 cntvoff); diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index d3dd05bbfe23fc07ccd6ce49786b7ad22fc35bb6..af06ccb7ee343304535c30b8855d6de8d8b1f4fc 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -49,6 +49,7 @@ #define KVM_REQ_RELOAD_GICv4 KVM_ARCH_REQ(4) #define KVM_REQ_RELOAD_PMU KVM_ARCH_REQ(5) #define KVM_REQ_SUSPEND KVM_ARCH_REQ(6) +#define KVM_REQ_RESYNC_PMU_EL0 KVM_ARCH_REQ(7) #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \ KVM_DIRTY_LOG_INITIALLY_SET) @@ -380,6 +381,7 @@ enum vcpu_sysreg { CPTR_EL2, /* Architectural Feature Trap Register (EL2) */ HSTR_EL2, /* Hypervisor System Trap Register */ HACR_EL2, /* Hypervisor Auxiliary Control Register */ + HCRX_EL2, /* Extended Hypervisor Configuration Register */ TTBR0_EL2, /* Translation Table Base Register 0 (EL2) */ TTBR1_EL2, /* Translation Table Base Register 1 (EL2) */ TCR_EL2, /* Translation Control Register (EL2) */ @@ -400,6 +402,11 @@ enum vcpu_sysreg { TPIDR_EL2, /* EL2 Software Thread ID Register */ CNTHCTL_EL2, /* Counter-timer Hypervisor Control register */ SP_EL2, /* EL2 Stack Pointer */ + HFGRTR_EL2, + HFGWTR_EL2, + HFGITR_EL2, + HDFGRTR_EL2, + HDFGWTR_EL2, CNTHP_CTL_EL2, CNTHP_CVAL_EL2, CNTHV_CTL_EL2, @@ -567,8 +574,7 @@ struct kvm_vcpu_arch { /* Cache some mmu pages needed inside spinlock regions */ struct kvm_mmu_memory_cache mmu_page_cache; - /* Target CPU and feature flags */ - int target; + /* feature flags */ DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES); /* Virtual SError ESR to restore when HCR_EL2.VSE is set */ @@ -669,6 +675,8 @@ struct kvm_vcpu_arch { #define VCPU_SVE_FINALIZED __vcpu_single_flag(cflags, BIT(1)) /* PTRAUTH exposed to guest */ #define GUEST_HAS_PTRAUTH __vcpu_single_flag(cflags, BIT(2)) +/* KVM_ARM_VCPU_INIT completed */ +#define VCPU_INITIALIZED __vcpu_single_flag(cflags, BIT(3)) /* Exception pending */ #define PENDING_EXCEPTION __vcpu_single_flag(iflags, BIT(0)) @@ -899,7 +907,6 @@ struct kvm_vcpu_stat { u64 exits; }; -void kvm_vcpu_preferred_target(struct kvm_vcpu_init *init); unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu); int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices); int kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); @@ -967,8 +974,6 @@ void kvm_arm_resume_guest(struct kvm *kvm); #define kvm_call_hyp_nvhe(f, ...) f(__VA_ARGS__) #endif /* __KVM_NVHE_HYPERVISOR__ */ -void force_vm_exit(const cpumask_t *mask); - int handle_exit(struct kvm_vcpu *vcpu, int exception_index); void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index); @@ -983,6 +988,7 @@ int kvm_handle_cp10_id(struct kvm_vcpu *vcpu); void kvm_reset_sys_regs(struct kvm_vcpu *vcpu); int __init kvm_sys_reg_table_init(void); +int __init populate_nv_trap_config(void); bool lock_all_vcpus(struct kvm *kvm); void unlock_all_vcpus(struct kvm *kvm); @@ -1049,8 +1055,6 @@ static inline bool kvm_system_needs_idmapped_vectors(void) return cpus_have_const_cap(ARM64_SPECTRE_V3A); } -void kvm_arm_vcpu_ptrauth_trap(struct kvm_vcpu *vcpu); - static inline void kvm_arch_sync_events(struct kvm *kvm) {} static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {} @@ -1113,13 +1117,15 @@ int __init kvm_set_ipa_limit(void); #define __KVM_HAVE_ARCH_VM_ALLOC struct kvm *kvm_arch_alloc_vm(void); +#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS + +#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE + static inline bool kvm_vm_is_protected(struct kvm *kvm) { return false; } -void kvm_init_protected_traps(struct kvm_vcpu *vcpu); - int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature); bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index b7238c72a04cfaee4cdd4510e408e5f0a6c4df6f..66efd67ea7e8d6dd98dc6088a6004fdb646df2a3 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -118,7 +118,7 @@ void deactivate_traps_vhe_put(struct kvm_vcpu *vcpu); u64 __guest_enter(struct kvm_vcpu *vcpu); -bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt); +bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt, u32 func_id); #ifdef __KVM_NVHE_HYPERVISOR__ void __noreturn __hyp_do_panic(struct kvm_cpu_context *host_ctxt, u64 spsr, diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index 0e1e1ab17b4d6e3e858684ff1b602d7b3516ae34..96a80e8f62263eb30d04d8e0107807b75199f18a 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -168,6 +168,7 @@ int create_hyp_io_mappings(phys_addr_t phys_addr, size_t size, void __iomem **haddr); int create_hyp_exec_mappings(phys_addr_t phys_addr, size_t size, void **haddr); +int create_hyp_stack(phys_addr_t phys_addr, unsigned long *haddr); void __init free_hyp_pgds(void); void stage2_unmap_vm(struct kvm *kvm); diff --git a/arch/arm64/include/asm/kvm_nested.h b/arch/arm64/include/asm/kvm_nested.h index 8fb67f032fd1918b8145850483c01e0e350983a0..fa23cc9c2adc77fb278128eb50a401e7eb3d9201 100644 --- a/arch/arm64/include/asm/kvm_nested.h +++ b/arch/arm64/include/asm/kvm_nested.h @@ -11,6 +11,8 @@ static inline bool vcpu_has_nv(const struct kvm_vcpu *vcpu) test_bit(KVM_ARM_VCPU_HAS_EL2, vcpu->arch.features)); } +extern bool __check_nv_sr_forward(struct kvm_vcpu *vcpu); + struct sys_reg_params; struct sys_reg_desc; diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h index 929d355eae0acbe73cce3982c3278a82d294043d..d3e354bb8351d7aa9762155e18cb49cffee938e0 100644 --- a/arch/arm64/include/asm/kvm_pgtable.h +++ b/arch/arm64/include/asm/kvm_pgtable.h @@ -746,4 +746,14 @@ enum kvm_pgtable_prot kvm_pgtable_stage2_pte_prot(kvm_pte_t pte); * kvm_pgtable_prot format. */ enum kvm_pgtable_prot kvm_pgtable_hyp_pte_prot(kvm_pte_t pte); + +/** + * kvm_tlb_flush_vmid_range() - Invalidate/flush a range of TLB entries + * + * @mmu: Stage-2 KVM MMU struct + * @addr: The base Intermediate physical address from which to invalidate + * @size: Size of the range from the base to invalidate + */ +void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, + phys_addr_t addr, size_t size); #endif /* __ARM64_KVM_PGTABLE_H__ */ diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 16464bf9a8aa07b4d704d8380572e05078114152..38296579a4fd54661ea7ffaa47c8ea89bffdea8d 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -124,6 +124,37 @@ #define SYS_DC_CIGSW sys_insn(1, 0, 7, 14, 4) #define SYS_DC_CIGDSW sys_insn(1, 0, 7, 14, 6) +#define SYS_IC_IALLUIS sys_insn(1, 0, 7, 1, 0) +#define SYS_IC_IALLU sys_insn(1, 0, 7, 5, 0) +#define SYS_IC_IVAU sys_insn(1, 3, 7, 5, 1) + +#define SYS_DC_IVAC sys_insn(1, 0, 7, 6, 1) +#define SYS_DC_IGVAC sys_insn(1, 0, 7, 6, 3) +#define SYS_DC_IGDVAC sys_insn(1, 0, 7, 6, 5) + +#define SYS_DC_CVAC sys_insn(1, 3, 7, 10, 1) +#define SYS_DC_CGVAC sys_insn(1, 3, 7, 10, 3) +#define SYS_DC_CGDVAC sys_insn(1, 3, 7, 10, 5) + +#define SYS_DC_CVAU sys_insn(1, 3, 7, 11, 1) + +#define SYS_DC_CVAP sys_insn(1, 3, 7, 12, 1) +#define SYS_DC_CGVAP sys_insn(1, 3, 7, 12, 3) +#define SYS_DC_CGDVAP sys_insn(1, 3, 7, 12, 5) + +#define SYS_DC_CVADP sys_insn(1, 3, 7, 13, 1) +#define SYS_DC_CGVADP sys_insn(1, 3, 7, 13, 3) +#define SYS_DC_CGDVADP sys_insn(1, 3, 7, 13, 5) + +#define SYS_DC_CIVAC sys_insn(1, 3, 7, 14, 1) +#define SYS_DC_CIGVAC sys_insn(1, 3, 7, 14, 3) +#define SYS_DC_CIGDVAC sys_insn(1, 3, 7, 14, 5) + +/* Data cache zero operations */ +#define SYS_DC_ZVA sys_insn(1, 3, 7, 4, 1) +#define SYS_DC_GVA sys_insn(1, 3, 7, 4, 3) +#define SYS_DC_GZVA sys_insn(1, 3, 7, 4, 4) + /* * Automatically generated definitions for system registers, the * manual encodings below are in the process of being converted to @@ -163,6 +194,82 @@ #define SYS_DBGDTRTX_EL0 sys_reg(2, 3, 0, 5, 0) #define SYS_DBGVCR32_EL2 sys_reg(2, 4, 0, 7, 0) +#define SYS_BRBINF_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 0)) +#define SYS_BRBINFINJ_EL1 sys_reg(2, 1, 9, 1, 0) +#define SYS_BRBSRC_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 1)) +#define SYS_BRBSRCINJ_EL1 sys_reg(2, 1, 9, 1, 1) +#define SYS_BRBTGT_EL1(n) sys_reg(2, 1, 8, (n & 15), (((n & 16) >> 2) | 2)) +#define SYS_BRBTGTINJ_EL1 sys_reg(2, 1, 9, 1, 2) +#define SYS_BRBTS_EL1 sys_reg(2, 1, 9, 0, 2) + +#define SYS_BRBCR_EL1 sys_reg(2, 1, 9, 0, 0) +#define SYS_BRBFCR_EL1 sys_reg(2, 1, 9, 0, 1) +#define SYS_BRBIDR0_EL1 sys_reg(2, 1, 9, 2, 0) + +#define SYS_TRCITECR_EL1 sys_reg(3, 0, 1, 2, 3) +#define SYS_TRCACATR(m) sys_reg(2, 1, 2, ((m & 7) << 1), (2 | (m >> 3))) +#define SYS_TRCACVR(m) sys_reg(2, 1, 2, ((m & 7) << 1), (0 | (m >> 3))) +#define SYS_TRCAUTHSTATUS sys_reg(2, 1, 7, 14, 6) +#define SYS_TRCAUXCTLR sys_reg(2, 1, 0, 6, 0) +#define SYS_TRCBBCTLR sys_reg(2, 1, 0, 15, 0) +#define SYS_TRCCCCTLR sys_reg(2, 1, 0, 14, 0) +#define SYS_TRCCIDCCTLR0 sys_reg(2, 1, 3, 0, 2) +#define SYS_TRCCIDCCTLR1 sys_reg(2, 1, 3, 1, 2) +#define SYS_TRCCIDCVR(m) sys_reg(2, 1, 3, ((m & 7) << 1), 0) +#define SYS_TRCCLAIMCLR sys_reg(2, 1, 7, 9, 6) +#define SYS_TRCCLAIMSET sys_reg(2, 1, 7, 8, 6) +#define SYS_TRCCNTCTLR(m) sys_reg(2, 1, 0, (4 | (m & 3)), 5) +#define SYS_TRCCNTRLDVR(m) sys_reg(2, 1, 0, (0 | (m & 3)), 5) +#define SYS_TRCCNTVR(m) sys_reg(2, 1, 0, (8 | (m & 3)), 5) +#define SYS_TRCCONFIGR sys_reg(2, 1, 0, 4, 0) +#define SYS_TRCDEVARCH sys_reg(2, 1, 7, 15, 6) +#define SYS_TRCDEVID sys_reg(2, 1, 7, 2, 7) +#define SYS_TRCEVENTCTL0R sys_reg(2, 1, 0, 8, 0) +#define SYS_TRCEVENTCTL1R sys_reg(2, 1, 0, 9, 0) +#define SYS_TRCEXTINSELR(m) sys_reg(2, 1, 0, (8 | (m & 3)), 4) +#define SYS_TRCIDR0 sys_reg(2, 1, 0, 8, 7) +#define SYS_TRCIDR10 sys_reg(2, 1, 0, 2, 6) +#define SYS_TRCIDR11 sys_reg(2, 1, 0, 3, 6) +#define SYS_TRCIDR12 sys_reg(2, 1, 0, 4, 6) +#define SYS_TRCIDR13 sys_reg(2, 1, 0, 5, 6) +#define SYS_TRCIDR1 sys_reg(2, 1, 0, 9, 7) +#define SYS_TRCIDR2 sys_reg(2, 1, 0, 10, 7) +#define SYS_TRCIDR3 sys_reg(2, 1, 0, 11, 7) +#define SYS_TRCIDR4 sys_reg(2, 1, 0, 12, 7) +#define SYS_TRCIDR5 sys_reg(2, 1, 0, 13, 7) +#define SYS_TRCIDR6 sys_reg(2, 1, 0, 14, 7) +#define SYS_TRCIDR7 sys_reg(2, 1, 0, 15, 7) +#define SYS_TRCIDR8 sys_reg(2, 1, 0, 0, 6) +#define SYS_TRCIDR9 sys_reg(2, 1, 0, 1, 6) +#define SYS_TRCIMSPEC(m) sys_reg(2, 1, 0, (m & 7), 7) +#define SYS_TRCITEEDCR sys_reg(2, 1, 0, 2, 1) +#define SYS_TRCOSLSR sys_reg(2, 1, 1, 1, 4) +#define SYS_TRCPRGCTLR sys_reg(2, 1, 0, 1, 0) +#define SYS_TRCQCTLR sys_reg(2, 1, 0, 1, 1) +#define SYS_TRCRSCTLR(m) sys_reg(2, 1, 1, (m & 15), (0 | (m >> 4))) +#define SYS_TRCRSR sys_reg(2, 1, 0, 10, 0) +#define SYS_TRCSEQEVR(m) sys_reg(2, 1, 0, (m & 3), 4) +#define SYS_TRCSEQRSTEVR sys_reg(2, 1, 0, 6, 4) +#define SYS_TRCSEQSTR sys_reg(2, 1, 0, 7, 4) +#define SYS_TRCSSCCR(m) sys_reg(2, 1, 1, (m & 7), 2) +#define SYS_TRCSSCSR(m) sys_reg(2, 1, 1, (8 | (m & 7)), 2) +#define SYS_TRCSSPCICR(m) sys_reg(2, 1, 1, (m & 7), 3) +#define SYS_TRCSTALLCTLR sys_reg(2, 1, 0, 11, 0) +#define SYS_TRCSTATR sys_reg(2, 1, 0, 3, 0) +#define SYS_TRCSYNCPR sys_reg(2, 1, 0, 13, 0) +#define SYS_TRCTRACEIDR sys_reg(2, 1, 0, 0, 1) +#define SYS_TRCTSCTLR sys_reg(2, 1, 0, 12, 0) +#define SYS_TRCVICTLR sys_reg(2, 1, 0, 0, 2) +#define SYS_TRCVIIECTLR sys_reg(2, 1, 0, 1, 2) +#define SYS_TRCVIPCSSCTLR sys_reg(2, 1, 0, 3, 2) +#define SYS_TRCVISSCTLR sys_reg(2, 1, 0, 2, 2) +#define SYS_TRCVMIDCCTLR0 sys_reg(2, 1, 3, 2, 2) +#define SYS_TRCVMIDCCTLR1 sys_reg(2, 1, 3, 3, 2) +#define SYS_TRCVMIDCVR(m) sys_reg(2, 1, 3, ((m & 7) << 1), 1) + +/* ETM */ +#define SYS_TRCOSLAR sys_reg(2, 1, 1, 0, 4) + #define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0) #define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5) #define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6) @@ -203,8 +310,13 @@ #define SYS_ERXCTLR_EL1 sys_reg(3, 0, 5, 4, 1) #define SYS_ERXSTATUS_EL1 sys_reg(3, 0, 5, 4, 2) #define SYS_ERXADDR_EL1 sys_reg(3, 0, 5, 4, 3) +#define SYS_ERXPFGF_EL1 sys_reg(3, 0, 5, 4, 4) +#define SYS_ERXPFGCTL_EL1 sys_reg(3, 0, 5, 4, 5) +#define SYS_ERXPFGCDN_EL1 sys_reg(3, 0, 5, 4, 6) #define SYS_ERXMISC0_EL1 sys_reg(3, 0, 5, 5, 0) #define SYS_ERXMISC1_EL1 sys_reg(3, 0, 5, 5, 1) +#define SYS_ERXMISC2_EL1 sys_reg(3, 0, 5, 5, 2) +#define SYS_ERXMISC3_EL1 sys_reg(3, 0, 5, 5, 3) #define SYS_TFSR_EL1 sys_reg(3, 0, 5, 6, 0) #define SYS_TFSRE0_EL1 sys_reg(3, 0, 5, 6, 1) @@ -275,6 +387,8 @@ #define SYS_ICC_IGRPEN0_EL1 sys_reg(3, 0, 12, 12, 6) #define SYS_ICC_IGRPEN1_EL1 sys_reg(3, 0, 12, 12, 7) +#define SYS_ACCDATA_EL1 sys_reg(3, 0, 13, 0, 5) + #define SYS_CNTKCTL_EL1 sys_reg(3, 0, 14, 1, 0) #define SYS_AIDR_EL1 sys_reg(3, 1, 0, 0, 7) @@ -383,8 +497,6 @@ #define SYS_VTCR_EL2 sys_reg(3, 4, 2, 1, 2) #define SYS_TRFCR_EL2 sys_reg(3, 4, 1, 2, 1) -#define SYS_HDFGRTR_EL2 sys_reg(3, 4, 3, 1, 4) -#define SYS_HDFGWTR_EL2 sys_reg(3, 4, 3, 1, 5) #define SYS_HAFGRTR_EL2 sys_reg(3, 4, 3, 1, 6) #define SYS_SPSR_EL2 sys_reg(3, 4, 4, 0, 0) #define SYS_ELR_EL2 sys_reg(3, 4, 4, 0, 1) @@ -478,6 +590,158 @@ #define SYS_SP_EL2 sys_reg(3, 6, 4, 1, 0) +/* AT instructions */ +#define AT_Op0 1 +#define AT_CRn 7 + +#define OP_AT_S1E1R sys_insn(AT_Op0, 0, AT_CRn, 8, 0) +#define OP_AT_S1E1W sys_insn(AT_Op0, 0, AT_CRn, 8, 1) +#define OP_AT_S1E0R sys_insn(AT_Op0, 0, AT_CRn, 8, 2) +#define OP_AT_S1E0W sys_insn(AT_Op0, 0, AT_CRn, 8, 3) +#define OP_AT_S1E1RP sys_insn(AT_Op0, 0, AT_CRn, 9, 0) +#define OP_AT_S1E1WP sys_insn(AT_Op0, 0, AT_CRn, 9, 1) +#define OP_AT_S1E2R sys_insn(AT_Op0, 4, AT_CRn, 8, 0) +#define OP_AT_S1E2W sys_insn(AT_Op0, 4, AT_CRn, 8, 1) +#define OP_AT_S12E1R sys_insn(AT_Op0, 4, AT_CRn, 8, 4) +#define OP_AT_S12E1W sys_insn(AT_Op0, 4, AT_CRn, 8, 5) +#define OP_AT_S12E0R sys_insn(AT_Op0, 4, AT_CRn, 8, 6) +#define OP_AT_S12E0W sys_insn(AT_Op0, 4, AT_CRn, 8, 7) + +/* TLBI instructions */ +#define OP_TLBI_VMALLE1OS sys_insn(1, 0, 8, 1, 0) +#define OP_TLBI_VAE1OS sys_insn(1, 0, 8, 1, 1) +#define OP_TLBI_ASIDE1OS sys_insn(1, 0, 8, 1, 2) +#define OP_TLBI_VAAE1OS sys_insn(1, 0, 8, 1, 3) +#define OP_TLBI_VALE1OS sys_insn(1, 0, 8, 1, 5) +#define OP_TLBI_VAALE1OS sys_insn(1, 0, 8, 1, 7) +#define OP_TLBI_RVAE1IS sys_insn(1, 0, 8, 2, 1) +#define OP_TLBI_RVAAE1IS sys_insn(1, 0, 8, 2, 3) +#define OP_TLBI_RVALE1IS sys_insn(1, 0, 8, 2, 5) +#define OP_TLBI_RVAALE1IS sys_insn(1, 0, 8, 2, 7) +#define OP_TLBI_VMALLE1IS sys_insn(1, 0, 8, 3, 0) +#define OP_TLBI_VAE1IS sys_insn(1, 0, 8, 3, 1) +#define OP_TLBI_ASIDE1IS sys_insn(1, 0, 8, 3, 2) +#define OP_TLBI_VAAE1IS sys_insn(1, 0, 8, 3, 3) +#define OP_TLBI_VALE1IS sys_insn(1, 0, 8, 3, 5) +#define OP_TLBI_VAALE1IS sys_insn(1, 0, 8, 3, 7) +#define OP_TLBI_RVAE1OS sys_insn(1, 0, 8, 5, 1) +#define OP_TLBI_RVAAE1OS sys_insn(1, 0, 8, 5, 3) +#define OP_TLBI_RVALE1OS sys_insn(1, 0, 8, 5, 5) +#define OP_TLBI_RVAALE1OS sys_insn(1, 0, 8, 5, 7) +#define OP_TLBI_RVAE1 sys_insn(1, 0, 8, 6, 1) +#define OP_TLBI_RVAAE1 sys_insn(1, 0, 8, 6, 3) +#define OP_TLBI_RVALE1 sys_insn(1, 0, 8, 6, 5) +#define OP_TLBI_RVAALE1 sys_insn(1, 0, 8, 6, 7) +#define OP_TLBI_VMALLE1 sys_insn(1, 0, 8, 7, 0) +#define OP_TLBI_VAE1 sys_insn(1, 0, 8, 7, 1) +#define OP_TLBI_ASIDE1 sys_insn(1, 0, 8, 7, 2) +#define OP_TLBI_VAAE1 sys_insn(1, 0, 8, 7, 3) +#define OP_TLBI_VALE1 sys_insn(1, 0, 8, 7, 5) +#define OP_TLBI_VAALE1 sys_insn(1, 0, 8, 7, 7) +#define OP_TLBI_VMALLE1OSNXS sys_insn(1, 0, 9, 1, 0) +#define OP_TLBI_VAE1OSNXS sys_insn(1, 0, 9, 1, 1) +#define OP_TLBI_ASIDE1OSNXS sys_insn(1, 0, 9, 1, 2) +#define OP_TLBI_VAAE1OSNXS sys_insn(1, 0, 9, 1, 3) +#define OP_TLBI_VALE1OSNXS sys_insn(1, 0, 9, 1, 5) +#define OP_TLBI_VAALE1OSNXS sys_insn(1, 0, 9, 1, 7) +#define OP_TLBI_RVAE1ISNXS sys_insn(1, 0, 9, 2, 1) +#define OP_TLBI_RVAAE1ISNXS sys_insn(1, 0, 9, 2, 3) +#define OP_TLBI_RVALE1ISNXS sys_insn(1, 0, 9, 2, 5) +#define OP_TLBI_RVAALE1ISNXS sys_insn(1, 0, 9, 2, 7) +#define OP_TLBI_VMALLE1ISNXS sys_insn(1, 0, 9, 3, 0) +#define OP_TLBI_VAE1ISNXS sys_insn(1, 0, 9, 3, 1) +#define OP_TLBI_ASIDE1ISNXS sys_insn(1, 0, 9, 3, 2) +#define OP_TLBI_VAAE1ISNXS sys_insn(1, 0, 9, 3, 3) +#define OP_TLBI_VALE1ISNXS sys_insn(1, 0, 9, 3, 5) +#define OP_TLBI_VAALE1ISNXS sys_insn(1, 0, 9, 3, 7) +#define OP_TLBI_RVAE1OSNXS sys_insn(1, 0, 9, 5, 1) +#define OP_TLBI_RVAAE1OSNXS sys_insn(1, 0, 9, 5, 3) +#define OP_TLBI_RVALE1OSNXS sys_insn(1, 0, 9, 5, 5) +#define OP_TLBI_RVAALE1OSNXS sys_insn(1, 0, 9, 5, 7) +#define OP_TLBI_RVAE1NXS sys_insn(1, 0, 9, 6, 1) +#define OP_TLBI_RVAAE1NXS sys_insn(1, 0, 9, 6, 3) +#define OP_TLBI_RVALE1NXS sys_insn(1, 0, 9, 6, 5) +#define OP_TLBI_RVAALE1NXS sys_insn(1, 0, 9, 6, 7) +#define OP_TLBI_VMALLE1NXS sys_insn(1, 0, 9, 7, 0) +#define OP_TLBI_VAE1NXS sys_insn(1, 0, 9, 7, 1) +#define OP_TLBI_ASIDE1NXS sys_insn(1, 0, 9, 7, 2) +#define OP_TLBI_VAAE1NXS sys_insn(1, 0, 9, 7, 3) +#define OP_TLBI_VALE1NXS sys_insn(1, 0, 9, 7, 5) +#define OP_TLBI_VAALE1NXS sys_insn(1, 0, 9, 7, 7) +#define OP_TLBI_IPAS2E1IS sys_insn(1, 4, 8, 0, 1) +#define OP_TLBI_RIPAS2E1IS sys_insn(1, 4, 8, 0, 2) +#define OP_TLBI_IPAS2LE1IS sys_insn(1, 4, 8, 0, 5) +#define OP_TLBI_RIPAS2LE1IS sys_insn(1, 4, 8, 0, 6) +#define OP_TLBI_ALLE2OS sys_insn(1, 4, 8, 1, 0) +#define OP_TLBI_VAE2OS sys_insn(1, 4, 8, 1, 1) +#define OP_TLBI_ALLE1OS sys_insn(1, 4, 8, 1, 4) +#define OP_TLBI_VALE2OS sys_insn(1, 4, 8, 1, 5) +#define OP_TLBI_VMALLS12E1OS sys_insn(1, 4, 8, 1, 6) +#define OP_TLBI_RVAE2IS sys_insn(1, 4, 8, 2, 1) +#define OP_TLBI_RVALE2IS sys_insn(1, 4, 8, 2, 5) +#define OP_TLBI_ALLE2IS sys_insn(1, 4, 8, 3, 0) +#define OP_TLBI_VAE2IS sys_insn(1, 4, 8, 3, 1) +#define OP_TLBI_ALLE1IS sys_insn(1, 4, 8, 3, 4) +#define OP_TLBI_VALE2IS sys_insn(1, 4, 8, 3, 5) +#define OP_TLBI_VMALLS12E1IS sys_insn(1, 4, 8, 3, 6) +#define OP_TLBI_IPAS2E1OS sys_insn(1, 4, 8, 4, 0) +#define OP_TLBI_IPAS2E1 sys_insn(1, 4, 8, 4, 1) +#define OP_TLBI_RIPAS2E1 sys_insn(1, 4, 8, 4, 2) +#define OP_TLBI_RIPAS2E1OS sys_insn(1, 4, 8, 4, 3) +#define OP_TLBI_IPAS2LE1OS sys_insn(1, 4, 8, 4, 4) +#define OP_TLBI_IPAS2LE1 sys_insn(1, 4, 8, 4, 5) +#define OP_TLBI_RIPAS2LE1 sys_insn(1, 4, 8, 4, 6) +#define OP_TLBI_RIPAS2LE1OS sys_insn(1, 4, 8, 4, 7) +#define OP_TLBI_RVAE2OS sys_insn(1, 4, 8, 5, 1) +#define OP_TLBI_RVALE2OS sys_insn(1, 4, 8, 5, 5) +#define OP_TLBI_RVAE2 sys_insn(1, 4, 8, 6, 1) +#define OP_TLBI_RVALE2 sys_insn(1, 4, 8, 6, 5) +#define OP_TLBI_ALLE2 sys_insn(1, 4, 8, 7, 0) +#define OP_TLBI_VAE2 sys_insn(1, 4, 8, 7, 1) +#define OP_TLBI_ALLE1 sys_insn(1, 4, 8, 7, 4) +#define OP_TLBI_VALE2 sys_insn(1, 4, 8, 7, 5) +#define OP_TLBI_VMALLS12E1 sys_insn(1, 4, 8, 7, 6) +#define OP_TLBI_IPAS2E1ISNXS sys_insn(1, 4, 9, 0, 1) +#define OP_TLBI_RIPAS2E1ISNXS sys_insn(1, 4, 9, 0, 2) +#define OP_TLBI_IPAS2LE1ISNXS sys_insn(1, 4, 9, 0, 5) +#define OP_TLBI_RIPAS2LE1ISNXS sys_insn(1, 4, 9, 0, 6) +#define OP_TLBI_ALLE2OSNXS sys_insn(1, 4, 9, 1, 0) +#define OP_TLBI_VAE2OSNXS sys_insn(1, 4, 9, 1, 1) +#define OP_TLBI_ALLE1OSNXS sys_insn(1, 4, 9, 1, 4) +#define OP_TLBI_VALE2OSNXS sys_insn(1, 4, 9, 1, 5) +#define OP_TLBI_VMALLS12E1OSNXS sys_insn(1, 4, 9, 1, 6) +#define OP_TLBI_RVAE2ISNXS sys_insn(1, 4, 9, 2, 1) +#define OP_TLBI_RVALE2ISNXS sys_insn(1, 4, 9, 2, 5) +#define OP_TLBI_ALLE2ISNXS sys_insn(1, 4, 9, 3, 0) +#define OP_TLBI_VAE2ISNXS sys_insn(1, 4, 9, 3, 1) +#define OP_TLBI_ALLE1ISNXS sys_insn(1, 4, 9, 3, 4) +#define OP_TLBI_VALE2ISNXS sys_insn(1, 4, 9, 3, 5) +#define OP_TLBI_VMALLS12E1ISNXS sys_insn(1, 4, 9, 3, 6) +#define OP_TLBI_IPAS2E1OSNXS sys_insn(1, 4, 9, 4, 0) +#define OP_TLBI_IPAS2E1NXS sys_insn(1, 4, 9, 4, 1) +#define OP_TLBI_RIPAS2E1NXS sys_insn(1, 4, 9, 4, 2) +#define OP_TLBI_RIPAS2E1OSNXS sys_insn(1, 4, 9, 4, 3) +#define OP_TLBI_IPAS2LE1OSNXS sys_insn(1, 4, 9, 4, 4) +#define OP_TLBI_IPAS2LE1NXS sys_insn(1, 4, 9, 4, 5) +#define OP_TLBI_RIPAS2LE1NXS sys_insn(1, 4, 9, 4, 6) +#define OP_TLBI_RIPAS2LE1OSNXS sys_insn(1, 4, 9, 4, 7) +#define OP_TLBI_RVAE2OSNXS sys_insn(1, 4, 9, 5, 1) +#define OP_TLBI_RVALE2OSNXS sys_insn(1, 4, 9, 5, 5) +#define OP_TLBI_RVAE2NXS sys_insn(1, 4, 9, 6, 1) +#define OP_TLBI_RVALE2NXS sys_insn(1, 4, 9, 6, 5) +#define OP_TLBI_ALLE2NXS sys_insn(1, 4, 9, 7, 0) +#define OP_TLBI_VAE2NXS sys_insn(1, 4, 9, 7, 1) +#define OP_TLBI_ALLE1NXS sys_insn(1, 4, 9, 7, 4) +#define OP_TLBI_VALE2NXS sys_insn(1, 4, 9, 7, 5) +#define OP_TLBI_VMALLS12E1NXS sys_insn(1, 4, 9, 7, 6) + +/* Misc instructions */ +#define OP_BRB_IALL sys_insn(1, 1, 7, 2, 4) +#define OP_BRB_INJ sys_insn(1, 1, 7, 2, 5) +#define OP_CFP_RCTX sys_insn(1, 3, 7, 3, 4) +#define OP_DVP_RCTX sys_insn(1, 3, 7, 3, 5) +#define OP_CPP_RCTX sys_insn(1, 3, 7, 3, 7) + /* Common SCTLR_ELx flags. */ #define SCTLR_ELx_ENTP2 (BIT(60)) #define SCTLR_ELx_DSSBS (BIT(44)) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 55b50e1d4a84f27345956b9f02f942c4bec3688c..b149cf9f91bc965d53f97689a83ec653e8278a8d 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -335,14 +335,77 @@ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch) */ #define MAX_TLBI_OPS PTRS_PER_PTE +/* + * __flush_tlb_range_op - Perform TLBI operation upon a range + * + * @op: TLBI instruction that operates on a range (has 'r' prefix) + * @start: The start address of the range + * @pages: Range as the number of pages from 'start' + * @stride: Flush granularity + * @asid: The ASID of the task (0 for IPA instructions) + * @tlb_level: Translation Table level hint, if known + * @tlbi_user: If 'true', call an additional __tlbi_user() + * (typically for user ASIDs). 'flase' for IPA instructions + * + * When the CPU does not support TLB range operations, flush the TLB + * entries one by one at the granularity of 'stride'. If the TLB + * range ops are supported, then: + * + * 1. If 'pages' is odd, flush the first page through non-range + * operations; + * + * 2. For remaining pages: the minimum range granularity is decided + * by 'scale', so multiple range TLBI operations may be required. + * Start from scale = 0, flush the corresponding number of pages + * ((num+1)*2^(5*scale+1) starting from 'addr'), then increase it + * until no pages left. + * + * Note that certain ranges can be represented by either num = 31 and + * scale or num = 0 and scale + 1. The loop below favours the latter + * since num is limited to 30 by the __TLBI_RANGE_NUM() macro. + */ +#define __flush_tlb_range_op(op, start, pages, stride, \ + asid, tlb_level, tlbi_user) \ +do { \ + int num = 0; \ + int scale = 0; \ + unsigned long addr; \ + \ + while (pages > 0) { \ + if (!system_supports_tlb_range() || \ + pages % 2 == 1) { \ + addr = __TLBI_VADDR(start, asid); \ + __tlbi_level(op, addr, tlb_level); \ + if (tlbi_user) \ + __tlbi_user_level(op, addr, tlb_level); \ + start += stride; \ + pages -= stride >> PAGE_SHIFT; \ + continue; \ + } \ + \ + num = __TLBI_RANGE_NUM(pages, scale); \ + if (num >= 0) { \ + addr = __TLBI_VADDR_RANGE(start, asid, scale, \ + num, tlb_level); \ + __tlbi(r##op, addr); \ + if (tlbi_user) \ + __tlbi_user(r##op, addr); \ + start += __TLBI_RANGE_PAGES(num, scale) << PAGE_SHIFT; \ + pages -= __TLBI_RANGE_PAGES(num, scale); \ + } \ + scale++; \ + } \ +} while (0) + +#define __flush_s2_tlb_range_op(op, start, pages, stride, tlb_level) \ + __flush_tlb_range_op(op, start, pages, stride, 0, tlb_level, false) + static inline void __flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end, unsigned long stride, bool last_level, int tlb_level) { - int num = 0; - int scale = 0; - unsigned long asid, addr, pages; + unsigned long asid, pages; start = round_down(start, stride); end = round_up(end, stride); @@ -364,56 +427,11 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, dsb(ishst); asid = ASID(vma->vm_mm); - /* - * When the CPU does not support TLB range operations, flush the TLB - * entries one by one at the granularity of 'stride'. If the TLB - * range ops are supported, then: - * - * 1. If 'pages' is odd, flush the first page through non-range - * operations; - * - * 2. For remaining pages: the minimum range granularity is decided - * by 'scale', so multiple range TLBI operations may be required. - * Start from scale = 0, flush the corresponding number of pages - * ((num+1)*2^(5*scale+1) starting from 'addr'), then increase it - * until no pages left. - * - * Note that certain ranges can be represented by either num = 31 and - * scale or num = 0 and scale + 1. The loop below favours the latter - * since num is limited to 30 by the __TLBI_RANGE_NUM() macro. - */ - while (pages > 0) { - if (!system_supports_tlb_range() || - pages % 2 == 1) { - addr = __TLBI_VADDR(start, asid); - if (last_level) { - __tlbi_level(vale1is, addr, tlb_level); - __tlbi_user_level(vale1is, addr, tlb_level); - } else { - __tlbi_level(vae1is, addr, tlb_level); - __tlbi_user_level(vae1is, addr, tlb_level); - } - start += stride; - pages -= stride >> PAGE_SHIFT; - continue; - } - - num = __TLBI_RANGE_NUM(pages, scale); - if (num >= 0) { - addr = __TLBI_VADDR_RANGE(start, asid, scale, - num, tlb_level); - if (last_level) { - __tlbi(rvale1is, addr); - __tlbi_user(rvale1is, addr); - } else { - __tlbi(rvae1is, addr); - __tlbi_user(rvae1is, addr); - } - start += __TLBI_RANGE_PAGES(num, scale) << PAGE_SHIFT; - pages -= __TLBI_RANGE_PAGES(num, scale); - } - scale++; - } + if (last_level) + __flush_tlb_range_op(vale1is, start, pages, stride, asid, tlb_level, true); + else + __flush_tlb_range_op(vae1is, start, pages, stride, asid, tlb_level, true); + dsb(ish); mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, start, end); } diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index be66e94a21bda37ff271ea7cd8204019f74b08aa..5706e74c55786a2f6498af9084c5dcab735f9218 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -730,6 +730,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = { .cpu_enable = cpu_clear_bf16_from_user_emulation, }, #endif +#ifdef CONFIG_ARM64_ERRATUM_2966298 + { + .desc = "ARM erratum 2966298", + .capability = ARM64_WORKAROUND_2966298, + /* Cortex-A520 r0p0 - r0p1 */ + ERRATA_MIDR_REV_RANGE(MIDR_CORTEX_A520, 0, 0, 1), + }, +#endif #ifdef CONFIG_AMPERE_ERRATUM_AC03_CPU_38 { .desc = "AmpereOne erratum AC03_CPU_38", diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index a5f533f63b60cc09dc061dc6dc7faeb707daf58a..444a73c2e63858cea758fb89c5d65b786bc6f5f3 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -222,7 +222,8 @@ static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { static const struct arm64_ftr_bits ftr_id_aa64isar2[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_CSSC_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_RPRFM_SHIFT, 4, 0), - ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_EL1_BC_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_CLRBHB_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_BC_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_EL1_MOPS_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE_IF_IS_ENABLED(CONFIG_ARM64_PTR_AUTH), FTR_STRICT, FTR_EXACT, ID_AA64ISAR2_EL1_APA3_SHIFT, 4, 0), @@ -2627,6 +2628,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, ARM64_CPUID_FIELDS(ID_AA64ISAR1_EL1, LRCPC, IMP) }, + { + .desc = "Fine Grained Traps", + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .capability = ARM64_HAS_FGT, + .matches = has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64MMFR0_EL1, FGT, IMP) + }, #ifdef CONFIG_ARM64_SME { .desc = "Scalable Matrix Extension", diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 6ad61de03d0a0c8a3dd1edf1952c28a12c97f2a0..a6030913cd58c44f1ce5cd7077fe61dac02c86db 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -428,6 +428,10 @@ alternative_else_nop_endif ldp x28, x29, [sp, #16 * 14] .if \el == 0 +alternative_if ARM64_WORKAROUND_2966298 + tlbi vale1, xzr + dsb nsh +alternative_else_nop_endif alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0 ldr lr, [sp, #S_LR] add sp, sp, #PT_REGS_SIZE // restore sp diff --git a/arch/arm64/kernel/idreg-override.c b/arch/arm64/kernel/idreg-override.c index aee12c75b738e01c265c02380998d3d695f4f440..3addc09f874615a5be5a6fa567e93c4f278025ae 100644 --- a/arch/arm64/kernel/idreg-override.c +++ b/arch/arm64/kernel/idreg-override.c @@ -262,9 +262,9 @@ static __init void __parse_cmdline(const char *cmdline, bool parse_aliases) if (!len) return; - len = strscpy(buf, cmdline, ARRAY_SIZE(buf)); - if (len == -E2BIG) - len = ARRAY_SIZE(buf) - 1; + len = min(len, ARRAY_SIZE(buf) - 1); + memcpy(buf, cmdline, len); + buf[len] = '\0'; if (strcmp(buf, "--") == 0) return; diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig index f531da6b362e902a06e9aa76968d9f0ad52e1a23..83c1e09be42e5b609d44d653e184b44d9cdc347c 100644 --- a/arch/arm64/kvm/Kconfig +++ b/arch/arm64/kvm/Kconfig @@ -25,7 +25,6 @@ menuconfig KVM select MMU_NOTIFIER select PREEMPT_NOTIFIERS select HAVE_KVM_CPU_RELAX_INTERCEPT - select HAVE_KVM_ARCH_TLB_FLUSH_ALL select KVM_MMIO select KVM_GENERIC_DIRTYLOG_READ_PROTECT select KVM_XFER_TO_GUEST_WORK @@ -43,6 +42,7 @@ menuconfig KVM select SCHED_INFO select GUEST_PERF_EVENTS if PERF_EVENTS select INTERVAL_TREE + select XARRAY_MULTI help Support hosting virtualized guest machines. diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index d1cb298a58a084c825d3ad13f2837da3afd0ba2e..4866b3f7b4ea3847d885e00cfac47a4d7abf9da3 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -365,7 +366,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) #endif /* Force users to call KVM_ARM_VCPU_INIT */ - vcpu->arch.target = -1; + vcpu_clear_flag(vcpu, VCPU_INITIALIZED); bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES); vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO; @@ -462,7 +463,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) vcpu_ptrauth_disable(vcpu); kvm_arch_vcpu_load_debug_state_flags(vcpu); - if (!cpumask_test_cpu(smp_processor_id(), vcpu->kvm->arch.supported_cpus)) + if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus)) vcpu_set_on_unsupported_cpu(vcpu); } @@ -574,7 +575,7 @@ unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu) static int kvm_vcpu_initialized(struct kvm_vcpu *vcpu) { - return vcpu->arch.target >= 0; + return vcpu_get_flag(vcpu, VCPU_INITIALIZED); } /* @@ -803,6 +804,9 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu) kvm_pmu_handle_pmcr(vcpu, __vcpu_sys_reg(vcpu, PMCR_EL0)); + if (kvm_check_request(KVM_REQ_RESYNC_PMU_EL0, vcpu)) + kvm_vcpu_pmu_restore_guest(vcpu); + if (kvm_check_request(KVM_REQ_SUSPEND, vcpu)) return kvm_vcpu_suspend(vcpu); @@ -818,6 +822,9 @@ static bool vcpu_mode_is_bad_32bit(struct kvm_vcpu *vcpu) if (likely(!vcpu_mode_is_32bit(vcpu))) return false; + if (vcpu_has_nv(vcpu)) + return true; + return !kvm_supports_32bit_el0(); } @@ -1058,7 +1065,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) * invalid. The VMM can try and fix it by issuing a * KVM_ARM_VCPU_INIT if it really wants to. */ - vcpu->arch.target = -1; + vcpu_clear_flag(vcpu, VCPU_INITIALIZED); ret = ARM_EXCEPTION_IL; } @@ -1219,8 +1226,7 @@ static bool kvm_vcpu_init_changed(struct kvm_vcpu *vcpu, { unsigned long features = init->features[0]; - return !bitmap_equal(vcpu->arch.features, &features, KVM_VCPU_MAX_FEATURES) || - vcpu->arch.target != init->target; + return !bitmap_equal(vcpu->arch.features, &features, KVM_VCPU_MAX_FEATURES); } static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu, @@ -1236,20 +1242,18 @@ static int __kvm_vcpu_set_target(struct kvm_vcpu *vcpu, !bitmap_equal(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES)) goto out_unlock; - vcpu->arch.target = init->target; bitmap_copy(vcpu->arch.features, &features, KVM_VCPU_MAX_FEATURES); /* Now we know what it is, we can reset it. */ ret = kvm_reset_vcpu(vcpu); if (ret) { - vcpu->arch.target = -1; bitmap_zero(vcpu->arch.features, KVM_VCPU_MAX_FEATURES); goto out_unlock; } bitmap_copy(kvm->arch.vcpu_features, &features, KVM_VCPU_MAX_FEATURES); set_bit(KVM_ARCH_FLAG_VCPU_FEATURES_CONFIGURED, &kvm->arch.flags); - + vcpu_set_flag(vcpu, VCPU_INITIALIZED); out_unlock: mutex_unlock(&kvm->arch.config_lock); return ret; @@ -1260,14 +1264,15 @@ static int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, { int ret; - if (init->target != kvm_target_cpu()) + if (init->target != KVM_ARM_TARGET_GENERIC_V8 && + init->target != kvm_target_cpu()) return -EINVAL; ret = kvm_vcpu_init_check_features(vcpu, init); if (ret) return ret; - if (vcpu->arch.target == -1) + if (!kvm_vcpu_initialized(vcpu)) return __kvm_vcpu_set_target(vcpu, init); if (kvm_vcpu_init_changed(vcpu, init)) @@ -1532,12 +1537,6 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) } -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, - const struct kvm_memory_slot *memslot) -{ - kvm_flush_remote_tlbs(kvm); -} - static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr) { @@ -1595,9 +1594,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) return kvm_vm_ioctl_set_device_addr(kvm, &dev_addr); } case KVM_ARM_PREFERRED_TARGET: { - struct kvm_vcpu_init init; - - kvm_vcpu_preferred_target(&init); + struct kvm_vcpu_init init = { + .target = KVM_ARM_TARGET_GENERIC_V8, + }; if (copy_to_user(argp, &init, sizeof(init))) return -EFAULT; @@ -2276,30 +2275,8 @@ static int __init init_hyp_mode(void) for_each_possible_cpu(cpu) { struct kvm_nvhe_init_params *params = per_cpu_ptr_nvhe_sym(kvm_init_params, cpu); char *stack_page = (char *)per_cpu(kvm_arm_hyp_stack_page, cpu); - unsigned long hyp_addr; - /* - * Allocate a contiguous HYP private VA range for the stack - * and guard page. The allocation is also aligned based on - * the order of its size. - */ - err = hyp_alloc_private_va_range(PAGE_SIZE * 2, &hyp_addr); - if (err) { - kvm_err("Cannot allocate hyp stack guard page\n"); - goto out_err; - } - - /* - * Since the stack grows downwards, map the stack to the page - * at the higher address and leave the lower guard page - * unbacked. - * - * Any valid stack address now has the PAGE_SHIFT bit as 1 - * and addresses corresponding to the guard page have the - * PAGE_SHIFT bit as 0 - this is used for overflow detection. - */ - err = __create_hyp_mappings(hyp_addr + PAGE_SIZE, PAGE_SIZE, - __pa(stack_page), PAGE_HYP); + err = create_hyp_stack(__pa(stack_page), ¶ms->stack_hyp_va); if (err) { kvm_err("Cannot map hyp stack\n"); goto out_err; @@ -2312,8 +2289,6 @@ static int __init init_hyp_mode(void) * has been mapped in the flexible private VA space. */ params->stack_pa = __pa(stack_page); - - params->stack_hyp_va = hyp_addr + (2 * PAGE_SIZE); } for_each_possible_cpu(cpu) { diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c index b96662029fb1ac64a28ef2ffea2ca593f9e461d8..9ced1bf0c2b7824aef1a7285826a4c8978b35170 100644 --- a/arch/arm64/kvm/emulate-nested.c +++ b/arch/arm64/kvm/emulate-nested.c @@ -14,6 +14,1858 @@ #include "trace.h" +enum trap_behaviour { + BEHAVE_HANDLE_LOCALLY = 0, + BEHAVE_FORWARD_READ = BIT(0), + BEHAVE_FORWARD_WRITE = BIT(1), + BEHAVE_FORWARD_ANY = BEHAVE_FORWARD_READ | BEHAVE_FORWARD_WRITE, +}; + +struct trap_bits { + const enum vcpu_sysreg index; + const enum trap_behaviour behaviour; + const u64 value; + const u64 mask; +}; + +/* Coarse Grained Trap definitions */ +enum cgt_group_id { + /* Indicates no coarse trap control */ + __RESERVED__, + + /* + * The first batch of IDs denote coarse trapping that are used + * on their own instead of being part of a combination of + * trap controls. + */ + CGT_HCR_TID1, + CGT_HCR_TID2, + CGT_HCR_TID3, + CGT_HCR_IMO, + CGT_HCR_FMO, + CGT_HCR_TIDCP, + CGT_HCR_TACR, + CGT_HCR_TSW, + CGT_HCR_TPC, + CGT_HCR_TPU, + CGT_HCR_TTLB, + CGT_HCR_TVM, + CGT_HCR_TDZ, + CGT_HCR_TRVM, + CGT_HCR_TLOR, + CGT_HCR_TERR, + CGT_HCR_APK, + CGT_HCR_NV, + CGT_HCR_NV_nNV2, + CGT_HCR_NV1_nNV2, + CGT_HCR_AT, + CGT_HCR_nFIEN, + CGT_HCR_TID4, + CGT_HCR_TICAB, + CGT_HCR_TOCU, + CGT_HCR_ENSCXT, + CGT_HCR_TTLBIS, + CGT_HCR_TTLBOS, + + CGT_MDCR_TPMCR, + CGT_MDCR_TPM, + CGT_MDCR_TDE, + CGT_MDCR_TDA, + CGT_MDCR_TDOSA, + CGT_MDCR_TDRA, + CGT_MDCR_E2PB, + CGT_MDCR_TPMS, + CGT_MDCR_TTRF, + CGT_MDCR_E2TB, + CGT_MDCR_TDCC, + + /* + * Anything after this point is a combination of coarse trap + * controls, which must all be evaluated to decide what to do. + */ + __MULTIPLE_CONTROL_BITS__, + CGT_HCR_IMO_FMO = __MULTIPLE_CONTROL_BITS__, + CGT_HCR_TID2_TID4, + CGT_HCR_TTLB_TTLBIS, + CGT_HCR_TTLB_TTLBOS, + CGT_HCR_TVM_TRVM, + CGT_HCR_TPU_TICAB, + CGT_HCR_TPU_TOCU, + CGT_HCR_NV1_nNV2_ENSCXT, + CGT_MDCR_TPM_TPMCR, + CGT_MDCR_TDE_TDA, + CGT_MDCR_TDE_TDOSA, + CGT_MDCR_TDE_TDRA, + CGT_MDCR_TDCC_TDE_TDA, + + /* + * Anything after this point requires a callback evaluating a + * complex trap condition. Ugly stuff. + */ + __COMPLEX_CONDITIONS__, + CGT_CNTHCTL_EL1PCTEN = __COMPLEX_CONDITIONS__, + CGT_CNTHCTL_EL1PTEN, + + /* Must be last */ + __NR_CGT_GROUP_IDS__ +}; + +static const struct trap_bits coarse_trap_bits[] = { + [CGT_HCR_TID1] = { + .index = HCR_EL2, + .value = HCR_TID1, + .mask = HCR_TID1, + .behaviour = BEHAVE_FORWARD_READ, + }, + [CGT_HCR_TID2] = { + .index = HCR_EL2, + .value = HCR_TID2, + .mask = HCR_TID2, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TID3] = { + .index = HCR_EL2, + .value = HCR_TID3, + .mask = HCR_TID3, + .behaviour = BEHAVE_FORWARD_READ, + }, + [CGT_HCR_IMO] = { + .index = HCR_EL2, + .value = HCR_IMO, + .mask = HCR_IMO, + .behaviour = BEHAVE_FORWARD_WRITE, + }, + [CGT_HCR_FMO] = { + .index = HCR_EL2, + .value = HCR_FMO, + .mask = HCR_FMO, + .behaviour = BEHAVE_FORWARD_WRITE, + }, + [CGT_HCR_TIDCP] = { + .index = HCR_EL2, + .value = HCR_TIDCP, + .mask = HCR_TIDCP, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TACR] = { + .index = HCR_EL2, + .value = HCR_TACR, + .mask = HCR_TACR, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TSW] = { + .index = HCR_EL2, + .value = HCR_TSW, + .mask = HCR_TSW, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TPC] = { /* Also called TCPC when FEAT_DPB is implemented */ + .index = HCR_EL2, + .value = HCR_TPC, + .mask = HCR_TPC, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TPU] = { + .index = HCR_EL2, + .value = HCR_TPU, + .mask = HCR_TPU, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TTLB] = { + .index = HCR_EL2, + .value = HCR_TTLB, + .mask = HCR_TTLB, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TVM] = { + .index = HCR_EL2, + .value = HCR_TVM, + .mask = HCR_TVM, + .behaviour = BEHAVE_FORWARD_WRITE, + }, + [CGT_HCR_TDZ] = { + .index = HCR_EL2, + .value = HCR_TDZ, + .mask = HCR_TDZ, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TRVM] = { + .index = HCR_EL2, + .value = HCR_TRVM, + .mask = HCR_TRVM, + .behaviour = BEHAVE_FORWARD_READ, + }, + [CGT_HCR_TLOR] = { + .index = HCR_EL2, + .value = HCR_TLOR, + .mask = HCR_TLOR, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TERR] = { + .index = HCR_EL2, + .value = HCR_TERR, + .mask = HCR_TERR, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_APK] = { + .index = HCR_EL2, + .value = 0, + .mask = HCR_APK, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_NV] = { + .index = HCR_EL2, + .value = HCR_NV, + .mask = HCR_NV, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_NV_nNV2] = { + .index = HCR_EL2, + .value = HCR_NV, + .mask = HCR_NV | HCR_NV2, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_NV1_nNV2] = { + .index = HCR_EL2, + .value = HCR_NV | HCR_NV1, + .mask = HCR_NV | HCR_NV1 | HCR_NV2, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_AT] = { + .index = HCR_EL2, + .value = HCR_AT, + .mask = HCR_AT, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_nFIEN] = { + .index = HCR_EL2, + .value = 0, + .mask = HCR_FIEN, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TID4] = { + .index = HCR_EL2, + .value = HCR_TID4, + .mask = HCR_TID4, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TICAB] = { + .index = HCR_EL2, + .value = HCR_TICAB, + .mask = HCR_TICAB, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TOCU] = { + .index = HCR_EL2, + .value = HCR_TOCU, + .mask = HCR_TOCU, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_ENSCXT] = { + .index = HCR_EL2, + .value = 0, + .mask = HCR_ENSCXT, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TTLBIS] = { + .index = HCR_EL2, + .value = HCR_TTLBIS, + .mask = HCR_TTLBIS, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_HCR_TTLBOS] = { + .index = HCR_EL2, + .value = HCR_TTLBOS, + .mask = HCR_TTLBOS, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TPMCR] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TPMCR, + .mask = MDCR_EL2_TPMCR, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TPM] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TPM, + .mask = MDCR_EL2_TPM, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TDE] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TDE, + .mask = MDCR_EL2_TDE, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TDA] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TDA, + .mask = MDCR_EL2_TDA, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TDOSA] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TDOSA, + .mask = MDCR_EL2_TDOSA, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TDRA] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TDRA, + .mask = MDCR_EL2_TDRA, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_E2PB] = { + .index = MDCR_EL2, + .value = 0, + .mask = BIT(MDCR_EL2_E2PB_SHIFT), + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TPMS] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TPMS, + .mask = MDCR_EL2_TPMS, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TTRF] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TTRF, + .mask = MDCR_EL2_TTRF, + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_E2TB] = { + .index = MDCR_EL2, + .value = 0, + .mask = BIT(MDCR_EL2_E2TB_SHIFT), + .behaviour = BEHAVE_FORWARD_ANY, + }, + [CGT_MDCR_TDCC] = { + .index = MDCR_EL2, + .value = MDCR_EL2_TDCC, + .mask = MDCR_EL2_TDCC, + .behaviour = BEHAVE_FORWARD_ANY, + }, +}; + +#define MCB(id, ...) \ + [id - __MULTIPLE_CONTROL_BITS__] = \ + (const enum cgt_group_id[]){ \ + __VA_ARGS__, __RESERVED__ \ + } + +static const enum cgt_group_id *coarse_control_combo[] = { + MCB(CGT_HCR_IMO_FMO, CGT_HCR_IMO, CGT_HCR_FMO), + MCB(CGT_HCR_TID2_TID4, CGT_HCR_TID2, CGT_HCR_TID4), + MCB(CGT_HCR_TTLB_TTLBIS, CGT_HCR_TTLB, CGT_HCR_TTLBIS), + MCB(CGT_HCR_TTLB_TTLBOS, CGT_HCR_TTLB, CGT_HCR_TTLBOS), + MCB(CGT_HCR_TVM_TRVM, CGT_HCR_TVM, CGT_HCR_TRVM), + MCB(CGT_HCR_TPU_TICAB, CGT_HCR_TPU, CGT_HCR_TICAB), + MCB(CGT_HCR_TPU_TOCU, CGT_HCR_TPU, CGT_HCR_TOCU), + MCB(CGT_HCR_NV1_nNV2_ENSCXT, CGT_HCR_NV1_nNV2, CGT_HCR_ENSCXT), + MCB(CGT_MDCR_TPM_TPMCR, CGT_MDCR_TPM, CGT_MDCR_TPMCR), + MCB(CGT_MDCR_TDE_TDA, CGT_MDCR_TDE, CGT_MDCR_TDA), + MCB(CGT_MDCR_TDE_TDOSA, CGT_MDCR_TDE, CGT_MDCR_TDOSA), + MCB(CGT_MDCR_TDE_TDRA, CGT_MDCR_TDE, CGT_MDCR_TDRA), + MCB(CGT_MDCR_TDCC_TDE_TDA, CGT_MDCR_TDCC, CGT_MDCR_TDE, CGT_MDCR_TDA), +}; + +typedef enum trap_behaviour (*complex_condition_check)(struct kvm_vcpu *); + +/* + * Warning, maximum confusion ahead. + * + * When E2H=0, CNTHCTL_EL2[1:0] are defined as EL1PCEN:EL1PCTEN + * When E2H=1, CNTHCTL_EL2[11:10] are defined as EL1PTEN:EL1PCTEN + * + * Note the single letter difference? Yet, the bits have the same + * function despite a different layout and a different name. + * + * We don't try to reconcile this mess. We just use the E2H=0 bits + * to generate something that is in the E2H=1 format, and live with + * it. You're welcome. + */ +static u64 get_sanitized_cnthctl(struct kvm_vcpu *vcpu) +{ + u64 val = __vcpu_sys_reg(vcpu, CNTHCTL_EL2); + + if (!vcpu_el2_e2h_is_set(vcpu)) + val = (val & (CNTHCTL_EL1PCEN | CNTHCTL_EL1PCTEN)) << 10; + + return val & ((CNTHCTL_EL1PCEN | CNTHCTL_EL1PCTEN) << 10); +} + +static enum trap_behaviour check_cnthctl_el1pcten(struct kvm_vcpu *vcpu) +{ + if (get_sanitized_cnthctl(vcpu) & (CNTHCTL_EL1PCTEN << 10)) + return BEHAVE_HANDLE_LOCALLY; + + return BEHAVE_FORWARD_ANY; +} + +static enum trap_behaviour check_cnthctl_el1pten(struct kvm_vcpu *vcpu) +{ + if (get_sanitized_cnthctl(vcpu) & (CNTHCTL_EL1PCEN << 10)) + return BEHAVE_HANDLE_LOCALLY; + + return BEHAVE_FORWARD_ANY; +} + +#define CCC(id, fn) \ + [id - __COMPLEX_CONDITIONS__] = fn + +static const complex_condition_check ccc[] = { + CCC(CGT_CNTHCTL_EL1PCTEN, check_cnthctl_el1pcten), + CCC(CGT_CNTHCTL_EL1PTEN, check_cnthctl_el1pten), +}; + +/* + * Bit assignment for the trap controls. We use a 64bit word with the + * following layout for each trapped sysreg: + * + * [9:0] enum cgt_group_id (10 bits) + * [13:10] enum fgt_group_id (4 bits) + * [19:14] bit number in the FGT register (6 bits) + * [20] trap polarity (1 bit) + * [25:21] FG filter (5 bits) + * [62:26] Unused (37 bits) + * [63] RES0 - Must be zero, as lost on insertion in the xarray + */ +#define TC_CGT_BITS 10 +#define TC_FGT_BITS 4 +#define TC_FGF_BITS 5 + +union trap_config { + u64 val; + struct { + unsigned long cgt:TC_CGT_BITS; /* Coarse Grained Trap id */ + unsigned long fgt:TC_FGT_BITS; /* Fine Grained Trap id */ + unsigned long bit:6; /* Bit number */ + unsigned long pol:1; /* Polarity */ + unsigned long fgf:TC_FGF_BITS; /* Fine Grained Filter */ + unsigned long unused:37; /* Unused, should be zero */ + unsigned long mbz:1; /* Must Be Zero */ + }; +}; + +struct encoding_to_trap_config { + const u32 encoding; + const u32 end; + const union trap_config tc; + const unsigned int line; +}; + +#define SR_RANGE_TRAP(sr_start, sr_end, trap_id) \ + { \ + .encoding = sr_start, \ + .end = sr_end, \ + .tc = { \ + .cgt = trap_id, \ + }, \ + .line = __LINE__, \ + } + +#define SR_TRAP(sr, trap_id) SR_RANGE_TRAP(sr, sr, trap_id) + +/* + * Map encoding to trap bits for exception reported with EC=0x18. + * These must only be evaluated when running a nested hypervisor, but + * that the current context is not a hypervisor context. When the + * trapped access matches one of the trap controls, the exception is + * re-injected in the nested hypervisor. + */ +static const struct encoding_to_trap_config encoding_to_cgt[] __initconst = { + SR_TRAP(SYS_REVIDR_EL1, CGT_HCR_TID1), + SR_TRAP(SYS_AIDR_EL1, CGT_HCR_TID1), + SR_TRAP(SYS_SMIDR_EL1, CGT_HCR_TID1), + SR_TRAP(SYS_CTR_EL0, CGT_HCR_TID2), + SR_TRAP(SYS_CCSIDR_EL1, CGT_HCR_TID2_TID4), + SR_TRAP(SYS_CCSIDR2_EL1, CGT_HCR_TID2_TID4), + SR_TRAP(SYS_CLIDR_EL1, CGT_HCR_TID2_TID4), + SR_TRAP(SYS_CSSELR_EL1, CGT_HCR_TID2_TID4), + SR_RANGE_TRAP(SYS_ID_PFR0_EL1, + sys_reg(3, 0, 0, 7, 7), CGT_HCR_TID3), + SR_TRAP(SYS_ICC_SGI0R_EL1, CGT_HCR_IMO_FMO), + SR_TRAP(SYS_ICC_ASGI1R_EL1, CGT_HCR_IMO_FMO), + SR_TRAP(SYS_ICC_SGI1R_EL1, CGT_HCR_IMO_FMO), + SR_RANGE_TRAP(sys_reg(3, 0, 11, 0, 0), + sys_reg(3, 0, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 1, 11, 0, 0), + sys_reg(3, 1, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 2, 11, 0, 0), + sys_reg(3, 2, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 3, 11, 0, 0), + sys_reg(3, 3, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 4, 11, 0, 0), + sys_reg(3, 4, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 5, 11, 0, 0), + sys_reg(3, 5, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 6, 11, 0, 0), + sys_reg(3, 6, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 7, 11, 0, 0), + sys_reg(3, 7, 11, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 0, 15, 0, 0), + sys_reg(3, 0, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 1, 15, 0, 0), + sys_reg(3, 1, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 2, 15, 0, 0), + sys_reg(3, 2, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 3, 15, 0, 0), + sys_reg(3, 3, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 4, 15, 0, 0), + sys_reg(3, 4, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 5, 15, 0, 0), + sys_reg(3, 5, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 6, 15, 0, 0), + sys_reg(3, 6, 15, 15, 7), CGT_HCR_TIDCP), + SR_RANGE_TRAP(sys_reg(3, 7, 15, 0, 0), + sys_reg(3, 7, 15, 15, 7), CGT_HCR_TIDCP), + SR_TRAP(SYS_ACTLR_EL1, CGT_HCR_TACR), + SR_TRAP(SYS_DC_ISW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CISW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_IGSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_IGDSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CGSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CGDSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CIGSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CIGDSW, CGT_HCR_TSW), + SR_TRAP(SYS_DC_CIVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CVAP, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CVADP, CGT_HCR_TPC), + SR_TRAP(SYS_DC_IVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CIGVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CIGDVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_IGVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_IGDVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CGVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CGDVAC, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CGVAP, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CGDVAP, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CGVADP, CGT_HCR_TPC), + SR_TRAP(SYS_DC_CGDVADP, CGT_HCR_TPC), + SR_TRAP(SYS_IC_IVAU, CGT_HCR_TPU_TOCU), + SR_TRAP(SYS_IC_IALLU, CGT_HCR_TPU_TOCU), + SR_TRAP(SYS_IC_IALLUIS, CGT_HCR_TPU_TICAB), + SR_TRAP(SYS_DC_CVAU, CGT_HCR_TPU_TOCU), + SR_TRAP(OP_TLBI_RVAE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVAAE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVALE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVAALE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VMALLE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VAE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_ASIDE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VAAE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VALE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VAALE1, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVAE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVAAE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVALE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVAALE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VMALLE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VAE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_ASIDE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VAAE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VALE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_VAALE1NXS, CGT_HCR_TTLB), + SR_TRAP(OP_TLBI_RVAE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVAAE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVALE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVAALE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VMALLE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VAE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_ASIDE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VAAE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VALE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VAALE1IS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVAE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVAAE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVALE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_RVAALE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VMALLE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VAE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_ASIDE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VAAE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VALE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VAALE1ISNXS, CGT_HCR_TTLB_TTLBIS), + SR_TRAP(OP_TLBI_VMALLE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VAE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_ASIDE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VAAE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VALE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VAALE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVAE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVAAE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVALE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVAALE1OS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VMALLE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VAE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_ASIDE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VAAE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VALE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_VAALE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVAE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVAAE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVALE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(OP_TLBI_RVAALE1OSNXS, CGT_HCR_TTLB_TTLBOS), + SR_TRAP(SYS_SCTLR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_TTBR0_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_TTBR1_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_TCR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_ESR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_FAR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_AFSR0_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_AFSR1_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_MAIR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_AMAIR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_CONTEXTIDR_EL1, CGT_HCR_TVM_TRVM), + SR_TRAP(SYS_DC_ZVA, CGT_HCR_TDZ), + SR_TRAP(SYS_DC_GVA, CGT_HCR_TDZ), + SR_TRAP(SYS_DC_GZVA, CGT_HCR_TDZ), + SR_TRAP(SYS_LORSA_EL1, CGT_HCR_TLOR), + SR_TRAP(SYS_LOREA_EL1, CGT_HCR_TLOR), + SR_TRAP(SYS_LORN_EL1, CGT_HCR_TLOR), + SR_TRAP(SYS_LORC_EL1, CGT_HCR_TLOR), + SR_TRAP(SYS_LORID_EL1, CGT_HCR_TLOR), + SR_TRAP(SYS_ERRIDR_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERRSELR_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXADDR_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXCTLR_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXFR_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXMISC0_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXMISC1_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXMISC2_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXMISC3_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_ERXSTATUS_EL1, CGT_HCR_TERR), + SR_TRAP(SYS_APIAKEYLO_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APIAKEYHI_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APIBKEYLO_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APIBKEYHI_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APDAKEYLO_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APDAKEYHI_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APDBKEYLO_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APDBKEYHI_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APGAKEYLO_EL1, CGT_HCR_APK), + SR_TRAP(SYS_APGAKEYHI_EL1, CGT_HCR_APK), + /* All _EL2 registers */ + SR_RANGE_TRAP(sys_reg(3, 4, 0, 0, 0), + sys_reg(3, 4, 3, 15, 7), CGT_HCR_NV), + /* Skip the SP_EL1 encoding... */ + SR_TRAP(SYS_SPSR_EL2, CGT_HCR_NV), + SR_TRAP(SYS_ELR_EL2, CGT_HCR_NV), + SR_RANGE_TRAP(sys_reg(3, 4, 4, 1, 1), + sys_reg(3, 4, 10, 15, 7), CGT_HCR_NV), + SR_RANGE_TRAP(sys_reg(3, 4, 12, 0, 0), + sys_reg(3, 4, 14, 15, 7), CGT_HCR_NV), + /* All _EL02, _EL12 registers */ + SR_RANGE_TRAP(sys_reg(3, 5, 0, 0, 0), + sys_reg(3, 5, 10, 15, 7), CGT_HCR_NV), + SR_RANGE_TRAP(sys_reg(3, 5, 12, 0, 0), + sys_reg(3, 5, 14, 15, 7), CGT_HCR_NV), + SR_TRAP(OP_AT_S1E2R, CGT_HCR_NV), + SR_TRAP(OP_AT_S1E2W, CGT_HCR_NV), + SR_TRAP(OP_AT_S12E1R, CGT_HCR_NV), + SR_TRAP(OP_AT_S12E1W, CGT_HCR_NV), + SR_TRAP(OP_AT_S12E0R, CGT_HCR_NV), + SR_TRAP(OP_AT_S12E0W, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2E1, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2E1, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2LE1, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2LE1, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVAE2, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVALE2, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE2, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VAE2, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE1, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VALE2, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VMALLS12E1, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2E1NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2E1NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2LE1NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2LE1NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVAE2NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVALE2NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE2NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VAE2NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE1NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VALE2NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VMALLS12E1NXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2E1IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2E1IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2LE1IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2LE1IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVAE2IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVALE2IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE2IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VAE2IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE1IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VALE2IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VMALLS12E1IS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2E1ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2E1ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2LE1ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2LE1ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVAE2ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVALE2ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE2ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VAE2ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE1ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VALE2ISNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VMALLS12E1ISNXS,CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE2OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VAE2OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE1OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VALE2OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VMALLS12E1OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2E1OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2E1OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2LE1OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2LE1OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVAE2OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVALE2OS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE2OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VAE2OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_ALLE1OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VALE2OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_VMALLS12E1OSNXS,CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2E1OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2E1OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_IPAS2LE1OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RIPAS2LE1OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVAE2OSNXS, CGT_HCR_NV), + SR_TRAP(OP_TLBI_RVALE2OSNXS, CGT_HCR_NV), + SR_TRAP(OP_CPP_RCTX, CGT_HCR_NV), + SR_TRAP(OP_DVP_RCTX, CGT_HCR_NV), + SR_TRAP(OP_CFP_RCTX, CGT_HCR_NV), + SR_TRAP(SYS_SP_EL1, CGT_HCR_NV_nNV2), + SR_TRAP(SYS_VBAR_EL1, CGT_HCR_NV1_nNV2), + SR_TRAP(SYS_ELR_EL1, CGT_HCR_NV1_nNV2), + SR_TRAP(SYS_SPSR_EL1, CGT_HCR_NV1_nNV2), + SR_TRAP(SYS_SCXTNUM_EL1, CGT_HCR_NV1_nNV2_ENSCXT), + SR_TRAP(SYS_SCXTNUM_EL0, CGT_HCR_ENSCXT), + SR_TRAP(OP_AT_S1E1R, CGT_HCR_AT), + SR_TRAP(OP_AT_S1E1W, CGT_HCR_AT), + SR_TRAP(OP_AT_S1E0R, CGT_HCR_AT), + SR_TRAP(OP_AT_S1E0W, CGT_HCR_AT), + SR_TRAP(OP_AT_S1E1RP, CGT_HCR_AT), + SR_TRAP(OP_AT_S1E1WP, CGT_HCR_AT), + SR_TRAP(SYS_ERXPFGF_EL1, CGT_HCR_nFIEN), + SR_TRAP(SYS_ERXPFGCTL_EL1, CGT_HCR_nFIEN), + SR_TRAP(SYS_ERXPFGCDN_EL1, CGT_HCR_nFIEN), + SR_TRAP(SYS_PMCR_EL0, CGT_MDCR_TPM_TPMCR), + SR_TRAP(SYS_PMCNTENSET_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMCNTENCLR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMOVSSET_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMOVSCLR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMCEID0_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMCEID1_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMXEVTYPER_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMSWINC_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMSELR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMXEVCNTR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMCCNTR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMUSERENR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_PMINTENSET_EL1, CGT_MDCR_TPM), + SR_TRAP(SYS_PMINTENCLR_EL1, CGT_MDCR_TPM), + SR_TRAP(SYS_PMMIR_EL1, CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(0), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(1), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(2), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(3), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(4), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(5), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(6), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(7), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(8), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(9), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(10), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(11), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(12), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(13), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(14), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(15), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(16), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(17), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(18), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(19), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(20), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(21), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(22), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(23), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(24), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(25), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(26), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(27), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(28), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(29), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVCNTRn_EL0(30), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(0), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(1), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(2), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(3), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(4), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(5), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(6), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(7), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(8), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(9), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(10), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(11), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(12), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(13), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(14), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(15), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(16), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(17), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(18), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(19), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(20), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(21), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(22), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(23), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(24), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(25), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(26), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(27), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(28), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(29), CGT_MDCR_TPM), + SR_TRAP(SYS_PMEVTYPERn_EL0(30), CGT_MDCR_TPM), + SR_TRAP(SYS_PMCCFILTR_EL0, CGT_MDCR_TPM), + SR_TRAP(SYS_MDCCSR_EL0, CGT_MDCR_TDCC_TDE_TDA), + SR_TRAP(SYS_MDCCINT_EL1, CGT_MDCR_TDCC_TDE_TDA), + SR_TRAP(SYS_OSDTRRX_EL1, CGT_MDCR_TDCC_TDE_TDA), + SR_TRAP(SYS_OSDTRTX_EL1, CGT_MDCR_TDCC_TDE_TDA), + SR_TRAP(SYS_DBGDTR_EL0, CGT_MDCR_TDCC_TDE_TDA), + /* + * Also covers DBGDTRRX_EL0, which has the same encoding as + * SYS_DBGDTRTX_EL0... + */ + SR_TRAP(SYS_DBGDTRTX_EL0, CGT_MDCR_TDCC_TDE_TDA), + SR_TRAP(SYS_MDSCR_EL1, CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_OSECCR_EL1, CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(0), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(1), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(2), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(3), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(4), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(5), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(6), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(7), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(8), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(9), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(10), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(11), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(12), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(13), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(14), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBVRn_EL1(15), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(0), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(1), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(2), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(3), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(4), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(5), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(6), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(7), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(8), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(9), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(10), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(11), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(12), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(13), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(14), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGBCRn_EL1(15), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(0), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(1), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(2), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(3), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(4), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(5), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(6), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(7), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(8), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(9), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(10), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(11), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(12), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(13), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(14), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWVRn_EL1(15), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(0), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(1), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(2), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(3), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(4), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(5), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(6), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(7), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(8), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(9), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(10), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(11), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(12), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(13), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGWCRn_EL1(14), CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGCLAIMSET_EL1, CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGCLAIMCLR_EL1, CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_DBGAUTHSTATUS_EL1, CGT_MDCR_TDE_TDA), + SR_TRAP(SYS_OSLAR_EL1, CGT_MDCR_TDE_TDOSA), + SR_TRAP(SYS_OSLSR_EL1, CGT_MDCR_TDE_TDOSA), + SR_TRAP(SYS_OSDLR_EL1, CGT_MDCR_TDE_TDOSA), + SR_TRAP(SYS_DBGPRCR_EL1, CGT_MDCR_TDE_TDOSA), + SR_TRAP(SYS_MDRAR_EL1, CGT_MDCR_TDE_TDRA), + SR_TRAP(SYS_PMBLIMITR_EL1, CGT_MDCR_E2PB), + SR_TRAP(SYS_PMBPTR_EL1, CGT_MDCR_E2PB), + SR_TRAP(SYS_PMBSR_EL1, CGT_MDCR_E2PB), + SR_TRAP(SYS_PMSCR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSEVFR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSFCR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSICR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSIDR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSIRR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSLATFR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_PMSNEVFR_EL1, CGT_MDCR_TPMS), + SR_TRAP(SYS_TRFCR_EL1, CGT_MDCR_TTRF), + SR_TRAP(SYS_TRBBASER_EL1, CGT_MDCR_E2TB), + SR_TRAP(SYS_TRBLIMITR_EL1, CGT_MDCR_E2TB), + SR_TRAP(SYS_TRBMAR_EL1, CGT_MDCR_E2TB), + SR_TRAP(SYS_TRBPTR_EL1, CGT_MDCR_E2TB), + SR_TRAP(SYS_TRBSR_EL1, CGT_MDCR_E2TB), + SR_TRAP(SYS_TRBTRG_EL1, CGT_MDCR_E2TB), + SR_TRAP(SYS_CNTP_TVAL_EL0, CGT_CNTHCTL_EL1PTEN), + SR_TRAP(SYS_CNTP_CVAL_EL0, CGT_CNTHCTL_EL1PTEN), + SR_TRAP(SYS_CNTP_CTL_EL0, CGT_CNTHCTL_EL1PTEN), + SR_TRAP(SYS_CNTPCT_EL0, CGT_CNTHCTL_EL1PCTEN), + SR_TRAP(SYS_CNTPCTSS_EL0, CGT_CNTHCTL_EL1PCTEN), +}; + +static DEFINE_XARRAY(sr_forward_xa); + +enum fgt_group_id { + __NO_FGT_GROUP__, + HFGxTR_GROUP, + HDFGRTR_GROUP, + HDFGWTR_GROUP, + HFGITR_GROUP, + + /* Must be last */ + __NR_FGT_GROUP_IDS__ +}; + +enum fg_filter_id { + __NO_FGF__, + HCRX_FGTnXS, + + /* Must be last */ + __NR_FG_FILTER_IDS__ +}; + +#define SR_FGF(sr, g, b, p, f) \ + { \ + .encoding = sr, \ + .end = sr, \ + .tc = { \ + .fgt = g ## _GROUP, \ + .bit = g ## _EL2_ ## b ## _SHIFT, \ + .pol = p, \ + .fgf = f, \ + }, \ + .line = __LINE__, \ + } + +#define SR_FGT(sr, g, b, p) SR_FGF(sr, g, b, p, __NO_FGF__) + +static const struct encoding_to_trap_config encoding_to_fgt[] __initconst = { + /* HFGRTR_EL2, HFGWTR_EL2 */ + SR_FGT(SYS_TPIDR2_EL0, HFGxTR, nTPIDR2_EL0, 0), + SR_FGT(SYS_SMPRI_EL1, HFGxTR, nSMPRI_EL1, 0), + SR_FGT(SYS_ACCDATA_EL1, HFGxTR, nACCDATA_EL1, 0), + SR_FGT(SYS_ERXADDR_EL1, HFGxTR, ERXADDR_EL1, 1), + SR_FGT(SYS_ERXPFGCDN_EL1, HFGxTR, ERXPFGCDN_EL1, 1), + SR_FGT(SYS_ERXPFGCTL_EL1, HFGxTR, ERXPFGCTL_EL1, 1), + SR_FGT(SYS_ERXPFGF_EL1, HFGxTR, ERXPFGF_EL1, 1), + SR_FGT(SYS_ERXMISC0_EL1, HFGxTR, ERXMISCn_EL1, 1), + SR_FGT(SYS_ERXMISC1_EL1, HFGxTR, ERXMISCn_EL1, 1), + SR_FGT(SYS_ERXMISC2_EL1, HFGxTR, ERXMISCn_EL1, 1), + SR_FGT(SYS_ERXMISC3_EL1, HFGxTR, ERXMISCn_EL1, 1), + SR_FGT(SYS_ERXSTATUS_EL1, HFGxTR, ERXSTATUS_EL1, 1), + SR_FGT(SYS_ERXCTLR_EL1, HFGxTR, ERXCTLR_EL1, 1), + SR_FGT(SYS_ERXFR_EL1, HFGxTR, ERXFR_EL1, 1), + SR_FGT(SYS_ERRSELR_EL1, HFGxTR, ERRSELR_EL1, 1), + SR_FGT(SYS_ERRIDR_EL1, HFGxTR, ERRIDR_EL1, 1), + SR_FGT(SYS_ICC_IGRPEN0_EL1, HFGxTR, ICC_IGRPENn_EL1, 1), + SR_FGT(SYS_ICC_IGRPEN1_EL1, HFGxTR, ICC_IGRPENn_EL1, 1), + SR_FGT(SYS_VBAR_EL1, HFGxTR, VBAR_EL1, 1), + SR_FGT(SYS_TTBR1_EL1, HFGxTR, TTBR1_EL1, 1), + SR_FGT(SYS_TTBR0_EL1, HFGxTR, TTBR0_EL1, 1), + SR_FGT(SYS_TPIDR_EL0, HFGxTR, TPIDR_EL0, 1), + SR_FGT(SYS_TPIDRRO_EL0, HFGxTR, TPIDRRO_EL0, 1), + SR_FGT(SYS_TPIDR_EL1, HFGxTR, TPIDR_EL1, 1), + SR_FGT(SYS_TCR_EL1, HFGxTR, TCR_EL1, 1), + SR_FGT(SYS_SCXTNUM_EL0, HFGxTR, SCXTNUM_EL0, 1), + SR_FGT(SYS_SCXTNUM_EL1, HFGxTR, SCXTNUM_EL1, 1), + SR_FGT(SYS_SCTLR_EL1, HFGxTR, SCTLR_EL1, 1), + SR_FGT(SYS_REVIDR_EL1, HFGxTR, REVIDR_EL1, 1), + SR_FGT(SYS_PAR_EL1, HFGxTR, PAR_EL1, 1), + SR_FGT(SYS_MPIDR_EL1, HFGxTR, MPIDR_EL1, 1), + SR_FGT(SYS_MIDR_EL1, HFGxTR, MIDR_EL1, 1), + SR_FGT(SYS_MAIR_EL1, HFGxTR, MAIR_EL1, 1), + SR_FGT(SYS_LORSA_EL1, HFGxTR, LORSA_EL1, 1), + SR_FGT(SYS_LORN_EL1, HFGxTR, LORN_EL1, 1), + SR_FGT(SYS_LORID_EL1, HFGxTR, LORID_EL1, 1), + SR_FGT(SYS_LOREA_EL1, HFGxTR, LOREA_EL1, 1), + SR_FGT(SYS_LORC_EL1, HFGxTR, LORC_EL1, 1), + SR_FGT(SYS_ISR_EL1, HFGxTR, ISR_EL1, 1), + SR_FGT(SYS_FAR_EL1, HFGxTR, FAR_EL1, 1), + SR_FGT(SYS_ESR_EL1, HFGxTR, ESR_EL1, 1), + SR_FGT(SYS_DCZID_EL0, HFGxTR, DCZID_EL0, 1), + SR_FGT(SYS_CTR_EL0, HFGxTR, CTR_EL0, 1), + SR_FGT(SYS_CSSELR_EL1, HFGxTR, CSSELR_EL1, 1), + SR_FGT(SYS_CPACR_EL1, HFGxTR, CPACR_EL1, 1), + SR_FGT(SYS_CONTEXTIDR_EL1, HFGxTR, CONTEXTIDR_EL1, 1), + SR_FGT(SYS_CLIDR_EL1, HFGxTR, CLIDR_EL1, 1), + SR_FGT(SYS_CCSIDR_EL1, HFGxTR, CCSIDR_EL1, 1), + SR_FGT(SYS_APIBKEYLO_EL1, HFGxTR, APIBKey, 1), + SR_FGT(SYS_APIBKEYHI_EL1, HFGxTR, APIBKey, 1), + SR_FGT(SYS_APIAKEYLO_EL1, HFGxTR, APIAKey, 1), + SR_FGT(SYS_APIAKEYHI_EL1, HFGxTR, APIAKey, 1), + SR_FGT(SYS_APGAKEYLO_EL1, HFGxTR, APGAKey, 1), + SR_FGT(SYS_APGAKEYHI_EL1, HFGxTR, APGAKey, 1), + SR_FGT(SYS_APDBKEYLO_EL1, HFGxTR, APDBKey, 1), + SR_FGT(SYS_APDBKEYHI_EL1, HFGxTR, APDBKey, 1), + SR_FGT(SYS_APDAKEYLO_EL1, HFGxTR, APDAKey, 1), + SR_FGT(SYS_APDAKEYHI_EL1, HFGxTR, APDAKey, 1), + SR_FGT(SYS_AMAIR_EL1, HFGxTR, AMAIR_EL1, 1), + SR_FGT(SYS_AIDR_EL1, HFGxTR, AIDR_EL1, 1), + SR_FGT(SYS_AFSR1_EL1, HFGxTR, AFSR1_EL1, 1), + SR_FGT(SYS_AFSR0_EL1, HFGxTR, AFSR0_EL1, 1), + /* HFGITR_EL2 */ + SR_FGT(OP_BRB_IALL, HFGITR, nBRBIALL, 0), + SR_FGT(OP_BRB_INJ, HFGITR, nBRBINJ, 0), + SR_FGT(SYS_DC_CVAC, HFGITR, DCCVAC, 1), + SR_FGT(SYS_DC_CGVAC, HFGITR, DCCVAC, 1), + SR_FGT(SYS_DC_CGDVAC, HFGITR, DCCVAC, 1), + SR_FGT(OP_CPP_RCTX, HFGITR, CPPRCTX, 1), + SR_FGT(OP_DVP_RCTX, HFGITR, DVPRCTX, 1), + SR_FGT(OP_CFP_RCTX, HFGITR, CFPRCTX, 1), + SR_FGT(OP_TLBI_VAALE1, HFGITR, TLBIVAALE1, 1), + SR_FGT(OP_TLBI_VALE1, HFGITR, TLBIVALE1, 1), + SR_FGT(OP_TLBI_VAAE1, HFGITR, TLBIVAAE1, 1), + SR_FGT(OP_TLBI_ASIDE1, HFGITR, TLBIASIDE1, 1), + SR_FGT(OP_TLBI_VAE1, HFGITR, TLBIVAE1, 1), + SR_FGT(OP_TLBI_VMALLE1, HFGITR, TLBIVMALLE1, 1), + SR_FGT(OP_TLBI_RVAALE1, HFGITR, TLBIRVAALE1, 1), + SR_FGT(OP_TLBI_RVALE1, HFGITR, TLBIRVALE1, 1), + SR_FGT(OP_TLBI_RVAAE1, HFGITR, TLBIRVAAE1, 1), + SR_FGT(OP_TLBI_RVAE1, HFGITR, TLBIRVAE1, 1), + SR_FGT(OP_TLBI_RVAALE1IS, HFGITR, TLBIRVAALE1IS, 1), + SR_FGT(OP_TLBI_RVALE1IS, HFGITR, TLBIRVALE1IS, 1), + SR_FGT(OP_TLBI_RVAAE1IS, HFGITR, TLBIRVAAE1IS, 1), + SR_FGT(OP_TLBI_RVAE1IS, HFGITR, TLBIRVAE1IS, 1), + SR_FGT(OP_TLBI_VAALE1IS, HFGITR, TLBIVAALE1IS, 1), + SR_FGT(OP_TLBI_VALE1IS, HFGITR, TLBIVALE1IS, 1), + SR_FGT(OP_TLBI_VAAE1IS, HFGITR, TLBIVAAE1IS, 1), + SR_FGT(OP_TLBI_ASIDE1IS, HFGITR, TLBIASIDE1IS, 1), + SR_FGT(OP_TLBI_VAE1IS, HFGITR, TLBIVAE1IS, 1), + SR_FGT(OP_TLBI_VMALLE1IS, HFGITR, TLBIVMALLE1IS, 1), + SR_FGT(OP_TLBI_RVAALE1OS, HFGITR, TLBIRVAALE1OS, 1), + SR_FGT(OP_TLBI_RVALE1OS, HFGITR, TLBIRVALE1OS, 1), + SR_FGT(OP_TLBI_RVAAE1OS, HFGITR, TLBIRVAAE1OS, 1), + SR_FGT(OP_TLBI_RVAE1OS, HFGITR, TLBIRVAE1OS, 1), + SR_FGT(OP_TLBI_VAALE1OS, HFGITR, TLBIVAALE1OS, 1), + SR_FGT(OP_TLBI_VALE1OS, HFGITR, TLBIVALE1OS, 1), + SR_FGT(OP_TLBI_VAAE1OS, HFGITR, TLBIVAAE1OS, 1), + SR_FGT(OP_TLBI_ASIDE1OS, HFGITR, TLBIASIDE1OS, 1), + SR_FGT(OP_TLBI_VAE1OS, HFGITR, TLBIVAE1OS, 1), + SR_FGT(OP_TLBI_VMALLE1OS, HFGITR, TLBIVMALLE1OS, 1), + /* nXS variants must be checked against HCRX_EL2.FGTnXS */ + SR_FGF(OP_TLBI_VAALE1NXS, HFGITR, TLBIVAALE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VALE1NXS, HFGITR, TLBIVALE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAAE1NXS, HFGITR, TLBIVAAE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_ASIDE1NXS, HFGITR, TLBIASIDE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAE1NXS, HFGITR, TLBIVAE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VMALLE1NXS, HFGITR, TLBIVMALLE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAALE1NXS, HFGITR, TLBIRVAALE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVALE1NXS, HFGITR, TLBIRVALE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAAE1NXS, HFGITR, TLBIRVAAE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAE1NXS, HFGITR, TLBIRVAE1, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAALE1ISNXS, HFGITR, TLBIRVAALE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVALE1ISNXS, HFGITR, TLBIRVALE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAAE1ISNXS, HFGITR, TLBIRVAAE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAE1ISNXS, HFGITR, TLBIRVAE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAALE1ISNXS, HFGITR, TLBIVAALE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VALE1ISNXS, HFGITR, TLBIVALE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAAE1ISNXS, HFGITR, TLBIVAAE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_ASIDE1ISNXS, HFGITR, TLBIASIDE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAE1ISNXS, HFGITR, TLBIVAE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VMALLE1ISNXS, HFGITR, TLBIVMALLE1IS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAALE1OSNXS, HFGITR, TLBIRVAALE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVALE1OSNXS, HFGITR, TLBIRVALE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAAE1OSNXS, HFGITR, TLBIRVAAE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_RVAE1OSNXS, HFGITR, TLBIRVAE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAALE1OSNXS, HFGITR, TLBIVAALE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VALE1OSNXS, HFGITR, TLBIVALE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAAE1OSNXS, HFGITR, TLBIVAAE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_ASIDE1OSNXS, HFGITR, TLBIASIDE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VAE1OSNXS, HFGITR, TLBIVAE1OS, 1, HCRX_FGTnXS), + SR_FGF(OP_TLBI_VMALLE1OSNXS, HFGITR, TLBIVMALLE1OS, 1, HCRX_FGTnXS), + SR_FGT(OP_AT_S1E1WP, HFGITR, ATS1E1WP, 1), + SR_FGT(OP_AT_S1E1RP, HFGITR, ATS1E1RP, 1), + SR_FGT(OP_AT_S1E0W, HFGITR, ATS1E0W, 1), + SR_FGT(OP_AT_S1E0R, HFGITR, ATS1E0R, 1), + SR_FGT(OP_AT_S1E1W, HFGITR, ATS1E1W, 1), + SR_FGT(OP_AT_S1E1R, HFGITR, ATS1E1R, 1), + SR_FGT(SYS_DC_ZVA, HFGITR, DCZVA, 1), + SR_FGT(SYS_DC_GVA, HFGITR, DCZVA, 1), + SR_FGT(SYS_DC_GZVA, HFGITR, DCZVA, 1), + SR_FGT(SYS_DC_CIVAC, HFGITR, DCCIVAC, 1), + SR_FGT(SYS_DC_CIGVAC, HFGITR, DCCIVAC, 1), + SR_FGT(SYS_DC_CIGDVAC, HFGITR, DCCIVAC, 1), + SR_FGT(SYS_DC_CVADP, HFGITR, DCCVADP, 1), + SR_FGT(SYS_DC_CGVADP, HFGITR, DCCVADP, 1), + SR_FGT(SYS_DC_CGDVADP, HFGITR, DCCVADP, 1), + SR_FGT(SYS_DC_CVAP, HFGITR, DCCVAP, 1), + SR_FGT(SYS_DC_CGVAP, HFGITR, DCCVAP, 1), + SR_FGT(SYS_DC_CGDVAP, HFGITR, DCCVAP, 1), + SR_FGT(SYS_DC_CVAU, HFGITR, DCCVAU, 1), + SR_FGT(SYS_DC_CISW, HFGITR, DCCISW, 1), + SR_FGT(SYS_DC_CIGSW, HFGITR, DCCISW, 1), + SR_FGT(SYS_DC_CIGDSW, HFGITR, DCCISW, 1), + SR_FGT(SYS_DC_CSW, HFGITR, DCCSW, 1), + SR_FGT(SYS_DC_CGSW, HFGITR, DCCSW, 1), + SR_FGT(SYS_DC_CGDSW, HFGITR, DCCSW, 1), + SR_FGT(SYS_DC_ISW, HFGITR, DCISW, 1), + SR_FGT(SYS_DC_IGSW, HFGITR, DCISW, 1), + SR_FGT(SYS_DC_IGDSW, HFGITR, DCISW, 1), + SR_FGT(SYS_DC_IVAC, HFGITR, DCIVAC, 1), + SR_FGT(SYS_DC_IGVAC, HFGITR, DCIVAC, 1), + SR_FGT(SYS_DC_IGDVAC, HFGITR, DCIVAC, 1), + SR_FGT(SYS_IC_IVAU, HFGITR, ICIVAU, 1), + SR_FGT(SYS_IC_IALLU, HFGITR, ICIALLU, 1), + SR_FGT(SYS_IC_IALLUIS, HFGITR, ICIALLUIS, 1), + /* HDFGRTR_EL2 */ + SR_FGT(SYS_PMBIDR_EL1, HDFGRTR, PMBIDR_EL1, 1), + SR_FGT(SYS_PMSNEVFR_EL1, HDFGRTR, nPMSNEVFR_EL1, 0), + SR_FGT(SYS_BRBINF_EL1(0), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(1), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(2), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(3), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(4), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(5), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(6), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(7), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(8), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(9), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(10), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(11), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(12), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(13), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(14), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(15), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(16), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(17), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(18), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(19), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(20), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(21), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(22), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(23), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(24), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(25), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(26), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(27), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(28), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(29), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(30), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINF_EL1(31), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBINFINJ_EL1, HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(0), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(1), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(2), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(3), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(4), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(5), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(6), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(7), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(8), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(9), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(10), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(11), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(12), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(13), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(14), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(15), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(16), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(17), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(18), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(19), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(20), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(21), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(22), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(23), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(24), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(25), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(26), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(27), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(28), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(29), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(30), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRC_EL1(31), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBSRCINJ_EL1, HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(0), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(1), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(2), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(3), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(4), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(5), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(6), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(7), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(8), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(9), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(10), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(11), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(12), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(13), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(14), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(15), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(16), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(17), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(18), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(19), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(20), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(21), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(22), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(23), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(24), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(25), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(26), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(27), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(28), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(29), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(30), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGT_EL1(31), HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTGTINJ_EL1, HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBTS_EL1, HDFGRTR, nBRBDATA, 0), + SR_FGT(SYS_BRBCR_EL1, HDFGRTR, nBRBCTL, 0), + SR_FGT(SYS_BRBFCR_EL1, HDFGRTR, nBRBCTL, 0), + SR_FGT(SYS_BRBIDR0_EL1, HDFGRTR, nBRBIDR, 0), + SR_FGT(SYS_PMCEID0_EL0, HDFGRTR, PMCEIDn_EL0, 1), + SR_FGT(SYS_PMCEID1_EL0, HDFGRTR, PMCEIDn_EL0, 1), + SR_FGT(SYS_PMUSERENR_EL0, HDFGRTR, PMUSERENR_EL0, 1), + SR_FGT(SYS_TRBTRG_EL1, HDFGRTR, TRBTRG_EL1, 1), + SR_FGT(SYS_TRBSR_EL1, HDFGRTR, TRBSR_EL1, 1), + SR_FGT(SYS_TRBPTR_EL1, HDFGRTR, TRBPTR_EL1, 1), + SR_FGT(SYS_TRBMAR_EL1, HDFGRTR, TRBMAR_EL1, 1), + SR_FGT(SYS_TRBLIMITR_EL1, HDFGRTR, TRBLIMITR_EL1, 1), + SR_FGT(SYS_TRBIDR_EL1, HDFGRTR, TRBIDR_EL1, 1), + SR_FGT(SYS_TRBBASER_EL1, HDFGRTR, TRBBASER_EL1, 1), + SR_FGT(SYS_TRCVICTLR, HDFGRTR, TRCVICTLR, 1), + SR_FGT(SYS_TRCSTATR, HDFGRTR, TRCSTATR, 1), + SR_FGT(SYS_TRCSSCSR(0), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(1), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(2), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(3), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(4), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(5), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(6), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSSCSR(7), HDFGRTR, TRCSSCSRn, 1), + SR_FGT(SYS_TRCSEQSTR, HDFGRTR, TRCSEQSTR, 1), + SR_FGT(SYS_TRCPRGCTLR, HDFGRTR, TRCPRGCTLR, 1), + SR_FGT(SYS_TRCOSLSR, HDFGRTR, TRCOSLSR, 1), + SR_FGT(SYS_TRCIMSPEC(0), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(1), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(2), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(3), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(4), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(5), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(6), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCIMSPEC(7), HDFGRTR, TRCIMSPECn, 1), + SR_FGT(SYS_TRCDEVARCH, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCDEVID, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR0, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR1, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR2, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR3, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR4, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR5, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR6, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR7, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR8, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR9, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR10, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR11, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR12, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCIDR13, HDFGRTR, TRCID, 1), + SR_FGT(SYS_TRCCNTVR(0), HDFGRTR, TRCCNTVRn, 1), + SR_FGT(SYS_TRCCNTVR(1), HDFGRTR, TRCCNTVRn, 1), + SR_FGT(SYS_TRCCNTVR(2), HDFGRTR, TRCCNTVRn, 1), + SR_FGT(SYS_TRCCNTVR(3), HDFGRTR, TRCCNTVRn, 1), + SR_FGT(SYS_TRCCLAIMCLR, HDFGRTR, TRCCLAIM, 1), + SR_FGT(SYS_TRCCLAIMSET, HDFGRTR, TRCCLAIM, 1), + SR_FGT(SYS_TRCAUXCTLR, HDFGRTR, TRCAUXCTLR, 1), + SR_FGT(SYS_TRCAUTHSTATUS, HDFGRTR, TRCAUTHSTATUS, 1), + SR_FGT(SYS_TRCACATR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(8), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(9), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(10), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(11), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(12), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(13), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(14), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACATR(15), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(8), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(9), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(10), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(11), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(12), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(13), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(14), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCACVR(15), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCBBCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCCCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCCTLR0, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCCTLR1, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCIDCVR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTCTLR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTCTLR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTCTLR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTCTLR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTRLDVR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTRLDVR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTRLDVR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCNTRLDVR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCCONFIGR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCEVENTCTL0R, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCEVENTCTL1R, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCEXTINSELR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCEXTINSELR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCEXTINSELR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCEXTINSELR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCQCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(8), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(9), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(10), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(11), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(12), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(13), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(14), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(15), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(16), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(17), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(18), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(19), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(20), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(21), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(22), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(23), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(24), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(25), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(26), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(27), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(28), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(29), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(30), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSCTLR(31), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCRSR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSEQEVR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSEQEVR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSEQEVR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSEQRSTEVR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSCCR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSSPCICR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSTALLCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCSYNCPR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCTRACEIDR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCTSCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVIIECTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVIPCSSCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVISSCTLR, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCCTLR0, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCCTLR1, HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(0), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(1), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(2), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(3), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(4), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(5), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(6), HDFGRTR, TRC, 1), + SR_FGT(SYS_TRCVMIDCVR(7), HDFGRTR, TRC, 1), + SR_FGT(SYS_PMSLATFR_EL1, HDFGRTR, PMSLATFR_EL1, 1), + SR_FGT(SYS_PMSIRR_EL1, HDFGRTR, PMSIRR_EL1, 1), + SR_FGT(SYS_PMSIDR_EL1, HDFGRTR, PMSIDR_EL1, 1), + SR_FGT(SYS_PMSICR_EL1, HDFGRTR, PMSICR_EL1, 1), + SR_FGT(SYS_PMSFCR_EL1, HDFGRTR, PMSFCR_EL1, 1), + SR_FGT(SYS_PMSEVFR_EL1, HDFGRTR, PMSEVFR_EL1, 1), + SR_FGT(SYS_PMSCR_EL1, HDFGRTR, PMSCR_EL1, 1), + SR_FGT(SYS_PMBSR_EL1, HDFGRTR, PMBSR_EL1, 1), + SR_FGT(SYS_PMBPTR_EL1, HDFGRTR, PMBPTR_EL1, 1), + SR_FGT(SYS_PMBLIMITR_EL1, HDFGRTR, PMBLIMITR_EL1, 1), + SR_FGT(SYS_PMMIR_EL1, HDFGRTR, PMMIR_EL1, 1), + SR_FGT(SYS_PMSELR_EL0, HDFGRTR, PMSELR_EL0, 1), + SR_FGT(SYS_PMOVSCLR_EL0, HDFGRTR, PMOVS, 1), + SR_FGT(SYS_PMOVSSET_EL0, HDFGRTR, PMOVS, 1), + SR_FGT(SYS_PMINTENCLR_EL1, HDFGRTR, PMINTEN, 1), + SR_FGT(SYS_PMINTENSET_EL1, HDFGRTR, PMINTEN, 1), + SR_FGT(SYS_PMCNTENCLR_EL0, HDFGRTR, PMCNTEN, 1), + SR_FGT(SYS_PMCNTENSET_EL0, HDFGRTR, PMCNTEN, 1), + SR_FGT(SYS_PMCCNTR_EL0, HDFGRTR, PMCCNTR_EL0, 1), + SR_FGT(SYS_PMCCFILTR_EL0, HDFGRTR, PMCCFILTR_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(0), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(1), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(2), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(3), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(4), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(5), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(6), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(7), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(8), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(9), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(10), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(11), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(12), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(13), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(14), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(15), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(16), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(17), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(18), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(19), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(20), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(21), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(22), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(23), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(24), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(25), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(26), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(27), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(28), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(29), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVTYPERn_EL0(30), HDFGRTR, PMEVTYPERn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(0), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(1), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(2), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(3), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(4), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(5), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(6), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(7), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(8), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(9), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(10), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(11), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(12), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(13), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(14), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(15), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(16), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(17), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(18), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(19), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(20), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(21), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(22), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(23), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(24), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(25), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(26), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(27), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(28), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(29), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_PMEVCNTRn_EL0(30), HDFGRTR, PMEVCNTRn_EL0, 1), + SR_FGT(SYS_OSDLR_EL1, HDFGRTR, OSDLR_EL1, 1), + SR_FGT(SYS_OSECCR_EL1, HDFGRTR, OSECCR_EL1, 1), + SR_FGT(SYS_OSLSR_EL1, HDFGRTR, OSLSR_EL1, 1), + SR_FGT(SYS_DBGPRCR_EL1, HDFGRTR, DBGPRCR_EL1, 1), + SR_FGT(SYS_DBGAUTHSTATUS_EL1, HDFGRTR, DBGAUTHSTATUS_EL1, 1), + SR_FGT(SYS_DBGCLAIMSET_EL1, HDFGRTR, DBGCLAIM, 1), + SR_FGT(SYS_DBGCLAIMCLR_EL1, HDFGRTR, DBGCLAIM, 1), + SR_FGT(SYS_MDSCR_EL1, HDFGRTR, MDSCR_EL1, 1), + /* + * The trap bits capture *64* debug registers per bit, but the + * ARM ARM only describes the encoding for the first 16, and + * we don't really support more than that anyway. + */ + SR_FGT(SYS_DBGWVRn_EL1(0), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(1), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(2), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(3), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(4), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(5), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(6), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(7), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(8), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(9), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(10), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(11), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(12), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(13), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(14), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWVRn_EL1(15), HDFGRTR, DBGWVRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(0), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(1), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(2), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(3), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(4), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(5), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(6), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(7), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(8), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(9), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(10), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(11), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(12), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(13), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(14), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGWCRn_EL1(15), HDFGRTR, DBGWCRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(0), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(1), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(2), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(3), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(4), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(5), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(6), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(7), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(8), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(9), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(10), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(11), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(12), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(13), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(14), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBVRn_EL1(15), HDFGRTR, DBGBVRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(0), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(1), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(2), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(3), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(4), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(5), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(6), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(7), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(8), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(9), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(10), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(11), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(12), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(13), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(14), HDFGRTR, DBGBCRn_EL1, 1), + SR_FGT(SYS_DBGBCRn_EL1(15), HDFGRTR, DBGBCRn_EL1, 1), + /* + * HDFGWTR_EL2 + * + * Although HDFGRTR_EL2 and HDFGWTR_EL2 registers largely + * overlap in their bit assignment, there are a number of bits + * that are RES0 on one side, and an actual trap bit on the + * other. The policy chosen here is to describe all the + * read-side mappings, and only the write-side mappings that + * differ from the read side, and the trap handler will pick + * the correct shadow register based on the access type. + */ + SR_FGT(SYS_TRFCR_EL1, HDFGWTR, TRFCR_EL1, 1), + SR_FGT(SYS_TRCOSLAR, HDFGWTR, TRCOSLAR, 1), + SR_FGT(SYS_PMCR_EL0, HDFGWTR, PMCR_EL0, 1), + SR_FGT(SYS_PMSWINC_EL0, HDFGWTR, PMSWINC_EL0, 1), + SR_FGT(SYS_OSLAR_EL1, HDFGWTR, OSLAR_EL1, 1), +}; + +static union trap_config get_trap_config(u32 sysreg) +{ + return (union trap_config) { + .val = xa_to_value(xa_load(&sr_forward_xa, sysreg)), + }; +} + +static __init void print_nv_trap_error(const struct encoding_to_trap_config *tc, + const char *type, int err) +{ + kvm_err("%s line %d encoding range " + "(%d, %d, %d, %d, %d) - (%d, %d, %d, %d, %d) (err=%d)\n", + type, tc->line, + sys_reg_Op0(tc->encoding), sys_reg_Op1(tc->encoding), + sys_reg_CRn(tc->encoding), sys_reg_CRm(tc->encoding), + sys_reg_Op2(tc->encoding), + sys_reg_Op0(tc->end), sys_reg_Op1(tc->end), + sys_reg_CRn(tc->end), sys_reg_CRm(tc->end), + sys_reg_Op2(tc->end), + err); +} + +int __init populate_nv_trap_config(void) +{ + int ret = 0; + + BUILD_BUG_ON(sizeof(union trap_config) != sizeof(void *)); + BUILD_BUG_ON(__NR_CGT_GROUP_IDS__ > BIT(TC_CGT_BITS)); + BUILD_BUG_ON(__NR_FGT_GROUP_IDS__ > BIT(TC_FGT_BITS)); + BUILD_BUG_ON(__NR_FG_FILTER_IDS__ > BIT(TC_FGF_BITS)); + + for (int i = 0; i < ARRAY_SIZE(encoding_to_cgt); i++) { + const struct encoding_to_trap_config *cgt = &encoding_to_cgt[i]; + void *prev; + + if (cgt->tc.val & BIT(63)) { + kvm_err("CGT[%d] has MBZ bit set\n", i); + ret = -EINVAL; + } + + if (cgt->encoding != cgt->end) { + prev = xa_store_range(&sr_forward_xa, + cgt->encoding, cgt->end, + xa_mk_value(cgt->tc.val), + GFP_KERNEL); + } else { + prev = xa_store(&sr_forward_xa, cgt->encoding, + xa_mk_value(cgt->tc.val), GFP_KERNEL); + if (prev && !xa_is_err(prev)) { + ret = -EINVAL; + print_nv_trap_error(cgt, "Duplicate CGT", ret); + } + } + + if (xa_is_err(prev)) { + ret = xa_err(prev); + print_nv_trap_error(cgt, "Failed CGT insertion", ret); + } + } + + kvm_info("nv: %ld coarse grained trap handlers\n", + ARRAY_SIZE(encoding_to_cgt)); + + if (!cpus_have_final_cap(ARM64_HAS_FGT)) + goto check_mcb; + + for (int i = 0; i < ARRAY_SIZE(encoding_to_fgt); i++) { + const struct encoding_to_trap_config *fgt = &encoding_to_fgt[i]; + union trap_config tc; + + if (fgt->tc.fgt >= __NR_FGT_GROUP_IDS__) { + ret = -EINVAL; + print_nv_trap_error(fgt, "Invalid FGT", ret); + } + + tc = get_trap_config(fgt->encoding); + + if (tc.fgt) { + ret = -EINVAL; + print_nv_trap_error(fgt, "Duplicate FGT", ret); + } + + tc.val |= fgt->tc.val; + xa_store(&sr_forward_xa, fgt->encoding, + xa_mk_value(tc.val), GFP_KERNEL); + } + + kvm_info("nv: %ld fine grained trap handlers\n", + ARRAY_SIZE(encoding_to_fgt)); + +check_mcb: + for (int id = __MULTIPLE_CONTROL_BITS__; id < __COMPLEX_CONDITIONS__; id++) { + const enum cgt_group_id *cgids; + + cgids = coarse_control_combo[id - __MULTIPLE_CONTROL_BITS__]; + + for (int i = 0; cgids[i] != __RESERVED__; i++) { + if (cgids[i] >= __MULTIPLE_CONTROL_BITS__) { + kvm_err("Recursive MCB %d/%d\n", id, cgids[i]); + ret = -EINVAL; + } + } + } + + if (ret) + xa_destroy(&sr_forward_xa); + + return ret; +} + +static enum trap_behaviour get_behaviour(struct kvm_vcpu *vcpu, + const struct trap_bits *tb) +{ + enum trap_behaviour b = BEHAVE_HANDLE_LOCALLY; + u64 val; + + val = __vcpu_sys_reg(vcpu, tb->index); + if ((val & tb->mask) == tb->value) + b |= tb->behaviour; + + return b; +} + +static enum trap_behaviour __compute_trap_behaviour(struct kvm_vcpu *vcpu, + const enum cgt_group_id id, + enum trap_behaviour b) +{ + switch (id) { + const enum cgt_group_id *cgids; + + case __RESERVED__ ... __MULTIPLE_CONTROL_BITS__ - 1: + if (likely(id != __RESERVED__)) + b |= get_behaviour(vcpu, &coarse_trap_bits[id]); + break; + case __MULTIPLE_CONTROL_BITS__ ... __COMPLEX_CONDITIONS__ - 1: + /* Yes, this is recursive. Don't do anything stupid. */ + cgids = coarse_control_combo[id - __MULTIPLE_CONTROL_BITS__]; + for (int i = 0; cgids[i] != __RESERVED__; i++) + b |= __compute_trap_behaviour(vcpu, cgids[i], b); + break; + default: + if (ARRAY_SIZE(ccc)) + b |= ccc[id - __COMPLEX_CONDITIONS__](vcpu); + break; + } + + return b; +} + +static enum trap_behaviour compute_trap_behaviour(struct kvm_vcpu *vcpu, + const union trap_config tc) +{ + enum trap_behaviour b = BEHAVE_HANDLE_LOCALLY; + + return __compute_trap_behaviour(vcpu, tc.cgt, b); +} + +static bool check_fgt_bit(u64 val, const union trap_config tc) +{ + return ((val >> tc.bit) & 1) == tc.pol; +} + +#define sanitised_sys_reg(vcpu, reg) \ + ({ \ + u64 __val; \ + __val = __vcpu_sys_reg(vcpu, reg); \ + __val &= ~__ ## reg ## _RES0; \ + (__val); \ + }) + +bool __check_nv_sr_forward(struct kvm_vcpu *vcpu) +{ + union trap_config tc; + enum trap_behaviour b; + bool is_read; + u32 sysreg; + u64 esr, val; + + if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu)) + return false; + + esr = kvm_vcpu_get_esr(vcpu); + sysreg = esr_sys64_to_sysreg(esr); + is_read = (esr & ESR_ELx_SYS64_ISS_DIR_MASK) == ESR_ELx_SYS64_ISS_DIR_READ; + + tc = get_trap_config(sysreg); + + /* + * A value of 0 for the whole entry means that we know nothing + * for this sysreg, and that it cannot be re-injected into the + * nested hypervisor. In this situation, let's cut it short. + * + * Note that ultimately, we could also make use of the xarray + * to store the index of the sysreg in the local descriptor + * array, avoiding another search... Hint, hint... + */ + if (!tc.val) + return false; + + switch ((enum fgt_group_id)tc.fgt) { + case __NO_FGT_GROUP__: + break; + + case HFGxTR_GROUP: + if (is_read) + val = sanitised_sys_reg(vcpu, HFGRTR_EL2); + else + val = sanitised_sys_reg(vcpu, HFGWTR_EL2); + break; + + case HDFGRTR_GROUP: + case HDFGWTR_GROUP: + if (is_read) + val = sanitised_sys_reg(vcpu, HDFGRTR_EL2); + else + val = sanitised_sys_reg(vcpu, HDFGWTR_EL2); + break; + + case HFGITR_GROUP: + val = sanitised_sys_reg(vcpu, HFGITR_EL2); + switch (tc.fgf) { + u64 tmp; + + case __NO_FGF__: + break; + + case HCRX_FGTnXS: + tmp = sanitised_sys_reg(vcpu, HCRX_EL2); + if (tmp & HCRX_EL2_FGTnXS) + tc.fgt = __NO_FGT_GROUP__; + } + break; + + case __NR_FGT_GROUP_IDS__: + /* Something is really wrong, bail out */ + WARN_ONCE(1, "__NR_FGT_GROUP_IDS__"); + return false; + } + + if (tc.fgt != __NO_FGT_GROUP__ && check_fgt_bit(val, tc)) + goto inject; + + b = compute_trap_behaviour(vcpu, tc); + + if (((b & BEHAVE_FORWARD_READ) && is_read) || + ((b & BEHAVE_FORWARD_WRITE) && !is_read)) + goto inject; + + return false; + +inject: + trace_kvm_forward_sysreg_trap(vcpu, sysreg, is_read); + + kvm_inject_nested_sync(vcpu, kvm_vcpu_get_esr(vcpu)); + return true; +} + static u64 kvm_check_illegal_exception_return(struct kvm_vcpu *vcpu, u64 spsr) { u64 mode = spsr & PSR_MODE_MASK; diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 20280a5233f679cea9ddf7c68b94b30b5ae83b47..95f6945c443252a2fc02934c49a1a9ab3bbfb530 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -884,21 +884,6 @@ u32 __attribute_const__ kvm_target_cpu(void) return KVM_ARM_TARGET_GENERIC_V8; } -void kvm_vcpu_preferred_target(struct kvm_vcpu_init *init) -{ - u32 target = kvm_target_cpu(); - - memset(init, 0, sizeof(*init)); - - /* - * For now, we don't return any features. - * In future, we might use features to return target - * specific features available for the preferred - * target type. - */ - init->target = (__u32)target; -} - int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) { return -EINVAL; diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 6dcd6604b6bc16c2230625f7361a5147f3ca6714..617ae6dea5d5bcf01c8c72cec5f16c684151905c 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -222,7 +222,33 @@ static int kvm_handle_eret(struct kvm_vcpu *vcpu) if (kvm_vcpu_get_esr(vcpu) & ESR_ELx_ERET_ISS_ERET) return kvm_handle_ptrauth(vcpu); - kvm_emulate_nested_eret(vcpu); + /* + * If we got here, two possibilities: + * + * - the guest is in EL2, and we need to fully emulate ERET + * + * - the guest is in EL1, and we need to reinject the + * exception into the L1 hypervisor. + * + * If KVM ever traps ERET for its own use, we'll have to + * revisit this. + */ + if (is_hyp_ctxt(vcpu)) + kvm_emulate_nested_eret(vcpu); + else + kvm_inject_nested_sync(vcpu, kvm_vcpu_get_esr(vcpu)); + + return 1; +} + +static int handle_svc(struct kvm_vcpu *vcpu) +{ + /* + * So far, SVC traps only for NV via HFGITR_EL2. A SVC from a + * 32bit guest would be caught by vpcu_mode_is_bad_32bit(), so + * we should only have to deal with a 64 bit exception. + */ + kvm_inject_nested_sync(vcpu, kvm_vcpu_get_esr(vcpu)); return 1; } @@ -239,6 +265,7 @@ static exit_handle_fn arm_exit_handlers[] = { [ESR_ELx_EC_SMC32] = handle_smc, [ESR_ELx_EC_HVC64] = handle_hvc, [ESR_ELx_EC_SMC64] = handle_smc, + [ESR_ELx_EC_SVC64] = handle_svc, [ESR_ELx_EC_SYS64] = kvm_handle_sys_reg, [ESR_ELx_EC_SVE] = handle_sve, [ESR_ELx_EC_ERET] = kvm_handle_eret, diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 34f222af61652bc05b8deaf1fa315a8ad42e9562..9cfe6bd1dbe459cb3588bccd94359369a546947e 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -70,20 +70,26 @@ static inline void __activate_traps_fpsimd32(struct kvm_vcpu *vcpu) } } -static inline bool __hfgxtr_traps_required(void) -{ - if (cpus_have_final_cap(ARM64_SME)) - return true; - - if (cpus_have_final_cap(ARM64_WORKAROUND_AMPERE_AC03_CPU_38)) - return true; +#define compute_clr_set(vcpu, reg, clr, set) \ + do { \ + u64 hfg; \ + hfg = __vcpu_sys_reg(vcpu, reg) & ~__ ## reg ## _RES0; \ + set |= hfg & __ ## reg ## _MASK; \ + clr |= ~hfg & __ ## reg ## _nMASK; \ + } while(0) - return false; -} -static inline void __activate_traps_hfgxtr(void) +static inline void __activate_traps_hfgxtr(struct kvm_vcpu *vcpu) { + struct kvm_cpu_context *hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; u64 r_clr = 0, w_clr = 0, r_set = 0, w_set = 0, tmp; + u64 r_val, w_val; + + if (!cpus_have_final_cap(ARM64_HAS_FGT)) + return; + + ctxt_sys_reg(hctxt, HFGRTR_EL2) = read_sysreg_s(SYS_HFGRTR_EL2); + ctxt_sys_reg(hctxt, HFGWTR_EL2) = read_sysreg_s(SYS_HFGWTR_EL2); if (cpus_have_final_cap(ARM64_SME)) { tmp = HFGxTR_EL2_nSMPRI_EL1_MASK | HFGxTR_EL2_nTPIDR2_EL0_MASK; @@ -98,26 +104,72 @@ static inline void __activate_traps_hfgxtr(void) if (cpus_have_final_cap(ARM64_WORKAROUND_AMPERE_AC03_CPU_38)) w_set |= HFGxTR_EL2_TCR_EL1_MASK; - sysreg_clear_set_s(SYS_HFGRTR_EL2, r_clr, r_set); - sysreg_clear_set_s(SYS_HFGWTR_EL2, w_clr, w_set); + if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) { + compute_clr_set(vcpu, HFGRTR_EL2, r_clr, r_set); + compute_clr_set(vcpu, HFGWTR_EL2, w_clr, w_set); + } + + /* The default is not to trap anything but ACCDATA_EL1 */ + r_val = __HFGRTR_EL2_nMASK & ~HFGxTR_EL2_nACCDATA_EL1; + r_val |= r_set; + r_val &= ~r_clr; + + w_val = __HFGWTR_EL2_nMASK & ~HFGxTR_EL2_nACCDATA_EL1; + w_val |= w_set; + w_val &= ~w_clr; + + write_sysreg_s(r_val, SYS_HFGRTR_EL2); + write_sysreg_s(w_val, SYS_HFGWTR_EL2); + + if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu)) + return; + + ctxt_sys_reg(hctxt, HFGITR_EL2) = read_sysreg_s(SYS_HFGITR_EL2); + + r_set = r_clr = 0; + compute_clr_set(vcpu, HFGITR_EL2, r_clr, r_set); + r_val = __HFGITR_EL2_nMASK; + r_val |= r_set; + r_val &= ~r_clr; + + write_sysreg_s(r_val, SYS_HFGITR_EL2); + + ctxt_sys_reg(hctxt, HDFGRTR_EL2) = read_sysreg_s(SYS_HDFGRTR_EL2); + ctxt_sys_reg(hctxt, HDFGWTR_EL2) = read_sysreg_s(SYS_HDFGWTR_EL2); + + r_clr = r_set = w_clr = w_set = 0; + + compute_clr_set(vcpu, HDFGRTR_EL2, r_clr, r_set); + compute_clr_set(vcpu, HDFGWTR_EL2, w_clr, w_set); + + r_val = __HDFGRTR_EL2_nMASK; + r_val |= r_set; + r_val &= ~r_clr; + + w_val = __HDFGWTR_EL2_nMASK; + w_val |= w_set; + w_val &= ~w_clr; + + write_sysreg_s(r_val, SYS_HDFGRTR_EL2); + write_sysreg_s(w_val, SYS_HDFGWTR_EL2); } -static inline void __deactivate_traps_hfgxtr(void) +static inline void __deactivate_traps_hfgxtr(struct kvm_vcpu *vcpu) { - u64 r_clr = 0, w_clr = 0, r_set = 0, w_set = 0, tmp; + struct kvm_cpu_context *hctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; - if (cpus_have_final_cap(ARM64_SME)) { - tmp = HFGxTR_EL2_nSMPRI_EL1_MASK | HFGxTR_EL2_nTPIDR2_EL0_MASK; + if (!cpus_have_final_cap(ARM64_HAS_FGT)) + return; - r_set |= tmp; - w_set |= tmp; - } + write_sysreg_s(ctxt_sys_reg(hctxt, HFGRTR_EL2), SYS_HFGRTR_EL2); + write_sysreg_s(ctxt_sys_reg(hctxt, HFGWTR_EL2), SYS_HFGWTR_EL2); - if (cpus_have_final_cap(ARM64_WORKAROUND_AMPERE_AC03_CPU_38)) - w_clr |= HFGxTR_EL2_TCR_EL1_MASK; + if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu)) + return; - sysreg_clear_set_s(SYS_HFGRTR_EL2, r_clr, r_set); - sysreg_clear_set_s(SYS_HFGWTR_EL2, w_clr, w_set); + write_sysreg_s(ctxt_sys_reg(hctxt, HFGITR_EL2), SYS_HFGITR_EL2); + write_sysreg_s(ctxt_sys_reg(hctxt, HDFGRTR_EL2), SYS_HDFGRTR_EL2); + write_sysreg_s(ctxt_sys_reg(hctxt, HDFGWTR_EL2), SYS_HDFGWTR_EL2); } static inline void __activate_traps_common(struct kvm_vcpu *vcpu) @@ -145,8 +197,21 @@ static inline void __activate_traps_common(struct kvm_vcpu *vcpu) vcpu->arch.mdcr_el2_host = read_sysreg(mdcr_el2); write_sysreg(vcpu->arch.mdcr_el2, mdcr_el2); - if (__hfgxtr_traps_required()) - __activate_traps_hfgxtr(); + if (cpus_have_final_cap(ARM64_HAS_HCX)) { + u64 hcrx = HCRX_GUEST_FLAGS; + if (vcpu_has_nv(vcpu) && !is_hyp_ctxt(vcpu)) { + u64 clr = 0, set = 0; + + compute_clr_set(vcpu, HCRX_EL2, clr, set); + + hcrx |= set; + hcrx &= ~clr; + } + + write_sysreg_s(hcrx, SYS_HCRX_EL2); + } + + __activate_traps_hfgxtr(vcpu); } static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) @@ -162,8 +227,10 @@ static inline void __deactivate_traps_common(struct kvm_vcpu *vcpu) vcpu_clear_flag(vcpu, PMUSERENR_ON_CPU); } - if (__hfgxtr_traps_required()) - __deactivate_traps_hfgxtr(); + if (cpus_have_final_cap(ARM64_HAS_HCX)) + write_sysreg_s(HCRX_HOST_FLAGS, SYS_HCRX_EL2); + + __deactivate_traps_hfgxtr(vcpu); } static inline void ___activate_traps(struct kvm_vcpu *vcpu) @@ -177,9 +244,6 @@ static inline void ___activate_traps(struct kvm_vcpu *vcpu) if (cpus_have_final_cap(ARM64_HAS_RAS_EXTN) && (hcr & HCR_VSE)) write_sysreg_s(vcpu->arch.vsesr_el2, SYS_VSESR_EL2); - - if (cpus_have_final_cap(ARM64_HAS_HCX)) - write_sysreg_s(HCRX_GUEST_FLAGS, SYS_HCRX_EL2); } static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) @@ -194,9 +258,6 @@ static inline void ___deactivate_traps(struct kvm_vcpu *vcpu) vcpu->arch.hcr_el2 &= ~HCR_VSE; vcpu->arch.hcr_el2 |= read_sysreg(hcr_el2) & HCR_VSE; } - - if (cpus_have_final_cap(ARM64_HAS_HCX)) - write_sysreg_s(HCRX_HOST_FLAGS, SYS_HCRX_EL2); } static inline bool __populate_fault_info(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/hyp/include/nvhe/ffa.h b/arch/arm64/kvm/hyp/include/nvhe/ffa.h index 1becb10ecd8057d49dac1730409bfb10077cd1e0..d9fd5e6c7d3cca73c1f687e64d7848cceae92f98 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/ffa.h +++ b/arch/arm64/kvm/hyp/include/nvhe/ffa.h @@ -12,6 +12,6 @@ #define FFA_MAX_FUNC_NUM 0x7F int hyp_ffa_init(void *pages); -bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt); +bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id); #endif /* __KVM_HYP_FFA_H */ diff --git a/arch/arm64/kvm/hyp/include/nvhe/mm.h b/arch/arm64/kvm/hyp/include/nvhe/mm.h index d5ec972b5c1ec8a3506d4e5851d249e7f8ea3b8b..230e4f2527def39a1f5fe175b54b1ba637e613f0 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/mm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/mm.h @@ -26,6 +26,7 @@ int pkvm_create_mappings_locked(void *from, void *to, enum kvm_pgtable_prot prot int __pkvm_create_private_mapping(phys_addr_t phys, size_t size, enum kvm_pgtable_prot prot, unsigned long *haddr); +int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr); int pkvm_alloc_private_va_range(size_t size, unsigned long *haddr); #endif /* __KVM_HYP_MM_H */ diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index ab4f5d160c58f9969f4820a479dbf3d119b2d617..6e4dba9eadef522fe7c4ccc93e362d405aa259c1 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -634,9 +634,8 @@ static bool do_ffa_features(struct arm_smccc_res *res, return true; } -bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt) +bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id) { - DECLARE_REG(u64, func_id, host_ctxt, 0); struct arm_smccc_res res; /* diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-init.S b/arch/arm64/kvm/hyp/nvhe/hyp-init.S index 90fade1b032e4bc614861ebd49e8738042768d65..1cc06e6797bda378a59b072de8c6f9a1612f6893 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-init.S +++ b/arch/arm64/kvm/hyp/nvhe/hyp-init.S @@ -57,6 +57,7 @@ __do_hyp_init: cmp x0, #HVC_STUB_HCALL_NR b.lo __kvm_handle_stub_hvc + bic x0, x0, #ARM_SMCCC_CALL_HINTS mov x3, #KVM_HOST_SMCCC_FUNC(__kvm_hyp_init) cmp x0, x3 b.eq 1f diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index a169c619db60b85e92f33c631ceb84d27173c674..2385fd03ed87c6c0450a1c2dbe830e95ce235ecc 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -135,6 +135,16 @@ static void handle___kvm_tlb_flush_vmid_ipa_nsh(struct kvm_cpu_context *host_ctx __kvm_tlb_flush_vmid_ipa_nsh(kern_hyp_va(mmu), ipa, level); } +static void +handle___kvm_tlb_flush_vmid_range(struct kvm_cpu_context *host_ctxt) +{ + DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1); + DECLARE_REG(phys_addr_t, start, host_ctxt, 2); + DECLARE_REG(unsigned long, pages, host_ctxt, 3); + + __kvm_tlb_flush_vmid_range(kern_hyp_va(mmu), start, pages); +} + static void handle___kvm_tlb_flush_vmid(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(struct kvm_s2_mmu *, mmu, host_ctxt, 1); @@ -327,6 +337,7 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__kvm_tlb_flush_vmid_ipa), HANDLE_FUNC(__kvm_tlb_flush_vmid_ipa_nsh), HANDLE_FUNC(__kvm_tlb_flush_vmid), + HANDLE_FUNC(__kvm_tlb_flush_vmid_range), HANDLE_FUNC(__kvm_flush_cpu_context), HANDLE_FUNC(__kvm_timer_set_cntvoff), HANDLE_FUNC(__vgic_v3_read_vmcr), @@ -357,6 +368,7 @@ static void handle_host_hcall(struct kvm_cpu_context *host_ctxt) if (static_branch_unlikely(&kvm_protected_mode_initialized)) hcall_min = __KVM_HOST_SMCCC_FUNC___pkvm_prot_finalize; + id &= ~ARM_SMCCC_CALL_HINTS; id -= KVM_HOST_SMCCC_ID(0); if (unlikely(id < hcall_min || id >= ARRAY_SIZE(host_hcall))) @@ -381,11 +393,14 @@ static void default_host_smc_handler(struct kvm_cpu_context *host_ctxt) static void handle_host_smc(struct kvm_cpu_context *host_ctxt) { + DECLARE_REG(u64, func_id, host_ctxt, 0); bool handled; - handled = kvm_host_psci_handler(host_ctxt); + func_id &= ~ARM_SMCCC_CALL_HINTS; + + handled = kvm_host_psci_handler(host_ctxt, func_id); if (!handled) - handled = kvm_host_ffa_handler(host_ctxt); + handled = kvm_host_ffa_handler(host_ctxt, func_id); if (!handled) default_host_smc_handler(host_ctxt); diff --git a/arch/arm64/kvm/hyp/nvhe/mm.c b/arch/arm64/kvm/hyp/nvhe/mm.c index 318298eb3d6b20eeda1854b10adf06ce676ee790..65a7a186d7b217e599688f465b9d49b64ee2b9f0 100644 --- a/arch/arm64/kvm/hyp/nvhe/mm.c +++ b/arch/arm64/kvm/hyp/nvhe/mm.c @@ -44,6 +44,27 @@ static int __pkvm_create_mappings(unsigned long start, unsigned long size, return err; } +static int __pkvm_alloc_private_va_range(unsigned long start, size_t size) +{ + unsigned long cur; + + hyp_assert_lock_held(&pkvm_pgd_lock); + + if (!start || start < __io_map_base) + return -EINVAL; + + /* The allocated size is always a multiple of PAGE_SIZE */ + cur = start + PAGE_ALIGN(size); + + /* Are we overflowing on the vmemmap ? */ + if (cur > __hyp_vmemmap) + return -ENOMEM; + + __io_map_base = cur; + + return 0; +} + /** * pkvm_alloc_private_va_range - Allocates a private VA range. * @size: The size of the VA range to reserve. @@ -56,27 +77,16 @@ static int __pkvm_create_mappings(unsigned long start, unsigned long size, */ int pkvm_alloc_private_va_range(size_t size, unsigned long *haddr) { - unsigned long base, addr; - int ret = 0; + unsigned long addr; + int ret; hyp_spin_lock(&pkvm_pgd_lock); - - /* Align the allocation based on the order of its size */ - addr = ALIGN(__io_map_base, PAGE_SIZE << get_order(size)); - - /* The allocated size is always a multiple of PAGE_SIZE */ - base = addr + PAGE_ALIGN(size); - - /* Are we overflowing on the vmemmap ? */ - if (!addr || base > __hyp_vmemmap) - ret = -ENOMEM; - else { - __io_map_base = base; - *haddr = addr; - } - + addr = __io_map_base; + ret = __pkvm_alloc_private_va_range(addr, size); hyp_spin_unlock(&pkvm_pgd_lock); + *haddr = addr; + return ret; } @@ -340,6 +350,45 @@ int hyp_create_idmap(u32 hyp_va_bits) return __pkvm_create_mappings(start, end - start, start, PAGE_HYP_EXEC); } +int pkvm_create_stack(phys_addr_t phys, unsigned long *haddr) +{ + unsigned long addr, prev_base; + size_t size; + int ret; + + hyp_spin_lock(&pkvm_pgd_lock); + + prev_base = __io_map_base; + /* + * Efficient stack verification using the PAGE_SHIFT bit implies + * an alignment of our allocation on the order of the size. + */ + size = PAGE_SIZE * 2; + addr = ALIGN(__io_map_base, size); + + ret = __pkvm_alloc_private_va_range(addr, size); + if (!ret) { + /* + * Since the stack grows downwards, map the stack to the page + * at the higher address and leave the lower guard page + * unbacked. + * + * Any valid stack address now has the PAGE_SHIFT bit as 1 + * and addresses corresponding to the guard page have the + * PAGE_SHIFT bit as 0 - this is used for overflow detection. + */ + ret = kvm_pgtable_hyp_map(&pkvm_pgtable, addr + PAGE_SIZE, + PAGE_SIZE, phys, PAGE_HYP); + if (ret) + __io_map_base = prev_base; + } + hyp_spin_unlock(&pkvm_pgd_lock); + + *haddr = addr + size; + + return ret; +} + static void *admit_host_page(void *arg) { struct kvm_hyp_memcache *host_mc = arg; diff --git a/arch/arm64/kvm/hyp/nvhe/psci-relay.c b/arch/arm64/kvm/hyp/nvhe/psci-relay.c index 24543d2a34905c66cfe39b68831b5e242359a5eb..d57bcb6ab94d25b9998bbabd5cd0cdb2fd27e794 100644 --- a/arch/arm64/kvm/hyp/nvhe/psci-relay.c +++ b/arch/arm64/kvm/hyp/nvhe/psci-relay.c @@ -273,9 +273,8 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_ } } -bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt) +bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt, u32 func_id) { - DECLARE_REG(u64, func_id, host_ctxt, 0); unsigned long ret; switch (kvm_host_psci_config.version) { diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index bb98630dfeafc5013ad942940e3101dee934237f..0d5e0a89ddce5df353fb5cccd6269686eaa674db 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -113,7 +113,6 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, for (i = 0; i < hyp_nr_cpus; i++) { struct kvm_nvhe_init_params *params = per_cpu_ptr(&kvm_init_params, i); - unsigned long hyp_addr; start = (void *)kern_hyp_va(per_cpu_base[i]); end = start + PAGE_ALIGN(hyp_percpu_size); @@ -121,33 +120,9 @@ static int recreate_hyp_mappings(phys_addr_t phys, unsigned long size, if (ret) return ret; - /* - * Allocate a contiguous HYP private VA range for the stack - * and guard page. The allocation is also aligned based on - * the order of its size. - */ - ret = pkvm_alloc_private_va_range(PAGE_SIZE * 2, &hyp_addr); + ret = pkvm_create_stack(params->stack_pa, ¶ms->stack_hyp_va); if (ret) return ret; - - /* - * Since the stack grows downwards, map the stack to the page - * at the higher address and leave the lower guard page - * unbacked. - * - * Any valid stack address now has the PAGE_SHIFT bit as 1 - * and addresses corresponding to the guard page have the - * PAGE_SHIFT bit as 0 - this is used for overflow detection. - */ - hyp_spin_lock(&pkvm_pgd_lock); - ret = kvm_pgtable_hyp_map(&pkvm_pgtable, hyp_addr + PAGE_SIZE, - PAGE_SIZE, params->stack_pa, PAGE_HYP); - hyp_spin_unlock(&pkvm_pgd_lock); - if (ret) - return ret; - - /* Update stack_hyp_va to end of the stack's private VA range */ - params->stack_hyp_va = hyp_addr + (2 * PAGE_SIZE); } /* diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index e89a23153e85e13a888a0d592c6691be23885e9a..c353a06ee7e6d624b41997021379b7b4cf77453d 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -236,7 +236,7 @@ static void early_exit_filter(struct kvm_vcpu *vcpu, u64 *exit_code) * KVM_ARM_VCPU_INIT, however, this is likely not possible for * protected VMs. */ - vcpu->arch.target = -1; + vcpu_clear_flag(vcpu, VCPU_INITIALIZED); *exit_code &= BIT(ARM_EXIT_WITH_SERROR_BIT); *exit_code |= ARM_EXCEPTION_IL; } diff --git a/arch/arm64/kvm/hyp/nvhe/tlb.c b/arch/arm64/kvm/hyp/nvhe/tlb.c index b9991bbd8e3fd47baf911ef84dd7402173603879..1b265713d6bede228b47a423d0f1d81a83070a5c 100644 --- a/arch/arm64/kvm/hyp/nvhe/tlb.c +++ b/arch/arm64/kvm/hyp/nvhe/tlb.c @@ -182,6 +182,36 @@ void __kvm_tlb_flush_vmid_ipa_nsh(struct kvm_s2_mmu *mmu, __tlb_switch_to_host(&cxt); } +void __kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, + phys_addr_t start, unsigned long pages) +{ + struct tlb_inv_context cxt; + unsigned long stride; + + /* + * Since the range of addresses may not be mapped at + * the same level, assume the worst case as PAGE_SIZE + */ + stride = PAGE_SIZE; + start = round_down(start, stride); + + /* Switch to requested VMID */ + __tlb_switch_to_guest(mmu, &cxt, false); + + __flush_s2_tlb_range_op(ipas2e1is, start, pages, stride, 0); + + dsb(ish); + __tlbi(vmalle1is); + dsb(ish); + isb(); + + /* See the comment in __kvm_tlb_flush_vmid_ipa() */ + if (icache_is_vpipt()) + icache_inval_all_pou(); + + __tlb_switch_to_host(&cxt); +} + void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu) { struct tlb_inv_context cxt; diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index f7a93ef292506c45d72a7d44c91b5fe02c366f2b..f155b8c9e98c7fbf1298f4ecf64c6826c76fdb23 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -670,6 +670,26 @@ static bool stage2_has_fwb(struct kvm_pgtable *pgt) return !(pgt->flags & KVM_PGTABLE_S2_NOFWB); } +void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, + phys_addr_t addr, size_t size) +{ + unsigned long pages, inval_pages; + + if (!system_supports_tlb_range()) { + kvm_call_hyp(__kvm_tlb_flush_vmid, mmu); + return; + } + + pages = size >> PAGE_SHIFT; + while (pages > 0) { + inval_pages = min(pages, MAX_TLBI_RANGE_PAGES); + kvm_call_hyp(__kvm_tlb_flush_vmid_range, mmu, addr, inval_pages); + + addr += inval_pages << PAGE_SHIFT; + pages -= inval_pages; + } +} + #define KVM_S2_MEMATTR(pgt, attr) PAGE_S2_MEMATTR(attr, stage2_has_fwb(pgt)) static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot prot, @@ -786,7 +806,8 @@ static bool stage2_try_break_pte(const struct kvm_pgtable_visit_ctx *ctx, * evicted pte value (if any). */ if (kvm_pte_table(ctx->old, ctx->level)) - kvm_call_hyp(__kvm_tlb_flush_vmid, mmu); + kvm_tlb_flush_vmid_range(mmu, ctx->addr, + kvm_granule_size(ctx->level)); else if (kvm_pte_valid(ctx->old)) kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, mmu, ctx->addr, ctx->level); @@ -810,16 +831,36 @@ static void stage2_make_pte(const struct kvm_pgtable_visit_ctx *ctx, kvm_pte_t n smp_store_release(ctx->ptep, new); } -static void stage2_put_pte(const struct kvm_pgtable_visit_ctx *ctx, struct kvm_s2_mmu *mmu, - struct kvm_pgtable_mm_ops *mm_ops) +static bool stage2_unmap_defer_tlb_flush(struct kvm_pgtable *pgt) +{ + /* + * If FEAT_TLBIRANGE is implemented, defer the individual + * TLB invalidations until the entire walk is finished, and + * then use the range-based TLBI instructions to do the + * invalidations. Condition deferred TLB invalidation on the + * system supporting FWB as the optimization is entirely + * pointless when the unmap walker needs to perform CMOs. + */ + return system_supports_tlb_range() && stage2_has_fwb(pgt); +} + +static void stage2_unmap_put_pte(const struct kvm_pgtable_visit_ctx *ctx, + struct kvm_s2_mmu *mmu, + struct kvm_pgtable_mm_ops *mm_ops) { + struct kvm_pgtable *pgt = ctx->arg; + /* - * Clear the existing PTE, and perform break-before-make with - * TLB maintenance if it was valid. + * Clear the existing PTE, and perform break-before-make if it was + * valid. Depending on the system support, defer the TLB maintenance + * for the same until the entire unmap walk is completed. */ if (kvm_pte_valid(ctx->old)) { kvm_clear_pte(ctx->ptep); - kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, mmu, ctx->addr, ctx->level); + + if (!stage2_unmap_defer_tlb_flush(pgt)) + kvm_call_hyp(__kvm_tlb_flush_vmid_ipa, mmu, + ctx->addr, ctx->level); } mm_ops->put_page(ctx->ptep); @@ -1077,7 +1118,7 @@ static int stage2_unmap_walker(const struct kvm_pgtable_visit_ctx *ctx, * block entry and rely on the remaining portions being faulted * back lazily. */ - stage2_put_pte(ctx, mmu, mm_ops); + stage2_unmap_put_pte(ctx, mmu, mm_ops); if (need_flush && mm_ops->dcache_clean_inval_poc) mm_ops->dcache_clean_inval_poc(kvm_pte_follow(ctx->old, mm_ops), @@ -1091,13 +1132,19 @@ static int stage2_unmap_walker(const struct kvm_pgtable_visit_ctx *ctx, int kvm_pgtable_stage2_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size) { + int ret; struct kvm_pgtable_walker walker = { .cb = stage2_unmap_walker, .arg = pgt, .flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST, }; - return kvm_pgtable_walk(pgt, addr, size, &walker); + ret = kvm_pgtable_walk(pgt, addr, size, &walker); + if (stage2_unmap_defer_tlb_flush(pgt)) + /* Perform the deferred TLB invalidations */ + kvm_tlb_flush_vmid_range(pgt->mmu, addr, size); + + return ret; } struct stage2_attr_data { diff --git a/arch/arm64/kvm/hyp/vhe/tlb.c b/arch/arm64/kvm/hyp/vhe/tlb.c index e69da550cdc5b50b5dce1de0283608ac063e7d27..46bd43f61d76f5dc1676cfad45a64830e138a526 100644 --- a/arch/arm64/kvm/hyp/vhe/tlb.c +++ b/arch/arm64/kvm/hyp/vhe/tlb.c @@ -143,6 +143,34 @@ void __kvm_tlb_flush_vmid_ipa_nsh(struct kvm_s2_mmu *mmu, __tlb_switch_to_host(&cxt); } +void __kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, + phys_addr_t start, unsigned long pages) +{ + struct tlb_inv_context cxt; + unsigned long stride; + + /* + * Since the range of addresses may not be mapped at + * the same level, assume the worst case as PAGE_SIZE + */ + stride = PAGE_SIZE; + start = round_down(start, stride); + + dsb(ishst); + + /* Switch to requested VMID */ + __tlb_switch_to_guest(mmu, &cxt); + + __flush_s2_tlb_range_op(ipas2e1is, start, pages, stride, 0); + + dsb(ish); + __tlbi(vmalle1is); + dsb(ish); + isb(); + + __tlb_switch_to_host(&cxt); +} + void __kvm_tlb_flush_vmid(struct kvm_s2_mmu *mmu) { struct tlb_inv_context cxt; diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index d3b4feed460c001d131e936e92f5660ac23882ad..482280fe22d7c59d2539b9f0e758cfc95afad0ff 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -161,15 +161,23 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot) } /** - * kvm_flush_remote_tlbs() - flush all VM TLB entries for v7/8 + * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8 * @kvm: pointer to kvm structure. * * Interface to HYP function to flush all VM TLB entries */ -void kvm_flush_remote_tlbs(struct kvm *kvm) +int kvm_arch_flush_remote_tlbs(struct kvm *kvm) { - ++kvm->stat.generic.remote_tlb_flush_requests; kvm_call_hyp(__kvm_tlb_flush_vmid, &kvm->arch.mmu); + return 0; +} + +int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, + gfn_t gfn, u64 nr_pages) +{ + kvm_tlb_flush_vmid_range(&kvm->arch.mmu, + gfn << PAGE_SHIFT, nr_pages << PAGE_SHIFT); + return 0; } static bool kvm_is_device_pfn(unsigned long pfn) @@ -592,6 +600,25 @@ int create_hyp_mappings(void *from, void *to, enum kvm_pgtable_prot prot) return 0; } +static int __hyp_alloc_private_va_range(unsigned long base) +{ + lockdep_assert_held(&kvm_hyp_pgd_mutex); + + if (!PAGE_ALIGNED(base)) + return -EINVAL; + + /* + * Verify that BIT(VA_BITS - 1) hasn't been flipped by + * allocating the new area, as it would indicate we've + * overflowed the idmap/IO address range. + */ + if ((base ^ io_map_base) & BIT(VA_BITS - 1)) + return -ENOMEM; + + io_map_base = base; + + return 0; +} /** * hyp_alloc_private_va_range - Allocates a private VA range. @@ -612,29 +639,22 @@ int hyp_alloc_private_va_range(size_t size, unsigned long *haddr) /* * This assumes that we have enough space below the idmap - * page to allocate our VAs. If not, the check below will - * kick. A potential alternative would be to detect that - * overflow and switch to an allocation above the idmap. + * page to allocate our VAs. If not, the check in + * __hyp_alloc_private_va_range() will kick. A potential + * alternative would be to detect that overflow and switch + * to an allocation above the idmap. * * The allocated size is always a multiple of PAGE_SIZE. */ - base = io_map_base - PAGE_ALIGN(size); - - /* Align the allocation based on the order of its size */ - base = ALIGN_DOWN(base, PAGE_SIZE << get_order(size)); - - /* - * Verify that BIT(VA_BITS - 1) hasn't been flipped by - * allocating the new area, as it would indicate we've - * overflowed the idmap/IO address range. - */ - if ((base ^ io_map_base) & BIT(VA_BITS - 1)) - ret = -ENOMEM; - else - *haddr = io_map_base = base; + size = PAGE_ALIGN(size); + base = io_map_base - size; + ret = __hyp_alloc_private_va_range(base); mutex_unlock(&kvm_hyp_pgd_mutex); + if (!ret) + *haddr = base; + return ret; } @@ -668,6 +688,48 @@ static int __create_hyp_private_mapping(phys_addr_t phys_addr, size_t size, return ret; } +int create_hyp_stack(phys_addr_t phys_addr, unsigned long *haddr) +{ + unsigned long base; + size_t size; + int ret; + + mutex_lock(&kvm_hyp_pgd_mutex); + /* + * Efficient stack verification using the PAGE_SHIFT bit implies + * an alignment of our allocation on the order of the size. + */ + size = PAGE_SIZE * 2; + base = ALIGN_DOWN(io_map_base - size, size); + + ret = __hyp_alloc_private_va_range(base); + + mutex_unlock(&kvm_hyp_pgd_mutex); + + if (ret) { + kvm_err("Cannot allocate hyp stack guard page\n"); + return ret; + } + + /* + * Since the stack grows downwards, map the stack to the page + * at the higher address and leave the lower guard page + * unbacked. + * + * Any valid stack address now has the PAGE_SHIFT bit as 1 + * and addresses corresponding to the guard page have the + * PAGE_SHIFT bit as 0 - this is used for overflow detection. + */ + ret = __create_hyp_mappings(base + PAGE_SIZE, PAGE_SIZE, phys_addr, + PAGE_HYP); + if (ret) + kvm_err("Cannot map hyp stack\n"); + + *haddr = base + size; + + return ret; +} + /** * create_hyp_io_mappings - Map IO into both kernel and HYP * @phys_addr: The physical start address which gets mapped @@ -1075,7 +1137,7 @@ static void kvm_mmu_wp_memory_region(struct kvm *kvm, int slot) write_lock(&kvm->mmu_lock); stage2_wp_range(&kvm->arch.mmu, start, end); write_unlock(&kvm->mmu_lock); - kvm_flush_remote_tlbs(kvm); + kvm_flush_remote_tlbs_memslot(kvm, memslot); } /** @@ -1541,7 +1603,6 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, out_unlock: read_unlock(&kvm->mmu_lock); - kvm_set_pfn_accessed(pfn); kvm_release_pfn_clean(pfn); return ret != -EAGAIN ? ret : 0; } @@ -1721,7 +1782,7 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { - kvm_pfn_t pfn = pte_pfn(range->pte); + kvm_pfn_t pfn = pte_pfn(range->arg.pte); if (!kvm->arch.mmu.pgt) return false; diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index 315354d279786c32fdcc5f6832e66f590382b7e9..042695a210cebdcf753120a380175a46169e82d9 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -71,8 +71,9 @@ void access_nested_id_reg(struct kvm_vcpu *v, struct sys_reg_params *p, break; case SYS_ID_AA64MMFR0_EL1: - /* Hide ECV, FGT, ExS, Secure Memory */ - val &= ~(GENMASK_ULL(63, 43) | + /* Hide ECV, ExS, Secure Memory */ + val &= ~(NV_FTR(MMFR0, ECV) | + NV_FTR(MMFR0, EXS) | NV_FTR(MMFR0, TGRAN4_2) | NV_FTR(MMFR0, TGRAN16_2) | NV_FTR(MMFR0, TGRAN64_2) | @@ -116,7 +117,8 @@ void access_nested_id_reg(struct kvm_vcpu *v, struct sys_reg_params *p, break; case SYS_ID_AA64MMFR1_EL1: - val &= (NV_FTR(MMFR1, PAN) | + val &= (NV_FTR(MMFR1, HCX) | + NV_FTR(MMFR1, PAN) | NV_FTR(MMFR1, LO) | NV_FTR(MMFR1, HPDS) | NV_FTR(MMFR1, VH) | @@ -124,8 +126,7 @@ void access_nested_id_reg(struct kvm_vcpu *v, struct sys_reg_params *p, break; case SYS_ID_AA64MMFR2_EL1: - val &= ~(NV_FTR(MMFR2, EVT) | - NV_FTR(MMFR2, BBM) | + val &= ~(NV_FTR(MMFR2, BBM) | NV_FTR(MMFR2, TTL) | GENMASK_ULL(47, 44) | NV_FTR(MMFR2, ST) | diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 5606509724787383ef90fb87e30ec4008303e6f9..6b066e04dc5dfe506572338cc3f1008c5f3263d6 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -14,6 +14,7 @@ #include #include #include +#include #define PERF_ATTR_CFG1_COUNTER_64BIT BIT(0) @@ -35,12 +36,8 @@ static struct kvm_pmc *kvm_vcpu_idx_to_pmc(struct kvm_vcpu *vcpu, int cnt_idx) return &vcpu->arch.pmu.pmc[cnt_idx]; } -static u32 kvm_pmu_event_mask(struct kvm *kvm) +static u32 __kvm_pmu_event_mask(unsigned int pmuver) { - unsigned int pmuver; - - pmuver = kvm->arch.arm_pmu->pmuver; - switch (pmuver) { case ID_AA64DFR0_EL1_PMUVer_IMP: return GENMASK(9, 0); @@ -55,6 +52,14 @@ static u32 kvm_pmu_event_mask(struct kvm *kvm) } } +static u32 kvm_pmu_event_mask(struct kvm *kvm) +{ + u64 dfr0 = IDREG(kvm, SYS_ID_AA64DFR0_EL1); + u8 pmuver = SYS_FIELD_GET(ID_AA64DFR0_EL1, PMUVer, dfr0); + + return __kvm_pmu_event_mask(pmuver); +} + /** * kvm_pmc_is_64bit - determine if counter is 64bit * @pmc: counter context @@ -672,8 +677,11 @@ void kvm_host_pmu_init(struct arm_pmu *pmu) { struct arm_pmu_entry *entry; - if (pmu->pmuver == ID_AA64DFR0_EL1_PMUVer_NI || - pmu->pmuver == ID_AA64DFR0_EL1_PMUVer_IMP_DEF) + /* + * Check the sanitised PMU version for the system, as KVM does not + * support implementations where PMUv3 exists on a subset of CPUs. + */ + if (!pmuv3_implemented(kvm_arm_pmu_get_pmuver_limit())) return; mutex_lock(&arm_pmus_lock); @@ -750,11 +758,12 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1) } else { val = read_sysreg(pmceid1_el0); /* - * Don't advertise STALL_SLOT, as PMMIR_EL0 is handled + * Don't advertise STALL_SLOT*, as PMMIR_EL0 is handled * as RAZ */ - if (vcpu->kvm->arch.arm_pmu->pmuver >= ID_AA64DFR0_EL1_PMUVer_V3P4) - val &= ~BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32); + val &= ~(BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT - 32) | + BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_FRONTEND - 32) | + BIT_ULL(ARMV8_PMUV3_PERFCTR_STALL_SLOT_BACKEND - 32)); base = 32; } @@ -950,11 +959,17 @@ int kvm_arm_pmu_v3_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) return 0; } case KVM_ARM_VCPU_PMU_V3_FILTER: { + u8 pmuver = kvm_arm_pmu_get_pmuver_limit(); struct kvm_pmu_event_filter __user *uaddr; struct kvm_pmu_event_filter filter; int nr_events; - nr_events = kvm_pmu_event_mask(kvm) + 1; + /* + * Allow userspace to specify an event filter for the entire + * event range supported by PMUVer of the hardware, rather + * than the guest's PMUVer for KVM backward compatibility. + */ + nr_events = __kvm_pmu_event_mask(pmuver) + 1; uaddr = (struct kvm_pmu_event_filter __user *)(long)attr->addr; diff --git a/arch/arm64/kvm/pmu.c b/arch/arm64/kvm/pmu.c index 121f1a14c829c2804127427d4331d91d84d1ab13..0eea225fd09a7ad0ea2da29d13db7683a4a58497 100644 --- a/arch/arm64/kvm/pmu.c +++ b/arch/arm64/kvm/pmu.c @@ -236,3 +236,21 @@ bool kvm_set_pmuserenr(u64 val) ctxt_sys_reg(hctxt, PMUSERENR_EL0) = val; return true; } + +/* + * If we interrupted the guest to update the host PMU context, make + * sure we re-apply the guest EL0 state. + */ +void kvm_vcpu_pmu_resync_el0(void) +{ + struct kvm_vcpu *vcpu; + + if (!has_vhe() || !in_interrupt()) + return; + + vcpu = kvm_get_running_vcpu(); + if (!vcpu) + return; + + kvm_make_request(KVM_REQ_RESYNC_PMU_EL0, vcpu); +} diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index bc8556b6f459063d6e3f03ee9c56d4806751611c..7a65a35ee4ac4165b4147c724d28b59f8150a0d2 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -248,21 +248,16 @@ int kvm_reset_vcpu(struct kvm_vcpu *vcpu) } } - switch (vcpu->arch.target) { - default: - if (vcpu_el1_is_32bit(vcpu)) { - pstate = VCPU_RESET_PSTATE_SVC; - } else if (vcpu_has_nv(vcpu)) { - pstate = VCPU_RESET_PSTATE_EL2; - } else { - pstate = VCPU_RESET_PSTATE_EL1; - } - - if (kvm_vcpu_has_pmu(vcpu) && !kvm_arm_support_pmu_v3()) { - ret = -EINVAL; - goto out; - } - break; + if (vcpu_el1_is_32bit(vcpu)) + pstate = VCPU_RESET_PSTATE_SVC; + else if (vcpu_has_nv(vcpu)) + pstate = VCPU_RESET_PSTATE_EL2; + else + pstate = VCPU_RESET_PSTATE_EL1; + + if (kvm_vcpu_has_pmu(vcpu) && !kvm_arm_support_pmu_v3()) { + ret = -EINVAL; + goto out; } /* Reset core registers */ diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 2ca2973abe66fa7c9ac7029b6935f226cf41d31b..e92ec810d4494bac8ec83c7f23d0a360d9651493 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -2151,6 +2151,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { { SYS_DESC(SYS_CONTEXTIDR_EL1), access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 }, { SYS_DESC(SYS_TPIDR_EL1), NULL, reset_unknown, TPIDR_EL1 }, + { SYS_DESC(SYS_ACCDATA_EL1), undef_access }, + { SYS_DESC(SYS_SCXTNUM_EL1), undef_access }, { SYS_DESC(SYS_CNTKCTL_EL1), NULL, reset_val, CNTKCTL_EL1, 0}, @@ -2365,8 +2367,13 @@ static const struct sys_reg_desc sys_reg_descs[] = { EL2_REG(MDCR_EL2, access_rw, reset_val, 0), EL2_REG(CPTR_EL2, access_rw, reset_val, CPTR_NVHE_EL2_RES1), EL2_REG(HSTR_EL2, access_rw, reset_val, 0), + EL2_REG(HFGRTR_EL2, access_rw, reset_val, 0), + EL2_REG(HFGWTR_EL2, access_rw, reset_val, 0), + EL2_REG(HFGITR_EL2, access_rw, reset_val, 0), EL2_REG(HACR_EL2, access_rw, reset_val, 0), + EL2_REG(HCRX_EL2, access_rw, reset_val, 0), + EL2_REG(TTBR0_EL2, access_rw, reset_val, 0), EL2_REG(TTBR1_EL2, access_rw, reset_val, 0), EL2_REG(TCR_EL2, access_rw, reset_val, TCR_EL2_RES1), @@ -2374,6 +2381,8 @@ static const struct sys_reg_desc sys_reg_descs[] = { EL2_REG(VTCR_EL2, access_rw, reset_val, 0), { SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 }, + EL2_REG(HDFGRTR_EL2, access_rw, reset_val, 0), + EL2_REG(HDFGWTR_EL2, access_rw, reset_val, 0), EL2_REG(SPSR_EL2, access_rw, reset_val, 0), EL2_REG(ELR_EL2, access_rw, reset_val, 0), { SYS_DESC(SYS_SP_EL1), access_sp_el1}, @@ -3170,6 +3179,9 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu) trace_kvm_handle_sys_reg(esr); + if (__check_nv_sr_forward(vcpu)) + return 1; + params = esr_sys64_to_params(esr); params.regval = vcpu_get_reg(vcpu, Rt); @@ -3587,5 +3599,8 @@ int __init kvm_sys_reg_table_init(void) if (!first_idreg) return -EINVAL; + if (kvm_get_mode() == KVM_MODE_NV) + return populate_nv_trap_config(); + return 0; } diff --git a/arch/arm64/kvm/trace_arm.h b/arch/arm64/kvm/trace_arm.h index 6ce5c025218dfee7a45e9b789a8ff3b3e89f7785..8ad53104934dcdfdc892852ce4f67cb1d0138f52 100644 --- a/arch/arm64/kvm/trace_arm.h +++ b/arch/arm64/kvm/trace_arm.h @@ -364,6 +364,32 @@ TRACE_EVENT(kvm_inject_nested_exception, __entry->hcr_el2) ); +TRACE_EVENT(kvm_forward_sysreg_trap, + TP_PROTO(struct kvm_vcpu *vcpu, u32 sysreg, bool is_read), + TP_ARGS(vcpu, sysreg, is_read), + + TP_STRUCT__entry( + __field(u64, pc) + __field(u32, sysreg) + __field(bool, is_read) + ), + + TP_fast_assign( + __entry->pc = *vcpu_pc(vcpu); + __entry->sysreg = sysreg; + __entry->is_read = is_read; + ), + + TP_printk("%llx %c (%d,%d,%d,%d,%d)", + __entry->pc, + __entry->is_read ? 'R' : 'W', + sys_reg_Op0(__entry->sysreg), + sys_reg_Op1(__entry->sysreg), + sys_reg_CRn(__entry->sysreg), + sys_reg_CRm(__entry->sysreg), + sys_reg_Op2(__entry->sysreg)) +); + #endif /* _TRACE_ARM_ARM64_KVM_H */ #undef TRACE_INCLUDE_PATH diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index f9923beedd2769e8cb63be01079093bc8952d7ac..0ab09b0d44404b7f6d7a3bee5689b0dccadaf1a1 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -199,7 +199,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu); void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr); void vgic_v2_clear_lr(struct kvm_vcpu *vcpu, int lr); void vgic_v2_set_underflow(struct kvm_vcpu *vcpu); -void vgic_v2_set_npie(struct kvm_vcpu *vcpu); int vgic_v2_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr); int vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write, int offset, u32 *val); @@ -233,7 +232,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu); void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr); void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr); void vgic_v3_set_underflow(struct kvm_vcpu *vcpu); -void vgic_v3_set_npie(struct kvm_vcpu *vcpu); void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); void vgic_v3_enable(struct kvm_vcpu *vcpu); diff --git a/arch/arm64/lib/csum.c b/arch/arm64/lib/csum.c index 78b87a64ca0a38286fc1390acc20bfc55f49e16f..2432683e48a61f2186a329db893bced8157a3cf1 100644 --- a/arch/arm64/lib/csum.c +++ b/arch/arm64/lib/csum.c @@ -24,7 +24,7 @@ unsigned int __no_sanitize_address do_csum(const unsigned char *buff, int len) const u64 *ptr; u64 data, sum64 = 0; - if (unlikely(len == 0)) + if (unlikely(len <= 0)) return 0; offset = (unsigned long)buff & 7; diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 9c52718ea7509a88ddbafa2eceef58b2e51615a6..13fd592228b188658bd25011b66fd0420ca44d66 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -241,15 +241,8 @@ static void clear_flush(struct mm_struct *mm, flush_tlb_range(&vma, saddr, addr); } -static inline struct folio *hugetlb_swap_entry_to_folio(swp_entry_t entry) -{ - VM_BUG_ON(!is_migration_entry(entry) && !is_hwpoison_entry(entry)); - - return page_folio(pfn_to_page(swp_offset_pfn(entry))); -} - void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pte) + pte_t *ptep, pte_t pte, unsigned long sz) { size_t pgsize; int i; @@ -257,13 +250,10 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, unsigned long pfn, dpfn; pgprot_t hugeprot; - if (!pte_present(pte)) { - struct folio *folio; - - folio = hugetlb_swap_entry_to_folio(pte_to_swp_entry(pte)); - ncontig = num_contig_ptes(folio_size(folio), &pgsize); + ncontig = num_contig_ptes(sz, &pgsize); - for (i = 0; i < ncontig; i++, ptep++) + if (!pte_present(pte)) { + for (i = 0; i < ncontig; i++, ptep++, addr += pgsize) set_pte_at(mm, addr, ptep, pte); return; } @@ -273,7 +263,6 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, return; } - ncontig = find_num_contig(mm, addr, ptep, &pgsize); pfn = pte_pfn(pte); dpfn = pgsize >> PAGE_SHIFT; hugeprot = pte_pgprot(pte); @@ -571,5 +560,7 @@ pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t old_pte, pte_t pte) { - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + unsigned long psize = huge_page_size(hstate_vma(vma)); + + set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize); } diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index c80ed4f3cbcee29bebd70bc3071ba3c39b3689d3..dea3dc89234b023e919f6ca3d2866e8e1e9d5bb8 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -26,6 +26,7 @@ HAS_ECV HAS_ECV_CNTPOFF HAS_EPAN HAS_EVT +HAS_FGT HAS_GENERIC_AUTH HAS_GENERIC_AUTH_ARCH_QARMA3 HAS_GENERIC_AUTH_ARCH_QARMA5 @@ -83,6 +84,7 @@ WORKAROUND_2077057 WORKAROUND_2457168 WORKAROUND_2645198 WORKAROUND_2658417 +WORKAROUND_2966298 WORKAROUND_AMPERE_AC03_CPU_38 WORKAROUND_TRBE_OVERWRITE_FILL_MODE WORKAROUND_TSB_FLUSH_FAILURE diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 65866bf819c338e2758bff6fee1a17cf1ae2482a..76ce150e7347e56e2f497e905c02dc28733e68e3 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -1347,7 +1347,11 @@ UnsignedEnum 51:48 RPRFM 0b0000 NI 0b0001 IMP EndEnum -Res0 47:28 +Res0 47:32 +UnsignedEnum 31:28 CLRBHB + 0b0000 NI + 0b0001 IMP +EndEnum UnsignedEnum 27:24 PAC_frac 0b0000 NI 0b0001 IMP @@ -2156,6 +2160,135 @@ Field 1 ICIALLU Field 0 ICIALLUIS EndSysreg +Sysreg HDFGRTR_EL2 3 4 3 1 4 +Field 63 PMBIDR_EL1 +Field 62 nPMSNEVFR_EL1 +Field 61 nBRBDATA +Field 60 nBRBCTL +Field 59 nBRBIDR +Field 58 PMCEIDn_EL0 +Field 57 PMUSERENR_EL0 +Field 56 TRBTRG_EL1 +Field 55 TRBSR_EL1 +Field 54 TRBPTR_EL1 +Field 53 TRBMAR_EL1 +Field 52 TRBLIMITR_EL1 +Field 51 TRBIDR_EL1 +Field 50 TRBBASER_EL1 +Res0 49 +Field 48 TRCVICTLR +Field 47 TRCSTATR +Field 46 TRCSSCSRn +Field 45 TRCSEQSTR +Field 44 TRCPRGCTLR +Field 43 TRCOSLSR +Res0 42 +Field 41 TRCIMSPECn +Field 40 TRCID +Res0 39:38 +Field 37 TRCCNTVRn +Field 36 TRCCLAIM +Field 35 TRCAUXCTLR +Field 34 TRCAUTHSTATUS +Field 33 TRC +Field 32 PMSLATFR_EL1 +Field 31 PMSIRR_EL1 +Field 30 PMSIDR_EL1 +Field 29 PMSICR_EL1 +Field 28 PMSFCR_EL1 +Field 27 PMSEVFR_EL1 +Field 26 PMSCR_EL1 +Field 25 PMBSR_EL1 +Field 24 PMBPTR_EL1 +Field 23 PMBLIMITR_EL1 +Field 22 PMMIR_EL1 +Res0 21:20 +Field 19 PMSELR_EL0 +Field 18 PMOVS +Field 17 PMINTEN +Field 16 PMCNTEN +Field 15 PMCCNTR_EL0 +Field 14 PMCCFILTR_EL0 +Field 13 PMEVTYPERn_EL0 +Field 12 PMEVCNTRn_EL0 +Field 11 OSDLR_EL1 +Field 10 OSECCR_EL1 +Field 9 OSLSR_EL1 +Res0 8 +Field 7 DBGPRCR_EL1 +Field 6 DBGAUTHSTATUS_EL1 +Field 5 DBGCLAIM +Field 4 MDSCR_EL1 +Field 3 DBGWVRn_EL1 +Field 2 DBGWCRn_EL1 +Field 1 DBGBVRn_EL1 +Field 0 DBGBCRn_EL1 +EndSysreg + +Sysreg HDFGWTR_EL2 3 4 3 1 5 +Res0 63 +Field 62 nPMSNEVFR_EL1 +Field 61 nBRBDATA +Field 60 nBRBCTL +Res0 59:58 +Field 57 PMUSERENR_EL0 +Field 56 TRBTRG_EL1 +Field 55 TRBSR_EL1 +Field 54 TRBPTR_EL1 +Field 53 TRBMAR_EL1 +Field 52 TRBLIMITR_EL1 +Res0 51 +Field 50 TRBBASER_EL1 +Field 49 TRFCR_EL1 +Field 48 TRCVICTLR +Res0 47 +Field 46 TRCSSCSRn +Field 45 TRCSEQSTR +Field 44 TRCPRGCTLR +Res0 43 +Field 42 TRCOSLAR +Field 41 TRCIMSPECn +Res0 40:38 +Field 37 TRCCNTVRn +Field 36 TRCCLAIM +Field 35 TRCAUXCTLR +Res0 34 +Field 33 TRC +Field 32 PMSLATFR_EL1 +Field 31 PMSIRR_EL1 +Res0 30 +Field 29 PMSICR_EL1 +Field 28 PMSFCR_EL1 +Field 27 PMSEVFR_EL1 +Field 26 PMSCR_EL1 +Field 25 PMBSR_EL1 +Field 24 PMBPTR_EL1 +Field 23 PMBLIMITR_EL1 +Res0 22 +Field 21 PMCR_EL0 +Field 20 PMSWINC_EL0 +Field 19 PMSELR_EL0 +Field 18 PMOVS +Field 17 PMINTEN +Field 16 PMCNTEN +Field 15 PMCCNTR_EL0 +Field 14 PMCCFILTR_EL0 +Field 13 PMEVTYPERn_EL0 +Field 12 PMEVCNTRn_EL0 +Field 11 OSDLR_EL1 +Field 10 OSECCR_EL1 +Res0 9 +Field 8 OSLAR_EL1 +Field 7 DBGPRCR_EL1 +Res0 6 +Field 5 DBGCLAIM +Field 4 MDSCR_EL1 +Field 3 DBGWVRn_EL1 +Field 2 DBGWCRn_EL1 +Field 1 DBGBVRn_EL1 +Field 0 DBGBCRn_EL1 +EndSysreg + Sysreg ZCR_EL2 3 4 1 2 0 Fields ZCR_ELx EndSysreg diff --git a/arch/ia64/include/asm/Kbuild b/arch/ia64/include/asm/Kbuild index 33733245f42ba1eb7284e23cd7fbd968cf1383c7..aefae2efde9f323d5223fad507a996bc5abcdeb5 100644 --- a/arch/ia64/include/asm/Kbuild +++ b/arch/ia64/include/asm/Kbuild @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 generated-y += syscall_table.h generic-y += agp.h -generic-y += export.h generic-y += kvm_para.h generic-y += mcs_spinlock.h generic-y += vtime.h diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 15f6cfddcc080b47693f2b6212d4ca5fab6bf3e0..41e8fe55cd9845f2c0e285e5db888915705d9b0d 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c @@ -907,3 +907,7 @@ EXPORT_SYMBOL(acpi_unregister_ioapic); * TBD when IA64 starts to support suspend... */ int acpi_suspend_lowlevel(void) { return 0; } + +void acpi_proc_quirk_mwait_check(void) +{ +} diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 5eba3fb2e3115285c752bb247ccda5d6dcd6f56d..ac06d44b9b278a83e09c12b3dbc19c912c8d1e10 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -37,7 +37,7 @@ * pNonSys: !pSys */ - +#include #include #include #include @@ -49,7 +49,6 @@ #include #include #include -#include #include "minstate.h" diff --git a/arch/ia64/kernel/esi_stub.S b/arch/ia64/kernel/esi_stub.S index 821e68d1059874454efb1cf0a845a668de4990ef..9928c5b2957c1a1d6a1dc3bbd3d20369506edac4 100644 --- a/arch/ia64/kernel/esi_stub.S +++ b/arch/ia64/kernel/esi_stub.S @@ -34,9 +34,9 @@ #define PSR_BITS_TO_SET \ (IA64_PSR_BN) +#include #include #include -#include /* * Inputs: diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S index c096500590e9623aa6087d1bca30cb3c6d001c1d..85c8a57da402a95165ddb509af595b5571a8face 100644 --- a/arch/ia64/kernel/head.S +++ b/arch/ia64/kernel/head.S @@ -20,7 +20,7 @@ * Support for CPU Hotplug */ - +#include #include #include #include @@ -33,7 +33,6 @@ #include #include #include -#include #ifdef CONFIG_HOTPLUG_CPU #define SAL_PSR_BITS_TO_SET \ diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S index 7a418e324d3000786e926547ef2e4fcffb38710e..da90c49df6281ab5f2b8275680310d17aa17229d 100644 --- a/arch/ia64/kernel/ivt.S +++ b/arch/ia64/kernel/ivt.S @@ -47,7 +47,7 @@ * Table is based upon EAS2.6 (Oct 1999) */ - +#include #include #include #include @@ -58,7 +58,6 @@ #include #include #include -#include #if 0 # define PSR_DEFAULT_BITS psr.ac diff --git a/arch/ia64/kernel/pal.S b/arch/ia64/kernel/pal.S index 06d01a070aae24e6b374c308f04003eff5b81017..fb6db6966f70ce72b3128b119fb81faa2ed328d6 100644 --- a/arch/ia64/kernel/pal.S +++ b/arch/ia64/kernel/pal.S @@ -13,9 +13,9 @@ * 05/24/2000 eranian Added support for physical mode static calls */ +#include #include #include -#include .data pal_entry_point: diff --git a/arch/ia64/lib/clear_page.S b/arch/ia64/lib/clear_page.S index 65b75085c8f46c231586745f22dc14a6aa13c11a..ba0dd2538fa56722ecf43d12d8fc430739492924 100644 --- a/arch/ia64/lib/clear_page.S +++ b/arch/ia64/lib/clear_page.S @@ -10,9 +10,9 @@ * 3/08/02 davidm Some more tweaking */ +#include #include #include -#include #ifdef CONFIG_ITANIUM # define L3_LINE_SIZE 64 // Itanium L3 line size diff --git a/arch/ia64/lib/clear_user.S b/arch/ia64/lib/clear_user.S index a28f39d349ebeabb784962f18f3df8df0ae220ff..1d9e45ccf8e52dd6c3f91e8b32b6e81805d22bb8 100644 --- a/arch/ia64/lib/clear_user.S +++ b/arch/ia64/lib/clear_user.S @@ -12,8 +12,8 @@ * Stephane Eranian */ +#include #include -#include // // arguments diff --git a/arch/ia64/lib/copy_page.S b/arch/ia64/lib/copy_page.S index 176f857c522e8a9d620026db1903c0e36206439a..c0a0e6b2af0012c258312869dcc16e4c0af8f29d 100644 --- a/arch/ia64/lib/copy_page.S +++ b/arch/ia64/lib/copy_page.S @@ -15,9 +15,9 @@ * * 4/06/01 davidm Tuned to make it perform well both for cached and uncached copies. */ +#include #include #include -#include #define PIPE_DEPTH 3 #define EPI p[PIPE_DEPTH-1] diff --git a/arch/ia64/lib/copy_page_mck.S b/arch/ia64/lib/copy_page_mck.S index d6fd56e4f1c1dcac5965ce4cb03516a7f15d4587..5e8bb4b4b5350d5ca3cc754847b58d19b9f62edc 100644 --- a/arch/ia64/lib/copy_page_mck.S +++ b/arch/ia64/lib/copy_page_mck.S @@ -60,9 +60,9 @@ * to fetch the second-half of the L2 cache line into L1, and the tX words are copied in * an order that avoids bank conflicts. */ +#include #include #include -#include #define PREFETCH_DIST 8 // McKinley sustains 16 outstanding L2 misses (8 ld, 8 st) diff --git a/arch/ia64/lib/copy_user.S b/arch/ia64/lib/copy_user.S index f681556c6b86d99525c3674daa071ff4d5c3e6d5..8daab72cfe7703145bc5d790b6ed6a9e8a7845c0 100644 --- a/arch/ia64/lib/copy_user.S +++ b/arch/ia64/lib/copy_user.S @@ -30,8 +30,8 @@ * - fix extraneous stop bit introduced by the EX() macro. */ +#include #include -#include // // Tuneable parameters diff --git a/arch/ia64/lib/flush.S b/arch/ia64/lib/flush.S index 8573d59c9ed17098bfd1b38243b9c5a178fdcb8f..f8e795fe45cb987ef35a2488246a28e3be6e9dfa 100644 --- a/arch/ia64/lib/flush.S +++ b/arch/ia64/lib/flush.S @@ -8,9 +8,8 @@ * 05/28/05 Zoltan Menyhart Dynamic stride size */ +#include #include -#include - /* * flush_icache_range(start,end) diff --git a/arch/ia64/lib/idiv32.S b/arch/ia64/lib/idiv32.S index def92b708e6e1f209ff6f26fb3158c20917509a2..83586fbc51ff123642c56f4c9c9fd6772e580bbc 100644 --- a/arch/ia64/lib/idiv32.S +++ b/arch/ia64/lib/idiv32.S @@ -15,8 +15,8 @@ * (http://www.goodreads.com/book/show/2019887.Ia_64_and_Elementary_Functions) */ +#include #include -#include #ifdef MODULO # define OP mod diff --git a/arch/ia64/lib/idiv64.S b/arch/ia64/lib/idiv64.S index a8ba3bd3d4d8cc545eaab2c62584ba4234efe15f..5c9113691f72f13d9d9fd4c969d99202717871a4 100644 --- a/arch/ia64/lib/idiv64.S +++ b/arch/ia64/lib/idiv64.S @@ -15,8 +15,8 @@ * (http://www.goodreads.com/book/show/2019887.Ia_64_and_Elementary_Functions) */ +#include #include -#include #ifdef MODULO # define OP mod diff --git a/arch/ia64/lib/ip_fast_csum.S b/arch/ia64/lib/ip_fast_csum.S index dc9e6e6fe87695a48160d634890987dac93ebcca..fcc0b812ce2e963c5347bed7ff889daee684a64b 100644 --- a/arch/ia64/lib/ip_fast_csum.S +++ b/arch/ia64/lib/ip_fast_csum.S @@ -13,8 +13,8 @@ * Copyright (C) 2002, 2006 Ken Chen */ +#include #include -#include /* * Since we know that most likely this function is called with buf aligned diff --git a/arch/ia64/lib/memcpy.S b/arch/ia64/lib/memcpy.S index 91a625fddbf0599e78b2419013be61f33ff727b4..35c9069a83452853859d1c254e82c1940f760825 100644 --- a/arch/ia64/lib/memcpy.S +++ b/arch/ia64/lib/memcpy.S @@ -14,8 +14,8 @@ * Stephane Eranian * David Mosberger-Tang */ +#include #include -#include GLOBAL_ENTRY(memcpy) diff --git a/arch/ia64/lib/memcpy_mck.S b/arch/ia64/lib/memcpy_mck.S index cc4e6ac914b6c2699749a700b7ff6e97a72ee02a..c0d4362217ae486b360230c76b21d744ee08295e 100644 --- a/arch/ia64/lib/memcpy_mck.S +++ b/arch/ia64/lib/memcpy_mck.S @@ -14,9 +14,9 @@ * Copyright (C) 2002 Intel Corp. * Copyright (C) 2002 Ken Chen */ +#include #include #include -#include #define EK(y...) EX(y) diff --git a/arch/ia64/lib/memset.S b/arch/ia64/lib/memset.S index 07a8b92c64965e2578e3607532325c170ae220c7..552c5c7e4d061ee769ec3e30f5b0f73f952908e9 100644 --- a/arch/ia64/lib/memset.S +++ b/arch/ia64/lib/memset.S @@ -18,8 +18,8 @@ Since a stf.spill f0 can store 16B in one go, we use this instruction to get peak speed when value = 0. */ +#include #include -#include #undef ret #define dest in0 diff --git a/arch/ia64/lib/strlen.S b/arch/ia64/lib/strlen.S index d66de596697441c8cab0c73589a017ca0fc6c466..1f4a46c151270ffb102a4029fe7561e4c1de335d 100644 --- a/arch/ia64/lib/strlen.S +++ b/arch/ia64/lib/strlen.S @@ -17,8 +17,8 @@ * 09/24/99 S.Eranian add speculation recovery code */ +#include #include -#include // // diff --git a/arch/ia64/lib/strncpy_from_user.S b/arch/ia64/lib/strncpy_from_user.S index 49eb81b69cd224f77addd7853d87079eac6c7994..a287169bd953ac745d74971a1da111025a3926f2 100644 --- a/arch/ia64/lib/strncpy_from_user.S +++ b/arch/ia64/lib/strncpy_from_user.S @@ -17,8 +17,8 @@ * by Andreas Schwab ). */ +#include #include -#include GLOBAL_ENTRY(__strncpy_from_user) alloc r2=ar.pfs,3,0,0,0 diff --git a/arch/ia64/lib/strnlen_user.S b/arch/ia64/lib/strnlen_user.S index 4b684d4da10644db089c5b84e4a62e835883c06a..a7eb56e840a9507cfdd37cd7f758c1d780689a7c 100644 --- a/arch/ia64/lib/strnlen_user.S +++ b/arch/ia64/lib/strnlen_user.S @@ -13,8 +13,8 @@ * Copyright (C) 1999, 2001 David Mosberger-Tang */ +#include #include -#include GLOBAL_ENTRY(__strnlen_user) .prologue diff --git a/arch/ia64/lib/xor.S b/arch/ia64/lib/xor.S index 5413dafe6b2e0103e418c7fbd8dd7b08c51e9e72..6e2a69662c06ef2740734aeb6b9b418d79b73d0a 100644 --- a/arch/ia64/lib/xor.S +++ b/arch/ia64/lib/xor.S @@ -5,8 +5,8 @@ * Optimized RAID-5 checksumming functions for IA-64. */ +#include #include -#include GLOBAL_ENTRY(xor_ia64_2) .prologue diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index ecf282dee513fedb807f5b456e6773fd1a633b4f..e14396a2ddcbfc6d6130b63dca258343a03f35cb 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -8,11 +8,13 @@ config LOONGARCH select ACPI_PPTT if ACPI select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI select ARCH_BINFMT_ELF_STATE + select ARCH_DISABLE_KASAN_INLINE select ARCH_ENABLE_MEMORY_HOTPLUG select ARCH_ENABLE_MEMORY_HOTREMOVE select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_FORTIFY_SOURCE + select ARCH_HAS_KCOV select ARCH_HAS_NMI_SAFE_THIS_CPU_OPS select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE select ARCH_HAS_PTE_SPECIAL @@ -91,6 +93,9 @@ config LOONGARCH select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE + select HAVE_ARCH_KASAN + select HAVE_ARCH_KFENCE + select HAVE_ARCH_KGDB if PERF_EVENTS select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_TRACEHOOK @@ -115,6 +120,7 @@ config LOONGARCH select HAVE_FUNCTION_GRAPH_RETVAL if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER + select HAVE_GCC_PLUGINS select HAVE_GENERIC_VDSO select HAVE_HW_BREAKPOINT if PERF_EVENTS select HAVE_IOREMAP_PROT @@ -254,6 +260,9 @@ config AS_HAS_LSX_EXTENSION config AS_HAS_LASX_EXTENSION def_bool $(as-instr,xvld \$xr0$(comma)\$a0$(comma)0) +config AS_HAS_LBT_EXTENSION + def_bool $(as-instr,movscr2gr \$a0$(comma)\$scr0) + menu "Kernel type and options" source "kernel/Kconfig.hz" @@ -534,6 +543,18 @@ config CPU_HAS_LASX If unsure, say Y. +config CPU_HAS_LBT + bool "Support for the Loongson Binary Translation Extension" + depends on AS_HAS_LBT_EXTENSION + help + Loongson Binary Translation (LBT) introduces 4 scratch registers (SCR0 + to SCR3), x86/ARM eflags (eflags) and x87 fpu stack pointer (ftop). + Enabling this option allows the kernel to allocate and switch registers + specific to LBT. + + If you want to use this feature, such as the Loongson Architecture + Translator (LAT), say Y. + config CPU_HAS_PREFETCH bool default y @@ -638,6 +659,11 @@ config ARCH_MMAP_RND_BITS_MAX config ARCH_SUPPORTS_UPROBES def_bool y +config KASAN_SHADOW_OFFSET + hex + default 0x0 + depends on KASAN + menu "Power management options" config ARCH_SUSPEND_POSSIBLE diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index ef87bab46754e63142761c54f688cd2850b9ebbd..fb0fada43197e4ad520359c8f2a6b6350d48d99d 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -84,7 +84,10 @@ LDFLAGS_vmlinux += -static -pie --no-dynamic-linker -z notext endif cflags-y += $(call cc-option, -mno-check-zero-division) + +ifndef CONFIG_KASAN cflags-y += -fno-builtin-memcpy -fno-builtin-memmove -fno-builtin-memset +endif load-y = 0x9000000000200000 bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index d64849b4cba1634849e89f35b2a623c0610c39e9..a3b52aaa83b33634c5be146bc02d50589bf5f762 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -30,7 +30,6 @@ CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_CHECKPOINT_RESTORE=y CONFIG_SCHED_AUTOGROUP=y -CONFIG_SYSFS_DEPRECATED=y CONFIG_RELAY=y CONFIG_BLK_DEV_INITRD=y CONFIG_EXPERT=y @@ -47,8 +46,12 @@ CONFIG_SMP=y CONFIG_HOTPLUG_CPU=y CONFIG_NR_CPUS=64 CONFIG_NUMA=y +CONFIG_CPU_HAS_FPU=y +CONFIG_CPU_HAS_LSX=y +CONFIG_CPU_HAS_LASX=y CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y +CONFIG_RANDOMIZE_BASE=y CONFIG_SUSPEND=y CONFIG_HIBERNATION=y CONFIG_ACPI=y @@ -63,6 +66,7 @@ CONFIG_EFI_ZBOOT=y CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y CONFIG_EFI_CAPSULE_LOADER=m CONFIG_EFI_TEST=m +CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_FORCE_LOAD=y CONFIG_MODULE_UNLOAD=y @@ -108,7 +112,12 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_IP_PNP_RARP=y CONFIG_NET_IPIP=m CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y CONFIG_INET_ESP=m CONFIG_INET_UDP_DIAG=y CONFIG_TCP_CONG_ADVANCED=y @@ -137,7 +146,6 @@ CONFIG_NFT_MASQ=m CONFIG_NFT_REDIR=m CONFIG_NFT_NAT=m CONFIG_NFT_TUNNEL=m -CONFIG_NFT_OBJREF=m CONFIG_NFT_QUEUE=m CONFIG_NFT_QUOTA=m CONFIG_NFT_REJECT=m @@ -208,7 +216,11 @@ CONFIG_IP_VS=m CONFIG_IP_VS_IPV6=y CONFIG_IP_VS_PROTO_TCP=y CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m CONFIG_IP_VS_NFCT=y CONFIG_NF_TABLES_IPV4=y CONFIG_NFT_DUP_IPV4=m @@ -227,7 +239,6 @@ CONFIG_IP_NF_TARGET_MASQUERADE=m CONFIG_IP_NF_TARGET_NETMAP=m CONFIG_IP_NF_TARGET_REDIRECT=m CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m @@ -363,6 +374,8 @@ CONFIG_MTD_CFI_AMDSTD=m CONFIG_MTD_CFI_STAA=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m +CONFIG_MTD_UBI=m +CONFIG_MTD_UBI_BLOCK=y CONFIG_PARPORT=y CONFIG_PARPORT_PC=y CONFIG_PARPORT_SERIAL=y @@ -370,6 +383,7 @@ CONFIG_PARPORT_PC_FIFO=y CONFIG_ZRAM=m CONFIG_ZRAM_DEF_COMP_ZSTD=y CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_DRBD=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=8192 @@ -516,6 +530,8 @@ CONFIG_STMMAC_ETH=y # CONFIG_NET_VENDOR_TEHUTI is not set # CONFIG_NET_VENDOR_TI is not set # CONFIG_NET_VENDOR_VIA is not set +CONFIG_NGBE=y +CONFIG_TXGBE=y # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_NET_VENDOR_XILINX is not set CONFIG_PPP=m @@ -602,9 +618,15 @@ CONFIG_HW_RANDOM_VIRTIO=m CONFIG_I2C_CHARDEV=y CONFIG_I2C_PIIX4=y CONFIG_I2C_GPIO=y +CONFIG_I2C_LS2X=y CONFIG_SPI=y +CONFIG_SPI_LOONGSON_PCI=m +CONFIG_SPI_LOONGSON_PLATFORM=m +CONFIG_PINCTRL=y +CONFIG_PINCTRL_LOONGSON2=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_LOONGSON=y +CONFIG_GPIO_LOONGSON_64BIT=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_RESTART=y CONFIG_POWER_RESET_SYSCON=y @@ -614,6 +636,7 @@ CONFIG_SENSORS_LM75=m CONFIG_SENSORS_LM93=m CONFIG_SENSORS_W83795=m CONFIG_SENSORS_W83627HF=m +CONFIG_LOONGSON2_THERMAL=m CONFIG_RC_CORE=m CONFIG_LIRC=y CONFIG_RC_DECODERS=y @@ -643,6 +666,7 @@ CONFIG_DRM_AMDGPU_USERPTR=y CONFIG_DRM_AST=y CONFIG_DRM_QXL=m CONFIG_DRM_VIRTIO_GPU=m +CONFIG_DRM_LOONGSON=y CONFIG_FB=y CONFIG_FB_EFI=y CONFIG_FB_RADEON=y @@ -712,6 +736,7 @@ CONFIG_UCSI_ACPI=m CONFIG_INFINIBAND=m CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_LOONGSON=y CONFIG_DMADEVICES=y CONFIG_UIO=m CONFIG_UIO_PDRV_GENIRQ=m @@ -745,7 +770,9 @@ CONFIG_COMEDI_NI_LABPC_PCI=m CONFIG_COMEDI_NI_PCIDIO=m CONFIG_COMEDI_NI_PCIMIO=m CONFIG_STAGING=y -CONFIG_R8188EU=m +CONFIG_COMMON_CLK_LOONGSON2=y +CONFIG_LOONGSON2_GUTS=y +CONFIG_LOONGSON2_PM=y CONFIG_PM_DEVFREQ=y CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y CONFIG_DEVFREQ_GOV_PERFORMANCE=y @@ -759,10 +786,17 @@ CONFIG_EXT2_FS_SECURITY=y CONFIG_EXT3_FS=y CONFIG_EXT3_FS_POSIX_ACL=y CONFIG_EXT3_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y CONFIG_XFS_FS=y CONFIG_XFS_QUOTA=y CONFIG_XFS_POSIX_ACL=y +CONFIG_GFS2_FS=m +CONFIG_GFS2_FS_LOCKING_DLM=y +CONFIG_OCFS2_FS=m CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y CONFIG_FANOTIFY=y CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA=y @@ -771,11 +805,14 @@ CONFIG_QFMT_V1=m CONFIG_QFMT_V2=m CONFIG_AUTOFS_FS=y CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_VIRTIO_FS=m CONFIG_OVERLAY_FS=y CONFIG_OVERLAY_FS_INDEX=y CONFIG_OVERLAY_FS_XINO_AUTO=y CONFIG_OVERLAY_FS_METACOPY=y CONFIG_FSCACHE=y +CONFIG_CACHEFILES=m CONFIG_ISO9660_FS=y CONFIG_JOLIET=y CONFIG_ZISOFS=y @@ -784,19 +821,42 @@ CONFIG_MSDOS_FS=m CONFIG_VFAT_FS=m CONFIG_FAT_DEFAULT_CODEPAGE=936 CONFIG_FAT_DEFAULT_IOCHARSET="gb2312" +CONFIG_EXFAT_FS=m +CONFIG_NTFS3_FS=m +CONFIG_NTFS3_64BIT_CLUSTER=y +CONFIG_NTFS3_LZX_XPRESS=y CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_HUGETLBFS=y CONFIG_CONFIGFS_FS=y +CONFIG_ORANGEFS_FS=m +CONFIG_ECRYPT_FS=m +CONFIG_ECRYPT_FS_MESSAGING=y CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m +CONFIG_UBIFS_FS=m +CONFIG_UBIFS_FS_ADVANCED_COMPR=y CONFIG_CRAMFS=m CONFIG_SQUASHFS=y CONFIG_SQUASHFS_XATTR=y CONFIG_SQUASHFS_LZ4=y CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y +CONFIG_MINIX_FS=m +CONFIG_ROMFS_FS=m +CONFIG_PSTORE=m +CONFIG_PSTORE_LZO_COMPRESS=m +CONFIG_PSTORE_LZ4_COMPRESS=m +CONFIG_PSTORE_LZ4HC_COMPRESS=m +CONFIG_PSTORE_842_COMPRESS=y +CONFIG_PSTORE_ZSTD_COMPRESS=y +CONFIG_PSTORE_ZSTD_COMPRESS_DEFAULT=y +CONFIG_SYSV_FS=m +CONFIG_UFS_FS=m +CONFIG_EROFS_FS=m +CONFIG_EROFS_FS_ZIP_LZMA=y +CONFIG_EROFS_FS_PCPU_KTHREAD=y CONFIG_NFS_FS=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y @@ -807,6 +867,10 @@ CONFIG_NFSD=y CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y CONFIG_NFSD_BLOCKLAYOUT=y +CONFIG_CEPH_FS=m +CONFIG_CEPH_FSCACHE=y +CONFIG_CEPH_FS_POSIX_ACL=y +CONFIG_CEPH_FS_SECURITY_LABEL=y CONFIG_CIFS=m # CONFIG_CIFS_DEBUG is not set CONFIG_9P_FS=y @@ -814,6 +878,7 @@ CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_936=y CONFIG_NLS_ASCII=y CONFIG_NLS_UTF8=y +CONFIG_DLM=m CONFIG_KEY_DH_OPERATIONS=y CONFIG_SECURITY=y CONFIG_SECURITY_SELINUX=y @@ -847,6 +912,7 @@ CONFIG_CRYPTO_USER_API_HASH=m CONFIG_CRYPTO_USER_API_SKCIPHER=m CONFIG_CRYPTO_USER_API_RNG=m CONFIG_CRYPTO_USER_API_AEAD=m +CONFIG_CRYPTO_CRC32_LOONGARCH=m CONFIG_CRYPTO_DEV_VIRTIO=m CONFIG_PRINTK_TIME=y CONFIG_STRIP_ASM_SYMS=y diff --git a/arch/loongarch/include/asm/addrspace.h b/arch/loongarch/include/asm/addrspace.h index 5c9c03bdf91569154b9a449019e29b46049d5bef..b24437e28c6eda457b2be003b51ad3809600f7cc 100644 --- a/arch/loongarch/include/asm/addrspace.h +++ b/arch/loongarch/include/asm/addrspace.h @@ -19,7 +19,7 @@ */ #ifndef __ASSEMBLY__ #ifndef PHYS_OFFSET -#define PHYS_OFFSET _AC(0, UL) +#define PHYS_OFFSET _UL(0) #endif extern unsigned long vm_map_base; #endif /* __ASSEMBLY__ */ @@ -43,7 +43,7 @@ extern unsigned long vm_map_base; * Memory above this physical address will be considered highmem. */ #ifndef HIGHMEM_START -#define HIGHMEM_START (_AC(1, UL) << _AC(DMW_PABITS, UL)) +#define HIGHMEM_START (_UL(1) << _UL(DMW_PABITS)) #endif #define TO_PHYS(x) ( ((x) & TO_PHYS_MASK)) @@ -65,16 +65,16 @@ extern unsigned long vm_map_base; #define _ATYPE_ #define _ATYPE32_ #define _ATYPE64_ -#define _CONST64_(x) x #else #define _ATYPE_ __PTRDIFF_TYPE__ #define _ATYPE32_ int #define _ATYPE64_ __s64 +#endif + #ifdef CONFIG_64BIT -#define _CONST64_(x) x ## UL +#define _CONST64_(x) _UL(x) #else -#define _CONST64_(x) x ## ULL -#endif +#define _CONST64_(x) _ULL(x) #endif /* diff --git a/arch/loongarch/include/asm/asm-prototypes.h b/arch/loongarch/include/asm/asm-prototypes.h index ed06d3997420833c173c4deab5697ff8749dedb8..cf8e1a4e7c19dad55c6adc9a56820b02e0fae977 100644 --- a/arch/loongarch/include/asm/asm-prototypes.h +++ b/arch/loongarch/include/asm/asm-prototypes.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include #include +#include #include #include #include diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h index 79e1d53fea89cb998f973ff557d20fb845968128..c9544f358c33991b46b67edec578af1df44939b7 100644 --- a/arch/loongarch/include/asm/asmmacro.h +++ b/arch/loongarch/include/asm/asmmacro.h @@ -10,113 +10,6 @@ #include #include - .macro parse_v var val - \var = \val - .endm - - .macro parse_r var r - \var = -1 - .ifc \r, $r0 - \var = 0 - .endif - .ifc \r, $r1 - \var = 1 - .endif - .ifc \r, $r2 - \var = 2 - .endif - .ifc \r, $r3 - \var = 3 - .endif - .ifc \r, $r4 - \var = 4 - .endif - .ifc \r, $r5 - \var = 5 - .endif - .ifc \r, $r6 - \var = 6 - .endif - .ifc \r, $r7 - \var = 7 - .endif - .ifc \r, $r8 - \var = 8 - .endif - .ifc \r, $r9 - \var = 9 - .endif - .ifc \r, $r10 - \var = 10 - .endif - .ifc \r, $r11 - \var = 11 - .endif - .ifc \r, $r12 - \var = 12 - .endif - .ifc \r, $r13 - \var = 13 - .endif - .ifc \r, $r14 - \var = 14 - .endif - .ifc \r, $r15 - \var = 15 - .endif - .ifc \r, $r16 - \var = 16 - .endif - .ifc \r, $r17 - \var = 17 - .endif - .ifc \r, $r18 - \var = 18 - .endif - .ifc \r, $r19 - \var = 19 - .endif - .ifc \r, $r20 - \var = 20 - .endif - .ifc \r, $r21 - \var = 21 - .endif - .ifc \r, $r22 - \var = 22 - .endif - .ifc \r, $r23 - \var = 23 - .endif - .ifc \r, $r24 - \var = 24 - .endif - .ifc \r, $r25 - \var = 25 - .endif - .ifc \r, $r26 - \var = 26 - .endif - .ifc \r, $r27 - \var = 27 - .endif - .ifc \r, $r28 - \var = 28 - .endif - .ifc \r, $r29 - \var = 29 - .endif - .ifc \r, $r30 - \var = 30 - .endif - .ifc \r, $r31 - \var = 31 - .endif - .iflt \var - .error "Unable to parse register name \r" - .endif - .endm - .macro cpu_save_nonscratch thread stptr.d s0, \thread, THREAD_REG23 stptr.d s1, \thread, THREAD_REG24 @@ -148,12 +41,51 @@ .macro fpu_save_csr thread tmp movfcsr2gr \tmp, fcsr0 - stptr.w \tmp, \thread, THREAD_FCSR + stptr.w \tmp, \thread, THREAD_FCSR +#ifdef CONFIG_CPU_HAS_LBT + /* TM bit is always 0 if LBT not supported */ + andi \tmp, \tmp, FPU_CSR_TM + beqz \tmp, 1f + /* Save FTOP */ + x86mftop \tmp + stptr.w \tmp, \thread, THREAD_FTOP + /* Turn off TM to ensure the order of FPR in memory independent of TM */ + x86clrtm +1: +#endif .endm - .macro fpu_restore_csr thread tmp - ldptr.w \tmp, \thread, THREAD_FCSR - movgr2fcsr fcsr0, \tmp + .macro fpu_restore_csr thread tmp0 tmp1 + ldptr.w \tmp0, \thread, THREAD_FCSR + movgr2fcsr fcsr0, \tmp0 +#ifdef CONFIG_CPU_HAS_LBT + /* TM bit is always 0 if LBT not supported */ + andi \tmp0, \tmp0, FPU_CSR_TM + beqz \tmp0, 2f + /* Restore FTOP */ + ldptr.w \tmp0, \thread, THREAD_FTOP + andi \tmp0, \tmp0, 0x7 + la.pcrel \tmp1, 1f + alsl.d \tmp1, \tmp0, \tmp1, 3 + jr \tmp1 +1: + x86mttop 0 + b 2f + x86mttop 1 + b 2f + x86mttop 2 + b 2f + x86mttop 3 + b 2f + x86mttop 4 + b 2f + x86mttop 5 + b 2f + x86mttop 6 + b 2f + x86mttop 7 +2: +#endif .endm .macro fpu_save_cc thread tmp0 tmp1 @@ -353,7 +285,7 @@ .macro lsx_restore_all thread tmp0 tmp1 lsx_restore_data \thread, \tmp0 fpu_restore_cc \thread, \tmp0, \tmp1 - fpu_restore_csr \thread, \tmp0 + fpu_restore_csr \thread, \tmp0, \tmp1 .endm .macro lsx_save_upper vd base tmp off @@ -563,7 +495,7 @@ .macro lasx_restore_all thread tmp0 tmp1 lasx_restore_data \thread, \tmp0 fpu_restore_cc \thread, \tmp0, \tmp1 - fpu_restore_csr \thread, \tmp0 + fpu_restore_csr \thread, \tmp0, \tmp1 .endm .macro lasx_save_upper xd base tmp off diff --git a/arch/loongarch/include/asm/elf.h b/arch/loongarch/include/asm/elf.h index 7af0cebf28d73c5d3551eb10ecc36beb6b08d2d7..b9a4ab54285c114360c05f420b023121de44de79 100644 --- a/arch/loongarch/include/asm/elf.h +++ b/arch/loongarch/include/asm/elf.h @@ -111,6 +111,15 @@ #define R_LARCH_TLS_GD_HI20 98 #define R_LARCH_32_PCREL 99 #define R_LARCH_RELAX 100 +#define R_LARCH_DELETE 101 +#define R_LARCH_ALIGN 102 +#define R_LARCH_PCREL20_S2 103 +#define R_LARCH_CFA 104 +#define R_LARCH_ADD6 105 +#define R_LARCH_SUB6 106 +#define R_LARCH_ADD_ULEB128 107 +#define R_LARCH_SUB_ULEB128 108 +#define R_LARCH_64_PCREL 109 #ifndef ELF_ARCH diff --git a/arch/loongarch/include/asm/exception.h b/arch/loongarch/include/asm/exception.h new file mode 100644 index 0000000000000000000000000000000000000000..af74a3fdcad179f7582d0234c5cfd4cdca6d5d65 --- /dev/null +++ b/arch/loongarch/include/asm/exception.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ASM_EXCEPTION_H +#define __ASM_EXCEPTION_H + +#include +#include + +void show_registers(struct pt_regs *regs); + +asmlinkage void cache_parity_error(void); +asmlinkage void noinstr do_ade(struct pt_regs *regs); +asmlinkage void noinstr do_ale(struct pt_regs *regs); +asmlinkage void noinstr do_bce(struct pt_regs *regs); +asmlinkage void noinstr do_bp(struct pt_regs *regs); +asmlinkage void noinstr do_ri(struct pt_regs *regs); +asmlinkage void noinstr do_fpu(struct pt_regs *regs); +asmlinkage void noinstr do_fpe(struct pt_regs *regs, unsigned long fcsr); +asmlinkage void noinstr do_lsx(struct pt_regs *regs); +asmlinkage void noinstr do_lasx(struct pt_regs *regs); +asmlinkage void noinstr do_lbt(struct pt_regs *regs); +asmlinkage void noinstr do_watch(struct pt_regs *regs); +asmlinkage void noinstr do_syscall(struct pt_regs *regs); +asmlinkage void noinstr do_reserved(struct pt_regs *regs); +asmlinkage void noinstr do_vint(struct pt_regs *regs, unsigned long sp); +asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, + unsigned long write, unsigned long address); + +asmlinkage void handle_ade(void); +asmlinkage void handle_ale(void); +asmlinkage void handle_bce(void); +asmlinkage void handle_sys(void); +asmlinkage void handle_bp(void); +asmlinkage void handle_ri(void); +asmlinkage void handle_fpu(void); +asmlinkage void handle_fpe(void); +asmlinkage void handle_lsx(void); +asmlinkage void handle_lasx(void); +asmlinkage void handle_lbt(void); +asmlinkage void handle_watch(void); +asmlinkage void handle_reserved(void); +asmlinkage void handle_vint(void); +asmlinkage void noinstr handle_loongarch_irq(struct pt_regs *regs); + +#endif /* __ASM_EXCEPTION_H */ diff --git a/arch/loongarch/include/asm/kasan.h b/arch/loongarch/include/asm/kasan.h new file mode 100644 index 0000000000000000000000000000000000000000..cd6084f4e153feef9140ba198207b4a50fe0cfc2 --- /dev/null +++ b/arch/loongarch/include/asm/kasan.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_KASAN_H +#define __ASM_KASAN_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include +#include + +#define KASAN_SHADOW_SCALE_SHIFT 3 +#define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL) + +#define XRANGE_SHIFT (48) + +/* Valid address length */ +#define XRANGE_SHADOW_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3) +/* Used for taking out the valid address */ +#define XRANGE_SHADOW_MASK GENMASK_ULL(XRANGE_SHADOW_SHIFT - 1, 0) +/* One segment whole address space size */ +#define XRANGE_SIZE (XRANGE_SHADOW_MASK + 1) + +/* 64-bit segment value. */ +#define XKPRANGE_UC_SEG (0x8000) +#define XKPRANGE_CC_SEG (0x9000) +#define XKVRANGE_VC_SEG (0xffff) + +/* Cached */ +#define XKPRANGE_CC_START CACHE_BASE +#define XKPRANGE_CC_SIZE XRANGE_SIZE +#define XKPRANGE_CC_KASAN_OFFSET (0) +#define XKPRANGE_CC_SHADOW_SIZE (XKPRANGE_CC_SIZE >> KASAN_SHADOW_SCALE_SHIFT) +#define XKPRANGE_CC_SHADOW_END (XKPRANGE_CC_KASAN_OFFSET + XKPRANGE_CC_SHADOW_SIZE) + +/* UnCached */ +#define XKPRANGE_UC_START UNCACHE_BASE +#define XKPRANGE_UC_SIZE XRANGE_SIZE +#define XKPRANGE_UC_KASAN_OFFSET XKPRANGE_CC_SHADOW_END +#define XKPRANGE_UC_SHADOW_SIZE (XKPRANGE_UC_SIZE >> KASAN_SHADOW_SCALE_SHIFT) +#define XKPRANGE_UC_SHADOW_END (XKPRANGE_UC_KASAN_OFFSET + XKPRANGE_UC_SHADOW_SIZE) + +/* VMALLOC (Cached or UnCached) */ +#define XKVRANGE_VC_START MODULES_VADDR +#define XKVRANGE_VC_SIZE round_up(KFENCE_AREA_END - MODULES_VADDR + 1, PGDIR_SIZE) +#define XKVRANGE_VC_KASAN_OFFSET XKPRANGE_UC_SHADOW_END +#define XKVRANGE_VC_SHADOW_SIZE (XKVRANGE_VC_SIZE >> KASAN_SHADOW_SCALE_SHIFT) +#define XKVRANGE_VC_SHADOW_END (XKVRANGE_VC_KASAN_OFFSET + XKVRANGE_VC_SHADOW_SIZE) + +/* KAsan shadow memory start right after vmalloc. */ +#define KASAN_SHADOW_START round_up(KFENCE_AREA_END, PGDIR_SIZE) +#define KASAN_SHADOW_SIZE (XKVRANGE_VC_SHADOW_END - XKPRANGE_CC_KASAN_OFFSET) +#define KASAN_SHADOW_END round_up(KASAN_SHADOW_START + KASAN_SHADOW_SIZE, PGDIR_SIZE) + +#define XKPRANGE_CC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_CC_KASAN_OFFSET) +#define XKPRANGE_UC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_UC_KASAN_OFFSET) +#define XKVRANGE_VC_SHADOW_OFFSET (KASAN_SHADOW_START + XKVRANGE_VC_KASAN_OFFSET) + +extern bool kasan_early_stage; +extern unsigned char kasan_early_shadow_page[PAGE_SIZE]; + +#define kasan_mem_to_shadow kasan_mem_to_shadow +void *kasan_mem_to_shadow(const void *addr); + +#define kasan_shadow_to_mem kasan_shadow_to_mem +const void *kasan_shadow_to_mem(const void *shadow_addr); + +#define kasan_arch_is_ready kasan_arch_is_ready +static __always_inline bool kasan_arch_is_ready(void) +{ + return !kasan_early_stage; +} + +#define addr_has_metadata addr_has_metadata +static __always_inline bool addr_has_metadata(const void *addr) +{ + return (kasan_mem_to_shadow((void *)addr) != NULL); +} + +void kasan_init(void); +asmlinkage void kasan_early_init(void); + +#endif +#endif diff --git a/arch/loongarch/include/asm/kfence.h b/arch/loongarch/include/asm/kfence.h new file mode 100644 index 0000000000000000000000000000000000000000..6c82aea1c99398c46484a77cc28da1316799affb --- /dev/null +++ b/arch/loongarch/include/asm/kfence.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * KFENCE support for LoongArch. + * + * Author: Enze Li + * Copyright (C) 2022-2023 KylinSoft Corporation. + */ + +#ifndef _ASM_LOONGARCH_KFENCE_H +#define _ASM_LOONGARCH_KFENCE_H + +#include +#include +#include + +static inline bool arch_kfence_init_pool(void) +{ + int err; + char *kfence_pool = __kfence_pool; + struct vm_struct *area; + + area = __get_vm_area_caller(KFENCE_POOL_SIZE, VM_IOREMAP, + KFENCE_AREA_START, KFENCE_AREA_END, + __builtin_return_address(0)); + if (!area) + return false; + + __kfence_pool = (char *)area->addr; + err = ioremap_page_range((unsigned long)__kfence_pool, + (unsigned long)__kfence_pool + KFENCE_POOL_SIZE, + virt_to_phys((void *)kfence_pool), PAGE_KERNEL); + if (err) { + free_vm_area(area); + __kfence_pool = kfence_pool; + return false; + } + + return true; +} + +/* Protect the given page and flush TLB. */ +static inline bool kfence_protect_page(unsigned long addr, bool protect) +{ + pte_t *pte = virt_to_kpte(addr); + + if (WARN_ON(!pte) || pte_none(*pte)) + return false; + + if (protect) + set_pte(pte, __pte(pte_val(*pte) & ~(_PAGE_VALID | _PAGE_PRESENT))); + else + set_pte(pte, __pte(pte_val(*pte) | (_PAGE_VALID | _PAGE_PRESENT))); + + preempt_disable(); + local_flush_tlb_one(addr); + preempt_enable(); + + return true; +} + +#endif /* _ASM_LOONGARCH_KFENCE_H */ diff --git a/arch/loongarch/include/asm/kgdb.h b/arch/loongarch/include/asm/kgdb.h new file mode 100644 index 0000000000000000000000000000000000000000..2041ae58b161576d8d9bf5e715f8331b8c93c243 --- /dev/null +++ b/arch/loongarch/include/asm/kgdb.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#ifndef _ASM_LOONGARCH_KGDB_H +#define _ASM_LOONGARCH_KGDB_H + +#define GDB_SIZEOF_REG sizeof(u64) + +/* gdb remote procotol expects the following register layout. */ + +/* + * General purpose registers: + * r0-r31: 64 bit + * orig_a0: 64 bit + * pc : 64 bit + * csr_badvaddr: 64 bit + */ +#define DBG_PT_REGS_BASE 0 +#define DBG_PT_REGS_NUM 35 +#define DBG_PT_REGS_END (DBG_PT_REGS_BASE + DBG_PT_REGS_NUM - 1) + +/* + * Floating point registers: + * f0-f31: 64 bit + */ +#define DBG_FPR_BASE (DBG_PT_REGS_END + 1) +#define DBG_FPR_NUM 32 +#define DBG_FPR_END (DBG_FPR_BASE + DBG_FPR_NUM - 1) + +/* + * Condition Flag registers: + * fcc0-fcc8: 8 bit + */ +#define DBG_FCC_BASE (DBG_FPR_END + 1) +#define DBG_FCC_NUM 8 +#define DBG_FCC_END (DBG_FCC_BASE + DBG_FCC_NUM - 1) + +/* + * Floating-point Control and Status registers: + * fcsr: 32 bit + */ +#define DBG_FCSR_NUM 1 +#define DBG_FCSR (DBG_FCC_END + 1) + +#define DBG_MAX_REG_NUM (DBG_FCSR + 1) + +/* + * Size of I/O buffer for gdb packet. + * considering to hold all register contents, size is set + */ +#define BUFMAX 2048 + +/* + * Number of bytes required for gdb_regs buffer. + * PT_REGS and FPR: 8 bytes; FCSR: 4 bytes; FCC: 1 bytes. + * GDB fails to connect for size beyond this with error + * "'g' packet reply is too long" + */ +#define NUMREGBYTES ((DBG_PT_REGS_NUM + DBG_FPR_NUM) * GDB_SIZEOF_REG + DBG_FCC_NUM * 1 + DBG_FCSR_NUM * 4) + +#define BREAK_INSTR_SIZE 4 +#define CACHE_FLUSH_IS_SAFE 0 + +/* Register numbers of various important registers. */ +enum dbg_loongarch_regnum { + DBG_LOONGARCH_ZERO = 0, + DBG_LOONGARCH_RA, + DBG_LOONGARCH_TP, + DBG_LOONGARCH_SP, + DBG_LOONGARCH_A0, + DBG_LOONGARCH_FP = 22, + DBG_LOONGARCH_S0, + DBG_LOONGARCH_S1, + DBG_LOONGARCH_S2, + DBG_LOONGARCH_S3, + DBG_LOONGARCH_S4, + DBG_LOONGARCH_S5, + DBG_LOONGARCH_S6, + DBG_LOONGARCH_S7, + DBG_LOONGARCH_S8, + DBG_LOONGARCH_ORIG_A0, + DBG_LOONGARCH_PC, + DBG_LOONGARCH_BADV +}; + +void kgdb_breakinst(void); +void arch_kgdb_breakpoint(void); + +#ifdef CONFIG_KGDB +bool kgdb_breakpoint_handler(struct pt_regs *regs); +#else /* !CONFIG_KGDB */ +static inline bool kgdb_breakpoint_handler(struct pt_regs *regs) { return false; } +#endif /* CONFIG_KGDB */ + +#endif /* __ASM_KGDB_H_ */ diff --git a/arch/loongarch/include/asm/lbt.h b/arch/loongarch/include/asm/lbt.h new file mode 100644 index 0000000000000000000000000000000000000000..e671978bf5523fba8b8f7d9da0657baed66ecd8f --- /dev/null +++ b/arch/loongarch/include/asm/lbt.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Author: Qi Hu + * Huacai Chen + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ +#ifndef _ASM_LBT_H +#define _ASM_LBT_H + +#include +#include +#include +#include + +extern void _init_lbt(void); +extern void _save_lbt(struct loongarch_lbt *); +extern void _restore_lbt(struct loongarch_lbt *); + +static inline int is_lbt_enabled(void) +{ + if (!cpu_has_lbt) + return 0; + + return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LBTEN) ? + 1 : 0; +} + +static inline int is_lbt_owner(void) +{ + return test_thread_flag(TIF_USEDLBT); +} + +#ifdef CONFIG_CPU_HAS_LBT + +static inline void enable_lbt(void) +{ + if (cpu_has_lbt) + csr_xchg32(CSR_EUEN_LBTEN, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN); +} + +static inline void disable_lbt(void) +{ + if (cpu_has_lbt) + csr_xchg32(0, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN); +} + +static inline void __own_lbt(void) +{ + enable_lbt(); + set_thread_flag(TIF_USEDLBT); + KSTK_EUEN(current) |= CSR_EUEN_LBTEN; +} + +static inline void own_lbt_inatomic(int restore) +{ + if (cpu_has_lbt && !is_lbt_owner()) { + __own_lbt(); + if (restore) + _restore_lbt(¤t->thread.lbt); + } +} + +static inline void own_lbt(int restore) +{ + preempt_disable(); + own_lbt_inatomic(restore); + preempt_enable(); +} + +static inline void lose_lbt_inatomic(int save, struct task_struct *tsk) +{ + if (cpu_has_lbt && is_lbt_owner()) { + if (save) + _save_lbt(&tsk->thread.lbt); + + disable_lbt(); + clear_tsk_thread_flag(tsk, TIF_USEDLBT); + } + KSTK_EUEN(tsk) &= ~(CSR_EUEN_LBTEN); +} + +static inline void lose_lbt(int save) +{ + preempt_disable(); + lose_lbt_inatomic(save, current); + preempt_enable(); +} + +static inline void init_lbt(void) +{ + __own_lbt(); + _init_lbt(); +} +#else +static inline void own_lbt_inatomic(int restore) {} +static inline void lose_lbt_inatomic(int save, struct task_struct *tsk) {} +static inline void init_lbt(void) {} +static inline void lose_lbt(int save) {} +#endif + +static inline int thread_lbt_context_live(void) +{ + if (!cpu_has_lbt) + return 0; + + return test_thread_flag(TIF_LBT_CTX_LIVE); +} + +#endif /* _ASM_LBT_H */ diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index 10748a20a2ab5593f5a19b5b2cbc5dba8ebdda2b..33531d432b492d201f98ae64b941255a94355f91 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -12,49 +12,6 @@ #ifndef __ASSEMBLY__ #include -/* - * parse_r var, r - Helper assembler macro for parsing register names. - * - * This converts the register name in $n form provided in \r to the - * corresponding register number, which is assigned to the variable \var. It is - * needed to allow explicit encoding of instructions in inline assembly where - * registers are chosen by the compiler in $n form, allowing us to avoid using - * fixed register numbers. - * - * It also allows newer instructions (not implemented by the assembler) to be - * transparently implemented using assembler macros, instead of needing separate - * cases depending on toolchain support. - * - * Simple usage example: - * __asm__ __volatile__("parse_r addr, %0\n\t" - * "#invtlb op, 0, %0\n\t" - * ".word ((0x6498000) | (addr << 10) | (0 << 5) | op)" - * : "=r" (status); - */ - -/* Match an individual register number and assign to \var */ -#define _IFC_REG(n) \ - ".ifc \\r, $r" #n "\n\t" \ - "\\var = " #n "\n\t" \ - ".endif\n\t" - -__asm__(".macro parse_r var r\n\t" - "\\var = -1\n\t" - _IFC_REG(0) _IFC_REG(1) _IFC_REG(2) _IFC_REG(3) - _IFC_REG(4) _IFC_REG(5) _IFC_REG(6) _IFC_REG(7) - _IFC_REG(8) _IFC_REG(9) _IFC_REG(10) _IFC_REG(11) - _IFC_REG(12) _IFC_REG(13) _IFC_REG(14) _IFC_REG(15) - _IFC_REG(16) _IFC_REG(17) _IFC_REG(18) _IFC_REG(19) - _IFC_REG(20) _IFC_REG(21) _IFC_REG(22) _IFC_REG(23) - _IFC_REG(24) _IFC_REG(25) _IFC_REG(26) _IFC_REG(27) - _IFC_REG(28) _IFC_REG(29) _IFC_REG(30) _IFC_REG(31) - ".iflt \\var\n\t" - ".error \"Unable to parse register name \\r\"\n\t" - ".endif\n\t" - ".endm"); - -#undef _IFC_REG - /* CPUCFG */ #define read_cpucfg(reg) __cpucfg(reg) @@ -1453,6 +1410,10 @@ __BUILD_CSR_OP(tlbidx) #define FPU_CSR_RU 0x200 /* towards +Infinity */ #define FPU_CSR_RD 0x300 /* towards -Infinity */ +/* Bit 6 of FPU Status Register specify the LBT TOP simulation mode */ +#define FPU_CSR_TM_SHIFT 0x6 +#define FPU_CSR_TM (_ULCAST_(1) << FPU_CSR_TM_SHIFT) + #define read_fcsr(source) \ ({ \ unsigned int __res; \ diff --git a/arch/loongarch/include/asm/mmzone.h b/arch/loongarch/include/asm/mmzone.h index fe67d0b4b33ddce60d9b28e72b1c942b6bdca91c..2b9a90727e191224875e23f041dc3f454a2ed540 100644 --- a/arch/loongarch/include/asm/mmzone.h +++ b/arch/loongarch/include/asm/mmzone.h @@ -13,6 +13,4 @@ extern struct pglist_data *node_data[]; #define NODE_DATA(nid) (node_data[(nid)]) -extern void setup_zero_pages(void); - #endif /* _ASM_MMZONE_H_ */ diff --git a/arch/loongarch/include/asm/page.h b/arch/loongarch/include/asm/page.h index 26e8dccb661909c2f306f2f002c730abdddaf5af..63f137ce82a41fc4e251f2adceef441e72dcd691 100644 --- a/arch/loongarch/include/asm/page.h +++ b/arch/loongarch/include/asm/page.h @@ -84,7 +84,12 @@ typedef struct { unsigned long pgprot; } pgprot_t; #define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) #define virt_to_pfn(kaddr) PFN_DOWN(PHYSADDR(kaddr)) -#define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr)) + +#define virt_to_page(kaddr) \ +({ \ + (likely((unsigned long)kaddr < vm_map_base)) ? \ + dmw_virt_to_page((unsigned long)kaddr) : tlb_virt_to_page((unsigned long)kaddr);\ +}) extern int __virt_addr_valid(volatile void *kaddr); #define virt_addr_valid(kaddr) __virt_addr_valid((volatile void *)(kaddr)) diff --git a/arch/loongarch/include/asm/pgalloc.h b/arch/loongarch/include/asm/pgalloc.h index 23f5b1107246a3cb5e0db4ced632e9e6aff8bcfe..79470f0b4f1d8dfbd715e4d1dc2e711949175b30 100644 --- a/arch/loongarch/include/asm/pgalloc.h +++ b/arch/loongarch/include/asm/pgalloc.h @@ -94,4 +94,5 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address) #endif /* __PAGETABLE_PUD_FOLDED */ +extern pte_t * __init populate_kernel_pte(unsigned long addr); #endif /* _ASM_PGALLOC_H */ diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 06963a172319da14c298cbeaca2c1f0beb80a5e8..29d9b12298bc843ecf24012d37f9042079d939e5 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -70,12 +70,9 @@ struct vm_area_struct; * for zero-mapped memory areas etc.. */ -extern unsigned long empty_zero_page; -extern unsigned long zero_page_mask; +extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; -#define ZERO_PAGE(vaddr) \ - (virt_to_page((void *)(empty_zero_page + (((unsigned long)(vaddr)) & zero_page_mask)))) -#define __HAVE_COLOR_ZERO_PAGE +#define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page) /* * TLB refill handlers may also map the vmalloc area into xkvrange. @@ -85,14 +82,30 @@ extern unsigned long zero_page_mask; #define MODULES_VADDR (vm_map_base + PCI_IOSIZE + (2 * PAGE_SIZE)) #define MODULES_END (MODULES_VADDR + SZ_256M) +#ifdef CONFIG_KFENCE +#define KFENCE_AREA_SIZE (((CONFIG_KFENCE_NUM_OBJECTS + 1) * 2 + 2) * PAGE_SIZE) +#else +#define KFENCE_AREA_SIZE 0 +#endif + #define VMALLOC_START MODULES_END + +#ifndef CONFIG_KASAN #define VMALLOC_END \ (vm_map_base + \ - min(PTRS_PER_PGD * PTRS_PER_PUD * PTRS_PER_PMD * PTRS_PER_PTE * PAGE_SIZE, (1UL << cpu_vabits)) - PMD_SIZE - VMEMMAP_SIZE) + min(PTRS_PER_PGD * PTRS_PER_PUD * PTRS_PER_PMD * PTRS_PER_PTE * PAGE_SIZE, (1UL << cpu_vabits)) - PMD_SIZE - VMEMMAP_SIZE - KFENCE_AREA_SIZE) +#else +#define VMALLOC_END \ + (vm_map_base + \ + min(PTRS_PER_PGD * PTRS_PER_PUD * PTRS_PER_PMD * PTRS_PER_PTE * PAGE_SIZE, (1UL << cpu_vabits) / 2) - PMD_SIZE - VMEMMAP_SIZE - KFENCE_AREA_SIZE) +#endif #define vmemmap ((struct page *)((VMALLOC_END + PMD_SIZE) & PMD_MASK)) #define VMEMMAP_END ((unsigned long)vmemmap + VMEMMAP_SIZE - 1) +#define KFENCE_AREA_START (VMEMMAP_END + 1) +#define KFENCE_AREA_END (KFENCE_AREA_START + KFENCE_AREA_SIZE - 1) + #define pte_ERROR(e) \ pr_err("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e)) #ifndef __PAGETABLE_PMD_FOLDED @@ -350,6 +363,9 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt extern pgd_t swapper_pg_dir[]; extern pgd_t invalid_pg_dir[]; +struct page *dmw_virt_to_page(unsigned long kaddr); +struct page *tlb_virt_to_page(unsigned long kaddr); + /* * The following only work if pte_present() is true. * Undefined behaviour if not.. @@ -596,6 +612,9 @@ static inline long pmd_protnone(pmd_t pmd) } #endif /* CONFIG_NUMA_BALANCING */ +#define pmd_leaf(pmd) ((pmd_val(pmd) & _PAGE_HUGE) != 0) +#define pud_leaf(pud) ((pud_val(pud) & _PAGE_HUGE) != 0) + /* * We provide our own get_unmapped area to cope with the virtual aliasing * constraints placed on us by the cache architecture. diff --git a/arch/loongarch/include/asm/processor.h b/arch/loongarch/include/asm/processor.h index 636e1c66398c17c7bfb930b5b225bd241f831a45..c3bc44b5f5b30b2d43b901da6c339c26594e08fc 100644 --- a/arch/loongarch/include/asm/processor.h +++ b/arch/loongarch/include/asm/processor.h @@ -80,11 +80,22 @@ BUILD_FPR_ACCESS(32) BUILD_FPR_ACCESS(64) struct loongarch_fpu { - unsigned int fcsr; uint64_t fcc; /* 8x8 */ + uint32_t fcsr; + uint32_t ftop; union fpureg fpr[NUM_FPU_REGS]; }; +struct loongarch_lbt { + /* Scratch registers */ + unsigned long scr0; + unsigned long scr1; + unsigned long scr2; + unsigned long scr3; + /* Eflags register */ + unsigned long eflags; +}; + #define INIT_CPUMASK { \ {0,} \ } @@ -113,15 +124,6 @@ struct thread_struct { unsigned long csr_ecfg; unsigned long csr_badvaddr; /* Last user fault */ - /* Scratch registers */ - unsigned long scr0; - unsigned long scr1; - unsigned long scr2; - unsigned long scr3; - - /* Eflags register */ - unsigned long eflags; - /* Other stuff associated with the thread. */ unsigned long trap_nr; unsigned long error_code; @@ -133,6 +135,7 @@ struct thread_struct { * context because they are conditionally copied at fork(). */ struct loongarch_fpu fpu FPU_ALIGN; + struct loongarch_lbt lbt; /* Also conditionally copied */ /* Hardware breakpoints pinned to this task. */ struct perf_event *hbp_break[LOONGARCH_MAX_BRP]; @@ -174,8 +177,9 @@ struct thread_struct { * FPU & vector registers \ */ \ .fpu = { \ - .fcsr = 0, \ .fcc = 0, \ + .fcsr = 0, \ + .ftop = 0, \ .fpr = {{{0,},},}, \ }, \ .hbp_break = {0}, \ diff --git a/arch/loongarch/include/asm/setup.h b/arch/loongarch/include/asm/setup.h index be05c0e706a2e23d1e5c82859f02be62c82ce0b9..a0bc159ce8bdc0348defe27953c07d973dd58b5f 100644 --- a/arch/loongarch/include/asm/setup.h +++ b/arch/loongarch/include/asm/setup.h @@ -7,6 +7,7 @@ #define _LOONGARCH_SETUP_H #include +#include #include #define VECSIZE 0x200 @@ -33,8 +34,13 @@ extern long __la_abs_end; extern long __rela_dyn_begin; extern long __rela_dyn_end; -extern void * __init relocate_kernel(void); +extern unsigned long __init relocate_kernel(void); #endif +static inline unsigned long kaslr_offset(void) +{ + return (unsigned long)&_text - VMLINUX_LOAD_ADDRESS; +} + #endif /* __SETUP_H */ diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index 66ecb480c894aa3f29843e711e374812fff3e658..f81e5f01d61905f5b8d7da4786ba512258381acd 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -70,6 +70,7 @@ struct secondary_data { extern struct secondary_data cpuboot_data; extern asmlinkage void smpboot_entry(void); +extern asmlinkage void start_secondary(void); extern void calculate_cpu_foreign_map(void); diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 7df80e6ae9d2c8d76af305b7478bd52776ed380a..4fb1e6408b982aec130c75413e04bb6d2fb6c52c 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -158,6 +158,10 @@ cfi_st u0, PT_R21, \docfi csrrd u0, PERCPU_BASE_KS 9: +#ifdef CONFIG_KGDB + li.w t0, CSR_CRMD_WE + csrxchg t0, t0, LOONGARCH_CSR_CRMD +#endif .endm .macro SAVE_ALL docfi=0 diff --git a/arch/loongarch/include/asm/string.h b/arch/loongarch/include/asm/string.h index 7b29cc9c70aa617049a3790f8cff9632ff100a66..5bb5a90d26815133e46a9c8ca2f304350acac18e 100644 --- a/arch/loongarch/include/asm/string.h +++ b/arch/loongarch/include/asm/string.h @@ -7,11 +7,31 @@ #define __HAVE_ARCH_MEMSET extern void *memset(void *__s, int __c, size_t __count); +extern void *__memset(void *__s, int __c, size_t __count); #define __HAVE_ARCH_MEMCPY extern void *memcpy(void *__to, __const__ void *__from, size_t __n); +extern void *__memcpy(void *__to, __const__ void *__from, size_t __n); #define __HAVE_ARCH_MEMMOVE extern void *memmove(void *__dest, __const__ void *__src, size_t __n); +extern void *__memmove(void *__dest, __const__ void *__src, size_t __n); + +#if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__) + +/* + * For files that are not instrumented (e.g. mm/slub.c) we + * should use not instrumented version of mem* functions. + */ + +#define memset(s, c, n) __memset(s, c, n) +#define memcpy(dst, src, len) __memcpy(dst, src, len) +#define memmove(dst, src, len) __memmove(dst, src, len) + +#ifndef __NO_FORTIFY +#define __NO_FORTIFY /* FORTIFY_SOURCE uses __builtin_memcpy, etc. */ +#endif + +#endif #endif /* _ASM_STRING_H */ diff --git a/arch/loongarch/include/asm/switch_to.h b/arch/loongarch/include/asm/switch_to.h index 24e3094bebab166c046264280ef9b5929b350377..5b225aff3ba21aa06d0713bc8e73e1b941389630 100644 --- a/arch/loongarch/include/asm/switch_to.h +++ b/arch/loongarch/include/asm/switch_to.h @@ -7,6 +7,7 @@ #include #include +#include struct task_struct; @@ -34,6 +35,7 @@ extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev, #define switch_to(prev, next, last) \ do { \ lose_fpu_inatomic(1, prev); \ + lose_lbt_inatomic(1, prev); \ hw_breakpoint_thread_switch(next); \ (last) = __switch_to(prev, next, task_thread_info(next), \ __builtin_return_address(0), __builtin_frame_address(0)); \ diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index 1a3354ca056e9af11e15bfe80ffce5ad97c9e640..8cb653d49a54343ebfa26814e037ddf844c98351 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -84,6 +84,8 @@ register unsigned long current_stack_pointer __asm__("$sp"); #define TIF_SINGLESTEP 16 /* Single Step */ #define TIF_LSX_CTX_LIVE 17 /* LSX context must be preserved */ #define TIF_LASX_CTX_LIVE 18 /* LASX context must be preserved */ +#define TIF_USEDLBT 19 /* LBT was used by this task this quantum (SMP) */ +#define TIF_LBT_CTX_LIVE 20 /* LBT context must be preserved */ #define _TIF_SIGPENDING (1< + */ +#ifndef _ASM_LOONGARCH_XOR_H +#define _ASM_LOONGARCH_XOR_H + +#include +#include + +#ifdef CONFIG_CPU_HAS_LSX +static struct xor_block_template xor_block_lsx = { + .name = "lsx", + .do_2 = xor_lsx_2, + .do_3 = xor_lsx_3, + .do_4 = xor_lsx_4, + .do_5 = xor_lsx_5, +}; + +#define XOR_SPEED_LSX() \ + do { \ + if (cpu_has_lsx) \ + xor_speed(&xor_block_lsx); \ + } while (0) +#else /* CONFIG_CPU_HAS_LSX */ +#define XOR_SPEED_LSX() +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX +static struct xor_block_template xor_block_lasx = { + .name = "lasx", + .do_2 = xor_lasx_2, + .do_3 = xor_lasx_3, + .do_4 = xor_lasx_4, + .do_5 = xor_lasx_5, +}; + +#define XOR_SPEED_LASX() \ + do { \ + if (cpu_has_lasx) \ + xor_speed(&xor_block_lasx); \ + } while (0) +#else /* CONFIG_CPU_HAS_LASX */ +#define XOR_SPEED_LASX() +#endif /* CONFIG_CPU_HAS_LASX */ + +/* + * For grins, also test the generic routines. + * + * More importantly: it cannot be ruled out at this point of time, that some + * future (maybe reduced) models could run the vector algorithms slower than + * the scalar ones, maybe for errata or micro-op reasons. It may be + * appropriate to revisit this after one or two more uarch generations. + */ +#include + +#undef XOR_TRY_TEMPLATES +#define XOR_TRY_TEMPLATES \ +do { \ + xor_speed(&xor_block_8regs); \ + xor_speed(&xor_block_8regs_p); \ + xor_speed(&xor_block_32regs); \ + xor_speed(&xor_block_32regs_p); \ + XOR_SPEED_LSX(); \ + XOR_SPEED_LASX(); \ +} while (0) + +#endif /* _ASM_LOONGARCH_XOR_H */ diff --git a/arch/loongarch/include/asm/xor_simd.h b/arch/loongarch/include/asm/xor_simd.h new file mode 100644 index 0000000000000000000000000000000000000000..471b96332f38176ebc5dff2d075697e169f72f58 --- /dev/null +++ b/arch/loongarch/include/asm/xor_simd.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 WANG Xuerui + */ +#ifndef _ASM_LOONGARCH_XOR_SIMD_H +#define _ASM_LOONGARCH_XOR_SIMD_H + +#ifdef CONFIG_CPU_HAS_LSX +void xor_lsx_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2); +void xor_lsx_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3); +void xor_lsx_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4); +void xor_lsx_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4, const unsigned long * __restrict p5); +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX +void xor_lasx_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2); +void xor_lasx_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3); +void xor_lasx_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4); +void xor_lasx_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4, const unsigned long * __restrict p5); +#endif /* CONFIG_CPU_HAS_LASX */ + +#endif /* _ASM_LOONGARCH_XOR_SIMD_H */ diff --git a/arch/loongarch/include/uapi/asm/ptrace.h b/arch/loongarch/include/uapi/asm/ptrace.h index 06e3be52cb0427c91e03ac2fdddcb85b28ca860c..ac915f84165053964105383889e4d8f48c72f741 100644 --- a/arch/loongarch/include/uapi/asm/ptrace.h +++ b/arch/loongarch/include/uapi/asm/ptrace.h @@ -56,6 +56,12 @@ struct user_lasx_state { uint64_t vregs[32*4]; }; +struct user_lbt_state { + uint64_t scr[4]; + uint32_t eflags; + uint32_t ftop; +}; + struct user_watch_state { uint64_t dbg_info; struct { diff --git a/arch/loongarch/include/uapi/asm/sigcontext.h b/arch/loongarch/include/uapi/asm/sigcontext.h index 4cd7d16f70377586a1d209dca634367c89713ffa..6c22f616b8f15d746316a62468a9a9a22c736443 100644 --- a/arch/loongarch/include/uapi/asm/sigcontext.h +++ b/arch/loongarch/include/uapi/asm/sigcontext.h @@ -59,4 +59,14 @@ struct lasx_context { __u32 fcsr; }; +/* LBT context */ +#define LBT_CTX_MAGIC 0x42540001 +#define LBT_CTX_ALIGN 8 +struct lbt_context { + __u64 regs[4]; + __u32 eflags; + __u32 ftop; +}; + + #endif /* _UAPI_ASM_SIGCONTEXT_H */ diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 8e279f04f9e7a8031784fc46f0a8a41148e1337b..4fcc168f07323154b4d7fc6712ab7a2298bb0d3e 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -15,8 +15,14 @@ obj-$(CONFIG_EFI) += efi.o obj-$(CONFIG_CPU_HAS_FPU) += fpu.o kfpu.o +obj-$(CONFIG_CPU_HAS_LBT) += lbt.o + obj-$(CONFIG_ARCH_STRICT_ALIGN) += unaligned.o +CFLAGS_module.o += $(call cc-option,-Wno-override-init,) +CFLAGS_syscall.o += $(call cc-option,-Wno-override-init,) +CFLAGS_perf_event.o += $(call cc-option,-Wno-override-init,) + ifdef CONFIG_FUNCTION_TRACER ifndef CONFIG_DYNAMIC_FTRACE obj-y += mcount.o ftrace.o @@ -32,6 +38,12 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_rethook_trampoline.o = $(CC_FLAGS_FTRACE) endif +KASAN_SANITIZE_efi.o := n +KASAN_SANITIZE_cpu-probe.o := n +KASAN_SANITIZE_traps.o := n +KASAN_SANITIZE_smp.o := n +KASAN_SANITIZE_vdso.o := n + obj-$(CONFIG_MODULES) += module.o module-sections.o obj-$(CONFIG_STACKTRACE) += stacktrace.o @@ -54,6 +66,7 @@ obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o +obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_RETHOOK) += rethook.o rethook_trampoline.o obj-$(CONFIG_UPROBES) += uprobes.o diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 9450e09073ebf7411e200a21a5a666c7dad092be..8e00a754e548943ae4dba5d330a1354426c713d1 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -281,7 +281,6 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n", pxm, pa->apic_id, node); } -void __init acpi_numa_arch_fixup(void) {} #endif void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size) diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c index 505e4bf59603148ba85ab979073b36933e6a9927..8da0726777edb41ea66d47f640308c18435f4551 100644 --- a/arch/loongarch/kernel/asm-offsets.c +++ b/arch/loongarch/kernel/asm-offsets.c @@ -118,13 +118,6 @@ void output_thread_defines(void) OFFSET(THREAD_CSRECFG, task_struct, thread.csr_ecfg); - OFFSET(THREAD_SCR0, task_struct, thread.scr0); - OFFSET(THREAD_SCR1, task_struct, thread.scr1); - OFFSET(THREAD_SCR2, task_struct, thread.scr2); - OFFSET(THREAD_SCR3, task_struct, thread.scr3); - - OFFSET(THREAD_EFLAGS, task_struct, thread.eflags); - OFFSET(THREAD_FPU, task_struct, thread.fpu); OFFSET(THREAD_BVADDR, task_struct, \ @@ -172,6 +165,17 @@ void output_thread_fpu_defines(void) OFFSET(THREAD_FCSR, loongarch_fpu, fcsr); OFFSET(THREAD_FCC, loongarch_fpu, fcc); + OFFSET(THREAD_FTOP, loongarch_fpu, ftop); + BLANK(); +} + +void output_thread_lbt_defines(void) +{ + OFFSET(THREAD_SCR0, loongarch_lbt, scr0); + OFFSET(THREAD_SCR1, loongarch_lbt, scr1); + OFFSET(THREAD_SCR2, loongarch_lbt, scr2); + OFFSET(THREAD_SCR3, loongarch_lbt, scr3); + OFFSET(THREAD_EFLAGS, loongarch_lbt, eflags); BLANK(); } diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index e925579c7a71eab8822ff683dd8444f8982e42d6..55320813ee0819f0f23429361b7fc9722b710d65 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -144,6 +144,20 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) c->options |= LOONGARCH_CPU_LVZ; elf_hwcap |= HWCAP_LOONGARCH_LVZ; } +#ifdef CONFIG_CPU_HAS_LBT + if (config & CPUCFG2_X86BT) { + c->options |= LOONGARCH_CPU_LBT_X86; + elf_hwcap |= HWCAP_LOONGARCH_LBT_X86; + } + if (config & CPUCFG2_ARMBT) { + c->options |= LOONGARCH_CPU_LBT_ARM; + elf_hwcap |= HWCAP_LOONGARCH_LBT_ARM; + } + if (config & CPUCFG2_MIPSBT) { + c->options |= LOONGARCH_CPU_LBT_MIPS; + elf_hwcap |= HWCAP_LOONGARCH_LBT_MIPS; + } +#endif config = read_cpucfg(LOONGARCH_CPUCFG6); if (config & CPUCFG6_PMP) diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S index d737e3cf42d3fd8ca882e08113d58d2887db32c1..65518bb8f47285e406b95f8ba4c63a29e2c23a4a 100644 --- a/arch/loongarch/kernel/entry.S +++ b/arch/loongarch/kernel/entry.S @@ -58,6 +58,11 @@ SYM_FUNC_START(handle_syscall) SAVE_STATIC +#ifdef CONFIG_KGDB + li.w t1, CSR_CRMD_WE + csrxchg t1, t1, LOONGARCH_CSR_CRMD +#endif + move u0, t0 li.d tp, ~_THREAD_MASK and tp, tp, sp diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 501094a09f5d89da2af50522b6c0fc9a9b65ec95..d53ab10f464465e3f88910614afefce92b5af607 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -22,7 +22,7 @@ .macro EX insn, reg, src, offs .ex\@: \insn \reg, \src, \offs - _asm_extable .ex\@, fault + _asm_extable .ex\@, .L_fpu_fault .endm .macro sc_save_fp base @@ -138,6 +138,13 @@ .macro sc_save_fcsr base, tmp0 movfcsr2gr \tmp0, fcsr0 EX st.w \tmp0, \base, 0 +#if defined(CONFIG_CPU_HAS_LBT) + /* TM bit is always 0 if LBT not supported */ + andi \tmp0, \tmp0, FPU_CSR_TM + beqz \tmp0, 1f + x86clrtm +1: +#endif .endm .macro sc_restore_fcsr base, tmp0 @@ -309,7 +316,7 @@ EXPORT_SYMBOL(_save_fp) */ SYM_FUNC_START(_restore_fp) fpu_restore_double a0 t1 # clobbers t1 - fpu_restore_csr a0 t1 + fpu_restore_csr a0 t1 t2 fpu_restore_cc a0 t1 t2 # clobbers t1, t2 jr ra SYM_FUNC_END(_restore_fp) @@ -514,7 +521,6 @@ SYM_FUNC_START(_restore_lasx_context) jr ra SYM_FUNC_END(_restore_lasx_context) -SYM_FUNC_START(fault) +.L_fpu_fault: li.w a0, -EFAULT # failure jr ra -SYM_FUNC_END(fault) diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index 5e828a8bc0a0e0b95006449d2a1ab10b14ca2d4a..53b883db0786207318b2cfc6857a05857b9dd1a9 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -95,12 +95,17 @@ SYM_CODE_START(kernel_entry) # kernel entry point PTR_LI sp, (_THREAD_SIZE - PT_SIZE) PTR_ADD sp, sp, tp set_saved_sp sp, t0, t1 -#endif - /* relocate_kernel() returns the new kernel entry point */ - jr a0 - ASM_BUG() + /* Jump to the new kernel: new_pc = current_pc + random_offset */ + pcaddi t0, 0 + add.d t0, t0, a0 + jirl zero, t0, 0xc +#endif /* CONFIG_RANDOMIZE_BASE */ + +#endif /* CONFIG_RELOCATABLE */ +#ifdef CONFIG_KASAN + bl kasan_early_init #endif bl start_kernel diff --git a/arch/loongarch/kernel/kfpu.c b/arch/loongarch/kernel/kfpu.c index 5c46ae8c6cac13b433ccbdfbdacf194eba6d0d19..ec5b28e570c963482d18e50f28043b066a425ffc 100644 --- a/arch/loongarch/kernel/kfpu.c +++ b/arch/loongarch/kernel/kfpu.c @@ -8,19 +8,40 @@ #include #include +static unsigned int euen_mask = CSR_EUEN_FPEN; + +/* + * The critical section between kernel_fpu_begin() and kernel_fpu_end() + * is non-reentrant. It is the caller's responsibility to avoid reentrance. + * See drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c as an example. + */ static DEFINE_PER_CPU(bool, in_kernel_fpu); +static DEFINE_PER_CPU(unsigned int, euen_current); void kernel_fpu_begin(void) { + unsigned int *euen_curr; + preempt_disable(); WARN_ON(this_cpu_read(in_kernel_fpu)); this_cpu_write(in_kernel_fpu, true); + euen_curr = this_cpu_ptr(&euen_current); - if (!is_fpu_owner()) - enable_fpu(); + *euen_curr = csr_xchg32(euen_mask, euen_mask, LOONGARCH_CSR_EUEN); + +#ifdef CONFIG_CPU_HAS_LASX + if (*euen_curr & CSR_EUEN_LASXEN) + _save_lasx(¤t->thread.fpu); + else +#endif +#ifdef CONFIG_CPU_HAS_LSX + if (*euen_curr & CSR_EUEN_LSXEN) + _save_lsx(¤t->thread.fpu); else +#endif + if (*euen_curr & CSR_EUEN_FPEN) _save_fp(¤t->thread.fpu); write_fcsr(LOONGARCH_FCSR0, 0); @@ -29,15 +50,41 @@ EXPORT_SYMBOL_GPL(kernel_fpu_begin); void kernel_fpu_end(void) { + unsigned int *euen_curr; + WARN_ON(!this_cpu_read(in_kernel_fpu)); - if (!is_fpu_owner()) - disable_fpu(); + euen_curr = this_cpu_ptr(&euen_current); + +#ifdef CONFIG_CPU_HAS_LASX + if (*euen_curr & CSR_EUEN_LASXEN) + _restore_lasx(¤t->thread.fpu); else +#endif +#ifdef CONFIG_CPU_HAS_LSX + if (*euen_curr & CSR_EUEN_LSXEN) + _restore_lsx(¤t->thread.fpu); + else +#endif + if (*euen_curr & CSR_EUEN_FPEN) _restore_fp(¤t->thread.fpu); + *euen_curr = csr_xchg32(*euen_curr, euen_mask, LOONGARCH_CSR_EUEN); + this_cpu_write(in_kernel_fpu, false); preempt_enable(); } EXPORT_SYMBOL_GPL(kernel_fpu_end); + +static int __init init_euen_mask(void) +{ + if (cpu_has_lsx) + euen_mask |= CSR_EUEN_LSXEN; + + if (cpu_has_lasx) + euen_mask |= CSR_EUEN_LASXEN; + + return 0; +} +arch_initcall(init_euen_mask); diff --git a/arch/loongarch/kernel/kgdb.c b/arch/loongarch/kernel/kgdb.c new file mode 100644 index 0000000000000000000000000000000000000000..445c452d72a79cac7f2527319301b7f430258c09 --- /dev/null +++ b/arch/loongarch/kernel/kgdb.c @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LoongArch KGDB support + * + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int kgdb_watch_activated; +static unsigned int stepped_opcode; +static unsigned long stepped_address; + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { + { "r0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) }, + { "r1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) }, + { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) }, + { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) }, + { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) }, + { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) }, + { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) }, + { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) }, + { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) }, + { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) }, + { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) }, + { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) }, + { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) }, + { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) }, + { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) }, + { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) }, + { "r16", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[16]) }, + { "r17", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[17]) }, + { "r18", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[18]) }, + { "r19", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[19]) }, + { "r20", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[20]) }, + { "r21", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[21]) }, + { "r22", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[22]) }, + { "r23", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[23]) }, + { "r24", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[24]) }, + { "r25", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[25]) }, + { "r26", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[26]) }, + { "r27", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[27]) }, + { "r28", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[28]) }, + { "r29", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[29]) }, + { "r30", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[30]) }, + { "r31", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[31]) }, + { "orig_a0", GDB_SIZEOF_REG, offsetof(struct pt_regs, orig_a0) }, + { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, csr_era) }, + { "badv", GDB_SIZEOF_REG, offsetof(struct pt_regs, csr_badvaddr) }, + { "f0", GDB_SIZEOF_REG, 0 }, + { "f1", GDB_SIZEOF_REG, 1 }, + { "f2", GDB_SIZEOF_REG, 2 }, + { "f3", GDB_SIZEOF_REG, 3 }, + { "f4", GDB_SIZEOF_REG, 4 }, + { "f5", GDB_SIZEOF_REG, 5 }, + { "f6", GDB_SIZEOF_REG, 6 }, + { "f7", GDB_SIZEOF_REG, 7 }, + { "f8", GDB_SIZEOF_REG, 8 }, + { "f9", GDB_SIZEOF_REG, 9 }, + { "f10", GDB_SIZEOF_REG, 10 }, + { "f11", GDB_SIZEOF_REG, 11 }, + { "f12", GDB_SIZEOF_REG, 12 }, + { "f13", GDB_SIZEOF_REG, 13 }, + { "f14", GDB_SIZEOF_REG, 14 }, + { "f15", GDB_SIZEOF_REG, 15 }, + { "f16", GDB_SIZEOF_REG, 16 }, + { "f17", GDB_SIZEOF_REG, 17 }, + { "f18", GDB_SIZEOF_REG, 18 }, + { "f19", GDB_SIZEOF_REG, 19 }, + { "f20", GDB_SIZEOF_REG, 20 }, + { "f21", GDB_SIZEOF_REG, 21 }, + { "f22", GDB_SIZEOF_REG, 22 }, + { "f23", GDB_SIZEOF_REG, 23 }, + { "f24", GDB_SIZEOF_REG, 24 }, + { "f25", GDB_SIZEOF_REG, 25 }, + { "f26", GDB_SIZEOF_REG, 26 }, + { "f27", GDB_SIZEOF_REG, 27 }, + { "f28", GDB_SIZEOF_REG, 28 }, + { "f29", GDB_SIZEOF_REG, 29 }, + { "f30", GDB_SIZEOF_REG, 30 }, + { "f31", GDB_SIZEOF_REG, 31 }, + { "fcc0", 1, 0 }, + { "fcc1", 1, 1 }, + { "fcc2", 1, 2 }, + { "fcc3", 1, 3 }, + { "fcc4", 1, 4 }, + { "fcc5", 1, 5 }, + { "fcc6", 1, 6 }, + { "fcc7", 1, 7 }, + { "fcsr", 4, 0 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + int reg_offset, reg_size; + + if (regno < 0 || regno >= DBG_MAX_REG_NUM) + return NULL; + + reg_offset = dbg_reg_def[regno].offset; + reg_size = dbg_reg_def[regno].size; + + if (reg_offset == -1) + goto out; + + /* Handle general-purpose/orig_a0/pc/badv registers */ + if (regno <= DBG_PT_REGS_END) { + memcpy(mem, (void *)regs + reg_offset, reg_size); + goto out; + } + + if (!(regs->csr_euen & CSR_EUEN_FPEN)) + goto out; + + save_fp(current); + + /* Handle FP registers */ + switch (regno) { + case DBG_FCSR: /* Process the fcsr */ + memcpy(mem, (void *)¤t->thread.fpu.fcsr, reg_size); + break; + case DBG_FCC_BASE ... DBG_FCC_END: /* Process the fcc */ + memcpy(mem, (void *)¤t->thread.fpu.fcc + reg_offset, reg_size); + break; + case DBG_FPR_BASE ... DBG_FPR_END: /* Process the fpr */ + memcpy(mem, (void *)¤t->thread.fpu.fpr[reg_offset], reg_size); + break; + default: + break; + } + +out: + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + int reg_offset, reg_size; + + if (regno < 0 || regno >= DBG_MAX_REG_NUM) + return -EINVAL; + + reg_offset = dbg_reg_def[regno].offset; + reg_size = dbg_reg_def[regno].size; + + if (reg_offset == -1) + return 0; + + /* Handle general-purpose/orig_a0/pc/badv registers */ + if (regno <= DBG_PT_REGS_END) { + memcpy((void *)regs + reg_offset, mem, reg_size); + return 0; + } + + if (!(regs->csr_euen & CSR_EUEN_FPEN)) + return 0; + + /* Handle FP registers */ + switch (regno) { + case DBG_FCSR: /* Process the fcsr */ + memcpy((void *)¤t->thread.fpu.fcsr, mem, reg_size); + break; + case DBG_FCC_BASE ... DBG_FCC_END: /* Process the fcc */ + memcpy((void *)¤t->thread.fpu.fcc + reg_offset, mem, reg_size); + break; + case DBG_FPR_BASE ... DBG_FPR_END: /* Process the fpr */ + memcpy((void *)¤t->thread.fpu.fpr[reg_offset], mem, reg_size); + break; + default: + break; + } + + restore_fp(current); + + return 0; +} + +/* + * Similar to regs_to_gdb_regs() except that process is sleeping and so + * we may not be able to get all the info. + */ +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + /* Initialize to zero */ + memset((char *)gdb_regs, 0, NUMREGBYTES); + + gdb_regs[DBG_LOONGARCH_RA] = p->thread.reg01; + gdb_regs[DBG_LOONGARCH_TP] = (long)p; + gdb_regs[DBG_LOONGARCH_SP] = p->thread.reg03; + + /* S0 - S8 */ + gdb_regs[DBG_LOONGARCH_S0] = p->thread.reg23; + gdb_regs[DBG_LOONGARCH_S1] = p->thread.reg24; + gdb_regs[DBG_LOONGARCH_S2] = p->thread.reg25; + gdb_regs[DBG_LOONGARCH_S3] = p->thread.reg26; + gdb_regs[DBG_LOONGARCH_S4] = p->thread.reg27; + gdb_regs[DBG_LOONGARCH_S5] = p->thread.reg28; + gdb_regs[DBG_LOONGARCH_S6] = p->thread.reg29; + gdb_regs[DBG_LOONGARCH_S7] = p->thread.reg30; + gdb_regs[DBG_LOONGARCH_S8] = p->thread.reg31; + + /* + * PC use return address (RA), i.e. the moment after return from __switch_to() + */ + gdb_regs[DBG_LOONGARCH_PC] = p->thread.reg01; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->csr_era = pc; +} + +void arch_kgdb_breakpoint(void) +{ + __asm__ __volatile__ ( \ + ".globl kgdb_breakinst\n\t" \ + "nop\n" \ + "kgdb_breakinst:\tbreak 2\n\t"); /* BRK_KDB = 2 */ +} + +/* + * Calls linux_debug_hook before the kernel dies. If KGDB is enabled, + * then try to fall into the debugger + */ +static int kgdb_loongarch_notify(struct notifier_block *self, unsigned long cmd, void *ptr) +{ + struct die_args *args = (struct die_args *)ptr; + struct pt_regs *regs = args->regs; + + /* Userspace events, ignore. */ + if (user_mode(regs)) + return NOTIFY_DONE; + + if (!kgdb_io_module_registered) + return NOTIFY_DONE; + + if (atomic_read(&kgdb_active) != -1) + kgdb_nmicallback(smp_processor_id(), regs); + + if (kgdb_handle_exception(args->trapnr, args->signr, cmd, regs)) + return NOTIFY_DONE; + + if (atomic_read(&kgdb_setting_breakpoint)) + if (regs->csr_era == (unsigned long)&kgdb_breakinst) + regs->csr_era += LOONGARCH_INSN_SIZE; + + return NOTIFY_STOP; +} + +bool kgdb_breakpoint_handler(struct pt_regs *regs) +{ + struct die_args args = { + .regs = regs, + .str = "Break", + .err = BRK_KDB, + .trapnr = read_csr_excode(), + .signr = SIGTRAP, + + }; + + return (kgdb_loongarch_notify(NULL, DIE_TRAP, &args) == NOTIFY_STOP) ? true : false; +} + +static struct notifier_block kgdb_notifier = { + .notifier_call = kgdb_loongarch_notify, +}; + +static inline void kgdb_arch_update_addr(struct pt_regs *regs, + char *remcom_in_buffer) +{ + unsigned long addr; + char *ptr; + + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->csr_era = addr; +} + +/* Calculate the new address for after a step */ +static int get_step_address(struct pt_regs *regs, unsigned long *next_addr) +{ + char cj_val; + unsigned int si, si_l, si_h, rd, rj, cj; + unsigned long pc = instruction_pointer(regs); + union loongarch_instruction *ip = (union loongarch_instruction *)pc; + + if (pc & 3) { + pr_warn("%s: invalid pc 0x%lx\n", __func__, pc); + return -EINVAL; + } + + *next_addr = pc + LOONGARCH_INSN_SIZE; + + si_h = ip->reg0i26_format.immediate_h; + si_l = ip->reg0i26_format.immediate_l; + switch (ip->reg0i26_format.opcode) { + case b_op: + *next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 27); + return 0; + case bl_op: + *next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 27); + regs->regs[1] = pc + LOONGARCH_INSN_SIZE; + return 0; + } + + rj = ip->reg1i21_format.rj; + cj = (rj & 0x07) + DBG_FCC_BASE; + si_l = ip->reg1i21_format.immediate_l; + si_h = ip->reg1i21_format.immediate_h; + dbg_get_reg(cj, &cj_val, regs); + switch (ip->reg1i21_format.opcode) { + case beqz_op: + if (regs->regs[rj] == 0) + *next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22); + return 0; + case bnez_op: + if (regs->regs[rj] != 0) + *next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22); + return 0; + case bceqz_op: /* bceqz_op = bcnez_op */ + if (((rj & 0x18) == 0x00) && !cj_val) /* bceqz */ + *next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22); + if (((rj & 0x18) == 0x08) && cj_val) /* bcnez */ + *next_addr = pc + sign_extend64((si_h << 16 | si_l) << 2, 22); + return 0; + } + + rj = ip->reg2i16_format.rj; + rd = ip->reg2i16_format.rd; + si = ip->reg2i16_format.immediate; + switch (ip->reg2i16_format.opcode) { + case beq_op: + if (regs->regs[rj] == regs->regs[rd]) + *next_addr = pc + sign_extend64(si << 2, 17); + return 0; + case bne_op: + if (regs->regs[rj] != regs->regs[rd]) + *next_addr = pc + sign_extend64(si << 2, 17); + return 0; + case blt_op: + if ((long)regs->regs[rj] < (long)regs->regs[rd]) + *next_addr = pc + sign_extend64(si << 2, 17); + return 0; + case bge_op: + if ((long)regs->regs[rj] >= (long)regs->regs[rd]) + *next_addr = pc + sign_extend64(si << 2, 17); + return 0; + case bltu_op: + if (regs->regs[rj] < regs->regs[rd]) + *next_addr = pc + sign_extend64(si << 2, 17); + return 0; + case bgeu_op: + if (regs->regs[rj] >= regs->regs[rd]) + *next_addr = pc + sign_extend64(si << 2, 17); + return 0; + case jirl_op: + regs->regs[rd] = pc + LOONGARCH_INSN_SIZE; + *next_addr = regs->regs[rj] + sign_extend64(si << 2, 17); + return 0; + } + + return 0; +} + +static int do_single_step(struct pt_regs *regs) +{ + int error = 0; + unsigned long addr = 0; /* Determine where the target instruction will send us to */ + + error = get_step_address(regs, &addr); + if (error) + return error; + + /* Store the opcode in the stepped address */ + error = get_kernel_nofault(stepped_opcode, (void *)addr); + if (error) + return error; + + stepped_address = addr; + + /* Replace the opcode with the break instruction */ + error = copy_to_kernel_nofault((void *)stepped_address, + arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); + flush_icache_range(addr, addr + BREAK_INSTR_SIZE); + + if (error) { + stepped_opcode = 0; + stepped_address = 0; + } else { + kgdb_single_step = 1; + atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id()); + } + + return error; +} + +/* Undo a single step */ +static void undo_single_step(struct pt_regs *regs) +{ + if (stepped_opcode) { + copy_to_kernel_nofault((void *)stepped_address, + (void *)&stepped_opcode, BREAK_INSTR_SIZE); + flush_icache_range(stepped_address, stepped_address + BREAK_INSTR_SIZE); + } + + stepped_opcode = 0; + stepped_address = 0; + kgdb_single_step = 0; + atomic_set(&kgdb_cpu_doing_single_step, -1); +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + int ret = 0; + + undo_single_step(regs); + regs->csr_prmd |= CSR_PRMD_PWE; + + switch (remcom_in_buffer[0]) { + case 'D': + case 'k': + regs->csr_prmd &= ~CSR_PRMD_PWE; + fallthrough; + case 'c': + kgdb_arch_update_addr(regs, remcom_in_buffer); + break; + case 's': + kgdb_arch_update_addr(regs, remcom_in_buffer); + ret = do_single_step(regs); + break; + default: + ret = -1; + } + + return ret; +} + +static struct hw_breakpoint { + unsigned int enabled; + unsigned long addr; + int len; + int type; + struct perf_event * __percpu *pev; +} breakinfo[LOONGARCH_MAX_BRP]; + +static int hw_break_reserve_slot(int breakno) +{ + int cpu, cnt = 0; + struct perf_event **pevent; + + for_each_online_cpu(cpu) { + cnt++; + pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); + if (dbg_reserve_bp_slot(*pevent)) + goto fail; + } + + return 0; + +fail: + for_each_online_cpu(cpu) { + cnt--; + if (!cnt) + break; + pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); + dbg_release_bp_slot(*pevent); + } + + return -1; +} + +static int hw_break_release_slot(int breakno) +{ + int cpu; + struct perf_event **pevent; + + if (dbg_is_early) + return 0; + + for_each_online_cpu(cpu) { + pevent = per_cpu_ptr(breakinfo[breakno].pev, cpu); + if (dbg_release_bp_slot(*pevent)) + /* + * The debugger is responsible for handing the retry on + * remove failure. + */ + return -1; + } + + return 0; +} + +static int kgdb_set_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) +{ + int i; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) + if (!breakinfo[i].enabled) + break; + + if (i == LOONGARCH_MAX_BRP) + return -1; + + switch (bptype) { + case BP_HARDWARE_BREAKPOINT: + breakinfo[i].type = HW_BREAKPOINT_X; + break; + case BP_READ_WATCHPOINT: + breakinfo[i].type = HW_BREAKPOINT_R; + break; + case BP_WRITE_WATCHPOINT: + breakinfo[i].type = HW_BREAKPOINT_W; + break; + case BP_ACCESS_WATCHPOINT: + breakinfo[i].type = HW_BREAKPOINT_RW; + break; + default: + return -1; + } + + switch (len) { + case 1: + breakinfo[i].len = HW_BREAKPOINT_LEN_1; + break; + case 2: + breakinfo[i].len = HW_BREAKPOINT_LEN_2; + break; + case 4: + breakinfo[i].len = HW_BREAKPOINT_LEN_4; + break; + case 8: + breakinfo[i].len = HW_BREAKPOINT_LEN_8; + break; + default: + return -1; + } + + breakinfo[i].addr = addr; + if (hw_break_reserve_slot(i)) { + breakinfo[i].addr = 0; + return -1; + } + breakinfo[i].enabled = 1; + + return 0; +} + +static int kgdb_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype bptype) +{ + int i; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) + if (breakinfo[i].addr == addr && breakinfo[i].enabled) + break; + + if (i == LOONGARCH_MAX_BRP) + return -1; + + if (hw_break_release_slot(i)) { + pr_err("Cannot remove hw breakpoint at %lx\n", addr); + return -1; + } + breakinfo[i].enabled = 0; + + return 0; +} + +static void kgdb_disable_hw_break(struct pt_regs *regs) +{ + int i; + int cpu = raw_smp_processor_id(); + struct perf_event *bp; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) { + if (!breakinfo[i].enabled) + continue; + + bp = *per_cpu_ptr(breakinfo[i].pev, cpu); + if (bp->attr.disabled == 1) + continue; + + arch_uninstall_hw_breakpoint(bp); + bp->attr.disabled = 1; + } + + /* Disable hardware debugging while we are in kgdb */ + csr_xchg32(0, CSR_CRMD_WE, LOONGARCH_CSR_CRMD); +} + +static void kgdb_remove_all_hw_break(void) +{ + int i; + int cpu = raw_smp_processor_id(); + struct perf_event *bp; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) { + if (!breakinfo[i].enabled) + continue; + + bp = *per_cpu_ptr(breakinfo[i].pev, cpu); + if (!bp->attr.disabled) { + arch_uninstall_hw_breakpoint(bp); + bp->attr.disabled = 1; + continue; + } + + if (hw_break_release_slot(i)) + pr_err("KGDB: hw bpt remove failed %lx\n", breakinfo[i].addr); + breakinfo[i].enabled = 0; + } + + csr_xchg32(0, CSR_CRMD_WE, LOONGARCH_CSR_CRMD); + kgdb_watch_activated = 0; +} + +static void kgdb_correct_hw_break(void) +{ + int i, activated = 0; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) { + struct perf_event *bp; + int val; + int cpu = raw_smp_processor_id(); + + if (!breakinfo[i].enabled) + continue; + + bp = *per_cpu_ptr(breakinfo[i].pev, cpu); + if (bp->attr.disabled != 1) + continue; + + bp->attr.bp_addr = breakinfo[i].addr; + bp->attr.bp_len = breakinfo[i].len; + bp->attr.bp_type = breakinfo[i].type; + + val = hw_breakpoint_arch_parse(bp, &bp->attr, counter_arch_bp(bp)); + if (val) + return; + + val = arch_install_hw_breakpoint(bp); + if (!val) + bp->attr.disabled = 0; + activated = 1; + } + + csr_xchg32(activated ? CSR_CRMD_WE : 0, CSR_CRMD_WE, LOONGARCH_CSR_CRMD); + kgdb_watch_activated = activated; +} + +const struct kgdb_arch arch_kgdb_ops = { + .gdb_bpt_instr = {0x02, 0x00, break_op >> 1, 0x00}, /* BRK_KDB = 2 */ + .flags = KGDB_HW_BREAKPOINT, + .set_hw_breakpoint = kgdb_set_hw_break, + .remove_hw_breakpoint = kgdb_remove_hw_break, + .disable_hw_break = kgdb_disable_hw_break, + .remove_all_hw_break = kgdb_remove_all_hw_break, + .correct_hw_break = kgdb_correct_hw_break, +}; + +int kgdb_arch_init(void) +{ + return register_die_notifier(&kgdb_notifier); +} + +void kgdb_arch_late(void) +{ + int i, cpu; + struct perf_event_attr attr; + struct perf_event **pevent; + + hw_breakpoint_init(&attr); + + attr.bp_addr = (unsigned long)kgdb_arch_init; + attr.bp_len = HW_BREAKPOINT_LEN_4; + attr.bp_type = HW_BREAKPOINT_W; + attr.disabled = 1; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) { + if (breakinfo[i].pev) + continue; + + breakinfo[i].pev = register_wide_hw_breakpoint(&attr, NULL, NULL); + if (IS_ERR((void * __force)breakinfo[i].pev)) { + pr_err("kgdb: Could not allocate hw breakpoints.\n"); + breakinfo[i].pev = NULL; + return; + } + + for_each_online_cpu(cpu) { + pevent = per_cpu_ptr(breakinfo[i].pev, cpu); + if (pevent[0]->destroy) { + pevent[0]->destroy = NULL; + release_bp_slot(*pevent); + } + } + } +} + +void kgdb_arch_exit(void) +{ + int i; + + for (i = 0; i < LOONGARCH_MAX_BRP; i++) { + if (breakinfo[i].pev) { + unregister_wide_hw_breakpoint(breakinfo[i].pev); + breakinfo[i].pev = NULL; + } + } + + unregister_die_notifier(&kgdb_notifier); +} diff --git a/arch/loongarch/kernel/lbt.S b/arch/loongarch/kernel/lbt.S new file mode 100644 index 0000000000000000000000000000000000000000..9c75120a26d836d75d3bb08c2b5a0022a8c5d58d --- /dev/null +++ b/arch/loongarch/kernel/lbt.S @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Author: Qi Hu + * Huacai Chen + * + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ +#include +#include +#include +#include +#include +#include + +#define SCR_REG_WIDTH 8 + + .macro EX insn, reg, src, offs +.ex\@: \insn \reg, \src, \offs + _asm_extable .ex\@, .L_lbt_fault + .endm + +/* + * Save a thread's lbt context. + */ +SYM_FUNC_START(_save_lbt) + movscr2gr t1, $scr0 # save scr + stptr.d t1, a0, THREAD_SCR0 + movscr2gr t1, $scr1 + stptr.d t1, a0, THREAD_SCR1 + movscr2gr t1, $scr2 + stptr.d t1, a0, THREAD_SCR2 + movscr2gr t1, $scr3 + stptr.d t1, a0, THREAD_SCR3 + + x86mfflag t1, 0x3f # save eflags + stptr.d t1, a0, THREAD_EFLAGS + jr ra +SYM_FUNC_END(_save_lbt) +EXPORT_SYMBOL(_save_lbt) + +/* + * Restore a thread's lbt context. + */ +SYM_FUNC_START(_restore_lbt) + ldptr.d t1, a0, THREAD_SCR0 # restore scr + movgr2scr $scr0, t1 + ldptr.d t1, a0, THREAD_SCR1 + movgr2scr $scr1, t1 + ldptr.d t1, a0, THREAD_SCR2 + movgr2scr $scr2, t1 + ldptr.d t1, a0, THREAD_SCR3 + movgr2scr $scr3, t1 + + ldptr.d t1, a0, THREAD_EFLAGS # restore eflags + x86mtflag t1, 0x3f + jr ra +SYM_FUNC_END(_restore_lbt) +EXPORT_SYMBOL(_restore_lbt) + +/* + * Load scr/eflag with zero. + */ +SYM_FUNC_START(_init_lbt) + movgr2scr $scr0, zero + movgr2scr $scr1, zero + movgr2scr $scr2, zero + movgr2scr $scr3, zero + + x86mtflag zero, 0x3f + jr ra +SYM_FUNC_END(_init_lbt) + +/* + * a0: scr + * a1: eflag + */ +SYM_FUNC_START(_save_lbt_context) + movscr2gr t1, $scr0 # save scr + EX st.d t1, a0, (0 * SCR_REG_WIDTH) + movscr2gr t1, $scr1 + EX st.d t1, a0, (1 * SCR_REG_WIDTH) + movscr2gr t1, $scr2 + EX st.d t1, a0, (2 * SCR_REG_WIDTH) + movscr2gr t1, $scr3 + EX st.d t1, a0, (3 * SCR_REG_WIDTH) + + x86mfflag t1, 0x3f # save eflags + EX st.w t1, a1, 0 + li.w a0, 0 # success + jr ra +SYM_FUNC_END(_save_lbt_context) + +/* + * a0: scr + * a1: eflag + */ +SYM_FUNC_START(_restore_lbt_context) + EX ld.d t1, a0, (0 * SCR_REG_WIDTH) # restore scr + movgr2scr $scr0, t1 + EX ld.d t1, a0, (1 * SCR_REG_WIDTH) + movgr2scr $scr1, t1 + EX ld.d t1, a0, (2 * SCR_REG_WIDTH) + movgr2scr $scr2, t1 + EX ld.d t1, a0, (3 * SCR_REG_WIDTH) + movgr2scr $scr3, t1 + + EX ld.w t1, a1, 0 # restore eflags + x86mtflag t1, 0x3f + li.w a0, 0 # success + jr ra +SYM_FUNC_END(_restore_lbt_context) + +/* + * a0: ftop + */ +SYM_FUNC_START(_save_ftop_context) + x86mftop t1 + st.w t1, a0, 0 + li.w a0, 0 # success + jr ra +SYM_FUNC_END(_save_ftop_context) + +/* + * a0: ftop + */ +SYM_FUNC_START(_restore_ftop_context) + ld.w t1, a0, 0 + andi t1, t1, 0x7 + la.pcrel a0, 1f + alsl.d a0, t1, a0, 3 + jr a0 +1: + x86mttop 0 + b 2f + x86mttop 1 + b 2f + x86mttop 2 + b 2f + x86mttop 3 + b 2f + x86mttop 4 + b 2f + x86mttop 5 + b 2f + x86mttop 6 + b 2f + x86mttop 7 +2: + li.w a0, 0 # success + jr ra +SYM_FUNC_END(_restore_ftop_context) + +.L_lbt_fault: + li.w a0, -EFAULT # failure + jr ra diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c index 4a4107a6a9651535fc6cfb72f21cc1fb64f7eeed..aed901c57fb439493c560de777fbc86689f827ae 100644 --- a/arch/loongarch/kernel/mem.c +++ b/arch/loongarch/kernel/mem.c @@ -50,7 +50,6 @@ void __init memblock_init(void) } memblock_set_current_limit(PFN_PHYS(max_low_pfn)); - memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); /* Reserve the first 2MB */ memblock_reserve(PHYS_OFFSET, 0x200000); @@ -58,4 +57,7 @@ void __init memblock_init(void) /* Reserve the kernel text/data/bss */ memblock_reserve(__pa_symbol(&_text), __pa_symbol(&_end) - __pa_symbol(&_text)); + + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0); + memblock_set_node(0, PHYS_ADDR_MAX, &memblock.reserved, 0); } diff --git a/arch/loongarch/kernel/module-sections.c b/arch/loongarch/kernel/module-sections.c index d4dbcda1c4b0a42a7d7de52fe9f457a22d4940cb..e2f30ff9afde825aea7f83bb529dc9e1cc5dd785 100644 --- a/arch/loongarch/kernel/module-sections.c +++ b/arch/loongarch/kernel/module-sections.c @@ -6,6 +6,7 @@ #include #include #include +#include #include Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val) diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c index b8b86088b2dd2dbda770bfe30e6954b592d6c4ea..b13b2858fe392398823cb8f261665a964250a836 100644 --- a/arch/loongarch/kernel/module.c +++ b/arch/loongarch/kernel/module.c @@ -367,6 +367,24 @@ static int apply_r_larch_got_pc(struct module *mod, return apply_r_larch_pcala(mod, location, got, rela_stack, rela_stack_top, type); } +static int apply_r_larch_32_pcrel(struct module *mod, u32 *location, Elf_Addr v, + s64 *rela_stack, size_t *rela_stack_top, unsigned int type) +{ + ptrdiff_t offset = (void *)v - (void *)location; + + *(u32 *)location = offset; + return 0; +} + +static int apply_r_larch_64_pcrel(struct module *mod, u32 *location, Elf_Addr v, + s64 *rela_stack, size_t *rela_stack_top, unsigned int type) +{ + ptrdiff_t offset = (void *)v - (void *)location; + + *(u64 *)location = offset; + return 0; +} + /* * reloc_handlers_rela() - Apply a particular relocation to a module * @mod: the module to apply the reloc to @@ -382,7 +400,7 @@ typedef int (*reloc_rela_handler)(struct module *mod, u32 *location, Elf_Addr v, /* The handlers for known reloc types */ static reloc_rela_handler reloc_rela_handlers[] = { - [R_LARCH_NONE ... R_LARCH_RELAX] = apply_r_larch_error, + [R_LARCH_NONE ... R_LARCH_64_PCREL] = apply_r_larch_error, [R_LARCH_NONE] = apply_r_larch_none, [R_LARCH_32] = apply_r_larch_32, @@ -396,6 +414,8 @@ static reloc_rela_handler reloc_rela_handlers[] = { [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field, [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub, [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala, + [R_LARCH_32_PCREL] = apply_r_larch_32_pcrel, + [R_LARCH_64_PCREL] = apply_r_larch_64_pcrel, }; int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c index 708665895b47d314df2f13c13db75c021f7b198a..6e65ff12d5c7dc5062bc4d9bce24f4ef68f3b8da 100644 --- a/arch/loongarch/kernel/numa.c +++ b/arch/loongarch/kernel/numa.c @@ -67,39 +67,7 @@ static int __init pcpu_cpu_distance(unsigned int from, unsigned int to) void __init pcpu_populate_pte(unsigned long addr) { - pgd_t *pgd = pgd_offset_k(addr); - p4d_t *p4d = p4d_offset(pgd, addr); - pud_t *pud; - pmd_t *pmd; - - if (p4d_none(*p4d)) { - pud_t *new; - - new = memblock_alloc(PAGE_SIZE, PAGE_SIZE); - pgd_populate(&init_mm, pgd, new); -#ifndef __PAGETABLE_PUD_FOLDED - pud_init(new); -#endif - } - - pud = pud_offset(p4d, addr); - if (pud_none(*pud)) { - pmd_t *new; - - new = memblock_alloc(PAGE_SIZE, PAGE_SIZE); - pud_populate(&init_mm, pud, new); -#ifndef __PAGETABLE_PMD_FOLDED - pmd_init(new); -#endif - } - - pmd = pmd_offset(pud, addr); - if (!pmd_present(*pmd)) { - pte_t *new; - - new = memblock_alloc(PAGE_SIZE, PAGE_SIZE); - pmd_populate_kernel(&init_mm, pmd, new); - } + populate_kernel_pte(addr); } void __init setup_per_cpu_areas(void) @@ -468,9 +436,8 @@ void __init paging_init(void) void __init mem_init(void) { - high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT); + high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); memblock_free_all(); - setup_zero_pages(); /* This comes from node 0 */ } int pcibus_to_node(struct pci_bus *bus) diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index ba457e43f5be535012431151f3269b7b0b36ce0c..767d94cce0de07d74892733b339a55dd5e6ded0e 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -37,7 +37,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -82,9 +84,11 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) euen = regs->csr_euen & ~(CSR_EUEN_FPEN); regs->csr_euen = euen; lose_fpu(0); + lose_lbt(0); clear_thread_flag(TIF_LSX_CTX_LIVE); clear_thread_flag(TIF_LASX_CTX_LIVE); + clear_thread_flag(TIF_LBT_CTX_LIVE); clear_used_math(); regs->csr_era = pc; regs->regs[3] = sp; @@ -121,10 +125,14 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) preempt_enable(); - if (used_math()) - memcpy(dst, src, sizeof(struct task_struct)); - else + if (!used_math()) memcpy(dst, src, offsetof(struct task_struct, thread.fpu.fpr)); + else + memcpy(dst, src, offsetof(struct task_struct, thread.lbt.scr0)); + +#ifdef CONFIG_CPU_HAS_LBT + memcpy(&dst->thread.lbt, &src->thread.lbt, sizeof(struct loongarch_lbt)); +#endif return 0; } @@ -189,8 +197,10 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) ptrace_hw_copy_thread(p); clear_tsk_thread_flag(p, TIF_USEDFPU); clear_tsk_thread_flag(p, TIF_USEDSIMD); + clear_tsk_thread_flag(p, TIF_USEDLBT); clear_tsk_thread_flag(p, TIF_LSX_CTX_LIVE); clear_tsk_thread_flag(p, TIF_LASX_CTX_LIVE); + clear_tsk_thread_flag(p, TIF_LBT_CTX_LIVE); return 0; } diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index f72adbf530c6465a15d5f53b1b8e311dc877280b..c114c5ef13325af024be5772b396690bbab7cbc4 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -338,6 +339,46 @@ static int simd_set(struct task_struct *target, #endif /* CONFIG_CPU_HAS_LSX */ +#ifdef CONFIG_CPU_HAS_LBT +static int lbt_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + int r; + + r = membuf_write(&to, &target->thread.lbt.scr0, sizeof(target->thread.lbt.scr0)); + r = membuf_write(&to, &target->thread.lbt.scr1, sizeof(target->thread.lbt.scr1)); + r = membuf_write(&to, &target->thread.lbt.scr2, sizeof(target->thread.lbt.scr2)); + r = membuf_write(&to, &target->thread.lbt.scr3, sizeof(target->thread.lbt.scr3)); + r = membuf_write(&to, &target->thread.lbt.eflags, sizeof(u32)); + r = membuf_write(&to, &target->thread.fpu.ftop, sizeof(u32)); + + return r; +} + +static int lbt_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int err = 0; + const int eflags_start = 4 * sizeof(target->thread.lbt.scr0); + const int ftop_start = eflags_start + sizeof(u32); + + err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.lbt.scr0, + 0, 4 * sizeof(target->thread.lbt.scr0)); + err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.lbt.eflags, + eflags_start, ftop_start); + err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.ftop, + ftop_start, ftop_start + sizeof(u32)); + + return err; +} +#endif /* CONFIG_CPU_HAS_LBT */ + #ifdef CONFIG_HAVE_HW_BREAKPOINT /* @@ -802,6 +843,9 @@ enum loongarch_regset { #ifdef CONFIG_CPU_HAS_LASX REGSET_LASX, #endif +#ifdef CONFIG_CPU_HAS_LBT + REGSET_LBT, +#endif #ifdef CONFIG_HAVE_HW_BREAKPOINT REGSET_HW_BREAK, REGSET_HW_WATCH, @@ -853,6 +897,16 @@ static const struct user_regset loongarch64_regsets[] = { .set = simd_set, }, #endif +#ifdef CONFIG_CPU_HAS_LBT + [REGSET_LBT] = { + .core_note_type = NT_LOONGARCH_LBT, + .n = 5, + .size = sizeof(u64), + .align = sizeof(u64), + .regset_get = lbt_get, + .set = lbt_set, + }, +#endif #ifdef CONFIG_HAVE_HW_BREAKPOINT [REGSET_HW_BREAK] = { .core_note_type = NT_LOONGARCH_HW_BREAK, diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c index 01f94d1e3edf6f8b26b31f30f2494a1ace76f0e8..6c3eff9af9fb1ed4cc4af8ffa9b9ea5490591ee3 100644 --- a/arch/loongarch/kernel/relocate.c +++ b/arch/loongarch/kernel/relocate.c @@ -157,12 +157,11 @@ static inline void __init update_reloc_offset(unsigned long *addr, long random_o *new_addr = (unsigned long)reloc_offset; } -void * __init relocate_kernel(void) +unsigned long __init relocate_kernel(void) { unsigned long kernel_length; unsigned long random_offset = 0; void *location_new = _text; /* Default to original kernel start */ - void *kernel_entry = start_kernel; /* Default to original kernel entry point */ char *cmdline = early_ioremap(fw_arg1, COMMAND_LINE_SIZE); /* Boot command line is passed in fw_arg1 */ strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE); @@ -190,9 +189,6 @@ void * __init relocate_kernel(void) reloc_offset += random_offset; - /* Return the new kernel's entry point */ - kernel_entry = RELOCATED_KASLR(start_kernel); - /* The current thread is now within the relocated kernel */ __current_thread_info = RELOCATED_KASLR(__current_thread_info); @@ -204,7 +200,7 @@ void * __init relocate_kernel(void) relocate_absolute(random_offset); - return kernel_entry; + return random_offset; } /* diff --git a/arch/loongarch/kernel/relocate_kernel.S b/arch/loongarch/kernel/relocate_kernel.S index d13252553a7c7d6f7916fda58f7fcdc1d9bae126..f49f6b053763d1e729b68b870b1b64ffe09d4504 100644 --- a/arch/loongarch/kernel/relocate_kernel.S +++ b/arch/loongarch/kernel/relocate_kernel.S @@ -72,7 +72,6 @@ copy_word: LONG_ADDI s5, s5, -1 beqz s5, process_entry b copy_word - b process_entry done: ibar 0 diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 9d830ab4e3025e8ab7afacfc21428dfa9665afde..7783f0a3d742c7e0cce68d93cc9252eee4e7f381 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -626,4 +626,8 @@ void __init setup_arch(char **cmdline_p) #endif paging_init(); + +#ifdef CONFIG_KASAN + kasan_init(); +#endif } diff --git a/arch/loongarch/kernel/signal.c b/arch/loongarch/kernel/signal.c index ceb899366c0a7b2ba2793e8631792f8aaaa03ab3..4a3686d133494921d64853e6c94b061dfe262bc0 100644 --- a/arch/loongarch/kernel/signal.c +++ b/arch/loongarch/kernel/signal.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include @@ -44,6 +46,9 @@ /* Make sure we will not lose FPU ownership */ #define lock_fpu_owner() ({ preempt_disable(); pagefault_disable(); }) #define unlock_fpu_owner() ({ pagefault_enable(); preempt_enable(); }) +/* Make sure we will not lose LBT ownership */ +#define lock_lbt_owner() ({ preempt_disable(); pagefault_disable(); }) +#define unlock_lbt_owner() ({ pagefault_enable(); preempt_enable(); }) /* Assembly functions to move context to/from the FPU */ extern asmlinkage int @@ -59,6 +64,13 @@ _save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); extern asmlinkage int _restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); +#ifdef CONFIG_CPU_HAS_LBT +extern asmlinkage int _save_lbt_context(void __user *regs, void __user *eflags); +extern asmlinkage int _restore_lbt_context(void __user *regs, void __user *eflags); +extern asmlinkage int _save_ftop_context(void __user *ftop); +extern asmlinkage int _restore_ftop_context(void __user *ftop); +#endif + struct rt_sigframe { struct siginfo rs_info; struct ucontext rs_uctx; @@ -75,6 +87,7 @@ struct extctx_layout { struct _ctx_layout fpu; struct _ctx_layout lsx; struct _ctx_layout lasx; + struct _ctx_layout lbt; struct _ctx_layout end; }; @@ -215,6 +228,52 @@ static int copy_lasx_from_sigcontext(struct lasx_context __user *ctx) return err; } +#ifdef CONFIG_CPU_HAS_LBT +static int copy_lbt_to_sigcontext(struct lbt_context __user *ctx) +{ + int err = 0; + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint32_t __user *eflags = (uint32_t *)&ctx->eflags; + + err |= __put_user(current->thread.lbt.scr0, ®s[0]); + err |= __put_user(current->thread.lbt.scr1, ®s[1]); + err |= __put_user(current->thread.lbt.scr2, ®s[2]); + err |= __put_user(current->thread.lbt.scr3, ®s[3]); + err |= __put_user(current->thread.lbt.eflags, eflags); + + return err; +} + +static int copy_lbt_from_sigcontext(struct lbt_context __user *ctx) +{ + int err = 0; + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint32_t __user *eflags = (uint32_t *)&ctx->eflags; + + err |= __get_user(current->thread.lbt.scr0, ®s[0]); + err |= __get_user(current->thread.lbt.scr1, ®s[1]); + err |= __get_user(current->thread.lbt.scr2, ®s[2]); + err |= __get_user(current->thread.lbt.scr3, ®s[3]); + err |= __get_user(current->thread.lbt.eflags, eflags); + + return err; +} + +static int copy_ftop_to_sigcontext(struct lbt_context __user *ctx) +{ + uint32_t __user *ftop = &ctx->ftop; + + return __put_user(current->thread.fpu.ftop, ftop); +} + +static int copy_ftop_from_sigcontext(struct lbt_context __user *ctx) +{ + uint32_t __user *ftop = &ctx->ftop; + + return __get_user(current->thread.fpu.ftop, ftop); +} +#endif + /* * Wrappers for the assembly _{save,restore}_fp_context functions. */ @@ -272,6 +331,41 @@ static int restore_hw_lasx_context(struct lasx_context __user *ctx) return _restore_lasx_context(regs, fcc, fcsr); } +/* + * Wrappers for the assembly _{save,restore}_lbt_context functions. + */ +#ifdef CONFIG_CPU_HAS_LBT +static int save_hw_lbt_context(struct lbt_context __user *ctx) +{ + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint32_t __user *eflags = (uint32_t *)&ctx->eflags; + + return _save_lbt_context(regs, eflags); +} + +static int restore_hw_lbt_context(struct lbt_context __user *ctx) +{ + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint32_t __user *eflags = (uint32_t *)&ctx->eflags; + + return _restore_lbt_context(regs, eflags); +} + +static int save_hw_ftop_context(struct lbt_context __user *ctx) +{ + uint32_t __user *ftop = &ctx->ftop; + + return _save_ftop_context(ftop); +} + +static int restore_hw_ftop_context(struct lbt_context __user *ctx) +{ + uint32_t __user *ftop = &ctx->ftop; + + return _restore_ftop_context(ftop); +} +#endif + static int fcsr_pending(unsigned int __user *fcsr) { int err, sig = 0; @@ -519,6 +613,77 @@ static int protected_restore_lasx_context(struct extctx_layout *extctx) return err ?: sig; } +#ifdef CONFIG_CPU_HAS_LBT +static int protected_save_lbt_context(struct extctx_layout *extctx) +{ + int err = 0; + struct sctx_info __user *info = extctx->lbt.addr; + struct lbt_context __user *lbt_ctx = + (struct lbt_context *)get_ctx_through_ctxinfo(info); + uint64_t __user *regs = (uint64_t *)&lbt_ctx->regs; + uint32_t __user *eflags = (uint32_t *)&lbt_ctx->eflags; + + while (1) { + lock_lbt_owner(); + if (is_lbt_owner()) + err |= save_hw_lbt_context(lbt_ctx); + else + err |= copy_lbt_to_sigcontext(lbt_ctx); + if (is_fpu_owner()) + err |= save_hw_ftop_context(lbt_ctx); + else + err |= copy_ftop_to_sigcontext(lbt_ctx); + unlock_lbt_owner(); + + err |= __put_user(LBT_CTX_MAGIC, &info->magic); + err |= __put_user(extctx->lbt.size, &info->size); + + if (likely(!err)) + break; + /* Touch the LBT context and try again */ + err = __put_user(0, ®s[0]) | __put_user(0, eflags); + + if (err) + return err; + } + + return err; +} + +static int protected_restore_lbt_context(struct extctx_layout *extctx) +{ + int err = 0, tmp __maybe_unused; + struct sctx_info __user *info = extctx->lbt.addr; + struct lbt_context __user *lbt_ctx = + (struct lbt_context *)get_ctx_through_ctxinfo(info); + uint64_t __user *regs = (uint64_t *)&lbt_ctx->regs; + uint32_t __user *eflags = (uint32_t *)&lbt_ctx->eflags; + + while (1) { + lock_lbt_owner(); + if (is_lbt_owner()) + err |= restore_hw_lbt_context(lbt_ctx); + else + err |= copy_lbt_from_sigcontext(lbt_ctx); + if (is_fpu_owner()) + err |= restore_hw_ftop_context(lbt_ctx); + else + err |= copy_ftop_from_sigcontext(lbt_ctx); + unlock_lbt_owner(); + + if (likely(!err)) + break; + /* Touch the LBT context and try again */ + err = __get_user(tmp, ®s[0]) | __get_user(tmp, eflags); + + if (err) + return err; + } + + return err; +} +#endif + static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, struct extctx_layout *extctx) { @@ -539,6 +704,11 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, else if (extctx->fpu.addr) err |= protected_save_fpu_context(extctx); +#ifdef CONFIG_CPU_HAS_LBT + if (extctx->lbt.addr) + err |= protected_save_lbt_context(extctx); +#endif + /* Set the "end" magic */ info = (struct sctx_info *)extctx->end.addr; err |= __put_user(0, &info->magic); @@ -584,6 +754,13 @@ static int parse_extcontext(struct sigcontext __user *sc, struct extctx_layout * extctx->lasx.addr = info; break; + case LBT_CTX_MAGIC: + if (size < (sizeof(struct sctx_info) + + sizeof(struct lbt_context))) + goto invalid; + extctx->lbt.addr = info; + break; + default: goto invalid; } @@ -636,6 +813,11 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc else if (extctx.fpu.addr) err |= protected_restore_fpu_context(&extctx); +#ifdef CONFIG_CPU_HAS_LBT + if (extctx.lbt.addr) + err |= protected_restore_lbt_context(&extctx); +#endif + bad: return err; } @@ -700,11 +882,18 @@ static unsigned long setup_extcontext(struct extctx_layout *extctx, unsigned lon sizeof(struct fpu_context), FPU_CTX_ALIGN, new_sp); } +#ifdef CONFIG_CPU_HAS_LBT + if (cpu_has_lbt && thread_lbt_context_live()) { + new_sp = extframe_alloc(extctx, &extctx->lbt, + sizeof(struct lbt_context), LBT_CTX_ALIGN, new_sp); + } +#endif + return new_sp; } -void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, - struct extctx_layout *extctx) +static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, + struct extctx_layout *extctx) { unsigned long sp; @@ -734,7 +923,7 @@ void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, * Atomically swap in the new signal mask, and wait for a signal. */ -asmlinkage long sys_rt_sigreturn(void) +SYSCALL_DEFINE0(rt_sigreturn) { int sig; sigset_t set; diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 6667b0a90f81de12e294d267c7f3f13c6e24fcdc..ef35c871244f0825e998359a3913318bfb872434 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -556,10 +557,12 @@ void smp_send_stop(void) smp_call_function(stop_this_cpu, NULL, 0); } +#ifdef CONFIG_PROFILING int setup_profiling_timer(unsigned int multiplier) { return 0; } +#endif static void flush_tlb_all_ipi(void *info) { diff --git a/arch/loongarch/kernel/stacktrace.c b/arch/loongarch/kernel/stacktrace.c index 2463d2fea21f5f4bc9dd762fd5b1ed3c77c0fc40..92270f14db948271b00167ef0887703620b50c31 100644 --- a/arch/loongarch/kernel/stacktrace.c +++ b/arch/loongarch/kernel/stacktrace.c @@ -18,17 +18,19 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie, struct pt_regs dummyregs; struct unwind_state state; - regs = &dummyregs; + if (!regs) { + regs = &dummyregs; - if (task == current) { - regs->regs[3] = (unsigned long)__builtin_frame_address(0); - regs->csr_era = (unsigned long)__builtin_return_address(0); - } else { - regs->regs[3] = thread_saved_fp(task); - regs->csr_era = thread_saved_ra(task); + if (task == current) { + regs->regs[3] = (unsigned long)__builtin_frame_address(0); + regs->csr_era = (unsigned long)__builtin_return_address(0); + } else { + regs->regs[3] = thread_saved_fp(task); + regs->csr_era = thread_saved_ra(task); + } + regs->regs[1] = 0; } - regs->regs[1] = 0; for (unwind_start(&state, task, regs); !unwind_done(&state) && !unwind_error(&state); unwind_next_frame(&state)) { addr = unwind_get_return_address(&state); diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c index 3fc4211db9895f71bc356b075fbb79f6779bbbe6..b4c5acd7aa3b324d133c0b5bd5e5169bfec5d4f7 100644 --- a/arch/loongarch/kernel/syscall.c +++ b/arch/loongarch/kernel/syscall.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index c189e03cd5da84b6ebcde9533560d66f98010b5e..3064af94db9c2e14e953a4aad68c2a8d28588447 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -29,7 +29,7 @@ static void constant_event_handler(struct clock_event_device *dev) { } -irqreturn_t constant_timer_interrupt(int irq, void *data) +static irqreturn_t constant_timer_interrupt(int irq, void *data) { int cpu = smp_processor_id(); struct clock_event_device *cd; diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c index caa7cd8590788ca39cdc27205951bc1a368fcff7..3fd1660066983b247d1493864a9437e831282e3e 100644 --- a/arch/loongarch/kernel/topology.c +++ b/arch/loongarch/kernel/topology.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 +#include #include #include #include @@ -7,6 +8,8 @@ #include #include +#include + static DEFINE_PER_CPU(struct cpu, cpu_devices); #ifdef CONFIG_HOTPLUG_CPU diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 89699db45cec17ab87724ca16b2d24995b88e99b..aebfc3733a760713836fb5255045e1e9948ca5f4 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -35,8 +34,11 @@ #include #include #include +#include #include +#include #include +#include #include #include #include @@ -51,21 +53,6 @@ #include "access-helper.h" -extern asmlinkage void handle_ade(void); -extern asmlinkage void handle_ale(void); -extern asmlinkage void handle_bce(void); -extern asmlinkage void handle_sys(void); -extern asmlinkage void handle_bp(void); -extern asmlinkage void handle_ri(void); -extern asmlinkage void handle_fpu(void); -extern asmlinkage void handle_fpe(void); -extern asmlinkage void handle_lbt(void); -extern asmlinkage void handle_lsx(void); -extern asmlinkage void handle_lasx(void); -extern asmlinkage void handle_reserved(void); -extern asmlinkage void handle_watch(void); -extern asmlinkage void handle_vint(void); - static void show_backtrace(struct task_struct *task, const struct pt_regs *regs, const char *loglvl, bool user) { @@ -437,8 +424,8 @@ static inline void setup_vint_size(unsigned int size) * happen together with Overflow or Underflow, and `ptrace' can set * any bits. */ -void force_fcsr_sig(unsigned long fcsr, void __user *fault_addr, - struct task_struct *tsk) +static void force_fcsr_sig(unsigned long fcsr, + void __user *fault_addr, struct task_struct *tsk) { int si_code = FPE_FLTUNK; @@ -456,7 +443,7 @@ void force_fcsr_sig(unsigned long fcsr, void __user *fault_addr, force_sig_fault(SIGFPE, si_code, fault_addr); } -int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcsr) +static int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcsr) { int si_code; @@ -702,6 +689,11 @@ asmlinkage void noinstr do_bp(struct pt_regs *regs) * pertain to them. */ switch (bcode) { + case BRK_KDB: + if (kgdb_breakpoint_handler(regs)) + goto out; + else + break; case BRK_KPROBE_BP: if (kprobe_breakpoint_handler(regs)) goto out; @@ -768,6 +760,9 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs) #ifndef CONFIG_HAVE_HW_BREAKPOINT pr_warn("Hardware watch point handler not implemented!\n"); #else + if (kgdb_breakpoint_handler(regs)) + goto out; + if (test_tsk_thread_flag(current, TIF_SINGLESTEP)) { int llbit = (csr_read32(LOONGARCH_CSR_LLBCTL) & 0x1); unsigned long pc = instruction_pointer(regs); @@ -814,7 +809,7 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs) asmlinkage void noinstr do_ri(struct pt_regs *regs) { int status = SIGILL; - unsigned int opcode = 0; + unsigned int __maybe_unused opcode; unsigned int __user *era = (unsigned int __user *)exception_era(regs); irqentry_state_t state = irqentry_enter(regs); @@ -966,13 +961,47 @@ asmlinkage void noinstr do_lasx(struct pt_regs *regs) irqentry_exit(regs, state); } +static void init_restore_lbt(void) +{ + if (!thread_lbt_context_live()) { + /* First time LBT context user */ + init_lbt(); + set_thread_flag(TIF_LBT_CTX_LIVE); + } else { + if (!is_lbt_owner()) + own_lbt_inatomic(1); + } + + BUG_ON(!is_lbt_enabled()); +} + asmlinkage void noinstr do_lbt(struct pt_regs *regs) { irqentry_state_t state = irqentry_enter(regs); - local_irq_enable(); - force_sig(SIGILL); - local_irq_disable(); + /* + * BTD (Binary Translation Disable exception) can be triggered + * during FP save/restore if TM (Top Mode) is on, which may + * cause irq_enable during 'switch_to'. To avoid this situation + * (including the user using 'MOVGR2GCSR' to turn on TM, which + * will not trigger the BTE), we need to check PRMD first. + */ + if (regs->csr_prmd & CSR_PRMD_PIE) + local_irq_enable(); + + if (!cpu_has_lbt) { + force_sig(SIGILL); + goto out; + } + BUG_ON(is_lbt_enabled()); + + preempt_disable(); + init_restore_lbt(); + preempt_enable(); + +out: + if (regs->csr_prmd & CSR_PRMD_PIE) + local_irq_disable(); irqentry_exit(regs, state); } diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index b1686afcf876663c99e010c40ec84f44d8d55a88..bb2ec86f37a8eb636ef61cbcb9f86fd5869eef4c 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -53,33 +53,6 @@ SECTIONS . = ALIGN(PECOFF_SEGMENT_ALIGN); _etext = .; - /* - * struct alt_inst entries. From the header (alternative.h): - * "Alternative instructions for different CPU types or capabilities" - * Think locking instructions on spinlocks. - */ - . = ALIGN(4); - .altinstructions : AT(ADDR(.altinstructions) - LOAD_OFFSET) { - __alt_instructions = .; - *(.altinstructions) - __alt_instructions_end = .; - } - -#ifdef CONFIG_RELOCATABLE - . = ALIGN(8); - .la_abs : AT(ADDR(.la_abs) - LOAD_OFFSET) { - __la_abs_begin = .; - *(.la_abs) - __la_abs_end = .; - } -#endif - - .got : ALIGN(16) { *(.got) } - .plt : ALIGN(16) { *(.plt) } - .got.plt : ALIGN(16) { *(.got.plt) } - - .data.rel : { *(.data.rel*) } - . = ALIGN(PECOFF_SEGMENT_ALIGN); __init_begin = .; __inittext_begin = .; @@ -94,6 +67,18 @@ SECTIONS __initdata_begin = .; + /* + * struct alt_inst entries. From the header (alternative.h): + * "Alternative instructions for different CPU types or capabilities" + * Think locking instructions on spinlocks. + */ + . = ALIGN(4); + .altinstructions : AT(ADDR(.altinstructions) - LOAD_OFFSET) { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + INIT_DATA_SECTION(16) .exit.data : { EXIT_DATA @@ -113,6 +98,11 @@ SECTIONS _sdata = .; RO_DATA(4096) + + .got : ALIGN(16) { *(.got) } + .plt : ALIGN(16) { *(.plt) } + .got.plt : ALIGN(16) { *(.got.plt) } + RW_DATA(1 << CONFIG_L1_CACHE_SHIFT, PAGE_SIZE, THREAD_SIZE) .rela.dyn : ALIGN(8) { @@ -121,6 +111,17 @@ SECTIONS __rela_dyn_end = .; } + .data.rel : { *(.data.rel*) } + +#ifdef CONFIG_RELOCATABLE + . = ALIGN(8); + .la_abs : AT(ADDR(.la_abs) - LOAD_OFFSET) { + __la_abs_begin = .; + *(.la_abs) + __la_abs_end = .; + } +#endif + .sdata : { *(.sdata) } diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile index d60d4e096cfa9e4ea1a5b3e1d2f01f1be26bc9b1..a77bf160bfc4246fa34cc666f037ace2eaf01996 100644 --- a/arch/loongarch/lib/Makefile +++ b/arch/loongarch/lib/Makefile @@ -6,4 +6,6 @@ lib-y += delay.o memset.o memcpy.o memmove.o \ clear_user.o copy_user.o csum.o dump_tlb.o unaligned.o +obj-$(CONFIG_CPU_HAS_LSX) += xor_simd.o xor_simd_glue.o + obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S index 0790eadce166dee8b3b9f96c8a371dadc4259353..be741544e62bf63f7198d451c72c8f47eb0501d7 100644 --- a/arch/loongarch/lib/clear_user.S +++ b/arch/loongarch/lib/clear_user.S @@ -11,19 +11,6 @@ #include #include -.irp to, 0, 1, 2, 3, 4, 5, 6, 7 -.L_fixup_handle_\to\(): - sub.d a0, a2, a0 - addi.d a0, a0, (\to) * (-8) - jr ra -.endr - -.irp to, 0, 2, 4 -.L_fixup_handle_s\to\(): - addi.d a0, a1, -\to - jr ra -.endr - SYM_FUNC_START(__clear_user) /* * Some CPUs support hardware unaligned access @@ -51,7 +38,7 @@ SYM_FUNC_START(__clear_user_generic) 2: move a0, a1 jr ra - _asm_extable 1b, .L_fixup_handle_s0 + _asm_extable 1b, 2b SYM_FUNC_END(__clear_user_generic) /* @@ -173,33 +160,47 @@ SYM_FUNC_START(__clear_user_fast) jr ra /* fixup and ex_table */ - _asm_extable 0b, .L_fixup_handle_0 - _asm_extable 1b, .L_fixup_handle_0 - _asm_extable 2b, .L_fixup_handle_1 - _asm_extable 3b, .L_fixup_handle_2 - _asm_extable 4b, .L_fixup_handle_3 - _asm_extable 5b, .L_fixup_handle_4 - _asm_extable 6b, .L_fixup_handle_5 - _asm_extable 7b, .L_fixup_handle_6 - _asm_extable 8b, .L_fixup_handle_7 - _asm_extable 9b, .L_fixup_handle_0 - _asm_extable 10b, .L_fixup_handle_1 - _asm_extable 11b, .L_fixup_handle_2 - _asm_extable 12b, .L_fixup_handle_3 - _asm_extable 13b, .L_fixup_handle_0 - _asm_extable 14b, .L_fixup_handle_1 - _asm_extable 15b, .L_fixup_handle_0 - _asm_extable 16b, .L_fixup_handle_0 - _asm_extable 17b, .L_fixup_handle_s0 - _asm_extable 18b, .L_fixup_handle_s0 - _asm_extable 19b, .L_fixup_handle_s0 - _asm_extable 20b, .L_fixup_handle_s2 - _asm_extable 21b, .L_fixup_handle_s0 - _asm_extable 22b, .L_fixup_handle_s0 - _asm_extable 23b, .L_fixup_handle_s4 - _asm_extable 24b, .L_fixup_handle_s0 - _asm_extable 25b, .L_fixup_handle_s4 - _asm_extable 26b, .L_fixup_handle_s0 - _asm_extable 27b, .L_fixup_handle_s4 - _asm_extable 28b, .L_fixup_handle_s0 +.Llarge_fixup: + sub.d a1, a2, a0 + +.Lsmall_fixup: +29: st.b zero, a0, 0 + addi.d a0, a0, 1 + addi.d a1, a1, -1 + bgt a1, zero, 29b + +.Lexit: + move a0, a1 + jr ra + + _asm_extable 0b, .Lsmall_fixup + _asm_extable 1b, .Llarge_fixup + _asm_extable 2b, .Llarge_fixup + _asm_extable 3b, .Llarge_fixup + _asm_extable 4b, .Llarge_fixup + _asm_extable 5b, .Llarge_fixup + _asm_extable 6b, .Llarge_fixup + _asm_extable 7b, .Llarge_fixup + _asm_extable 8b, .Llarge_fixup + _asm_extable 9b, .Llarge_fixup + _asm_extable 10b, .Llarge_fixup + _asm_extable 11b, .Llarge_fixup + _asm_extable 12b, .Llarge_fixup + _asm_extable 13b, .Llarge_fixup + _asm_extable 14b, .Llarge_fixup + _asm_extable 15b, .Llarge_fixup + _asm_extable 16b, .Llarge_fixup + _asm_extable 17b, .Lexit + _asm_extable 18b, .Lsmall_fixup + _asm_extable 19b, .Lsmall_fixup + _asm_extable 20b, .Lsmall_fixup + _asm_extable 21b, .Lsmall_fixup + _asm_extable 22b, .Lsmall_fixup + _asm_extable 23b, .Lsmall_fixup + _asm_extable 24b, .Lsmall_fixup + _asm_extable 25b, .Lsmall_fixup + _asm_extable 26b, .Lsmall_fixup + _asm_extable 27b, .Lsmall_fixup + _asm_extable 28b, .Lsmall_fixup + _asm_extable 29b, .Lexit SYM_FUNC_END(__clear_user_fast) diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S index bfe3d2793d0002cf68338000e202059be9cbc44a..feec3d3628032f433dbf65e006b8a07acfbc161c 100644 --- a/arch/loongarch/lib/copy_user.S +++ b/arch/loongarch/lib/copy_user.S @@ -11,19 +11,6 @@ #include #include -.irp to, 0, 1, 2, 3, 4, 5, 6, 7 -.L_fixup_handle_\to\(): - sub.d a0, a2, a0 - addi.d a0, a0, (\to) * (-8) - jr ra -.endr - -.irp to, 0, 2, 4 -.L_fixup_handle_s\to\(): - addi.d a0, a2, -\to - jr ra -.endr - SYM_FUNC_START(__copy_user) /* * Some CPUs support hardware unaligned access @@ -54,8 +41,8 @@ SYM_FUNC_START(__copy_user_generic) 3: move a0, a2 jr ra - _asm_extable 1b, .L_fixup_handle_s0 - _asm_extable 2b, .L_fixup_handle_s0 + _asm_extable 1b, 3b + _asm_extable 2b, 3b SYM_FUNC_END(__copy_user_generic) /* @@ -69,10 +56,10 @@ SYM_FUNC_START(__copy_user_fast) sltui t0, a2, 9 bnez t0, .Lsmall - add.d a3, a1, a2 - add.d a2, a0, a2 0: ld.d t0, a1, 0 1: st.d t0, a0, 0 + add.d a3, a1, a2 + add.d a2, a0, a2 /* align up destination address */ andi t1, a0, 7 @@ -94,7 +81,6 @@ SYM_FUNC_START(__copy_user_fast) 7: ld.d t5, a1, 40 8: ld.d t6, a1, 48 9: ld.d t7, a1, 56 - addi.d a1, a1, 64 10: st.d t0, a0, 0 11: st.d t1, a0, 8 12: st.d t2, a0, 16 @@ -103,6 +89,7 @@ SYM_FUNC_START(__copy_user_fast) 15: st.d t5, a0, 40 16: st.d t6, a0, 48 17: st.d t7, a0, 56 + addi.d a1, a1, 64 addi.d a0, a0, 64 bltu a1, a4, .Lloop64 @@ -114,11 +101,11 @@ SYM_FUNC_START(__copy_user_fast) 19: ld.d t1, a1, 8 20: ld.d t2, a1, 16 21: ld.d t3, a1, 24 - addi.d a1, a1, 32 22: st.d t0, a0, 0 23: st.d t1, a0, 8 24: st.d t2, a0, 16 25: st.d t3, a0, 24 + addi.d a1, a1, 32 addi.d a0, a0, 32 .Llt32: @@ -126,9 +113,9 @@ SYM_FUNC_START(__copy_user_fast) bgeu a1, a4, .Llt16 26: ld.d t0, a1, 0 27: ld.d t1, a1, 8 - addi.d a1, a1, 16 28: st.d t0, a0, 0 29: st.d t1, a0, 8 + addi.d a1, a1, 16 addi.d a0, a0, 16 .Llt16: @@ -136,6 +123,7 @@ SYM_FUNC_START(__copy_user_fast) bgeu a1, a4, .Llt8 30: ld.d t0, a1, 0 31: st.d t0, a0, 0 + addi.d a1, a1, 8 addi.d a0, a0, 8 .Llt8: @@ -214,62 +202,79 @@ SYM_FUNC_START(__copy_user_fast) jr ra /* fixup and ex_table */ - _asm_extable 0b, .L_fixup_handle_0 - _asm_extable 1b, .L_fixup_handle_0 - _asm_extable 2b, .L_fixup_handle_0 - _asm_extable 3b, .L_fixup_handle_0 - _asm_extable 4b, .L_fixup_handle_0 - _asm_extable 5b, .L_fixup_handle_0 - _asm_extable 6b, .L_fixup_handle_0 - _asm_extable 7b, .L_fixup_handle_0 - _asm_extable 8b, .L_fixup_handle_0 - _asm_extable 9b, .L_fixup_handle_0 - _asm_extable 10b, .L_fixup_handle_0 - _asm_extable 11b, .L_fixup_handle_1 - _asm_extable 12b, .L_fixup_handle_2 - _asm_extable 13b, .L_fixup_handle_3 - _asm_extable 14b, .L_fixup_handle_4 - _asm_extable 15b, .L_fixup_handle_5 - _asm_extable 16b, .L_fixup_handle_6 - _asm_extable 17b, .L_fixup_handle_7 - _asm_extable 18b, .L_fixup_handle_0 - _asm_extable 19b, .L_fixup_handle_0 - _asm_extable 20b, .L_fixup_handle_0 - _asm_extable 21b, .L_fixup_handle_0 - _asm_extable 22b, .L_fixup_handle_0 - _asm_extable 23b, .L_fixup_handle_1 - _asm_extable 24b, .L_fixup_handle_2 - _asm_extable 25b, .L_fixup_handle_3 - _asm_extable 26b, .L_fixup_handle_0 - _asm_extable 27b, .L_fixup_handle_0 - _asm_extable 28b, .L_fixup_handle_0 - _asm_extable 29b, .L_fixup_handle_1 - _asm_extable 30b, .L_fixup_handle_0 - _asm_extable 31b, .L_fixup_handle_0 - _asm_extable 32b, .L_fixup_handle_0 - _asm_extable 33b, .L_fixup_handle_0 - _asm_extable 34b, .L_fixup_handle_s0 - _asm_extable 35b, .L_fixup_handle_s0 - _asm_extable 36b, .L_fixup_handle_s0 - _asm_extable 37b, .L_fixup_handle_s0 - _asm_extable 38b, .L_fixup_handle_s0 - _asm_extable 39b, .L_fixup_handle_s0 - _asm_extable 40b, .L_fixup_handle_s0 - _asm_extable 41b, .L_fixup_handle_s2 - _asm_extable 42b, .L_fixup_handle_s0 - _asm_extable 43b, .L_fixup_handle_s0 - _asm_extable 44b, .L_fixup_handle_s0 - _asm_extable 45b, .L_fixup_handle_s0 - _asm_extable 46b, .L_fixup_handle_s0 - _asm_extable 47b, .L_fixup_handle_s4 - _asm_extable 48b, .L_fixup_handle_s0 - _asm_extable 49b, .L_fixup_handle_s0 - _asm_extable 50b, .L_fixup_handle_s0 - _asm_extable 51b, .L_fixup_handle_s4 - _asm_extable 52b, .L_fixup_handle_s0 - _asm_extable 53b, .L_fixup_handle_s0 - _asm_extable 54b, .L_fixup_handle_s0 - _asm_extable 55b, .L_fixup_handle_s4 - _asm_extable 56b, .L_fixup_handle_s0 - _asm_extable 57b, .L_fixup_handle_s0 +.Llarge_fixup: + sub.d a2, a2, a0 + +.Lsmall_fixup: +58: ld.b t0, a1, 0 +59: st.b t0, a0, 0 + addi.d a0, a0, 1 + addi.d a1, a1, 1 + addi.d a2, a2, -1 + bgt a2, zero, 58b + +.Lexit: + move a0, a2 + jr ra + + _asm_extable 0b, .Lsmall_fixup + _asm_extable 1b, .Lsmall_fixup + _asm_extable 2b, .Llarge_fixup + _asm_extable 3b, .Llarge_fixup + _asm_extable 4b, .Llarge_fixup + _asm_extable 5b, .Llarge_fixup + _asm_extable 6b, .Llarge_fixup + _asm_extable 7b, .Llarge_fixup + _asm_extable 8b, .Llarge_fixup + _asm_extable 9b, .Llarge_fixup + _asm_extable 10b, .Llarge_fixup + _asm_extable 11b, .Llarge_fixup + _asm_extable 12b, .Llarge_fixup + _asm_extable 13b, .Llarge_fixup + _asm_extable 14b, .Llarge_fixup + _asm_extable 15b, .Llarge_fixup + _asm_extable 16b, .Llarge_fixup + _asm_extable 17b, .Llarge_fixup + _asm_extable 18b, .Llarge_fixup + _asm_extable 19b, .Llarge_fixup + _asm_extable 20b, .Llarge_fixup + _asm_extable 21b, .Llarge_fixup + _asm_extable 22b, .Llarge_fixup + _asm_extable 23b, .Llarge_fixup + _asm_extable 24b, .Llarge_fixup + _asm_extable 25b, .Llarge_fixup + _asm_extable 26b, .Llarge_fixup + _asm_extable 27b, .Llarge_fixup + _asm_extable 28b, .Llarge_fixup + _asm_extable 29b, .Llarge_fixup + _asm_extable 30b, .Llarge_fixup + _asm_extable 31b, .Llarge_fixup + _asm_extable 32b, .Llarge_fixup + _asm_extable 33b, .Llarge_fixup + _asm_extable 34b, .Lexit + _asm_extable 35b, .Lexit + _asm_extable 36b, .Lsmall_fixup + _asm_extable 37b, .Lsmall_fixup + _asm_extable 38b, .Lsmall_fixup + _asm_extable 39b, .Lsmall_fixup + _asm_extable 40b, .Lsmall_fixup + _asm_extable 41b, .Lsmall_fixup + _asm_extable 42b, .Lsmall_fixup + _asm_extable 43b, .Lsmall_fixup + _asm_extable 44b, .Lsmall_fixup + _asm_extable 45b, .Lsmall_fixup + _asm_extable 46b, .Lsmall_fixup + _asm_extable 47b, .Lsmall_fixup + _asm_extable 48b, .Lsmall_fixup + _asm_extable 49b, .Lsmall_fixup + _asm_extable 50b, .Lsmall_fixup + _asm_extable 51b, .Lsmall_fixup + _asm_extable 52b, .Lsmall_fixup + _asm_extable 53b, .Lsmall_fixup + _asm_extable 54b, .Lsmall_fixup + _asm_extable 55b, .Lsmall_fixup + _asm_extable 56b, .Lsmall_fixup + _asm_extable 57b, .Lsmall_fixup + _asm_extable 58b, .Lexit + _asm_extable 59b, .Lexit SYM_FUNC_END(__copy_user_fast) diff --git a/arch/loongarch/lib/memcpy.S b/arch/loongarch/lib/memcpy.S index cc30b3b6252f7fd2ecc70fca7b0ffed82d64714f..fa1148878d2b9d06ccdfcf6f2c14fd302fd14114 100644 --- a/arch/loongarch/lib/memcpy.S +++ b/arch/loongarch/lib/memcpy.S @@ -10,6 +10,8 @@ #include #include +.section .noinstr.text, "ax" + SYM_FUNC_START(memcpy) /* * Some CPUs support hardware unaligned access @@ -17,9 +19,13 @@ SYM_FUNC_START(memcpy) ALTERNATIVE "b __memcpy_generic", \ "b __memcpy_fast", CPU_FEATURE_UAL SYM_FUNC_END(memcpy) -_ASM_NOKPROBE(memcpy) +SYM_FUNC_ALIAS(__memcpy, memcpy) EXPORT_SYMBOL(memcpy) +EXPORT_SYMBOL(__memcpy) + +_ASM_NOKPROBE(memcpy) +_ASM_NOKPROBE(__memcpy) /* * void *__memcpy_generic(void *dst, const void *src, size_t n) diff --git a/arch/loongarch/lib/memmove.S b/arch/loongarch/lib/memmove.S index 7dc76d1484b6a84252a76c5f334a69d252d24465..82dae062fec85497a3c01d4fb3084df8ebb277c9 100644 --- a/arch/loongarch/lib/memmove.S +++ b/arch/loongarch/lib/memmove.S @@ -10,23 +10,29 @@ #include #include +.section .noinstr.text, "ax" + SYM_FUNC_START(memmove) - blt a0, a1, memcpy /* dst < src, memcpy */ - blt a1, a0, rmemcpy /* src < dst, rmemcpy */ - jr ra /* dst == src, return */ + blt a0, a1, __memcpy /* dst < src, memcpy */ + blt a1, a0, __rmemcpy /* src < dst, rmemcpy */ + jr ra /* dst == src, return */ SYM_FUNC_END(memmove) -_ASM_NOKPROBE(memmove) +SYM_FUNC_ALIAS(__memmove, memmove) EXPORT_SYMBOL(memmove) +EXPORT_SYMBOL(__memmove) + +_ASM_NOKPROBE(memmove) +_ASM_NOKPROBE(__memmove) -SYM_FUNC_START(rmemcpy) +SYM_FUNC_START(__rmemcpy) /* * Some CPUs support hardware unaligned access */ ALTERNATIVE "b __rmemcpy_generic", \ "b __rmemcpy_fast", CPU_FEATURE_UAL -SYM_FUNC_END(rmemcpy) -_ASM_NOKPROBE(rmemcpy) +SYM_FUNC_END(__rmemcpy) +_ASM_NOKPROBE(__rmemcpy) /* * void *__rmemcpy_generic(void *dst, const void *src, size_t n) diff --git a/arch/loongarch/lib/memset.S b/arch/loongarch/lib/memset.S index 3f20f7996e8ed6e3a38ffcffb1eab7629275da17..06d3ca54cbfe7d73c6cc7a5cadeca4d558b3e6dc 100644 --- a/arch/loongarch/lib/memset.S +++ b/arch/loongarch/lib/memset.S @@ -16,6 +16,8 @@ bstrins.d \r0, \r0, 63, 32 .endm +.section .noinstr.text, "ax" + SYM_FUNC_START(memset) /* * Some CPUs support hardware unaligned access @@ -23,9 +25,13 @@ SYM_FUNC_START(memset) ALTERNATIVE "b __memset_generic", \ "b __memset_fast", CPU_FEATURE_UAL SYM_FUNC_END(memset) -_ASM_NOKPROBE(memset) +SYM_FUNC_ALIAS(__memset, memset) EXPORT_SYMBOL(memset) +EXPORT_SYMBOL(__memset) + +_ASM_NOKPROBE(memset) +_ASM_NOKPROBE(__memset) /* * void *__memset_generic(void *s, int c, size_t n) diff --git a/arch/loongarch/lib/xor_simd.c b/arch/loongarch/lib/xor_simd.c new file mode 100644 index 0000000000000000000000000000000000000000..84cd24b728c47968344ef2b771895e0e321262dd --- /dev/null +++ b/arch/loongarch/lib/xor_simd.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LoongArch SIMD XOR operations + * + * Copyright (C) 2023 WANG Xuerui + */ + +#include "xor_simd.h" + +/* + * Process one cache line (64 bytes) per loop. This is assuming all future + * popular LoongArch cores are similar performance-characteristics-wise to the + * current models. + */ +#define LINE_WIDTH 64 + +#ifdef CONFIG_CPU_HAS_LSX + +#define LD(reg, base, offset) \ + "vld $vr" #reg ", %[" #base "], " #offset "\n\t" +#define ST(reg, base, offset) \ + "vst $vr" #reg ", %[" #base "], " #offset "\n\t" +#define XOR(dj, k) "vxor.v $vr" #dj ", $vr" #dj ", $vr" #k "\n\t" + +#define LD_INOUT_LINE(base) \ + LD(0, base, 0) \ + LD(1, base, 16) \ + LD(2, base, 32) \ + LD(3, base, 48) + +#define LD_AND_XOR_LINE(base) \ + LD(4, base, 0) \ + LD(5, base, 16) \ + LD(6, base, 32) \ + LD(7, base, 48) \ + XOR(0, 4) \ + XOR(1, 5) \ + XOR(2, 6) \ + XOR(3, 7) + +#define ST_LINE(base) \ + ST(0, base, 0) \ + ST(1, base, 16) \ + ST(2, base, 32) \ + ST(3, base, 48) + +#define XOR_FUNC_NAME(nr) __xor_lsx_##nr +#include "xor_template.c" + +#undef LD +#undef ST +#undef XOR +#undef LD_INOUT_LINE +#undef LD_AND_XOR_LINE +#undef ST_LINE +#undef XOR_FUNC_NAME + +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX + +#define LD(reg, base, offset) \ + "xvld $xr" #reg ", %[" #base "], " #offset "\n\t" +#define ST(reg, base, offset) \ + "xvst $xr" #reg ", %[" #base "], " #offset "\n\t" +#define XOR(dj, k) "xvxor.v $xr" #dj ", $xr" #dj ", $xr" #k "\n\t" + +#define LD_INOUT_LINE(base) \ + LD(0, base, 0) \ + LD(1, base, 32) + +#define LD_AND_XOR_LINE(base) \ + LD(2, base, 0) \ + LD(3, base, 32) \ + XOR(0, 2) \ + XOR(1, 3) + +#define ST_LINE(base) \ + ST(0, base, 0) \ + ST(1, base, 32) + +#define XOR_FUNC_NAME(nr) __xor_lasx_##nr +#include "xor_template.c" + +#undef LD +#undef ST +#undef XOR +#undef LD_INOUT_LINE +#undef LD_AND_XOR_LINE +#undef ST_LINE +#undef XOR_FUNC_NAME + +#endif /* CONFIG_CPU_HAS_LASX */ diff --git a/arch/loongarch/lib/xor_simd.h b/arch/loongarch/lib/xor_simd.h new file mode 100644 index 0000000000000000000000000000000000000000..f50f32514d80481519b4a5eed0e688fcbb232f47 --- /dev/null +++ b/arch/loongarch/lib/xor_simd.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Simple interface to link xor_simd.c and xor_simd_glue.c + * + * Separating these files ensures that no SIMD instructions are run outside of + * the kfpu critical section. + */ + +#ifndef __LOONGARCH_LIB_XOR_SIMD_H +#define __LOONGARCH_LIB_XOR_SIMD_H + +#ifdef CONFIG_CPU_HAS_LSX +void __xor_lsx_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2); +void __xor_lsx_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3); +void __xor_lsx_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4); +void __xor_lsx_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4, const unsigned long * __restrict p5); +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX +void __xor_lasx_2(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2); +void __xor_lasx_3(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3); +void __xor_lasx_4(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4); +void __xor_lasx_5(unsigned long bytes, unsigned long * __restrict p1, + const unsigned long * __restrict p2, const unsigned long * __restrict p3, + const unsigned long * __restrict p4, const unsigned long * __restrict p5); +#endif /* CONFIG_CPU_HAS_LASX */ + +#endif /* __LOONGARCH_LIB_XOR_SIMD_H */ diff --git a/arch/loongarch/lib/xor_simd_glue.c b/arch/loongarch/lib/xor_simd_glue.c new file mode 100644 index 0000000000000000000000000000000000000000..393f689dbcf67827a034e07f4c2b4ce43b44b721 --- /dev/null +++ b/arch/loongarch/lib/xor_simd_glue.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * LoongArch SIMD XOR operations + * + * Copyright (C) 2023 WANG Xuerui + */ + +#include +#include +#include +#include +#include "xor_simd.h" + +#define MAKE_XOR_GLUE_2(flavor) \ +void xor_##flavor##_2(unsigned long bytes, unsigned long * __restrict p1, \ + const unsigned long * __restrict p2) \ +{ \ + kernel_fpu_begin(); \ + __xor_##flavor##_2(bytes, p1, p2); \ + kernel_fpu_end(); \ +} \ +EXPORT_SYMBOL_GPL(xor_##flavor##_2) + +#define MAKE_XOR_GLUE_3(flavor) \ +void xor_##flavor##_3(unsigned long bytes, unsigned long * __restrict p1, \ + const unsigned long * __restrict p2, \ + const unsigned long * __restrict p3) \ +{ \ + kernel_fpu_begin(); \ + __xor_##flavor##_3(bytes, p1, p2, p3); \ + kernel_fpu_end(); \ +} \ +EXPORT_SYMBOL_GPL(xor_##flavor##_3) + +#define MAKE_XOR_GLUE_4(flavor) \ +void xor_##flavor##_4(unsigned long bytes, unsigned long * __restrict p1, \ + const unsigned long * __restrict p2, \ + const unsigned long * __restrict p3, \ + const unsigned long * __restrict p4) \ +{ \ + kernel_fpu_begin(); \ + __xor_##flavor##_4(bytes, p1, p2, p3, p4); \ + kernel_fpu_end(); \ +} \ +EXPORT_SYMBOL_GPL(xor_##flavor##_4) + +#define MAKE_XOR_GLUE_5(flavor) \ +void xor_##flavor##_5(unsigned long bytes, unsigned long * __restrict p1, \ + const unsigned long * __restrict p2, \ + const unsigned long * __restrict p3, \ + const unsigned long * __restrict p4, \ + const unsigned long * __restrict p5) \ +{ \ + kernel_fpu_begin(); \ + __xor_##flavor##_5(bytes, p1, p2, p3, p4, p5); \ + kernel_fpu_end(); \ +} \ +EXPORT_SYMBOL_GPL(xor_##flavor##_5) + +#define MAKE_XOR_GLUES(flavor) \ + MAKE_XOR_GLUE_2(flavor); \ + MAKE_XOR_GLUE_3(flavor); \ + MAKE_XOR_GLUE_4(flavor); \ + MAKE_XOR_GLUE_5(flavor) + +#ifdef CONFIG_CPU_HAS_LSX +MAKE_XOR_GLUES(lsx); +#endif + +#ifdef CONFIG_CPU_HAS_LASX +MAKE_XOR_GLUES(lasx); +#endif diff --git a/arch/loongarch/lib/xor_template.c b/arch/loongarch/lib/xor_template.c new file mode 100644 index 0000000000000000000000000000000000000000..0358ced7fe333838e4c0ca4c144a13b12925d062 --- /dev/null +++ b/arch/loongarch/lib/xor_template.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 WANG Xuerui + * + * Template for XOR operations, instantiated in xor_simd.c. + * + * Expected preprocessor definitions: + * + * - LINE_WIDTH + * - XOR_FUNC_NAME(nr) + * - LD_INOUT_LINE(buf) + * - LD_AND_XOR_LINE(buf) + * - ST_LINE(buf) + */ + +void XOR_FUNC_NAME(2)(unsigned long bytes, + unsigned long * __restrict v1, + const unsigned long * __restrict v2) +{ + unsigned long lines = bytes / LINE_WIDTH; + + do { + __asm__ __volatile__ ( + LD_INOUT_LINE(v1) + LD_AND_XOR_LINE(v2) + ST_LINE(v1) + : : [v1] "r"(v1), [v2] "r"(v2) : "memory" + ); + + v1 += LINE_WIDTH / sizeof(unsigned long); + v2 += LINE_WIDTH / sizeof(unsigned long); + } while (--lines > 0); +} + +void XOR_FUNC_NAME(3)(unsigned long bytes, + unsigned long * __restrict v1, + const unsigned long * __restrict v2, + const unsigned long * __restrict v3) +{ + unsigned long lines = bytes / LINE_WIDTH; + + do { + __asm__ __volatile__ ( + LD_INOUT_LINE(v1) + LD_AND_XOR_LINE(v2) + LD_AND_XOR_LINE(v3) + ST_LINE(v1) + : : [v1] "r"(v1), [v2] "r"(v2), [v3] "r"(v3) : "memory" + ); + + v1 += LINE_WIDTH / sizeof(unsigned long); + v2 += LINE_WIDTH / sizeof(unsigned long); + v3 += LINE_WIDTH / sizeof(unsigned long); + } while (--lines > 0); +} + +void XOR_FUNC_NAME(4)(unsigned long bytes, + unsigned long * __restrict v1, + const unsigned long * __restrict v2, + const unsigned long * __restrict v3, + const unsigned long * __restrict v4) +{ + unsigned long lines = bytes / LINE_WIDTH; + + do { + __asm__ __volatile__ ( + LD_INOUT_LINE(v1) + LD_AND_XOR_LINE(v2) + LD_AND_XOR_LINE(v3) + LD_AND_XOR_LINE(v4) + ST_LINE(v1) + : : [v1] "r"(v1), [v2] "r"(v2), [v3] "r"(v3), [v4] "r"(v4) + : "memory" + ); + + v1 += LINE_WIDTH / sizeof(unsigned long); + v2 += LINE_WIDTH / sizeof(unsigned long); + v3 += LINE_WIDTH / sizeof(unsigned long); + v4 += LINE_WIDTH / sizeof(unsigned long); + } while (--lines > 0); +} + +void XOR_FUNC_NAME(5)(unsigned long bytes, + unsigned long * __restrict v1, + const unsigned long * __restrict v2, + const unsigned long * __restrict v3, + const unsigned long * __restrict v4, + const unsigned long * __restrict v5) +{ + unsigned long lines = bytes / LINE_WIDTH; + + do { + __asm__ __volatile__ ( + LD_INOUT_LINE(v1) + LD_AND_XOR_LINE(v2) + LD_AND_XOR_LINE(v3) + LD_AND_XOR_LINE(v4) + LD_AND_XOR_LINE(v5) + ST_LINE(v1) + : : [v1] "r"(v1), [v2] "r"(v2), [v3] "r"(v3), [v4] "r"(v4), + [v5] "r"(v5) : "memory" + ); + + v1 += LINE_WIDTH / sizeof(unsigned long); + v2 += LINE_WIDTH / sizeof(unsigned long); + v3 += LINE_WIDTH / sizeof(unsigned long); + v4 += LINE_WIDTH / sizeof(unsigned long); + v5 += LINE_WIDTH / sizeof(unsigned long); + } while (--lines > 0); +} diff --git a/arch/loongarch/mm/Makefile b/arch/loongarch/mm/Makefile index 8ffc6383f8360e33a833be7a09de3934c89a5d91..e4d1e581dbae3aee294ad1eb81ec72516dbcd9ae 100644 --- a/arch/loongarch/mm/Makefile +++ b/arch/loongarch/mm/Makefile @@ -7,3 +7,6 @@ obj-y += init.o cache.o tlb.o tlbex.o extable.o \ fault.o ioremap.o maccess.o mmap.o pgtable.o page.o obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o +obj-$(CONFIG_KASAN) += kasan_init.o + +KASAN_SANITIZE_kasan_init.o := n diff --git a/arch/loongarch/mm/cache.c b/arch/loongarch/mm/cache.c index 72685a48eaf084f61ce967bcd16fd0bb79bf9ce4..6be04d36ca0769658a2b52d25af50dd6ad7e07e0 100644 --- a/arch/loongarch/mm/cache.c +++ b/arch/loongarch/mm/cache.c @@ -156,7 +156,6 @@ void cpu_cache_init(void) current_cpu_data.cache_leaves_present = leaf; current_cpu_data.options |= LOONGARCH_CPU_PREFETCH; - shm_align_mask = PAGE_SIZE - 1; } static const pgprot_t protection_map[16] = { diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c index da5b6d518cdb1d6c5ec550083f52f588ccf29066..1fc2f6813ea027d43ccf24af8aade31f1093df62 100644 --- a/arch/loongarch/mm/fault.c +++ b/arch/loongarch/mm/fault.c @@ -20,17 +20,19 @@ #include #include #include -#include #include #include +#include #include +#include #include #include int show_unhandled_signals = 1; -static void __kprobes no_context(struct pt_regs *regs, unsigned long address) +static void __kprobes no_context(struct pt_regs *regs, + unsigned long write, unsigned long address) { const int field = sizeof(unsigned long) * 2; @@ -38,6 +40,9 @@ static void __kprobes no_context(struct pt_regs *regs, unsigned long address) if (fixup_exception(regs)) return; + if (kfence_handle_page_fault(address, write, regs)) + return; + /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. @@ -51,14 +56,15 @@ static void __kprobes no_context(struct pt_regs *regs, unsigned long address) die("Oops", regs); } -static void __kprobes do_out_of_memory(struct pt_regs *regs, unsigned long address) +static void __kprobes do_out_of_memory(struct pt_regs *regs, + unsigned long write, unsigned long address) { /* * We ran out of memory, call the OOM killer, and return the userspace * (which will retry the fault, or kill us if we got oom-killed). */ if (!user_mode(regs)) { - no_context(regs, address); + no_context(regs, write, address); return; } pagefault_out_of_memory(); @@ -69,7 +75,7 @@ static void __kprobes do_sigbus(struct pt_regs *regs, { /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) { - no_context(regs, address); + no_context(regs, write, address); return; } @@ -90,7 +96,7 @@ static void __kprobes do_sigsegv(struct pt_regs *regs, /* Kernel mode? Handle exceptions or die */ if (!user_mode(regs)) { - no_context(regs, address); + no_context(regs, write, address); return; } @@ -149,7 +155,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, */ if (address & __UA_LIMIT) { if (!user_mode(regs)) - no_context(regs, address); + no_context(regs, write, address); else do_sigsegv(regs, write, address, si_code); return; @@ -211,7 +217,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, if (fault_signal_pending(fault, regs)) { if (!user_mode(regs)) - no_context(regs, address); + no_context(regs, write, address); return; } @@ -232,7 +238,7 @@ static void __kprobes __do_page_fault(struct pt_regs *regs, if (unlikely(fault & VM_FAULT_ERROR)) { mmap_read_unlock(mm); if (fault & VM_FAULT_OOM) { - do_out_of_memory(regs, address); + do_out_of_memory(regs, write, address); return; } else if (fault & VM_FAULT_SIGSEGV) { do_sigsegv(regs, write, address, si_code); diff --git a/arch/loongarch/mm/hugetlbpage.c b/arch/loongarch/mm/hugetlbpage.c index ba138117b1247e450431b54c4afe562a56acb312..1e76fcb83093dd6892801200e6027e92d9c68d37 100644 --- a/arch/loongarch/mm/hugetlbpage.c +++ b/arch/loongarch/mm/hugetlbpage.c @@ -50,18 +50,6 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr, return (pte_t *) pmd; } -/* - * This function checks for proper alignment of input addr and len parameters. - */ -int is_aligned_hugepage_range(unsigned long addr, unsigned long len) -{ - if (len & ~HPAGE_MASK) - return -EINVAL; - if (addr & ~HPAGE_MASK) - return -EINVAL; - return 0; -} - int pmd_huge(pmd_t pmd) { return (pmd_val(pmd) & _PAGE_HUGE) != 0; diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index 3b7d8129570b83ac87455e8bb1c2f7c8a0ee35b1..f3fe8c06ba4db352ca373824254b782dc9893226 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -35,33 +35,8 @@ #include #include -/* - * We have up to 8 empty zeroed pages so we can map one of the right colour - * when needed. Since page is never written to after the initialization we - * don't have to care about aliases on other CPUs. - */ -unsigned long empty_zero_page, zero_page_mask; +unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_aligned_bss; EXPORT_SYMBOL(empty_zero_page); -EXPORT_SYMBOL(zero_page_mask); - -void setup_zero_pages(void) -{ - unsigned int order, i; - struct page *page; - - order = 0; - - empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); - if (!empty_zero_page) - panic("Oh boy, that early out of memory?"); - - page = virt_to_page((void *)empty_zero_page); - split_page(page, order); - for (i = 0; i < (1 << order); i++, page++) - mark_page_reserved(page); - - zero_page_mask = ((PAGE_SIZE << order) - 1) & PAGE_MASK; -} void copy_user_highpage(struct page *to, struct page *from, unsigned long vaddr, struct vm_area_struct *vma) @@ -106,7 +81,6 @@ void __init mem_init(void) high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT); memblock_free_all(); - setup_zero_pages(); /* Setup zeroed pages. */ } #endif /* !CONFIG_NUMA */ @@ -191,43 +165,42 @@ void vmemmap_free(unsigned long start, unsigned long end, struct vmem_altmap *al #endif #endif -static pte_t *fixmap_pte(unsigned long addr) +pte_t * __init populate_kernel_pte(unsigned long addr) { - pgd_t *pgd; - p4d_t *p4d; + pgd_t *pgd = pgd_offset_k(addr); + p4d_t *p4d = p4d_offset(pgd, addr); pud_t *pud; pmd_t *pmd; - pgd = pgd_offset_k(addr); - p4d = p4d_offset(pgd, addr); - - if (pgd_none(*pgd)) { - pud_t *new __maybe_unused; - - new = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); - pgd_populate(&init_mm, pgd, new); + if (p4d_none(*p4d)) { + pud = memblock_alloc(PAGE_SIZE, PAGE_SIZE); + if (!pud) + panic("%s: Failed to allocate memory\n", __func__); + p4d_populate(&init_mm, p4d, pud); #ifndef __PAGETABLE_PUD_FOLDED - pud_init(new); + pud_init(pud); #endif } pud = pud_offset(p4d, addr); if (pud_none(*pud)) { - pmd_t *new __maybe_unused; - - new = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); - pud_populate(&init_mm, pud, new); + pmd = memblock_alloc(PAGE_SIZE, PAGE_SIZE); + if (!pmd) + panic("%s: Failed to allocate memory\n", __func__); + pud_populate(&init_mm, pud, pmd); #ifndef __PAGETABLE_PMD_FOLDED - pmd_init(new); + pmd_init(pmd); #endif } pmd = pmd_offset(pud, addr); - if (pmd_none(*pmd)) { - pte_t *new __maybe_unused; + if (!pmd_present(*pmd)) { + pte_t *pte; - new = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); - pmd_populate_kernel(&init_mm, pmd, new); + pte = memblock_alloc(PAGE_SIZE, PAGE_SIZE); + if (!pte) + panic("%s: Failed to allocate memory\n", __func__); + pmd_populate_kernel(&init_mm, pmd, pte); } return pte_offset_kernel(pmd, addr); @@ -241,7 +214,7 @@ void __init __set_fixmap(enum fixed_addresses idx, BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses); - ptep = fixmap_pte(addr); + ptep = populate_kernel_pte(addr); if (!pte_none(*ptep)) { pte_ERROR(*ptep); return; diff --git a/arch/loongarch/mm/ioremap.c b/arch/loongarch/mm/ioremap.c index 73b0980ab6f5e716650ade71b1a729bd7e892050..70ca7301981106b680bbc506b2ef340d01e73911 100644 --- a/arch/loongarch/mm/ioremap.c +++ b/arch/loongarch/mm/ioremap.c @@ -4,6 +4,7 @@ */ #include +#include void __init __iomem *early_ioremap(u64 phys_addr, unsigned long size) { diff --git a/arch/loongarch/mm/kasan_init.c b/arch/loongarch/mm/kasan_init.c new file mode 100644 index 0000000000000000000000000000000000000000..cc3e81fe0186f4f0fa8de9cedfc75138583ce23f --- /dev/null +++ b/arch/loongarch/mm/kasan_init.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Loongson Technology Corporation Limited + */ +#define pr_fmt(fmt) "kasan: " fmt +#include +#include +#include + +#include +#include +#include + +static pgd_t kasan_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); + +#ifdef __PAGETABLE_PUD_FOLDED +#define __p4d_none(early, p4d) (0) +#else +#define __p4d_none(early, p4d) (early ? (p4d_val(p4d) == 0) : \ +(__pa(p4d_val(p4d)) == (unsigned long)__pa(kasan_early_shadow_pud))) +#endif + +#ifdef __PAGETABLE_PMD_FOLDED +#define __pud_none(early, pud) (0) +#else +#define __pud_none(early, pud) (early ? (pud_val(pud) == 0) : \ +(__pa(pud_val(pud)) == (unsigned long)__pa(kasan_early_shadow_pmd))) +#endif + +#define __pmd_none(early, pmd) (early ? (pmd_val(pmd) == 0) : \ +(__pa(pmd_val(pmd)) == (unsigned long)__pa(kasan_early_shadow_pte))) + +#define __pte_none(early, pte) (early ? pte_none(pte) : \ +((pte_val(pte) & _PFN_MASK) == (unsigned long)__pa(kasan_early_shadow_page))) + +bool kasan_early_stage = true; + +void *kasan_mem_to_shadow(const void *addr) +{ + if (!kasan_arch_is_ready()) { + return (void *)(kasan_early_shadow_page); + } else { + unsigned long maddr = (unsigned long)addr; + unsigned long xrange = (maddr >> XRANGE_SHIFT) & 0xffff; + unsigned long offset = 0; + + maddr &= XRANGE_SHADOW_MASK; + switch (xrange) { + case XKPRANGE_CC_SEG: + offset = XKPRANGE_CC_SHADOW_OFFSET; + break; + case XKPRANGE_UC_SEG: + offset = XKPRANGE_UC_SHADOW_OFFSET; + break; + case XKVRANGE_VC_SEG: + offset = XKVRANGE_VC_SHADOW_OFFSET; + break; + default: + WARN_ON(1); + return NULL; + } + + return (void *)((maddr >> KASAN_SHADOW_SCALE_SHIFT) + offset); + } +} + +const void *kasan_shadow_to_mem(const void *shadow_addr) +{ + unsigned long addr = (unsigned long)shadow_addr; + + if (unlikely(addr > KASAN_SHADOW_END) || + unlikely(addr < KASAN_SHADOW_START)) { + WARN_ON(1); + return NULL; + } + + if (addr >= XKVRANGE_VC_SHADOW_OFFSET) + return (void *)(((addr - XKVRANGE_VC_SHADOW_OFFSET) << KASAN_SHADOW_SCALE_SHIFT) + XKVRANGE_VC_START); + else if (addr >= XKPRANGE_UC_SHADOW_OFFSET) + return (void *)(((addr - XKPRANGE_UC_SHADOW_OFFSET) << KASAN_SHADOW_SCALE_SHIFT) + XKPRANGE_UC_START); + else if (addr >= XKPRANGE_CC_SHADOW_OFFSET) + return (void *)(((addr - XKPRANGE_CC_SHADOW_OFFSET) << KASAN_SHADOW_SCALE_SHIFT) + XKPRANGE_CC_START); + else { + WARN_ON(1); + return NULL; + } +} + +/* + * Alloc memory for shadow memory page table. + */ +static phys_addr_t __init kasan_alloc_zeroed_page(int node) +{ + void *p = memblock_alloc_try_nid(PAGE_SIZE, PAGE_SIZE, + __pa(MAX_DMA_ADDRESS), MEMBLOCK_ALLOC_ACCESSIBLE, node); + if (!p) + panic("%s: Failed to allocate %lu bytes align=0x%lx nid=%d from=%llx\n", + __func__, PAGE_SIZE, PAGE_SIZE, node, __pa(MAX_DMA_ADDRESS)); + + return __pa(p); +} + +static pte_t *__init kasan_pte_offset(pmd_t *pmdp, unsigned long addr, int node, bool early) +{ + if (__pmd_none(early, READ_ONCE(*pmdp))) { + phys_addr_t pte_phys = early ? + __pa_symbol(kasan_early_shadow_pte) : kasan_alloc_zeroed_page(node); + if (!early) + memcpy(__va(pte_phys), kasan_early_shadow_pte, sizeof(kasan_early_shadow_pte)); + pmd_populate_kernel(NULL, pmdp, (pte_t *)__va(pte_phys)); + } + + return pte_offset_kernel(pmdp, addr); +} + +static pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node, bool early) +{ + if (__pud_none(early, READ_ONCE(*pudp))) { + phys_addr_t pmd_phys = early ? + __pa_symbol(kasan_early_shadow_pmd) : kasan_alloc_zeroed_page(node); + if (!early) + memcpy(__va(pmd_phys), kasan_early_shadow_pmd, sizeof(kasan_early_shadow_pmd)); + pud_populate(&init_mm, pudp, (pmd_t *)__va(pmd_phys)); + } + + return pmd_offset(pudp, addr); +} + +static pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node, bool early) +{ + if (__p4d_none(early, READ_ONCE(*p4dp))) { + phys_addr_t pud_phys = early ? + __pa_symbol(kasan_early_shadow_pud) : kasan_alloc_zeroed_page(node); + if (!early) + memcpy(__va(pud_phys), kasan_early_shadow_pud, sizeof(kasan_early_shadow_pud)); + p4d_populate(&init_mm, p4dp, (pud_t *)__va(pud_phys)); + } + + return pud_offset(p4dp, addr); +} + +static void __init kasan_pte_populate(pmd_t *pmdp, unsigned long addr, + unsigned long end, int node, bool early) +{ + unsigned long next; + pte_t *ptep = kasan_pte_offset(pmdp, addr, node, early); + + do { + phys_addr_t page_phys = early ? + __pa_symbol(kasan_early_shadow_page) + : kasan_alloc_zeroed_page(node); + next = addr + PAGE_SIZE; + set_pte(ptep, pfn_pte(__phys_to_pfn(page_phys), PAGE_KERNEL)); + } while (ptep++, addr = next, addr != end && __pte_none(early, READ_ONCE(*ptep))); +} + +static void __init kasan_pmd_populate(pud_t *pudp, unsigned long addr, + unsigned long end, int node, bool early) +{ + unsigned long next; + pmd_t *pmdp = kasan_pmd_offset(pudp, addr, node, early); + + do { + next = pmd_addr_end(addr, end); + kasan_pte_populate(pmdp, addr, next, node, early); + } while (pmdp++, addr = next, addr != end && __pmd_none(early, READ_ONCE(*pmdp))); +} + +static void __init kasan_pud_populate(p4d_t *p4dp, unsigned long addr, + unsigned long end, int node, bool early) +{ + unsigned long next; + pud_t *pudp = kasan_pud_offset(p4dp, addr, node, early); + + do { + next = pud_addr_end(addr, end); + kasan_pmd_populate(pudp, addr, next, node, early); + } while (pudp++, addr = next, addr != end); +} + +static void __init kasan_p4d_populate(pgd_t *pgdp, unsigned long addr, + unsigned long end, int node, bool early) +{ + unsigned long next; + p4d_t *p4dp = p4d_offset(pgdp, addr); + + do { + next = p4d_addr_end(addr, end); + kasan_pud_populate(p4dp, addr, next, node, early); + } while (p4dp++, addr = next, addr != end); +} + +static void __init kasan_pgd_populate(unsigned long addr, unsigned long end, + int node, bool early) +{ + unsigned long next; + pgd_t *pgdp; + + pgdp = pgd_offset_k(addr); + + do { + next = pgd_addr_end(addr, end); + kasan_p4d_populate(pgdp, addr, next, node, early); + } while (pgdp++, addr = next, addr != end); + +} + +/* Set up full kasan mappings, ensuring that the mapped pages are zeroed */ +static void __init kasan_map_populate(unsigned long start, unsigned long end, + int node) +{ + kasan_pgd_populate(start & PAGE_MASK, PAGE_ALIGN(end), node, false); +} + +asmlinkage void __init kasan_early_init(void) +{ + BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_START, PGDIR_SIZE)); + BUILD_BUG_ON(!IS_ALIGNED(KASAN_SHADOW_END, PGDIR_SIZE)); +} + +static inline void kasan_set_pgd(pgd_t *pgdp, pgd_t pgdval) +{ + WRITE_ONCE(*pgdp, pgdval); +} + +static void __init clear_pgds(unsigned long start, unsigned long end) +{ + /* + * Remove references to kasan page tables from + * swapper_pg_dir. pgd_clear() can't be used + * here because it's nop on 2,3-level pagetable setups + */ + for (; start < end; start += PGDIR_SIZE) + kasan_set_pgd((pgd_t *)pgd_offset_k(start), __pgd(0)); +} + +void __init kasan_init(void) +{ + u64 i; + phys_addr_t pa_start, pa_end; + + /* + * PGD was populated as invalid_pmd_table or invalid_pud_table + * in pagetable_init() which depends on how many levels of page + * table you are using, but we had to clean the gpd of kasan + * shadow memory, as the pgd value is none-zero. + * The assertion pgd_none is going to be false and the formal populate + * afterwards is not going to create any new pgd at all. + */ + memcpy(kasan_pg_dir, swapper_pg_dir, sizeof(kasan_pg_dir)); + csr_write64(__pa_symbol(kasan_pg_dir), LOONGARCH_CSR_PGDH); + local_flush_tlb_all(); + + clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END); + + /* Maps everything to a single page of zeroes */ + kasan_pgd_populate(KASAN_SHADOW_START, KASAN_SHADOW_END, NUMA_NO_NODE, true); + + kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START), + kasan_mem_to_shadow((void *)KFENCE_AREA_END)); + + kasan_early_stage = false; + + /* Populate the linear mapping */ + for_each_mem_range(i, &pa_start, &pa_end) { + void *start = (void *)phys_to_virt(pa_start); + void *end = (void *)phys_to_virt(pa_end); + + if (start >= end) + break; + + kasan_map_populate((unsigned long)kasan_mem_to_shadow(start), + (unsigned long)kasan_mem_to_shadow(end), NUMA_NO_NODE); + } + + /* Populate modules mapping */ + kasan_map_populate((unsigned long)kasan_mem_to_shadow((void *)MODULES_VADDR), + (unsigned long)kasan_mem_to_shadow((void *)MODULES_END), NUMA_NO_NODE); + /* + * KAsan may reuse the contents of kasan_early_shadow_pte directly, so we + * should make sure that it maps the zero page read-only. + */ + for (i = 0; i < PTRS_PER_PTE; i++) + set_pte(&kasan_early_shadow_pte[i], + pfn_pte(__phys_to_pfn(__pa_symbol(kasan_early_shadow_page)), PAGE_KERNEL_RO)); + + memset(kasan_early_shadow_page, 0, PAGE_SIZE); + csr_write64(__pa_symbol(swapper_pg_dir), LOONGARCH_CSR_PGDH); + local_flush_tlb_all(); + + /* At this point kasan is fully initialized. Enable error messages */ + init_task.kasan_depth = 0; + pr_info("KernelAddressSanitizer initialized.\n"); +} diff --git a/arch/loongarch/mm/mmap.c b/arch/loongarch/mm/mmap.c index fbe1a4856fc42d4f255d9e9c3a9e349c3fa09e9a..a9630a81b38abbfc575ea4174af049ccd5a9a888 100644 --- a/arch/loongarch/mm/mmap.c +++ b/arch/loongarch/mm/mmap.c @@ -8,12 +8,11 @@ #include #include -unsigned long shm_align_mask = PAGE_SIZE - 1; /* Sane caches */ -EXPORT_SYMBOL(shm_align_mask); +#define SHM_ALIGN_MASK (SHMLBA - 1) -#define COLOUR_ALIGN(addr, pgoff) \ - ((((addr) + shm_align_mask) & ~shm_align_mask) + \ - (((pgoff) << PAGE_SHIFT) & shm_align_mask)) +#define COLOUR_ALIGN(addr, pgoff) \ + ((((addr) + SHM_ALIGN_MASK) & ~SHM_ALIGN_MASK) \ + + (((pgoff) << PAGE_SHIFT) & SHM_ALIGN_MASK)) enum mmap_allocation_direction {UP, DOWN}; @@ -40,7 +39,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, * cache aliasing constraints. */ if ((flags & MAP_SHARED) && - ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask)) + ((addr - (pgoff << PAGE_SHIFT)) & SHM_ALIGN_MASK)) return -EINVAL; return addr; } @@ -63,7 +62,7 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp, } info.length = len; - info.align_mask = do_color_align ? (PAGE_MASK & shm_align_mask) : 0; + info.align_mask = do_color_align ? (PAGE_MASK & SHM_ALIGN_MASK) : 0; info.align_offset = pgoff << PAGE_SHIFT; if (dir == DOWN) { diff --git a/arch/loongarch/mm/pgtable.c b/arch/loongarch/mm/pgtable.c index b14343e211b63f492b7aedd6ee0db9071156aa4a..71d0539e2d0b0207f901e3ef75679bc4e6bc2fa3 100644 --- a/arch/loongarch/mm/pgtable.c +++ b/arch/loongarch/mm/pgtable.c @@ -9,6 +9,18 @@ #include #include +struct page *dmw_virt_to_page(unsigned long kaddr) +{ + return pfn_to_page(virt_to_pfn(kaddr)); +} +EXPORT_SYMBOL_GPL(dmw_virt_to_page); + +struct page *tlb_virt_to_page(unsigned long kaddr) +{ + return pfn_to_page(pte_pfn(*virt_to_kpte(kaddr))); +} +EXPORT_SYMBOL_GPL(tlb_virt_to_page); + pgd_t *pgd_alloc(struct mm_struct *mm) { pgd_t *init, *ret = NULL; diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c index eb8572e201ea075d5e28a359f65ae8e3d117ebf4..2c0a411f23aa778bb62160bd511252736fc987be 100644 --- a/arch/loongarch/mm/tlb.c +++ b/arch/loongarch/mm/tlb.c @@ -261,7 +261,7 @@ unsigned long pcpu_handlers[NR_CPUS]; #endif extern long exception_handlers[VECSIZE * 128 / sizeof(long)]; -void setup_tlb_handler(int cpu) +static void setup_tlb_handler(int cpu) { setup_ptwalker(); local_flush_tlb_all(); diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile index a50308b6fc259082fb61e29e7b5b9fb99082fb83..5c97d14633282186534b2aea039add1af77b0518 100644 --- a/arch/loongarch/vdso/Makefile +++ b/arch/loongarch/vdso/Makefile @@ -1,6 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 # Objects to go into the VDSO. +KASAN_SANITIZE := n +KCOV_INSTRUMENT := n + # Include the generic Makefile to check the built vdso. include $(srctree)/lib/vdso/Makefile diff --git a/arch/m68k/coldfire/dma_timer.c b/arch/m68k/coldfire/dma_timer.c index cbb28943960651fc172a77e3f1124ab4f35bc07c..91e6728f51ed650f24c6ad4dd7d4d7d7bc6e5adc 100644 --- a/arch/m68k/coldfire/dma_timer.c +++ b/arch/m68k/coldfire/dma_timer.c @@ -48,7 +48,7 @@ static struct clocksource clocksource_cf_dt = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; -static int __init init_cf_dt_clocksource(void) +static int __init init_cf_dt_clocksource(void) { /* * We setup DMA timer 0 in free run mode. This incrementing counter is diff --git a/arch/m68k/include/asm/ide.h b/arch/m68k/include/asm/ide.h deleted file mode 100644 index 05cc7dc00e0c14371ec7a5b7ddcbc5b06ca1642e..0000000000000000000000000000000000000000 --- a/arch/m68k/include/asm/ide.h +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* Copyright(c) 1996 Kars de Jong */ -/* Based on the ide driver from 1.2.13pl8 */ - -/* - * Credits (alphabetical): - * - * - Bjoern Brauel - * - Kars de Jong - * - Torsten Ebeling - * - Dwight Engen - * - Thorsten Floeck - * - Roman Hodek - * - Guenther Kelleter - * - Chris Lawrence - * - Michael Rausch - * - Christian Sauer - * - Michael Schmitz - * - Jes Soerensen - * - Michael Thurm - * - Geert Uytterhoeven - */ - -#ifndef _M68K_IDE_H -#define _M68K_IDE_H - -#ifdef __KERNEL__ -#include -#include -#include - -#ifdef CONFIG_MMU - -/* - * Get rid of defs from io.h - ide has its private and conflicting versions - * Since so far no single m68k platform uses ISA/PCI I/O space for IDE, we - * always use the `raw' MMIO versions - */ -#undef readb -#undef readw -#undef writeb -#undef writew - -#define readb in_8 -#define readw in_be16 -#define __ide_mm_insw(port, addr, n) raw_insw((u16 *)port, addr, n) -#define __ide_mm_insl(port, addr, n) raw_insl((u32 *)port, addr, n) -#define writeb(val, port) out_8(port, val) -#define writew(val, port) out_be16(port, val) -#define __ide_mm_outsw(port, addr, n) raw_outsw((u16 *)port, addr, n) -#define __ide_mm_outsl(port, addr, n) raw_outsl((u32 *)port, addr, n) - -#else - -#define __ide_mm_insw(port, addr, n) io_insw((unsigned int)port, addr, n) -#define __ide_mm_insl(port, addr, n) io_insl((unsigned int)port, addr, n) -#define __ide_mm_outsw(port, addr, n) io_outsw((unsigned int)port, addr, n) -#define __ide_mm_outsl(port, addr, n) io_outsl((unsigned int)port, addr, n) - -#endif /* CONFIG_MMU */ - -#endif /* __KERNEL__ */ -#endif /* _M68K_IDE_H */ diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c index b0e110d3d2e600904c2caa727713f8021d70cf70..9504eb19d73a9cbc5fbbda4ba7fd4e0dfc348efb 100644 --- a/arch/m68k/kernel/pcibios.c +++ b/arch/m68k/kernel/pcibios.c @@ -92,9 +92,3 @@ void pcibios_fixup_bus(struct pci_bus *bus) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 32); } } - -char *pcibios_setup(char *str) -{ - return str; -} - diff --git a/arch/microblaze/include/asm/page.h b/arch/microblaze/include/asm/page.h index 337f23eabc71b2b47bd3d5a56254a4520187019f..86a4ce07c192b11feddce52000e81847a8097c7b 100644 --- a/arch/microblaze/include/asm/page.h +++ b/arch/microblaze/include/asm/page.h @@ -99,9 +99,6 @@ extern int page_is_ram(unsigned long pfn); # define phys_to_pfn(phys) (PFN_DOWN(phys)) # define pfn_to_phys(pfn) (PFN_PHYS(pfn)) -# define virt_to_pfn(vaddr) (phys_to_pfn((__pa(vaddr)))) -# define pfn_to_virt(pfn) __va(pfn_to_phys((pfn))) - # define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) # define page_to_virt(page) __va(page_to_pfn(page) << PAGE_SHIFT) # define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) @@ -109,11 +106,6 @@ extern int page_is_ram(unsigned long pfn); # define ARCH_PFN_OFFSET (memory_start >> PAGE_SHIFT) # endif /* __ASSEMBLY__ */ -#define virt_addr_valid(vaddr) (pfn_valid(virt_to_pfn(vaddr))) - -# define __pa(x) __virt_to_phys((unsigned long)(x)) -# define __va(x) ((void *)__phys_to_virt((unsigned long)(x))) - /* Convert between virtual and physical address for MMU. */ /* Handle MicroBlaze processor with virtual memory. */ #define __virt_to_phys(addr) \ @@ -125,6 +117,25 @@ extern int page_is_ram(unsigned long pfn); #define tovirt(rd, rs) \ addik rd, rs, (CONFIG_KERNEL_START - CONFIG_KERNEL_BASE_ADDR) +#ifndef __ASSEMBLY__ + +# define __pa(x) __virt_to_phys((unsigned long)(x)) +# define __va(x) ((void *)__phys_to_virt((unsigned long)(x))) + +static inline unsigned long virt_to_pfn(const void *vaddr) +{ + return phys_to_pfn(__pa(vaddr)); +} + +static inline const void *pfn_to_virt(unsigned long pfn) +{ + return __va(pfn_to_phys((pfn))); +} + +#define virt_addr_valid(vaddr) (pfn_valid(virt_to_pfn(vaddr))) + +#endif /* __ASSEMBLY__ */ + #define TOPHYS(addr) __virt_to_phys(addr) #endif /* __KERNEL__ */ diff --git a/arch/microblaze/include/asm/setup.h b/arch/microblaze/include/asm/setup.h index 3657f5e78a3d0e2cb96a8fe2a169dc8126b4c77b..bf2600f75959322287881e3d48e4e088c1cac92a 100644 --- a/arch/microblaze/include/asm/setup.h +++ b/arch/microblaze/include/asm/setup.h @@ -25,7 +25,5 @@ void machine_shutdown(void); void machine_halt(void); void machine_power_off(void); -extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask); - # endif /* __ASSEMBLY__ */ #endif /* _ASM_MICROBLAZE_SETUP_H */ diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c index 5f4722908164da2020e07298dee054915ca4cf73..2f66c7963084dd064e4679d22e0568e1af80c67c 100644 --- a/arch/microblaze/kernel/reset.c +++ b/arch/microblaze/kernel/reset.c @@ -9,7 +9,6 @@ #include #include -#include #include void machine_shutdown(void) diff --git a/arch/microblaze/mm/init.c b/arch/microblaze/mm/init.c index 353fabdfcbc54051cc6f5535e94d9cf6e0b26740..3827dc76edd8236ce40ff6f0664bbf4725a9173f 100644 --- a/arch/microblaze/mm/init.c +++ b/arch/microblaze/mm/init.c @@ -270,22 +270,6 @@ asmlinkage void __init mmu_init(void) memblock_dump_all(); } -void * __ref zalloc_maybe_bootmem(size_t size, gfp_t mask) -{ - void *p; - - if (mem_init_done) { - p = kzalloc(size, mask); - } else { - p = memblock_alloc(size, SMP_CACHE_BYTES); - if (!p) - panic("%s: Failed to allocate %zu bytes\n", - __func__, size); - } - - return p; -} - static const pgprot_t protection_map[16] = { [VM_NONE] = PAGE_NONE, [VM_READ] = PAGE_READONLY_X, diff --git a/arch/mips/Makefile b/arch/mips/Makefile index a47593d72f6f5f3d4561481894eb8a1bb1f5c045..f49807e1f19bc56ac2fd536aef4f4041d3bcad7a 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -181,12 +181,16 @@ endif cflags-$(CONFIG_CAVIUM_CN63XXP1) += -Wa,-mfix-cn63xxp1 cflags-$(CONFIG_CPU_BMIPS) += -march=mips32 -Wa,-mips32 -Wa,--trap -cflags-$(CONFIG_CPU_LOONGSON2E) += $(call cc-option,-march=loongson2e) -Wa,--trap -cflags-$(CONFIG_CPU_LOONGSON2F) += $(call cc-option,-march=loongson2f) -Wa,--trap -cflags-$(CONFIG_CPU_LOONGSON64) += $(call cc-option,-march=loongson3a,-march=mips64r2) -Wa,--trap +cflags-$(CONFIG_CPU_LOONGSON2E) += -march=loongson2e -Wa,--trap +cflags-$(CONFIG_CPU_LOONGSON2F) += -march=loongson2f -Wa,--trap # Some -march= flags enable MMI instructions, and GCC complains about that # support being enabled alongside -msoft-float. Thus explicitly disable MMI. cflags-$(CONFIG_CPU_LOONGSON2EF) += $(call cc-option,-mno-loongson-mmi) +ifdef CONFIG_CPU_LOONGSON64 +cflags-$(CONFIG_CPU_LOONGSON64) += -Wa,--trap +cflags-$(CONFIG_CC_IS_GCC) += -march=loongson3a +cflags-$(CONFIG_CC_IS_CLANG) += -march=mips64r2 +endif cflags-$(CONFIG_CPU_LOONGSON64) += $(call cc-option,-mno-loongson-mmi) cflags-$(CONFIG_CPU_R4000_WORKAROUNDS) += $(call cc-option,-mfix-r4000,) @@ -299,8 +303,8 @@ ifdef CONFIG_64BIT endif endif - ifeq ($(KBUILD_SYM32)$(call cc-option-yn,-msym32), yy) - cflags-y += -msym32 -DKBUILD_64BIT_SYM32 + ifeq ($(KBUILD_SYM32), y) + cflags-$(KBUILD_SYM32) += -msym32 -DKBUILD_64BIT_SYM32 else ifeq ($(CONFIG_CPU_DADDI_WORKAROUNDS), y) $(error CONFIG_CPU_DADDI_WORKAROUNDS unsupported without -msym32) @@ -341,7 +345,7 @@ KBUILD_CFLAGS += -fno-asynchronous-unwind-tables KBUILD_LDFLAGS += -m $(ld-emul) -ifdef CONFIG_MIPS +ifdef need-compiler CHECKFLAGS += $(shell $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) -dM -E -x c /dev/null | \ grep -E -vw '__GNUC_(MINOR_|PATCHLEVEL_)?_' | \ sed -e "s/^\#define /-D'/" -e "s/ /'='/" -e "s/$$/'/" -e 's/\$$/&&/g') diff --git a/arch/mips/alchemy/devboards/db1000.c b/arch/mips/alchemy/devboards/db1000.c index 012da042d0a4f749a307f312ed46a2abdd2f10a6..7b9f91db227f2d4538dde233a5cc96306501c622 100644 --- a/arch/mips/alchemy/devboards/db1000.c +++ b/arch/mips/alchemy/devboards/db1000.c @@ -164,6 +164,7 @@ static struct platform_device db1x00_audio_dev = { /******************************************************************************/ +#ifdef CONFIG_MMC_AU1X static irqreturn_t db1100_mmc_cd(int irq, void *ptr) { mmc_detect_change(ptr, msecs_to_jiffies(500)); @@ -369,6 +370,7 @@ static struct platform_device db1100_mmc1_dev = { .num_resources = ARRAY_SIZE(au1100_mmc1_res), .resource = au1100_mmc1_res, }; +#endif /* CONFIG_MMC_AU1X */ /******************************************************************************/ @@ -440,8 +442,10 @@ static struct platform_device *db1x00_devs[] = { static struct platform_device *db1100_devs[] = { &au1100_lcd_device, +#ifdef CONFIG_MMC_AU1X &db1100_mmc0_dev, &db1100_mmc1_dev, +#endif }; int __init db1000_dev_setup(void) diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c index 76080c71a2a7b6c15cd32675f270ca1356a534e0..f521874ebb07b22495b01972ca92828491311779 100644 --- a/arch/mips/alchemy/devboards/db1200.c +++ b/arch/mips/alchemy/devboards/db1200.c @@ -326,6 +326,7 @@ static struct platform_device db1200_ide_dev = { /**********************************************************************/ +#ifdef CONFIG_MMC_AU1X /* SD carddetects: they're supposed to be edge-triggered, but ack * doesn't seem to work (CPLD Rev 2). Instead, the screaming one * is disabled and its counterpart enabled. The 200ms timeout is @@ -584,6 +585,7 @@ static struct platform_device pb1200_mmc1_dev = { .num_resources = ARRAY_SIZE(au1200_mmc1_res), .resource = au1200_mmc1_res, }; +#endif /* CONFIG_MMC_AU1X */ /**********************************************************************/ @@ -751,7 +753,9 @@ static struct platform_device db1200_audiodma_dev = { static struct platform_device *db1200_devs[] __initdata = { NULL, /* PSC0, selected by S6.8 */ &db1200_ide_dev, +#ifdef CONFIG_MMC_AU1X &db1200_mmc0_dev, +#endif &au1200_lcd_dev, &db1200_eth_dev, &db1200_nand_dev, @@ -762,7 +766,9 @@ static struct platform_device *db1200_devs[] __initdata = { }; static struct platform_device *pb1200_devs[] __initdata = { +#ifdef CONFIG_MMC_AU1X &pb1200_mmc1_dev, +#endif }; /* Some peripheral base addresses differ on the PB1200 */ diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c index ff61901329c62696cfdaf585944dd07d57e72707..d377e043b49f86539b88fd6f79c5395f1a42278d 100644 --- a/arch/mips/alchemy/devboards/db1300.c +++ b/arch/mips/alchemy/devboards/db1300.c @@ -450,6 +450,7 @@ static struct platform_device db1300_ide_dev = { /**********************************************************************/ +#ifdef CONFIG_MMC_AU1X static irqreturn_t db1300_mmc_cd(int irq, void *ptr) { disable_irq_nosync(irq); @@ -632,6 +633,7 @@ static struct platform_device db1300_sd0_dev = { .resource = au1300_sd0_res, .num_resources = ARRAY_SIZE(au1300_sd0_res), }; +#endif /* CONFIG_MMC_AU1X */ /**********************************************************************/ @@ -767,8 +769,10 @@ static struct platform_device *db1300_dev[] __initdata = { &db1300_5waysw_dev, &db1300_nand_dev, &db1300_ide_dev, +#ifdef CONFIG_MMC_AU1X &db1300_sd0_dev, &db1300_sd1_dev, +#endif &db1300_lcd_dev, &db1300_ac97_dev, &db1300_i2s_dev, diff --git a/arch/mips/bmips/setup.c b/arch/mips/bmips/setup.c index 053805cb741c3d789ffcba82d49e0c5ab74b3c1d..ec180ab92eaa836607381cac0d3beeabc2828923 100644 --- a/arch/mips/bmips/setup.c +++ b/arch/mips/bmips/setup.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/arch/mips/cavium-octeon/flash_setup.c b/arch/mips/cavium-octeon/flash_setup.c index c8a8c6d359b9ca00c07e8c4ffe02305c891fcf36..3395acde4d60accc3fc99193b79a9aeaa53505ff 100644 --- a/arch/mips/cavium-octeon/flash_setup.c +++ b/arch/mips/cavium-octeon/flash_setup.c @@ -12,7 +12,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/arch/mips/cavium-octeon/octeon-memcpy.S b/arch/mips/cavium-octeon/octeon-memcpy.S index 25860fba6218d888559b9a683c79a9fb975616a5..fef0c6de3fa107f13be619f2ee0cc6a752bb1100 100644 --- a/arch/mips/cavium-octeon/octeon-memcpy.S +++ b/arch/mips/cavium-octeon/octeon-memcpy.S @@ -13,9 +13,9 @@ * Mnemonic names for arguments to memcpy/__copy_user */ +#include #include #include -#include #include #define dst a0 diff --git a/arch/mips/cavium-octeon/octeon-platform.c b/arch/mips/cavium-octeon/octeon-platform.c index 235c77ce7b18ab3727593591879d9bea77dc3a40..f76783c24338b9a236eb3a3cea172c491ef51139 100644 --- a/arch/mips/cavium-octeon/octeon-platform.c +++ b/arch/mips/cavium-octeon/octeon-platform.c @@ -8,8 +8,10 @@ */ #include +#include #include #include +#include #include #include diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index 44821f497261342d61db0926c7bd09b3e607d83d..dc49b09d492b72893cad4ff7f23fcb3723a2ca0f 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -127,7 +127,6 @@ CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m diff --git a/arch/mips/configs/loongson3_defconfig b/arch/mips/configs/loongson3_defconfig index 2b4133176930703da771740a25953d3249d59d63..07839a4b397e5bcc006a68d06286a3d9d8d11875 100644 --- a/arch/mips/configs/loongson3_defconfig +++ b/arch/mips/configs/loongson3_defconfig @@ -283,6 +283,7 @@ CONFIG_DRM_AMDGPU_USERPTR=y CONFIG_DRM_AMD_ACP=y CONFIG_DRM_AMD_DC=y CONFIG_DRM_AMD_DC_SI=y +CONFIG_DRM_AST=m CONFIG_DRM_RADEON=m CONFIG_DRM_QXL=y CONFIG_DRM_VIRTIO_GPU=y diff --git a/arch/mips/configs/malta_defconfig b/arch/mips/configs/malta_defconfig index 7432090477920389cc6157c17c11453c8c3f837e..ae1a7793e810f8a5d9549ec2162293be1fbda3e4 100644 --- a/arch/mips/configs/malta_defconfig +++ b/arch/mips/configs/malta_defconfig @@ -127,7 +127,6 @@ CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m diff --git a/arch/mips/configs/malta_kvm_defconfig b/arch/mips/configs/malta_kvm_defconfig index dd2b9c181f32a0f69234efd5d2444c8b5a0f8652..c07e30f63d8bc6094935ec356529a66d6b137719 100644 --- a/arch/mips/configs/malta_kvm_defconfig +++ b/arch/mips/configs/malta_kvm_defconfig @@ -131,7 +131,6 @@ CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m diff --git a/arch/mips/configs/maltaup_xpa_defconfig b/arch/mips/configs/maltaup_xpa_defconfig index 97c2d7f530b39a3dd8eadce2767a3c919692d131..0a5701020d3f25b6afc11212b45699af25cd4ccf 100644 --- a/arch/mips/configs/maltaup_xpa_defconfig +++ b/arch/mips/configs/maltaup_xpa_defconfig @@ -128,7 +128,6 @@ CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m diff --git a/arch/mips/configs/rm200_defconfig b/arch/mips/configs/rm200_defconfig index e0e312dd968a2ed66995eb3a950b2a1dcabbf16f..5c5e2186210cfd4e6401094bd68b6e6d4ba9516b 100644 --- a/arch/mips/configs/rm200_defconfig +++ b/arch/mips/configs/rm200_defconfig @@ -90,7 +90,6 @@ CONFIG_IP_NF_MATCH_TTL=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_TTL=m CONFIG_IP_NF_RAW=m diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild index dee1727165817fbf17b153ad08db81a96bc2656c..7ba67a0d6c97b2879fb710aca05ae1e2d47c8ce2 100644 --- a/arch/mips/include/asm/Kbuild +++ b/arch/mips/include/asm/Kbuild @@ -7,7 +7,6 @@ generated-y += unistd_nr_n32.h generated-y += unistd_nr_n64.h generated-y += unistd_nr_o32.h -generic-y += export.h generic-y += kvm_para.h generic-y += mcs_spinlock.h generic-y += parport.h diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 04cedf9f88115e0f25a7b7731a725a1a64e72739..54a85f1d4f2c8d8822f25974c000d2d7aafc86c0 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -896,7 +896,6 @@ static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {} static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {} static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {} -#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLB -int kvm_arch_flush_remote_tlb(struct kvm *kvm); +#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS #endif /* __MIPS_KVM_HOST_H__ */ diff --git a/arch/mips/include/asm/mach-loongson32/loongson1.h b/arch/mips/include/asm/mach-loongson32/loongson1.h index 7971272345d31bbaed3e816d0c7e60f8300bc33f..84f45461c83204f2d96666a9165f14bd76c44f2f 100644 --- a/arch/mips/include/asm/mach-loongson32/loongson1.h +++ b/arch/mips/include/asm/mach-loongson32/loongson1.h @@ -45,8 +45,6 @@ #define LS1X_NAND_BASE 0x1fe78000 #define LS1X_CLK_BASE 0x1fe78030 -#include #include -#include #endif /* __ASM_MACH_LOONGSON32_LOONGSON1_H */ diff --git a/arch/mips/include/asm/mach-loongson32/regs-clk.h b/arch/mips/include/asm/mach-loongson32/regs-clk.h deleted file mode 100644 index 98136fa8bee1462377e9a1fc207c8c4d5f14cf9f..0000000000000000000000000000000000000000 --- a/arch/mips/include/asm/mach-loongson32/regs-clk.h +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2011 Zhang, Keguang - * - * Loongson 1 Clock Register Definitions. - */ - -#ifndef __ASM_MACH_LOONGSON32_REGS_CLK_H -#define __ASM_MACH_LOONGSON32_REGS_CLK_H - -#define LS1X_CLK_REG(x) \ - ((void __iomem *)KSEG1ADDR(LS1X_CLK_BASE + (x))) - -#define LS1X_CLK_PLL_FREQ LS1X_CLK_REG(0x0) -#define LS1X_CLK_PLL_DIV LS1X_CLK_REG(0x4) - -#if defined(CONFIG_LOONGSON1_LS1B) -/* Clock PLL Divisor Register Bits */ -#define DIV_DC_EN BIT(31) -#define DIV_DC_RST BIT(30) -#define DIV_CPU_EN BIT(25) -#define DIV_CPU_RST BIT(24) -#define DIV_DDR_EN BIT(19) -#define DIV_DDR_RST BIT(18) -#define RST_DC_EN BIT(5) -#define RST_DC BIT(4) -#define RST_DDR_EN BIT(3) -#define RST_DDR BIT(2) -#define RST_CPU_EN BIT(1) -#define RST_CPU BIT(0) - -#define DIV_DC_SHIFT 26 -#define DIV_CPU_SHIFT 20 -#define DIV_DDR_SHIFT 14 - -#define DIV_DC_WIDTH 4 -#define DIV_CPU_WIDTH 4 -#define DIV_DDR_WIDTH 4 - -#define BYPASS_DC_SHIFT 12 -#define BYPASS_DDR_SHIFT 10 -#define BYPASS_CPU_SHIFT 8 - -#define BYPASS_DC_WIDTH 1 -#define BYPASS_DDR_WIDTH 1 -#define BYPASS_CPU_WIDTH 1 - -#elif defined(CONFIG_LOONGSON1_LS1C) -/* PLL/SDRAM Frequency configuration register Bits */ -#define PLL_VALID BIT(31) -#define FRAC_N GENMASK(23, 16) -#define RST_TIME GENMASK(3, 2) -#define SDRAM_DIV GENMASK(1, 0) - -/* CPU/CAMERA/DC Frequency configuration register Bits */ -#define DIV_DC_EN BIT(31) -#define DIV_DC GENMASK(30, 24) -#define DIV_CAM_EN BIT(23) -#define DIV_CAM GENMASK(22, 16) -#define DIV_CPU_EN BIT(15) -#define DIV_CPU GENMASK(14, 8) -#define DIV_DC_SEL_EN BIT(5) -#define DIV_DC_SEL BIT(4) -#define DIV_CAM_SEL_EN BIT(3) -#define DIV_CAM_SEL BIT(2) -#define DIV_CPU_SEL_EN BIT(1) -#define DIV_CPU_SEL BIT(0) - -#define DIV_DC_SHIFT 24 -#define DIV_CAM_SHIFT 16 -#define DIV_CPU_SHIFT 8 -#define DIV_DDR_SHIFT 0 - -#define DIV_DC_WIDTH 7 -#define DIV_CAM_WIDTH 7 -#define DIV_CPU_WIDTH 7 -#define DIV_DDR_WIDTH 2 - -#endif - -#endif /* __ASM_MACH_LOONGSON32_REGS_CLK_H */ diff --git a/arch/mips/include/asm/mach-loongson32/regs-rtc.h b/arch/mips/include/asm/mach-loongson32/regs-rtc.h deleted file mode 100644 index a3d096be160770d9c8dcb99a87fd75434cd508d3..0000000000000000000000000000000000000000 --- a/arch/mips/include/asm/mach-loongson32/regs-rtc.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2016 Yang Ling - * - * Loongson 1 RTC timer Register Definitions. - */ - -#ifndef __ASM_MACH_LOONGSON32_REGS_RTC_H -#define __ASM_MACH_LOONGSON32_REGS_RTC_H - -#define LS1X_RTC_REG(x) \ - ((void __iomem *)KSEG1ADDR(LS1X_RTC_BASE + (x))) - -#define LS1X_RTC_CTRL LS1X_RTC_REG(0x40) - -#define RTC_EXTCLK_OK (BIT(5) | BIT(8)) -#define RTC_EXTCLK_EN BIT(8) - -#endif /* __ASM_MACH_LOONGSON32_REGS_RTC_H */ diff --git a/arch/mips/kernel/mcount.S b/arch/mips/kernel/mcount.S index cff52b283e03843519201ca8fe8754e0899c0c3c..fcec579f64e967f7c0405e8208182b0f5855d8b4 100644 --- a/arch/mips/kernel/mcount.S +++ b/arch/mips/kernel/mcount.S @@ -10,7 +10,7 @@ * Author: Wu Zhangjin */ -#include +#include #include #include #include diff --git a/arch/mips/kernel/octeon_switch.S b/arch/mips/kernel/octeon_switch.S index 9b7c8ab6f08c6ff611b8ec2da1a45d2a7935e33c..447a3ea14aa11e984919ffa82f7c860f8519db79 100644 --- a/arch/mips/kernel/octeon_switch.S +++ b/arch/mips/kernel/octeon_switch.S @@ -11,7 +11,6 @@ * written by Carsten Langgaard, carstenl@mips.com */ #include -#include #include #include #include diff --git a/arch/mips/kernel/r2300_fpu.S b/arch/mips/kernel/r2300_fpu.S index 6c745aa9e82598fdf5608039d1f5c81ad391f953..c000b22e3fcd182f54cf238d3557773e0f9f443d 100644 --- a/arch/mips/kernel/r2300_fpu.S +++ b/arch/mips/kernel/r2300_fpu.S @@ -11,10 +11,10 @@ * Further modifications to make this work: * Copyright (c) 1998 Harald Koerfgen */ +#include #include #include #include -#include #include #include #include diff --git a/arch/mips/kernel/r2300_switch.S b/arch/mips/kernel/r2300_switch.S index 71b1aafae1bb1c1a209e4722043b369a34dbfc3e..48e63943e6f769d98373621b410efb1f68948cb2 100644 --- a/arch/mips/kernel/r2300_switch.S +++ b/arch/mips/kernel/r2300_switch.S @@ -13,7 +13,6 @@ */ #include #include -#include #include #include #include diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S index 4e8c98517d9d4ebe44830e9c26f7f91ddf47857d..4bb97ee8990428f12fb4ff327cc8db251ab2db1c 100644 --- a/arch/mips/kernel/r4k_fpu.S +++ b/arch/mips/kernel/r4k_fpu.S @@ -12,10 +12,10 @@ * Copyright (C) 2000 MIPS Technologies, Inc. * Copyright (C) 1999, 2001 Silicon Graphics, Inc. */ +#include #include #include #include -#include #include #include #include diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c index aa5583a7b05be136ddb0ee3dac85f387ee41077a..231ac052b506b9a69768e2f06be969fabddd0e45 100644 --- a/arch/mips/kvm/mips.c +++ b/arch/mips/kvm/mips.c @@ -199,7 +199,7 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, /* Flush slot from GPA */ kvm_mips_flush_gpa_pt(kvm, slot->base_gfn, slot->base_gfn + slot->npages - 1); - kvm_arch_flush_remote_tlbs_memslot(kvm, slot); + kvm_flush_remote_tlbs_memslot(kvm, slot); spin_unlock(&kvm->mmu_lock); } @@ -235,7 +235,7 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, needs_flush = kvm_mips_mkclean_gpa_pt(kvm, new->base_gfn, new->base_gfn + new->npages - 1); if (needs_flush) - kvm_arch_flush_remote_tlbs_memslot(kvm, new); + kvm_flush_remote_tlbs_memslot(kvm, new); spin_unlock(&kvm->mmu_lock); } } @@ -981,18 +981,12 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) } -int kvm_arch_flush_remote_tlb(struct kvm *kvm) +int kvm_arch_flush_remote_tlbs(struct kvm *kvm) { kvm_mips_callbacks->prepare_flush_shadow(kvm); return 1; } -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, - const struct kvm_memory_slot *memslot) -{ - kvm_flush_remote_tlbs(kvm); -} - int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { int r; diff --git a/arch/mips/kvm/mmu.c b/arch/mips/kvm/mmu.c index e8c08988ed377dabebc0dbf89d343d77aced3a1d..7b2ac1319d70acafd68cfe6b30680abc7bd04234 100644 --- a/arch/mips/kvm/mmu.c +++ b/arch/mips/kvm/mmu.c @@ -447,7 +447,7 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { gpa_t gpa = range->start << PAGE_SHIFT; - pte_t hva_pte = range->pte; + pte_t hva_pte = range->arg.pte; pte_t *gpa_pte = kvm_mips_pte_for_gpa(kvm, NULL, gpa); pte_t old_pte; diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c index 20622bf0a9b3dea7fc9a85fc36abb38171e822fb..8f208007b8e84c7b89d678b9bacc5b8410fb93b0 100644 --- a/arch/mips/lantiq/irq.c +++ b/arch/mips/lantiq/irq.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/mips/lantiq/xway/dcdc.c b/arch/mips/lantiq/xway/dcdc.c index 96199966a350df18feab326f24ee988529198ce8..4a808f8c5bebf58189696250e429c7a4a5b87259 100644 --- a/arch/mips/lantiq/xway/dcdc.c +++ b/arch/mips/lantiq/xway/dcdc.c @@ -6,7 +6,8 @@ */ #include -#include +#include +#include #include diff --git a/arch/mips/lantiq/xway/gptu.c b/arch/mips/lantiq/xway/gptu.c index a492b1eb19250d82a164dd15785efa6021e33a85..8d52001301deeb5a36e7953cbe794fc41a7129ec 100644 --- a/arch/mips/lantiq/xway/gptu.c +++ b/arch/mips/lantiq/xway/gptu.c @@ -8,8 +8,9 @@ #include #include #include -#include +#include #include +#include #include #include "../clk.h" diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c index d444a1b98a724d07db257446daaf24a57ffe748c..3ed0782252229a60d7e72d19b74a0d13e1b04794 100644 --- a/arch/mips/lantiq/xway/sysctrl.c +++ b/arch/mips/lantiq/xway/sysctrl.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/arch/mips/lantiq/xway/vmmc.c b/arch/mips/lantiq/xway/vmmc.c index 2796e87dfcae31fbcebb60e218ae4bd6b5107d1f..37c133052ef75e0d89feb82e9f9f70dfc76d9b56 100644 --- a/arch/mips/lantiq/xway/vmmc.c +++ b/arch/mips/lantiq/xway/vmmc.c @@ -7,7 +7,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/arch/mips/lib/csum_partial.S b/arch/mips/lib/csum_partial.S index 7767137c3e49ad35690451002a9078704e190557..3d2ff4118d7990d2ced5a9b85103db11e4da35f6 100644 --- a/arch/mips/lib/csum_partial.S +++ b/arch/mips/lib/csum_partial.S @@ -11,9 +11,9 @@ * Copyright (C) 2014 Imagination Technologies Ltd. */ #include +#include #include #include -#include #include #ifdef CONFIG_64BIT diff --git a/arch/mips/lib/memcpy.S b/arch/mips/lib/memcpy.S index 18a43f2e29c81a797635608d07f3c458bffe2dd9..a4b4e805ff135b4009f0a232ca2f2dbd696e984b 100644 --- a/arch/mips/lib/memcpy.S +++ b/arch/mips/lib/memcpy.S @@ -32,9 +32,9 @@ #undef CONFIG_CPU_HAS_PREFETCH #endif +#include #include #include -#include #include #define dst a0 diff --git a/arch/mips/lib/memset.S b/arch/mips/lib/memset.S index 0b342bae9a98c13c63e06f8618288232885c2715..79405c32cc853397c1c46e1d97937309f56f12be 100644 --- a/arch/mips/lib/memset.S +++ b/arch/mips/lib/memset.S @@ -8,9 +8,9 @@ * Copyright (C) 2007 by Maciej W. Rozycki * Copyright (C) 2011, 2012 MIPS Technologies, Inc. */ +#include #include #include -#include #include #if LONGSIZE == 4 diff --git a/arch/mips/lib/strncpy_user.S b/arch/mips/lib/strncpy_user.S index 13aaa9927ad124957fbc2329229771c2641b1bd1..94f4203563c1c2a6c332857e27337c3fa340afcf 100644 --- a/arch/mips/lib/strncpy_user.S +++ b/arch/mips/lib/strncpy_user.S @@ -7,9 +7,9 @@ * Copyright (C) 2011 MIPS Technologies, Inc. */ #include +#include #include #include -#include #include #define EX(insn,reg,addr,handler) \ diff --git a/arch/mips/lib/strnlen_user.S b/arch/mips/lib/strnlen_user.S index 6de31b616f9c1a808ea26311c3e9d41abea71866..c192a6f6cd841afb34f47ac38be1c082bdd1f492 100644 --- a/arch/mips/lib/strnlen_user.S +++ b/arch/mips/lib/strnlen_user.S @@ -6,9 +6,9 @@ * Copyright (c) 1996, 1998, 1999, 2004 by Ralf Baechle * Copyright (c) 1999 Silicon Graphics, Inc. */ +#include #include #include -#include #include #define EX(insn,reg,addr,handler) \ diff --git a/arch/mips/loongson32/common/platform.c b/arch/mips/loongson32/common/platform.c index 64d7979394e63b0f22003ba1352a54fb0e21a222..8075590a9f8346d59873347e3d3bd2644280b6a5 100644 --- a/arch/mips/loongson32/common/platform.c +++ b/arch/mips/loongson32/common/platform.c @@ -265,14 +265,6 @@ struct platform_device ls1x_ehci_pdev = { }; /* Real Time Clock */ -void __init ls1x_rtc_set_extclk(struct platform_device *pdev) -{ - u32 val = __raw_readl(LS1X_RTC_CTRL); - - if (!(val & RTC_EXTCLK_OK)) - __raw_writel(val | RTC_EXTCLK_EN, LS1X_RTC_CTRL); -} - struct platform_device ls1x_rtc_pdev = { .name = "ls1x-rtc", .id = -1, diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index cdecd7af11a6a0d8fe87f2c15c6e917148a5e97a..e015a26a40f7a55388da15033ad0b2ff08cf4777 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -187,181 +187,181 @@ static void csr_ipi_probe(void) static void ipi_set0_regs_init(void) { - ipi_set0_regs[0] = (void *) + ipi_set0_regs[0] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0); - ipi_set0_regs[1] = (void *) + ipi_set0_regs[1] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0); - ipi_set0_regs[2] = (void *) + ipi_set0_regs[2] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0); - ipi_set0_regs[3] = (void *) + ipi_set0_regs[3] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0); - ipi_set0_regs[4] = (void *) + ipi_set0_regs[4] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0); - ipi_set0_regs[5] = (void *) + ipi_set0_regs[5] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0); - ipi_set0_regs[6] = (void *) + ipi_set0_regs[6] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0); - ipi_set0_regs[7] = (void *) + ipi_set0_regs[7] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0); - ipi_set0_regs[8] = (void *) + ipi_set0_regs[8] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0); - ipi_set0_regs[9] = (void *) + ipi_set0_regs[9] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0); - ipi_set0_regs[10] = (void *) + ipi_set0_regs[10] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0); - ipi_set0_regs[11] = (void *) + ipi_set0_regs[11] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0); - ipi_set0_regs[12] = (void *) + ipi_set0_regs[12] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0); - ipi_set0_regs[13] = (void *) + ipi_set0_regs[13] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0); - ipi_set0_regs[14] = (void *) + ipi_set0_regs[14] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0); - ipi_set0_regs[15] = (void *) + ipi_set0_regs[15] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0); } static void ipi_clear0_regs_init(void) { - ipi_clear0_regs[0] = (void *) + ipi_clear0_regs[0] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0); - ipi_clear0_regs[1] = (void *) + ipi_clear0_regs[1] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0); - ipi_clear0_regs[2] = (void *) + ipi_clear0_regs[2] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0); - ipi_clear0_regs[3] = (void *) + ipi_clear0_regs[3] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0); - ipi_clear0_regs[4] = (void *) + ipi_clear0_regs[4] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0); - ipi_clear0_regs[5] = (void *) + ipi_clear0_regs[5] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0); - ipi_clear0_regs[6] = (void *) + ipi_clear0_regs[6] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0); - ipi_clear0_regs[7] = (void *) + ipi_clear0_regs[7] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0); - ipi_clear0_regs[8] = (void *) + ipi_clear0_regs[8] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0); - ipi_clear0_regs[9] = (void *) + ipi_clear0_regs[9] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0); - ipi_clear0_regs[10] = (void *) + ipi_clear0_regs[10] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0); - ipi_clear0_regs[11] = (void *) + ipi_clear0_regs[11] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0); - ipi_clear0_regs[12] = (void *) + ipi_clear0_regs[12] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0); - ipi_clear0_regs[13] = (void *) + ipi_clear0_regs[13] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0); - ipi_clear0_regs[14] = (void *) + ipi_clear0_regs[14] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0); - ipi_clear0_regs[15] = (void *) + ipi_clear0_regs[15] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0); } static void ipi_status0_regs_init(void) { - ipi_status0_regs[0] = (void *) + ipi_status0_regs[0] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0); - ipi_status0_regs[1] = (void *) + ipi_status0_regs[1] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0); - ipi_status0_regs[2] = (void *) + ipi_status0_regs[2] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0); - ipi_status0_regs[3] = (void *) + ipi_status0_regs[3] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0); - ipi_status0_regs[4] = (void *) + ipi_status0_regs[4] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0); - ipi_status0_regs[5] = (void *) + ipi_status0_regs[5] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0); - ipi_status0_regs[6] = (void *) + ipi_status0_regs[6] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0); - ipi_status0_regs[7] = (void *) + ipi_status0_regs[7] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0); - ipi_status0_regs[8] = (void *) + ipi_status0_regs[8] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0); - ipi_status0_regs[9] = (void *) + ipi_status0_regs[9] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0); - ipi_status0_regs[10] = (void *) + ipi_status0_regs[10] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0); - ipi_status0_regs[11] = (void *) + ipi_status0_regs[11] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0); - ipi_status0_regs[12] = (void *) + ipi_status0_regs[12] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0); - ipi_status0_regs[13] = (void *) + ipi_status0_regs[13] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0); - ipi_status0_regs[14] = (void *) + ipi_status0_regs[14] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0); - ipi_status0_regs[15] = (void *) + ipi_status0_regs[15] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0); } static void ipi_en0_regs_init(void) { - ipi_en0_regs[0] = (void *) + ipi_en0_regs[0] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0); - ipi_en0_regs[1] = (void *) + ipi_en0_regs[1] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0); - ipi_en0_regs[2] = (void *) + ipi_en0_regs[2] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0); - ipi_en0_regs[3] = (void *) + ipi_en0_regs[3] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0); - ipi_en0_regs[4] = (void *) + ipi_en0_regs[4] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0); - ipi_en0_regs[5] = (void *) + ipi_en0_regs[5] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0); - ipi_en0_regs[6] = (void *) + ipi_en0_regs[6] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0); - ipi_en0_regs[7] = (void *) + ipi_en0_regs[7] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0); - ipi_en0_regs[8] = (void *) + ipi_en0_regs[8] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0); - ipi_en0_regs[9] = (void *) + ipi_en0_regs[9] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0); - ipi_en0_regs[10] = (void *) + ipi_en0_regs[10] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0); - ipi_en0_regs[11] = (void *) + ipi_en0_regs[11] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0); - ipi_en0_regs[12] = (void *) + ipi_en0_regs[12] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0); - ipi_en0_regs[13] = (void *) + ipi_en0_regs[13] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0); - ipi_en0_regs[14] = (void *) + ipi_en0_regs[14] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0); - ipi_en0_regs[15] = (void *) + ipi_en0_regs[15] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0); } static void ipi_mailbox_buf_init(void) { - ipi_mailbox_buf[0] = (void *) + ipi_mailbox_buf[0] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF); - ipi_mailbox_buf[1] = (void *) + ipi_mailbox_buf[1] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF); - ipi_mailbox_buf[2] = (void *) + ipi_mailbox_buf[2] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF); - ipi_mailbox_buf[3] = (void *) + ipi_mailbox_buf[3] = (void __iomem *) (SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF); - ipi_mailbox_buf[4] = (void *) + ipi_mailbox_buf[4] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF); - ipi_mailbox_buf[5] = (void *) + ipi_mailbox_buf[5] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF); - ipi_mailbox_buf[6] = (void *) + ipi_mailbox_buf[6] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF); - ipi_mailbox_buf[7] = (void *) + ipi_mailbox_buf[7] = (void __iomem *) (SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF); - ipi_mailbox_buf[8] = (void *) + ipi_mailbox_buf[8] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF); - ipi_mailbox_buf[9] = (void *) + ipi_mailbox_buf[9] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF); - ipi_mailbox_buf[10] = (void *) + ipi_mailbox_buf[10] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF); - ipi_mailbox_buf[11] = (void *) + ipi_mailbox_buf[11] = (void __iomem *) (SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF); - ipi_mailbox_buf[12] = (void *) + ipi_mailbox_buf[12] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF); - ipi_mailbox_buf[13] = (void *) + ipi_mailbox_buf[13] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF); - ipi_mailbox_buf[14] = (void *) + ipi_mailbox_buf[14] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF); - ipi_mailbox_buf[15] = (void *) + ipi_mailbox_buf[15] = (void __iomem *) (SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF); } diff --git a/arch/mips/mm/page-funcs.S b/arch/mips/mm/page-funcs.S index 43181ac0a1afc6ccf4e7e2d972979da7fce79a0e..42d0516ca18a6979460c2aeae1a25d97eefbc266 100644 --- a/arch/mips/mm/page-funcs.S +++ b/arch/mips/mm/page-funcs.S @@ -8,8 +8,8 @@ * Copyright (C) 2012 MIPS Technologies, Inc. * Copyright (C) 2012 Ralf Baechle */ +#include #include -#include #include #ifdef CONFIG_SIBYTE_DMA_PAGEOPS diff --git a/arch/mips/mm/tlb-funcs.S b/arch/mips/mm/tlb-funcs.S index 00fef578c8cdcfbc1359e476255d81f5c6cd4363..2705d7dcb33eac5dc9ad1f578a7b215171580778 100644 --- a/arch/mips/mm/tlb-funcs.S +++ b/arch/mips/mm/tlb-funcs.S @@ -11,8 +11,8 @@ * Copyright (C) 2012 MIPS Technologies, Inc. * Copyright (C) 2012 Ralf Baechle */ +#include #include -#include #include #define FASTPATH_SIZE 128 diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c index 79e29bf42a24129d72cbdfd188b7bdc7de4e3b33..80f7293166bb76dee8ec12be4e328a912ced2d7e 100644 --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -13,9 +13,9 @@ #include #include #include -#include -#include +#include #include +#include #include diff --git a/arch/mips/pci/pci-rt2880.c b/arch/mips/pci/pci-rt2880.c index e9dd01431f212beccb80a5afc63867cbb8080952..1cada09fa5db081a9fb754e7b4ffefeb0a221238 100644 --- a/arch/mips/pci/pci-rt2880.c +++ b/arch/mips/pci/pci-rt2880.c @@ -13,9 +13,8 @@ #include #include #include -#include -#include -#include +#include +#include #include diff --git a/arch/mips/pic32/pic32mzda/config.c b/arch/mips/pic32/pic32mzda/config.c index f6953200771746f510c957ba72eedd31ac54fca4..73be5689e0dfb8b18c0a44ee860ca2786201471e 100644 --- a/arch/mips/pic32/pic32mzda/config.c +++ b/arch/mips/pic32/pic32mzda/config.c @@ -5,7 +5,7 @@ */ #include #include -#include +#include #include diff --git a/arch/mips/ralink/ill_acc.c b/arch/mips/ralink/ill_acc.c index f395ae218470f9dc8d8e0316b3561ab1f7bbea5e..25341b2319d0fa3739cd846d7052e279a5acad44 100644 --- a/arch/mips/ralink/ill_acc.c +++ b/arch/mips/ralink/ill_acc.c @@ -5,8 +5,10 @@ */ #include +#include #include #include +#include #include diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c index fa353bc1394746658ba1e126815218b300e3bfb2..46aef0a1b22acd3b58c2bd51c3b082774d2e4056 100644 --- a/arch/mips/ralink/irq.c +++ b/arch/mips/ralink/irq.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c index 45d60c094496f15318847d0ab9b64deba870056b..7f90068c68f22fcd27e8b442f6a92529f4815d78 100644 --- a/arch/mips/ralink/of.c +++ b/arch/mips/ralink/of.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c index aaac1e6ec7d9a7c61f885ae3b02286530d4c40ee..c3b96861844c6d2e24af23e82c89d0af9cb17bfb 100644 --- a/arch/mips/ralink/prom.c +++ b/arch/mips/ralink/prom.c @@ -7,8 +7,6 @@ */ #include -#include -#include #include #include diff --git a/arch/mips/txx9/generic/pci.c b/arch/mips/txx9/generic/pci.c index e98845543b772fa0090a1755d57fe358abbe74b3..5ae30b78d38d3bd5e25cadad8169b99364c45a36 100644 --- a/arch/mips/txx9/generic/pci.c +++ b/arch/mips/txx9/generic/pci.c @@ -51,6 +51,7 @@ int __init txx9_pci66_check(struct pci_controller *hose, int top_bus, unsigned short vid; int cap66 = -1; u16 stat; + int ret; /* It seems SLC90E66 needs some time after PCI reset... */ mdelay(80); @@ -60,9 +61,9 @@ int __init txx9_pci66_check(struct pci_controller *hose, int top_bus, for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { if (PCI_FUNC(pci_devfn)) continue; - if (early_read_config_word(hose, top_bus, current_bus, - pci_devfn, PCI_VENDOR_ID, &vid) != - PCIBIOS_SUCCESSFUL) + ret = early_read_config_word(hose, top_bus, current_bus, + pci_devfn, PCI_VENDOR_ID, &vid); + if (ret != PCIBIOS_SUCCESSFUL) continue; if (vid == 0xffff) continue; @@ -343,26 +344,28 @@ static void tc35815_fixup(struct pci_dev *dev) static void final_fixup(struct pci_dev *dev) { + unsigned long timeout; unsigned char bist; + int ret; /* Do build-in self test */ - if (pci_read_config_byte(dev, PCI_BIST, &bist) == PCIBIOS_SUCCESSFUL && - (bist & PCI_BIST_CAPABLE)) { - unsigned long timeout; - pci_set_power_state(dev, PCI_D0); - pr_info("PCI: %s BIST...", pci_name(dev)); - pci_write_config_byte(dev, PCI_BIST, PCI_BIST_START); - timeout = jiffies + HZ * 2; /* timeout after 2 sec */ - do { - pci_read_config_byte(dev, PCI_BIST, &bist); - if (time_after(jiffies, timeout)) - break; - } while (bist & PCI_BIST_START); - if (bist & (PCI_BIST_CODE_MASK | PCI_BIST_START)) - pr_cont("failed. (0x%x)\n", bist); - else - pr_cont("OK.\n"); - } + ret = pci_read_config_byte(dev, PCI_BIST, &bist); + if ((ret != PCIBIOS_SUCCESSFUL) || !(bist & PCI_BIST_CAPABLE)) + return; + + pci_set_power_state(dev, PCI_D0); + pr_info("PCI: %s BIST...", pci_name(dev)); + pci_write_config_byte(dev, PCI_BIST, PCI_BIST_START); + timeout = jiffies + HZ * 2; /* timeout after 2 sec */ + do { + pci_read_config_byte(dev, PCI_BIST, &bist); + if (time_after(jiffies, timeout)) + break; + } while (bist & PCI_BIST_START); + if (bist & (PCI_BIST_CODE_MASK | PCI_BIST_START)) + pr_cont("failed. (0x%x)\n", bist); + else + pr_cont("OK.\n"); } #ifdef CONFIG_TOSHIBA_FPCIB0 diff --git a/arch/mips/vdso/vdso.lds.S b/arch/mips/vdso/vdso.lds.S index d90b65724d78e1c23f7ae78014d85505327f34d4..836465e3bcb8a15cb3f0f96d852d2b252c9d5e2a 100644 --- a/arch/mips/vdso/vdso.lds.S +++ b/arch/mips/vdso/vdso.lds.S @@ -94,7 +94,9 @@ VERSION #ifndef CONFIG_MIPS_DISABLE_VDSO global: __vdso_clock_gettime; +#ifdef CONFIG_MIPS_CLOCK_VSYSCALL __vdso_gettimeofday; +#endif __vdso_clock_getres; #if _MIPS_SIM != _MIPS_SIM_ABI64 __vdso_clock_gettime64; diff --git a/arch/openrisc/include/asm/bug.h b/arch/openrisc/include/asm/bug.h new file mode 100644 index 0000000000000000000000000000000000000000..6d04776eaf10afa439b13b7f717cdca448e46784 --- /dev/null +++ b/arch/openrisc/include/asm/bug.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_OPENRISC_BUG_H +#define __ASM_OPENRISC_BUG_H + +#include + +struct pt_regs; + +void __noreturn die(const char *str, struct pt_regs *regs, long err); + +#endif /* __ASM_OPENRISC_BUG_H */ diff --git a/arch/openrisc/include/asm/page.h b/arch/openrisc/include/asm/page.h index 52b0d7e7644618bf16eec630538f696152688219..44fc1fd5671726c4fcc8be3c90d754f7f4e74f8d 100644 --- a/arch/openrisc/include/asm/page.h +++ b/arch/openrisc/include/asm/page.h @@ -72,8 +72,15 @@ typedef struct page *pgtable_t; #define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET)) #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET) -#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) -#define pfn_to_virt(pfn) __va((pfn) << PAGE_SHIFT) +static inline unsigned long virt_to_pfn(const void *kaddr) +{ + return __pa(kaddr) >> PAGE_SHIFT; +} + +static inline void * pfn_to_virt(unsigned long pfn) +{ + return (void *)((unsigned long)__va(pfn) << PAGE_SHIFT); +} #define virt_to_page(addr) \ (mem_map + (((unsigned long)(addr)-PAGE_OFFSET) >> PAGE_SHIFT)) diff --git a/arch/openrisc/include/asm/processor.h b/arch/openrisc/include/asm/processor.h index ed9efb430afa17c9b655ef84789ebd7ee7b47aa2..3b736e74e6eddca9cce901b3fd7d9df1ebc05869 100644 --- a/arch/openrisc/include/asm/processor.h +++ b/arch/openrisc/include/asm/processor.h @@ -73,6 +73,7 @@ struct thread_struct { void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp); unsigned long __get_wchan(struct task_struct *p); +void show_registers(struct pt_regs *regs); #define cpu_relax() barrier() diff --git a/arch/openrisc/kernel/process.c b/arch/openrisc/kernel/process.c index dfa558f98ed8c010c11d9b9108f6158800c27013..86e02929f3ace4c0be107523988b6394f3a61ce3 100644 --- a/arch/openrisc/kernel/process.c +++ b/arch/openrisc/kernel/process.c @@ -14,6 +14,7 @@ */ #define __KERNEL_SYSCALLS__ +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include @@ -119,8 +121,6 @@ void flush_thread(void) void show_regs(struct pt_regs *regs) { - extern void show_registers(struct pt_regs *regs); - show_regs_print_info(KERN_DEFAULT); /* __PHX__ cleanup this mess */ show_registers(regs); diff --git a/arch/openrisc/kernel/ptrace.c b/arch/openrisc/kernel/ptrace.c index 0b7d2ca6ba3b92b48ea700c1ceaa016a0bf02689..1eeac3b62e9d3ab075011c6b95b9563a373b8c79 100644 --- a/arch/openrisc/kernel/ptrace.c +++ b/arch/openrisc/kernel/ptrace.c @@ -27,6 +27,10 @@ #include #include +asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); + +asmlinkage void do_syscall_trace_leave(struct pt_regs *regs); + /* * Copy the thread state to a regset that can be interpreted by userspace. * diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c index 2e7257a433ff4f94a58c1562614b7e1e63b22f2c..e2f21a5d8ad9a9b4b5bcc3fa7a555f7841c4d2e7 100644 --- a/arch/openrisc/kernel/signal.c +++ b/arch/openrisc/kernel/signal.c @@ -34,6 +34,11 @@ struct rt_sigframe { unsigned char retcode[16]; /* trampoline code */ }; +asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs); + +asmlinkage int do_work_pending(struct pt_regs *regs, unsigned int thread_flags, + int syscall); + static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { @@ -224,7 +229,7 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs) * mode below. */ -int do_signal(struct pt_regs *regs, int syscall) +static int do_signal(struct pt_regs *regs, int syscall) { struct ksignal ksig; unsigned long continue_addr = 0; diff --git a/arch/openrisc/kernel/smp.c b/arch/openrisc/kernel/smp.c index 0a7a059e2dff4d67e69fc6fa63d9469a51f53e13..1c5a2d71d6753e8a02a8903712c163802e56891c 100644 --- a/arch/openrisc/kernel/smp.c +++ b/arch/openrisc/kernel/smp.c @@ -23,6 +23,8 @@ #include #include +asmlinkage __init void secondary_start_kernel(void); + static void (*smp_cross_call)(const struct cpumask *, unsigned int); unsigned long secondary_release = -1; diff --git a/arch/openrisc/kernel/time.c b/arch/openrisc/kernel/time.c index 8e26c1af544191857c830f35e07b574c59a307fd..764c7bfb5df34b1f9a59c839703f0aa019c48e53 100644 --- a/arch/openrisc/kernel/time.c +++ b/arch/openrisc/kernel/time.c @@ -25,6 +25,8 @@ #include #include +irqreturn_t __irq_entry timer_interrupt(struct pt_regs *regs); + /* Test the timer ticks to count, used in sync routine */ inline void openrisc_timer_set(unsigned long count) { diff --git a/arch/openrisc/kernel/traps.c b/arch/openrisc/kernel/traps.c index 0aa6b07efda1b34cc2de4c2d181f001019be2d92..9370888c9a7e310c07fe829769dd41d3c0cdeafb 100644 --- a/arch/openrisc/kernel/traps.c +++ b/arch/openrisc/kernel/traps.c @@ -30,14 +30,23 @@ #include #include +#include #include +#include #include #include -static int kstack_depth_to_print = 0x180; int lwa_flag; static unsigned long __user *lwa_addr; +asmlinkage void unhandled_exception(struct pt_regs *regs, int ea, int vector); +asmlinkage void do_trap(struct pt_regs *regs, unsigned long address); +asmlinkage void do_fpe_trap(struct pt_regs *regs, unsigned long address); +asmlinkage void do_unaligned_access(struct pt_regs *regs, unsigned long address); +asmlinkage void do_bus_fault(struct pt_regs *regs, unsigned long address); +asmlinkage void do_illegal_instruction(struct pt_regs *regs, + unsigned long address); + static void print_trace(void *data, unsigned long addr, int reliable) { const char *loglvl = data; @@ -143,80 +152,6 @@ void show_registers(struct pt_regs *regs) printk("\n"); } -void nommu_dump_state(struct pt_regs *regs, - unsigned long ea, unsigned long vector) -{ - int i; - unsigned long addr, stack = regs->sp; - - printk("\n\r[nommu_dump_state] :: ea %lx, vector %lx\n\r", ea, vector); - - printk("CPU #: %d\n" - " PC: %08lx SR: %08lx SP: %08lx\n", - 0, regs->pc, regs->sr, regs->sp); - printk("GPR00: %08lx GPR01: %08lx GPR02: %08lx GPR03: %08lx\n", - 0L, regs->gpr[1], regs->gpr[2], regs->gpr[3]); - printk("GPR04: %08lx GPR05: %08lx GPR06: %08lx GPR07: %08lx\n", - regs->gpr[4], regs->gpr[5], regs->gpr[6], regs->gpr[7]); - printk("GPR08: %08lx GPR09: %08lx GPR10: %08lx GPR11: %08lx\n", - regs->gpr[8], regs->gpr[9], regs->gpr[10], regs->gpr[11]); - printk("GPR12: %08lx GPR13: %08lx GPR14: %08lx GPR15: %08lx\n", - regs->gpr[12], regs->gpr[13], regs->gpr[14], regs->gpr[15]); - printk("GPR16: %08lx GPR17: %08lx GPR18: %08lx GPR19: %08lx\n", - regs->gpr[16], regs->gpr[17], regs->gpr[18], regs->gpr[19]); - printk("GPR20: %08lx GPR21: %08lx GPR22: %08lx GPR23: %08lx\n", - regs->gpr[20], regs->gpr[21], regs->gpr[22], regs->gpr[23]); - printk("GPR24: %08lx GPR25: %08lx GPR26: %08lx GPR27: %08lx\n", - regs->gpr[24], regs->gpr[25], regs->gpr[26], regs->gpr[27]); - printk("GPR28: %08lx GPR29: %08lx GPR30: %08lx GPR31: %08lx\n", - regs->gpr[28], regs->gpr[29], regs->gpr[30], regs->gpr[31]); - printk(" RES: %08lx oGPR11: %08lx\n", - regs->gpr[11], regs->orig_gpr11); - - printk("Process %s (pid: %d, stackpage=%08lx)\n", - ((struct task_struct *)(__pa(current)))->comm, - ((struct task_struct *)(__pa(current)))->pid, - (unsigned long)current); - - printk("\nStack: "); - printk("Stack dump [0x%08lx]:\n", (unsigned long)stack); - for (i = 0; i < kstack_depth_to_print; i++) { - if (((long)stack & (THREAD_SIZE - 1)) == 0) - break; - stack++; - - printk("%lx :: sp + %02d: 0x%08lx\n", stack, i * 4, - *((unsigned long *)(__pa(stack)))); - } - printk("\n"); - - printk("Call Trace: "); - i = 1; - while (((long)stack & (THREAD_SIZE - 1)) != 0) { - addr = *((unsigned long *)__pa(stack)); - stack++; - - if (kernel_text_address(addr)) { - if (i && ((i % 6) == 0)) - printk("\n "); - printk(" [<%08lx>]", addr); - i++; - } - } - printk("\n"); - - printk("\nCode: "); - - for (i = -24; i < 24; i++) { - unsigned long word; - - word = ((unsigned long *)(__pa(regs->pc)))[i]; - - print_data(regs->pc, word, i); - } - printk("\n"); -} - /* This is normally the 'Oops' routine */ void __noreturn die(const char *str, struct pt_regs *regs, long err) { diff --git a/arch/openrisc/mm/fault.c b/arch/openrisc/mm/fault.c index a9dcd4381d1a11a7c62fa72ee358fb1cf4c5e852..29e232d78d8285f370b5d2faaa77aa061af49e66 100644 --- a/arch/openrisc/mm/fault.c +++ b/arch/openrisc/mm/fault.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -30,7 +31,8 @@ */ volatile pgd_t *current_pgd[NR_CPUS]; -extern void __noreturn die(char *, struct pt_regs *, long); +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long vector, int write_acc); /* * This routine handles page faults. It determines the address, diff --git a/arch/openrisc/mm/init.c b/arch/openrisc/mm/init.c index d531ab82be128da382da99ff8f3dbb4855647938..1dcd78c8f0e99b89a66f00eb476f0b1f9057d40a 100644 --- a/arch/openrisc/mm/init.c +++ b/arch/openrisc/mm/init.c @@ -123,8 +123,6 @@ static void __init map_ram(void) void __init paging_init(void) { - extern void tlb_init(void); - int i; printk(KERN_INFO "Setting up paging and PTEs.\n"); diff --git a/arch/openrisc/mm/ioremap.c b/arch/openrisc/mm/ioremap.c index 91c8259d4b7ed63e783f70768a4a1465048afd5c..f59ea4c10b0f81f2c53015bfe0205e78268ea831 100644 --- a/arch/openrisc/mm/ioremap.c +++ b/arch/openrisc/mm/ioremap.c @@ -22,7 +22,7 @@ extern int mem_init_done; -/** +/* * OK, this one's a bit tricky... ioremap can get called before memory is * initialized (early serial console does this) and will want to alloc a page * for its mapping. No userspace pages will ever get allocated before memory diff --git a/arch/openrisc/mm/tlb.c b/arch/openrisc/mm/tlb.c index e2f2a3c3bb224a612f8473c1b340ada4f7cd27d2..3115f2e4f864f8ff78b1fefafa408232effcae98 100644 --- a/arch/openrisc/mm/tlb.c +++ b/arch/openrisc/mm/tlb.c @@ -182,12 +182,3 @@ void destroy_context(struct mm_struct *mm) flush_tlb_mm(mm); } - -/* called once during VM initialization, from init.c */ - -void __init tlb_init(void) -{ - /* Do nothing... */ - /* invalidate the entire TLB */ - /* flush_tlb_all(); */ -} diff --git a/arch/parisc/include/asm/cache.h b/arch/parisc/include/asm/cache.h index e23d06b51a2044016cab18b1704953fb49641826..2a60d7a72f1fa80b96b058eced6c33895693b9b2 100644 --- a/arch/parisc/include/asm/cache.h +++ b/arch/parisc/include/asm/cache.h @@ -37,6 +37,7 @@ extern int split_tlb; extern int dcache_stride; extern int icache_stride; extern struct pdc_cache_info cache_info; +extern struct pdc_btlb_info btlb_info; void parisc_setup_cache_timing(void); #define pdtlb(sr, addr) asm volatile("pdtlb 0(%%sr%0,%1)" \ diff --git a/arch/parisc/include/asm/hugetlb.h b/arch/parisc/include/asm/hugetlb.h index f7f078c2872c439307783fd81961e1b2faf577da..72daacc472a0a305772d7473aa8601a5316fbbe4 100644 --- a/arch/parisc/include/asm/hugetlb.h +++ b/arch/parisc/include/asm/hugetlb.h @@ -6,7 +6,7 @@ #define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pte); + pte_t *ptep, pte_t pte, unsigned long sz); #define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, diff --git a/arch/parisc/include/asm/ide.h b/arch/parisc/include/asm/ide.h deleted file mode 100644 index 7aa75b93a1b6c76dc7aa1a813109eb6eac6ecfd3..0000000000000000000000000000000000000000 --- a/arch/parisc/include/asm/ide.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * linux/include/asm-parisc/ide.h - * - * Copyright (C) 1994-1996 Linus Torvalds & authors - */ - -/* - * This file contains the PARISC architecture specific IDE code. - */ - -#ifndef __ASM_PARISC_IDE_H -#define __ASM_PARISC_IDE_H - -/* Generic I/O and MEMIO string operations. */ - -#define __ide_insw insw -#define __ide_insl insl -#define __ide_outsw outsw -#define __ide_outsl outsl - -static __inline__ void __ide_mm_insw(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - *(u16 *)addr = __raw_readw(port); - addr += 2; - } -} - -static __inline__ void __ide_mm_insl(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - *(u32 *)addr = __raw_readl(port); - addr += 4; - } -} - -static __inline__ void __ide_mm_outsw(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - __raw_writew(*(u16 *)addr, port); - addr += 2; - } -} - -static __inline__ void __ide_mm_outsl(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - __raw_writel(*(u32 *)addr, port); - addr += 4; - } -} - -#endif /* __ASM_PARISC_IDE_H */ diff --git a/arch/parisc/include/asm/ldcw.h b/arch/parisc/include/asm/ldcw.h index 6d28b5514699afd2b537fdd10156dc961056f0a8..ee9e071859b2f44c2f4138436204a44a909ff7c0 100644 --- a/arch/parisc/include/asm/ldcw.h +++ b/arch/parisc/include/asm/ldcw.h @@ -2,39 +2,42 @@ #ifndef __PARISC_LDCW_H #define __PARISC_LDCW_H -#ifndef CONFIG_PA20 /* Because kmalloc only guarantees 8-byte alignment for kmalloc'd data, and GCC only guarantees 8-byte alignment for stack locals, we can't be assured of 16-byte alignment for atomic lock data even if we specify "__attribute ((aligned(16)))" in the type declaration. So, we use a struct containing an array of four ints for the atomic lock type and dynamically select the 16-byte aligned int from the array - for the semaphore. */ + for the semaphore. */ + +/* From: "Jim Hull" + I've attached a summary of the change, but basically, for PA 2.0, as + long as the ",CO" (coherent operation) completer is implemented, then the + 16-byte alignment requirement for ldcw and ldcd is relaxed, and instead + they only require "natural" alignment (4-byte for ldcw, 8-byte for + ldcd). + + Although the cache control hint is accepted by all PA 2.0 processors, + it is only implemented on PA8800/PA8900 CPUs. Prior PA8X00 CPUs still + require 16-byte alignment. If the address is unaligned, the operation + of the instruction is undefined. The ldcw instruction does not generate + unaligned data reference traps so misaligned accesses are not detected. + This hid the problem for years. So, restore the 16-byte alignment dropped + by Kyle McMartin in "Remove __ldcw_align for PA-RISC 2.0 processors". */ #define __PA_LDCW_ALIGNMENT 16 -#define __PA_LDCW_ALIGN_ORDER 4 #define __ldcw_align(a) ({ \ unsigned long __ret = (unsigned long) &(a)->lock[0]; \ __ret = (__ret + __PA_LDCW_ALIGNMENT - 1) \ & ~(__PA_LDCW_ALIGNMENT - 1); \ (volatile unsigned int *) __ret; \ }) -#define __LDCW "ldcw" -#else /*CONFIG_PA20*/ -/* From: "Jim Hull" - I've attached a summary of the change, but basically, for PA 2.0, as - long as the ",CO" (coherent operation) completer is specified, then the - 16-byte alignment requirement for ldcw and ldcd is relaxed, and instead - they only require "natural" alignment (4-byte for ldcw, 8-byte for - ldcd). */ - -#define __PA_LDCW_ALIGNMENT 4 -#define __PA_LDCW_ALIGN_ORDER 2 -#define __ldcw_align(a) (&(a)->slock) +#ifdef CONFIG_PA20 #define __LDCW "ldcw,co" - -#endif /*!CONFIG_PA20*/ +#else +#define __LDCW "ldcw" +#endif /* LDCW, the only atomic read-write operation PA-RISC has. *sigh*. We don't explicitly expose that "*a" may be written as reload diff --git a/arch/parisc/include/asm/mckinley.h b/arch/parisc/include/asm/mckinley.h deleted file mode 100644 index 1314390b9034b3cf93290989ab5fa142634d5b38..0000000000000000000000000000000000000000 --- a/arch/parisc/include/asm/mckinley.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ASM_PARISC_MCKINLEY_H -#define ASM_PARISC_MCKINLEY_H - -/* declared in arch/parisc/kernel/setup.c */ -extern struct proc_dir_entry * proc_mckinley_root; - -#endif /*ASM_PARISC_MCKINLEY_H*/ diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index 269b9a159f01fbec54e164d7baaffcf629c1ce6e..5d2d9737e579dc5e9ba65812dff47d1ac0da8f82 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -44,10 +44,11 @@ int pdc_model_capabilities(unsigned long *capabilities); int pdc_model_platform_info(char *orig_prod_num, char *current_prod_num, char *serial_no); int pdc_cache_info(struct pdc_cache_info *cache); int pdc_spaceid_bits(unsigned long *space_bits); -#ifndef CONFIG_PA20 int pdc_btlb_info(struct pdc_btlb_info *btlb); +int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, + unsigned long entry_info, unsigned long slot); +int pdc_btlb_purge_all(void); int pdc_mem_map_hpa(struct pdc_memory_map *r_addr, struct pdc_module_path *mod_path); -#endif /* !CONFIG_PA20 */ int pdc_pim_toc11(struct pdc_toc_pim_11 *ret); int pdc_pim_toc20(struct pdc_toc_pim_20 *ret); int pdc_lan_station_id(char *lan_addr, unsigned long net_hpa); diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index d77c43d329743dcc77d47f306a4982b693b7d27c..ff6cbdb6903bca6dceea03000bad4ed97d4b69f2 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h @@ -310,6 +310,7 @@ extern void do_syscall_trace_exit(struct pt_regs *); struct seq_file; extern void early_trap_init(void); extern void collect_boot_cpu_data(void); +extern void btlb_init_per_cpu(void); extern int show_cpuinfo (struct seq_file *m, void *v); /* driver code in driver/parisc */ diff --git a/arch/parisc/include/asm/ropes.h b/arch/parisc/include/asm/ropes.h index fd96706c7234a3e344fa11cf284f8c7e564efbf1..e2d2d7e9bfdea021f3839b25b5aa08b5ed2216dd 100644 --- a/arch/parisc/include/asm/ropes.h +++ b/arch/parisc/include/asm/ropes.h @@ -29,7 +29,7 @@ struct ioc { void __iomem *ioc_hpa; /* I/O MMU base address */ char *res_map; /* resource map, bit == pdir entry */ - u64 *pdir_base; /* physical base address */ + __le64 *pdir_base; /* physical base address */ unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */ unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */ #ifdef ZX1_SUPPORT @@ -86,6 +86,9 @@ struct sba_device { struct ioc ioc[MAX_IOC]; }; +/* list of SBA's in system, see drivers/parisc/sba_iommu.c */ +extern struct sba_device *sba_list; + #define ASTRO_RUNWAY_PORT 0x582 #define IKE_MERCED_PORT 0x803 #define REO_MERCED_PORT 0x804 @@ -110,7 +113,7 @@ static inline int IS_PLUTO(struct parisc_device *d) { #define SBA_PDIR_VALID_BIT 0x8000000000000000ULL -#define SBA_AGPGART_COOKIE 0x0000badbadc0ffeeULL +#define SBA_AGPGART_COOKIE (__force __le64) 0x0000badbadc0ffeeULL #define SBA_FUNC_ID 0x0000 /* function id */ #define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ diff --git a/arch/parisc/include/asm/shmparam.h b/arch/parisc/include/asm/shmparam.h index 74f74e4d35b72bc353f458b1711a983c4ca97735..5a95b0f62b87ee9a2c3386e629fe24028f622544 100644 --- a/arch/parisc/include/asm/shmparam.h +++ b/arch/parisc/include/asm/shmparam.h @@ -2,6 +2,21 @@ #ifndef _ASMPARISC_SHMPARAM_H #define _ASMPARISC_SHMPARAM_H +/* + * PA-RISC uses virtually indexed & physically tagged (VIPT) caches + * which has strict requirements when two pages to the same physical + * address are accessed through different mappings. Read the section + * "Address Aliasing" in the arch docs for more detail: + * PA-RISC 1.1 (page 3-6): + * https://parisc.wiki.kernel.org/images-parisc/6/68/Pa11_acd.pdf + * PA-RISC 2.0 (page F-5): + * https://parisc.wiki.kernel.org/images-parisc/7/73/Parisc2.0.pdf + * + * For Linux we allow kernel and userspace to map pages on page size + * granularity (SHMLBA) but have to ensure that, if two pages are + * mapped to the same physical address, the virtual and physical + * addresses modulo SHM_COLOUR are identical. + */ #define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ #define SHM_COLOUR 0x00400000 /* shared mappings colouring */ diff --git a/arch/parisc/include/asm/spinlock_types.h b/arch/parisc/include/asm/spinlock_types.h index efd06a897c6a3b10b6adb7a2e1b4b14abbef930a..7b986b09dba84edece72b56eb4dfe54fff191816 100644 --- a/arch/parisc/include/asm/spinlock_types.h +++ b/arch/parisc/include/asm/spinlock_types.h @@ -9,15 +9,10 @@ #ifndef __ASSEMBLY__ typedef struct { -#ifdef CONFIG_PA20 - volatile unsigned int slock; -# define __ARCH_SPIN_LOCK_UNLOCKED { __ARCH_SPIN_LOCK_UNLOCKED_VAL } -#else volatile unsigned int lock[4]; # define __ARCH_SPIN_LOCK_UNLOCKED \ { { __ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL, \ __ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL } } -#endif } arch_spinlock_t; diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c index 94652e13c2603ee42fb6dbc1f451ba9738577b7e..757816a7bd4b287d71592be356769bfbb1b0a3ab 100644 --- a/arch/parisc/kernel/asm-offsets.c +++ b/arch/parisc/kernel/asm-offsets.c @@ -275,6 +275,8 @@ int main(void) * and kernel data on physical huge pages */ #ifdef CONFIG_HUGETLB_PAGE DEFINE(HUGEPAGE_SIZE, 1UL << REAL_HPAGE_SHIFT); +#elif !defined(CONFIG_64BIT) + DEFINE(HUGEPAGE_SIZE, 4*1024*1024); #else DEFINE(HUGEPAGE_SIZE, PAGE_SIZE); #endif diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index 442109a489406fc36753a44af8e073a2fc0b377a..268d90a9325b468603b634b86b48980a31b4fba7 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -58,7 +58,7 @@ int pa_serialize_tlb_flushes __ro_after_init; struct pdc_cache_info cache_info __ro_after_init; #ifndef CONFIG_PA20 -static struct pdc_btlb_info btlb_info __ro_after_init; +struct pdc_btlb_info btlb_info __ro_after_init; #endif DEFINE_STATIC_KEY_TRUE(parisc_has_cache); @@ -264,12 +264,6 @@ parisc_cache_init(void) icache_stride = CAFL_STRIDE(cache_info.ic_conf); #undef CAFL_STRIDE -#ifndef CONFIG_PA20 - if (pdc_btlb_info(&btlb_info) < 0) { - memset(&btlb_info, 0, sizeof btlb_info); - } -#endif - if ((boot_cpu_data.pdc.capabilities & PDC_MODEL_NVA_MASK) == PDC_MODEL_NVA_UNSUPPORTED) { printk(KERN_WARNING "parisc_cache_init: Only equivalent aliasing supported!\n"); diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index 8f4b77648491a4b38f15bc1df1c12cfe9c1b0c8e..ed8b759480614beb459a81c0a03278273c75c1f0 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -925,9 +925,9 @@ static __init void qemu_header(void) pr_info("#define PARISC_MODEL \"%s\"\n\n", boot_cpu_data.pdc.sys_model_name); + #define p ((unsigned long *)&boot_cpu_data.pdc.model) pr_info("#define PARISC_PDC_MODEL 0x%lx, 0x%lx, 0x%lx, " "0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n\n", - #define p ((unsigned long *)&boot_cpu_data.pdc.model) p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); #undef p diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c index 8f37e75f2fb9caf51557d46fb7ee6070845e6bba..81078abec521a71c56183b56ff50916e034906df 100644 --- a/arch/parisc/kernel/firmware.c +++ b/arch/parisc/kernel/firmware.c @@ -687,7 +687,6 @@ int pdc_spaceid_bits(unsigned long *space_bits) return retval; } -#ifndef CONFIG_PA20 /** * pdc_btlb_info - Return block TLB information. * @btlb: The return buffer. @@ -696,18 +695,51 @@ int pdc_spaceid_bits(unsigned long *space_bits) */ int pdc_btlb_info(struct pdc_btlb_info *btlb) { - int retval; + int retval; unsigned long flags; - spin_lock_irqsave(&pdc_lock, flags); - retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0); - memcpy(btlb, pdc_result, sizeof(*btlb)); - spin_unlock_irqrestore(&pdc_lock, flags); + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; - if(retval < 0) { - btlb->max_size = 0; - } - return retval; + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0); + memcpy(btlb, pdc_result, sizeof(*btlb)); + spin_unlock_irqrestore(&pdc_lock, flags); + + if(retval < 0) { + btlb->max_size = 0; + } + return retval; +} + +int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, + unsigned long entry_info, unsigned long slot) +{ + int retval; + unsigned long flags; + + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INSERT, (unsigned long) (vpage >> 32), + (unsigned long) vpage, physpage, len, entry_info, slot); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +int pdc_btlb_purge_all(void) +{ + int retval; + unsigned long flags; + + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_PURGE_ALL); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; } /** @@ -728,6 +760,9 @@ int pdc_mem_map_hpa(struct pdc_memory_map *address, int retval; unsigned long flags; + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + spin_lock_irqsave(&pdc_lock, flags); memcpy(pdc_result2, mod_path, sizeof(*mod_path)); retval = mem_pdc_call(PDC_MEM_MAP, PDC_MEM_MAP_HPA, __pa(pdc_result), @@ -737,7 +772,6 @@ int pdc_mem_map_hpa(struct pdc_memory_map *address, return retval; } -#endif /* !CONFIG_PA20 */ /** * pdc_lan_station_id - Get the LAN address. diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S index fd15fd4bbb61b8f4cd7567b20a7b6d29f36b7864..a171bf3c6b318da29a6162bd71de0fd46ad15afc 100644 --- a/arch/parisc/kernel/head.S +++ b/arch/parisc/kernel/head.S @@ -180,10 +180,10 @@ $pgt_fill_loop: std %dp,0x18(%r10) #endif -#ifdef CONFIG_64BIT - /* Get PDCE_PROC for monarch CPU. */ #define MEM_PDC_LO 0x388 #define MEM_PDC_HI 0x35C +#ifdef CONFIG_64BIT + /* Get PDCE_PROC for monarch CPU. */ ldw MEM_PDC_LO(%r0),%r3 ldw MEM_PDC_HI(%r0),%r10 depd %r10, 31, 32, %r3 /* move to upper word */ @@ -269,7 +269,17 @@ stext_pdc_ret: tovirt_r1 %r6 mtctl %r6,%cr30 /* restore task thread info */ #endif - + +#ifndef CONFIG_64BIT + /* clear all BTLBs */ + ldi PDC_BLOCK_TLB,%arg0 + load32 PA(stext_pdc_btlb_ret), %rp + ldw MEM_PDC_LO(%r0),%r3 + bv (%r3) + ldi PDC_BTLB_PURGE_ALL,%arg1 +stext_pdc_btlb_ret: +#endif + /* PARANOID: clear user scratch/user space SR's */ mtsp %r0,%sr0 mtsp %r0,%sr1 diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index 12c4d4104ade4a6fbb9504cd5a7ad5e06083f587..2f81bfd4f15e17bc0b0ec3fd1b524e571c2924ed 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -365,7 +365,7 @@ union irq_stack_union { volatile unsigned int lock[1]; }; -DEFINE_PER_CPU(union irq_stack_union, irq_stack_union) = { +static DEFINE_PER_CPU(union irq_stack_union, irq_stack_union) = { .slock = { 1,1,1,1 }, }; #endif diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c index a0e2d37c5b3b530818b22069b09bba9e88418936..1fc89fa2c2d21420f8475685d24a2095235cc004 100644 --- a/arch/parisc/kernel/processor.c +++ b/arch/parisc/kernel/processor.c @@ -368,6 +368,8 @@ int init_per_cpu(int cpunum) /* FUTURE: Enable Performance Monitor : ccr bit 0x20 */ init_percpu_prof(cpunum); + btlb_init_per_cpu(); + return ret; } diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index 4098f9a0964b9b8741c533acce88cb018b844c6a..2019c1f04bd037af0d346f4c4e03f42c769eb16f 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c @@ -440,7 +440,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle) if (cpu_online(cpu)) return 0; - if (num_online_cpus() < setup_max_cpus && smp_boot_one_cpu(cpu, tidle)) + if (num_online_cpus() < nr_cpu_ids && + num_online_cpus() < setup_max_cpus && + smp_boot_one_cpu(cpu, tidle)) return -EIO; return cpu_online(cpu) ? 0 : -EIO; diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S index 1aaa2ca0980033d337597b9c8b2f14bea36c628e..58694d1989c23351772602e369028402bced2030 100644 --- a/arch/parisc/kernel/vmlinux.lds.S +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -154,6 +154,7 @@ SECTIONS } /* End of data section */ + . = ALIGN(PAGE_SIZE); _edata = .; /* BSS */ diff --git a/arch/parisc/mm/hugetlbpage.c b/arch/parisc/mm/hugetlbpage.c index a8a1a7c1e16eb4ef1b751144377a943be3eecaa9..a9f7e21f66567aad1d7244a47664c356346097aa 100644 --- a/arch/parisc/mm/hugetlbpage.c +++ b/arch/parisc/mm/hugetlbpage.c @@ -140,7 +140,7 @@ static void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, } void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t entry) + pte_t *ptep, pte_t entry, unsigned long sz) { __set_huge_pte_at(mm, addr, ptep, entry); } diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index a088c243edeaa40733ead6e9200ef160cf76f6c0..a2a3e89f2d9ab371a5a62ed24b7fa29f459726ab 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -32,6 +32,7 @@ #include #include #include +#include extern int data_start; extern void parisc_kernel_start(void); /* Kernel entry point in head.S */ @@ -720,6 +721,77 @@ void __init paging_init(void) parisc_bootmem_free(); } +static void alloc_btlb(unsigned long start, unsigned long end, int *slot, + unsigned long entry_info) +{ + const int slot_max = btlb_info.fixed_range_info.num_comb; + int min_num_pages = btlb_info.min_size; + unsigned long size; + + /* map at minimum 4 pages */ + if (min_num_pages < 4) + min_num_pages = 4; + + size = HUGEPAGE_SIZE; + while (start < end && *slot < slot_max && size >= PAGE_SIZE) { + /* starting address must have same alignment as size! */ + /* if correctly aligned and fits in double size, increase */ + if (((start & (2 * size - 1)) == 0) && + (end - start) >= (2 * size)) { + size <<= 1; + continue; + } + /* if current size alignment is too big, try smaller size */ + if ((start & (size - 1)) != 0) { + size >>= 1; + continue; + } + if ((end - start) >= size) { + if ((size >> PAGE_SHIFT) >= min_num_pages) + pdc_btlb_insert(start >> PAGE_SHIFT, __pa(start) >> PAGE_SHIFT, + size >> PAGE_SHIFT, entry_info, *slot); + (*slot)++; + start += size; + continue; + } + size /= 2; + continue; + } +} + +void btlb_init_per_cpu(void) +{ + unsigned long s, t, e; + int slot; + + /* BTLBs are not available on 64-bit CPUs */ + if (IS_ENABLED(CONFIG_PA20)) + return; + else if (pdc_btlb_info(&btlb_info) < 0) { + memset(&btlb_info, 0, sizeof btlb_info); + } + + /* insert BLTLBs for code and data segments */ + s = (uintptr_t) dereference_function_descriptor(&_stext); + e = (uintptr_t) dereference_function_descriptor(&_etext); + t = (uintptr_t) dereference_function_descriptor(&_sdata); + BUG_ON(t != e); + + /* code segments */ + slot = 0; + alloc_btlb(s, e, &slot, 0x13800000); + + /* sanity check */ + t = (uintptr_t) dereference_function_descriptor(&_edata); + e = (uintptr_t) dereference_function_descriptor(&__bss_start); + BUG_ON(t != e); + + /* data segments */ + s = (uintptr_t) dereference_function_descriptor(&_sdata); + e = (uintptr_t) dereference_function_descriptor(&__bss_stop); + alloc_btlb(s, e, &slot, 0x11800000); +} + #ifdef CONFIG_PA20 /* diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 54b9387c3691643ae486248d60e5482e011cc060..3aaadfd2c8eb24504774235228eb43850ea7de2a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -255,7 +255,7 @@ config PPC select HAVE_KPROBES select HAVE_KPROBES_ON_FTRACE select HAVE_KRETPROBES - select HAVE_LD_DEAD_CODE_DATA_ELIMINATION if HAVE_OBJTOOL_MCOUNT + select HAVE_LD_DEAD_CODE_DATA_ELIMINATION if HAVE_OBJTOOL_MCOUNT && (!ARCH_USING_PATCHABLE_FUNCTION_ENTRY || (!CC_IS_GCC || GCC_VERSION >= 110100)) select HAVE_LIVEPATCH if HAVE_DYNAMIC_FTRACE_WITH_REGS select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI if PERF_EVENTS || (PPC64 && PPC_BOOK3S) diff --git a/arch/powerpc/configs/disable-werror.config b/arch/powerpc/configs/disable-werror.config index 6ea12a12432c9dc831d7c2f189b260a228fa40fb..7776b91da37f13c5e213791b89cdc6588e55afbf 100644 --- a/arch/powerpc/configs/disable-werror.config +++ b/arch/powerpc/configs/disable-werror.config @@ -1 +1,2 @@ +# Help: Disable -Werror CONFIG_PPC_DISABLE_WERROR=y diff --git a/arch/powerpc/configs/security.config b/arch/powerpc/configs/security.config index 1c91a35c6a733ac5961336dea14dc9faba0e917e..0d54e29e2cdfc6ef34aa921855f8ba5202a6a78a 100644 --- a/arch/powerpc/configs/security.config +++ b/arch/powerpc/configs/security.config @@ -1,3 +1,5 @@ +# Help: Common security options for PowerPC builds + # This is the equivalent of booting with lockdown=integrity CONFIG_SECURITY=y CONFIG_SECURITYFS=y @@ -12,4 +14,4 @@ CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y # UBSAN bounds checking is very cheap and good for hardening CONFIG_UBSAN=y -# CONFIG_UBSAN_MISC is not set \ No newline at end of file +# CONFIG_UBSAN_MISC is not set diff --git a/arch/powerpc/crypto/Kconfig b/arch/powerpc/crypto/Kconfig index 803da4a8a9a2a2f19a37ba4370986670a5928d99..6fc2248ca561668f8200135a68caccc5d452aa42 100644 --- a/arch/powerpc/crypto/Kconfig +++ b/arch/powerpc/crypto/Kconfig @@ -113,7 +113,7 @@ config CRYPTO_AES_GCM_P10 config CRYPTO_CHACHA20_P10 tristate "Ciphers: ChaCha20, XChacha20, XChacha12 (P10 or later)" - depends on PPC64 && CPU_LITTLE_ENDIAN + depends on PPC64 && CPU_LITTLE_ENDIAN && VSX select CRYPTO_SKCIPHER select CRYPTO_LIB_CHACHA_GENERIC select CRYPTO_ARCH_HAVE_LIB_CHACHA @@ -127,7 +127,7 @@ config CRYPTO_CHACHA20_P10 config CRYPTO_POLY1305_P10 tristate "Hash functions: Poly1305 (P10 or later)" - depends on PPC64 && CPU_LITTLE_ENDIAN + depends on PPC64 && CPU_LITTLE_ENDIAN && VSX select CRYPTO_HASH select CRYPTO_LIB_POLY1305_GENERIC help diff --git a/arch/powerpc/include/asm/ide.h b/arch/powerpc/include/asm/ide.h deleted file mode 100644 index ce87a4441ca34887f80bb28ad60e3d172e76d232..0000000000000000000000000000000000000000 --- a/arch/powerpc/include/asm/ide.h +++ /dev/null @@ -1,18 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 1994-1996 Linus Torvalds & authors - * - * This file contains the powerpc architecture specific IDE code. - */ -#ifndef _ASM_POWERPC_IDE_H -#define _ASM_POWERPC_IDE_H - -#include -#include - -#define __ide_mm_insw(p, a, c) readsw((void __iomem *)(p), (a), (c)) -#define __ide_mm_insl(p, a, c) readsl((void __iomem *)(p), (a), (c)) -#define __ide_mm_outsw(p, a, c) writesw((void __iomem *)(p), (a), (c)) -#define __ide_mm_outsl(p, a, c) writesl((void __iomem *)(p), (a), (c)) - -#endif /* _ASM_POWERPC_IDE_H */ diff --git a/arch/powerpc/include/asm/nohash/32/hugetlb-8xx.h b/arch/powerpc/include/asm/nohash/32/hugetlb-8xx.h index de092b04ee1a1292b6352c74888291e179f5b0ae..92df40c6cc6b5e0b4015192aff3c74f9f0537ac8 100644 --- a/arch/powerpc/include/asm/nohash/32/hugetlb-8xx.h +++ b/arch/powerpc/include/asm/nohash/32/hugetlb-8xx.h @@ -46,7 +46,8 @@ static inline int check_and_get_huge_psize(int shift) } #define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT -void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte); +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, + pte_t pte, unsigned long sz); #define __HAVE_ARCH_HUGE_PTE_CLEAR static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr, diff --git a/arch/powerpc/kernel/hw_breakpoint.c b/arch/powerpc/kernel/hw_breakpoint.c index b8513dc3e53ac8cb654177591b1513b5a1f17c49..a1318ce18d0e61b34f49869b12ae4dcfd3cc5f95 100644 --- a/arch/powerpc/kernel/hw_breakpoint.c +++ b/arch/powerpc/kernel/hw_breakpoint.c @@ -230,13 +230,15 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs) struct arch_hw_breakpoint *info; int i; + preempt_disable(); + for (i = 0; i < nr_wp_slots(); i++) { struct perf_event *bp = __this_cpu_read(bp_per_reg[i]); if (unlikely(bp && counter_arch_bp(bp)->perf_single_step)) goto reset; } - return; + goto out; reset: regs_set_return_msr(regs, regs->msr & ~MSR_SE); @@ -245,6 +247,9 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs) __set_breakpoint(i, info); info->perf_single_step = false; } + +out: + preempt_enable(); } static bool is_larx_stcx_instr(int type) @@ -363,6 +368,11 @@ static void handle_p10dd1_spurious_exception(struct perf_event **bp, } } +/* + * Handle a DABR or DAWR exception. + * + * Called in atomic context. + */ int hw_breakpoint_handler(struct die_args *args) { bool err = false; @@ -490,6 +500,8 @@ NOKPROBE_SYMBOL(hw_breakpoint_handler); /* * Handle single-step exceptions following a DABR hit. + * + * Called in atomic context. */ static int single_step_dabr_instruction(struct die_args *args) { @@ -541,6 +553,8 @@ NOKPROBE_SYMBOL(single_step_dabr_instruction); /* * Handle debug exception notifications. + * + * Called in atomic context. */ int hw_breakpoint_exceptions_notify( struct notifier_block *unused, unsigned long val, void *data) diff --git a/arch/powerpc/kernel/hw_breakpoint_constraints.c b/arch/powerpc/kernel/hw_breakpoint_constraints.c index a74623025f3ab87b5a3375bab26e19c25b1ae173..9e51801c49152e32236b4ef5aa8a89ee05501445 100644 --- a/arch/powerpc/kernel/hw_breakpoint_constraints.c +++ b/arch/powerpc/kernel/hw_breakpoint_constraints.c @@ -131,8 +131,13 @@ void wp_get_instr_detail(struct pt_regs *regs, ppc_inst_t *instr, int *type, int *size, unsigned long *ea) { struct instruction_op op; + int err; - if (__get_user_instr(*instr, (void __user *)regs->nip)) + pagefault_disable(); + err = __get_user_instr(*instr, (void __user *)regs->nip); + pagefault_enable(); + + if (err) return; analyse_instr(&op, regs, *instr); diff --git a/arch/powerpc/kernel/stacktrace.c b/arch/powerpc/kernel/stacktrace.c index b15f15dcacb5c42a5b26645460c45e1e5f29340f..e6a958a5da2763deca47037b1246e2d5a83576d5 100644 --- a/arch/powerpc/kernel/stacktrace.c +++ b/arch/powerpc/kernel/stacktrace.c @@ -73,29 +73,12 @@ int __no_sanitize_address arch_stack_walk_reliable(stack_trace_consume_fn consum bool firstframe; stack_end = stack_page + THREAD_SIZE; - if (!is_idle_task(task)) { - /* - * For user tasks, this is the SP value loaded on - * kernel entry, see "PACAKSAVE(r13)" in _switch() and - * system_call_common(). - * - * Likewise for non-swapper kernel threads, - * this also happens to be the top of the stack - * as setup by copy_thread(). - * - * Note that stack backlinks are not properly setup by - * copy_thread() and thus, a forked task() will have - * an unreliable stack trace until it's been - * _switch()'ed to for the first time. - */ - stack_end -= STACK_USER_INT_FRAME_SIZE; - } else { - /* - * idle tasks have a custom stack layout, - * c.f. cpu_idle_thread_init(). - */ + + // See copy_thread() for details. + if (task->flags & PF_KTHREAD) stack_end -= STACK_FRAME_MIN_SIZE; - } + else + stack_end -= STACK_USER_INT_FRAME_SIZE; if (task == current) sp = current_stack_frame(); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index eeff136b83d97b22343cf1b7ce8b3b0144f5943c..64ff37721fd06f4914ba8bf6b80abc19f1b2cc32 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1512,23 +1512,11 @@ static void do_program_check(struct pt_regs *regs) return; } - if (cpu_has_feature(CPU_FTR_DEXCR_NPHIE) && user_mode(regs)) { - ppc_inst_t insn; - - if (get_user_instr(insn, (void __user *)regs->nip)) { - _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); - return; - } - - if (ppc_inst_primary_opcode(insn) == 31 && - get_xop(ppc_inst_val(insn)) == OP_31_XOP_HASHCHK) { - _exception(SIGILL, regs, ILL_ILLOPN, regs->nip); - return; - } + /* User mode considers other cases after enabling IRQs */ + if (!user_mode(regs)) { + _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); + return; } - - _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); - return; } #ifdef CONFIG_PPC_TRANSACTIONAL_MEM if (reason & REASON_TM) { @@ -1561,16 +1549,44 @@ static void do_program_check(struct pt_regs *regs) /* * If we took the program check in the kernel skip down to sending a - * SIGILL. The subsequent cases all relate to emulating instructions - * which we should only do for userspace. We also do not want to enable - * interrupts for kernel faults because that might lead to further - * faults, and loose the context of the original exception. + * SIGILL. The subsequent cases all relate to user space, such as + * emulating instructions which we should only do for user space. We + * also do not want to enable interrupts for kernel faults because that + * might lead to further faults, and loose the context of the original + * exception. */ if (!user_mode(regs)) goto sigill; interrupt_cond_local_irq_enable(regs); + /* + * (reason & REASON_TRAP) is mostly handled before enabling IRQs, + * except get_user_instr() can sleep so we cannot reliably inspect the + * current instruction in that context. Now that we know we are + * handling a user space trap and can sleep, we can check if the trap + * was a hashchk failure. + */ + if (reason & REASON_TRAP) { + if (cpu_has_feature(CPU_FTR_DEXCR_NPHIE)) { + ppc_inst_t insn; + + if (get_user_instr(insn, (void __user *)regs->nip)) { + _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); + return; + } + + if (ppc_inst_primary_opcode(insn) == 31 && + get_xop(ppc_inst_val(insn)) == OP_31_XOP_HASHCHK) { + _exception(SIGILL, regs, ILL_ILLOPN, regs->nip); + return; + } + } + + _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip); + return; + } + /* (reason & REASON_ILLEGAL) would be the obvious thing here, * but there seems to be a hardware bug on the 405GP (RevD) * that means ESR is sometimes set incorrectly - either to diff --git a/arch/powerpc/mm/book3s64/hugetlbpage.c b/arch/powerpc/mm/book3s64/hugetlbpage.c index 3bc0eb21b2a005538a7493cf8ebc7c34a6332801..5a2e512e96db6b310dde8b7c9a25dd1199acf31e 100644 --- a/arch/powerpc/mm/book3s64/hugetlbpage.c +++ b/arch/powerpc/mm/book3s64/hugetlbpage.c @@ -143,11 +143,14 @@ pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t old_pte, pte_t pte) { + unsigned long psize; if (radix_enabled()) return radix__huge_ptep_modify_prot_commit(vma, addr, ptep, old_pte, pte); - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + + psize = huge_page_size(hstate_vma(vma)); + set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize); } void __init hugetlbpage_init_defaultsize(void) diff --git a/arch/powerpc/mm/book3s64/radix_hugetlbpage.c b/arch/powerpc/mm/book3s64/radix_hugetlbpage.c index 17075c78d4bc3dfef9c662f98cbfc3429dbc6d41..35fd2a95be24c662e0ac1514d6ec51daa9402a14 100644 --- a/arch/powerpc/mm/book3s64/radix_hugetlbpage.c +++ b/arch/powerpc/mm/book3s64/radix_hugetlbpage.c @@ -47,6 +47,7 @@ void radix__huge_ptep_modify_prot_commit(struct vm_area_struct *vma, pte_t old_pte, pte_t pte) { struct mm_struct *mm = vma->vm_mm; + unsigned long psize = huge_page_size(hstate_vma(vma)); /* * POWER9 NMMU must flush the TLB after clearing the PTE before @@ -58,5 +59,5 @@ void radix__huge_ptep_modify_prot_commit(struct vm_area_struct *vma, atomic_read(&mm->context.copros) > 0) radix__flush_hugetlb_page(vma, addr); - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize); } diff --git a/arch/powerpc/mm/nohash/8xx.c b/arch/powerpc/mm/nohash/8xx.c index dbbfe897455dc443e1c804aa2d8ff8ad1a2aab28..a642a79298929dda334d712678e2930f83375bcd 100644 --- a/arch/powerpc/mm/nohash/8xx.c +++ b/arch/powerpc/mm/nohash/8xx.c @@ -91,7 +91,8 @@ static int __ref __early_map_kernel_hugepage(unsigned long va, phys_addr_t pa, if (new && WARN_ON(pte_present(*ptep) && pgprot_val(prot))) return -EINVAL; - set_huge_pte_at(&init_mm, va, ptep, pte_mkhuge(pfn_pte(pa >> PAGE_SHIFT, prot))); + set_huge_pte_at(&init_mm, va, ptep, + pte_mkhuge(pfn_pte(pa >> PAGE_SHIFT, prot)), psize); return 0; } diff --git a/arch/powerpc/mm/pgtable.c b/arch/powerpc/mm/pgtable.c index 3f86fd217690be96e6868f7f9670e6a570ef240b..3ba9fe41160469842186963aae0e2abc880e7198 100644 --- a/arch/powerpc/mm/pgtable.c +++ b/arch/powerpc/mm/pgtable.c @@ -288,7 +288,8 @@ int huge_ptep_set_access_flags(struct vm_area_struct *vma, } #if defined(CONFIG_PPC_8xx) -void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, + pte_t pte, unsigned long sz) { pmd_t *pmd = pmd_off(mm, addr); pte_basic_t val; diff --git a/arch/powerpc/perf/hv-24x7.c b/arch/powerpc/perf/hv-24x7.c index 317175791d23c34912bf7b7d9ac8a40e30648e41..3449be7c0d51f7e9e62112523a0f182891f4bf8d 100644 --- a/arch/powerpc/perf/hv-24x7.c +++ b/arch/powerpc/perf/hv-24x7.c @@ -1418,7 +1418,7 @@ static int h_24x7_event_init(struct perf_event *event) } domain = event_get_domain(event); - if (domain >= HV_PERF_DOMAIN_MAX) { + if (domain == 0 || domain >= HV_PERF_DOMAIN_MAX) { pr_devel("invalid domain %d\n", domain); return -EINVAL; } diff --git a/arch/powerpc/platforms/82xx/Kconfig b/arch/powerpc/platforms/82xx/Kconfig index d9f1a2a83158aa47b6763e6fe7180617096ac73f..1824536cf6f21f80d6ececef62697be5ed3396de 100644 --- a/arch/powerpc/platforms/82xx/Kconfig +++ b/arch/powerpc/platforms/82xx/Kconfig @@ -2,6 +2,7 @@ menuconfig PPC_82xx bool "82xx-based boards (PQ II)" depends on PPC_BOOK3S_32 + select FSL_SOC if PPC_82xx @@ -9,7 +10,6 @@ config EP8248E bool "Embedded Planet EP8248E (a.k.a. CWH-PPC-8248N-VE)" select CPM2 select PPC_INDIRECT_PCI if PCI - select FSL_SOC select PHYLIB if NETDEVICES select MDIO_BITBANG if PHYLIB help @@ -22,7 +22,6 @@ config MGCOGE bool "Keymile MGCOGE" select CPM2 select PPC_INDIRECT_PCI if PCI - select FSL_SOC help This enables support for the Keymile MGCOGE board. diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 5138dce1a0b4ce9b2fcce8a451d4d3117820a60b..d607ab0f7c6dafa4f754f34a910ef66464e5a06d 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -273,7 +273,14 @@ config RISCV_DMA_NONCOHERENT select ARCH_HAS_SYNC_DMA_FOR_CPU select ARCH_HAS_SYNC_DMA_FOR_DEVICE select DMA_BOUNCE_UNALIGNED_KMALLOC if SWIOTLB - select DMA_DIRECT_REMAP + select DMA_DIRECT_REMAP if MMU + +config RISCV_NONSTANDARD_CACHE_OPS + bool + depends on RISCV_DMA_NONCOHERENT + help + This enables function pointer support for non-standard noncoherent + systems to handle cache management. config AS_HAS_INSN def_bool $(as-instr,.insn r 51$(comma) 0$(comma) 0$(comma) t0$(comma) t0$(comma) zero) @@ -713,6 +720,25 @@ config RELOCATABLE If unsure, say N. +config RANDOMIZE_BASE + bool "Randomize the address of the kernel image" + select RELOCATABLE + depends on MMU && 64BIT && !XIP_KERNEL + help + Randomizes the virtual address at which the kernel image is + loaded, as a security feature that deters exploit attempts + relying on knowledge of the location of kernel internals. + + It is the bootloader's job to provide entropy, by passing a + random u64 value in /chosen/kaslr-seed at kernel entry. + + When booting via the UEFI stub, it will invoke the firmware's + EFI_RNG_PROTOCOL implementation (if available) to supply entropy + to the kernel proper. In addition, it will randomise the physical + location of the kernel Image as well. + + If unsure, say N. + endmenu # "Kernel features" menu "Boot options" diff --git a/arch/riscv/Kconfig.errata b/arch/riscv/Kconfig.errata index 0c8f4652cd8289b62256f8c93756b84d327f4a88..566bcefeab502c45f69b332c43857ea657abef57 100644 --- a/arch/riscv/Kconfig.errata +++ b/arch/riscv/Kconfig.errata @@ -1,5 +1,26 @@ menu "CPU errata selection" +config ERRATA_ANDES + bool "Andes AX45MP errata" + depends on RISCV_ALTERNATIVE && RISCV_SBI + help + All Andes errata Kconfig depend on this Kconfig. Disabling + this Kconfig will disable all Andes errata. Please say "Y" + here if your platform uses Andes CPU cores. + + Otherwise, please say "N" here to avoid unnecessary overhead. + +config ERRATA_ANDES_CMO + bool "Apply Andes cache management errata" + depends on ERRATA_ANDES && ARCH_R9A07G043 + select RISCV_DMA_NONCOHERENT + default y + help + This will apply the cache management errata to handle the + non-standard handling on non-coherent operations on Andes cores. + + If you don't know what to do here, say "Y". + config ERRATA_SIFIVE bool "SiFive errata" depends on RISCV_ALTERNATIVE diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index d79f94432b279b1cc2328825ff6f84a9b5367e7a..12ebe979235635bafc30a90207017f43bea93467 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -262,7 +262,7 @@ reg = <0x100000 0x400000>; }; reserved-data@600000 { - reg = <0x600000 0x1000000>; + reg = <0x600000 0xa00000>; }; }; }; @@ -440,30 +440,6 @@ }; }; - uart0_pins: uart0-0 { - tx-pins { - pinmux = ; - bias-disable; - drive-strength = <12>; - input-disable; - input-schmitt-disable; - slew-rate = <0>; - }; - - rx-pins { - pinmux = ; - bias-disable; /* external pull-up */ - drive-strength = <2>; - input-enable; - input-schmitt-enable; - slew-rate = <0>; - }; - }; - tdm_pins: tdm-0 { tx-pins { pinmux = ; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pinmux = ; + bias-disable; /* external pull-up */ + drive-strength = <2>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; }; &tdm { @@ -513,6 +513,7 @@ &usb0 { dr_mode = "peripheral"; + status = "okay"; }; &U74_1 { diff --git a/arch/riscv/configs/32-bit.config b/arch/riscv/configs/32-bit.config index f6af0f708df4a6e728c168542966060822f50f4b..16ee163847b44779ee44948b0f53a2251da9503a 100644 --- a/arch/riscv/configs/32-bit.config +++ b/arch/riscv/configs/32-bit.config @@ -1,3 +1,4 @@ +# Help: Build a 32-bit image CONFIG_ARCH_RV32I=y CONFIG_32BIT=y # CONFIG_PORTABLE is not set diff --git a/arch/riscv/configs/64-bit.config b/arch/riscv/configs/64-bit.config index 313edc554d84cc0fbe63b3a1e9c903a7fd684560..d872a2d533f20e2268b8ccabafef69a7e5631810 100644 --- a/arch/riscv/configs/64-bit.config +++ b/arch/riscv/configs/64-bit.config @@ -1,2 +1,3 @@ +# Help: Build a 64-bit image CONFIG_ARCH_RV64I=y CONFIG_64BIT=y diff --git a/arch/riscv/errata/Makefile b/arch/riscv/errata/Makefile index 7b2637c8c332653f5c5551a88c0947f732233b23..8a27394851233dbda1a4f0841e1e7e918b61f8e0 100644 --- a/arch/riscv/errata/Makefile +++ b/arch/riscv/errata/Makefile @@ -2,5 +2,6 @@ ifdef CONFIG_RELOCATABLE KBUILD_CFLAGS += -fno-pie endif +obj-$(CONFIG_ERRATA_ANDES) += andes/ obj-$(CONFIG_ERRATA_SIFIVE) += sifive/ obj-$(CONFIG_ERRATA_THEAD) += thead/ diff --git a/arch/riscv/errata/andes/Makefile b/arch/riscv/errata/andes/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2d644e19caefcb1b5fa8b2aff4a9e456199dc72c --- /dev/null +++ b/arch/riscv/errata/andes/Makefile @@ -0,0 +1 @@ +obj-y += errata.o diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c new file mode 100644 index 0000000000000000000000000000000000000000..197db68cc8daf711ad05a1469697ef50dcd9acd7 --- /dev/null +++ b/arch/riscv/errata/andes/errata.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Erratas to be applied for Andes CPU cores + * + * Copyright (C) 2023 Renesas Electronics Corporation. + * + * Author: Lad Prabhakar + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define ANDESTECH_AX45MP_MARCHID 0x8000000000008a45UL +#define ANDESTECH_AX45MP_MIMPID 0x500UL +#define ANDESTECH_SBI_EXT_ANDES 0x0900031E + +#define ANDES_SBI_EXT_IOCP_SW_WORKAROUND 1 + +static long ax45mp_iocp_sw_workaround(void) +{ + struct sbiret ret; + + /* + * ANDES_SBI_EXT_IOCP_SW_WORKAROUND SBI EXT checks if the IOCP is missing and + * cache is controllable only then CMO will be applied to the platform. + */ + ret = sbi_ecall(ANDESTECH_SBI_EXT_ANDES, ANDES_SBI_EXT_IOCP_SW_WORKAROUND, + 0, 0, 0, 0, 0, 0); + + return ret.error ? 0 : ret.value; +} + +static bool errata_probe_iocp(unsigned int stage, unsigned long arch_id, unsigned long impid) +{ + if (!IS_ENABLED(CONFIG_ERRATA_ANDES_CMO)) + return false; + + if (arch_id != ANDESTECH_AX45MP_MARCHID || impid != ANDESTECH_AX45MP_MIMPID) + return false; + + if (!ax45mp_iocp_sw_workaround()) + return false; + + /* Set this just to make core cbo code happy */ + riscv_cbom_block_size = 1; + riscv_noncoherent_supported(); + + return true; +} + +void __init_or_module andes_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, + unsigned long archid, unsigned long impid, + unsigned int stage) +{ + errata_probe_iocp(stage, archid, impid); + + /* we have nothing to patch here ATM so just return back */ +} diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c index be84b14f01180a1db6b80846d5ce8be6a16a6b8c..0554ed4bf087cf6cd06dc2967c0c2c38e9784887 100644 --- a/arch/riscv/errata/thead/errata.c +++ b/arch/riscv/errata/thead/errata.c @@ -120,11 +120,3 @@ void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) local_flush_icache_all(); } - -void thead_feature_probe_func(unsigned int cpu, - unsigned long archid, - unsigned long impid) -{ - if ((archid == 0) && (impid == 0)) - per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_FAST; -} diff --git a/arch/riscv/include/asm/alternative.h b/arch/riscv/include/asm/alternative.h index 6a41537826a7dc9d6451a6185887cabbeefcf72f..3c2b59b25017929df92b4e6741ac1a9308bfec54 100644 --- a/arch/riscv/include/asm/alternative.h +++ b/arch/riscv/include/asm/alternative.h @@ -30,7 +30,6 @@ #define ALT_OLD_PTR(a) __ALT_PTR(a, old_offset) #define ALT_ALT_PTR(a) __ALT_PTR(a, alt_offset) -void probe_vendor_features(unsigned int cpu); void __init apply_boot_alternatives(void); void __init apply_early_boot_alternatives(void); void apply_module_alternatives(void *start, size_t length); @@ -46,6 +45,9 @@ struct alt_entry { u32 patch_id; /* The patch ID (erratum ID or cpufeature ID) */ }; +void andes_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, + unsigned long archid, unsigned long impid, + unsigned int stage); void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, unsigned long archid, unsigned long impid, unsigned int stage); @@ -53,15 +55,11 @@ void thead_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, unsigned long archid, unsigned long impid, unsigned int stage); -void thead_feature_probe_func(unsigned int cpu, unsigned long archid, - unsigned long impid); - void riscv_cpufeature_patch_func(struct alt_entry *begin, struct alt_entry *end, unsigned int stage); #else /* CONFIG_RISCV_ALTERNATIVE */ -static inline void probe_vendor_features(unsigned int cpu) { } static inline void apply_boot_alternatives(void) { } static inline void apply_early_boot_alternatives(void) { } static inline void apply_module_alternatives(void *start, size_t length) { } diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h index 23fed53b88157842308fd8e67640b981dacde40b..d0345bd659c94f11f349e6d92b9acd6b178218f6 100644 --- a/arch/riscv/include/asm/cpufeature.h +++ b/arch/riscv/include/asm/cpufeature.h @@ -30,4 +30,6 @@ DECLARE_PER_CPU(long, misaligned_access_speed); /* Per-cpu ISA extensions. */ extern struct riscv_isainfo hart_isa[NR_CPUS]; +void check_unaligned_access(int cpu); + #endif diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 7bac43a3176ed1d45aad779e2e625e0dfa17ecd5..777cb8299551ca3a33147471f032d319cdbcfb35 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -54,6 +54,7 @@ #ifndef CONFIG_64BIT #define SATP_PPN _AC(0x003FFFFF, UL) #define SATP_MODE_32 _AC(0x80000000, UL) +#define SATP_MODE_SHIFT 31 #define SATP_ASID_BITS 9 #define SATP_ASID_SHIFT 22 #define SATP_ASID_MASK _AC(0x1FF, UL) @@ -62,6 +63,7 @@ #define SATP_MODE_39 _AC(0x8000000000000000, UL) #define SATP_MODE_48 _AC(0x9000000000000000, UL) #define SATP_MODE_57 _AC(0xa000000000000000, UL) +#define SATP_MODE_SHIFT 60 #define SATP_ASID_BITS 16 #define SATP_ASID_SHIFT 44 #define SATP_ASID_MASK _AC(0xFFFF, UL) diff --git a/arch/riscv/include/asm/dma-noncoherent.h b/arch/riscv/include/asm/dma-noncoherent.h new file mode 100644 index 0000000000000000000000000000000000000000..312cfa0858fbe92ed9215ae43dfa4ba1eb106269 --- /dev/null +++ b/arch/riscv/include/asm/dma-noncoherent.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 Renesas Electronics Corp. + */ + +#ifndef __ASM_DMA_NONCOHERENT_H +#define __ASM_DMA_NONCOHERENT_H + +#include + +/* + * struct riscv_nonstd_cache_ops - Structure for non-standard CMO function pointers + * + * @wback: Function pointer for cache writeback + * @inv: Function pointer for invalidating cache + * @wback_inv: Function pointer for flushing the cache (writeback + invalidating) + */ +struct riscv_nonstd_cache_ops { + void (*wback)(phys_addr_t paddr, size_t size); + void (*inv)(phys_addr_t paddr, size_t size); + void (*wback_inv)(phys_addr_t paddr, size_t size); +}; + +extern struct riscv_nonstd_cache_ops noncoherent_cache_ops; + +void riscv_noncoherent_register_cache_ops(const struct riscv_nonstd_cache_ops *ops); + +#endif /* __ASM_DMA_NONCOHERENT_H */ diff --git a/arch/riscv/include/asm/efi.h b/arch/riscv/include/asm/efi.h index 8a6a128ec57fc02b63a0228776925707b5342d32..46a355913b27d4bf2a2b31356b662b7aa35371e8 100644 --- a/arch/riscv/include/asm/efi.h +++ b/arch/riscv/include/asm/efi.h @@ -45,4 +45,6 @@ void arch_efi_call_virt_teardown(void); unsigned long stext_offset(void); +void efi_icache_sync(unsigned long start, unsigned long end); + #endif /* _ASM_EFI_H */ diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index fb1a810f3d8cef1266f791f4c52eed07c158fa54..b55b434f0059108342229b1e2013e351a7b81961 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -11,6 +11,11 @@ #include #include +#ifdef CONFIG_ERRATA_ANDES +#define ERRATA_ANDESTECH_NO_IOCP 0 +#define ERRATA_ANDESTECH_NUMBER 1 +#endif + #ifdef CONFIG_ERRATA_SIFIVE #define ERRATA_SIFIVE_CIP_453 0 #define ERRATA_SIFIVE_CIP_1200 1 @@ -100,7 +105,7 @@ asm volatile(ALTERNATIVE( \ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | * 0000001 01001 rs1 000 00000 0001011 * dcache.cva rs1 (clean, virtual address) - * 0000001 00100 rs1 000 00000 0001011 + * 0000001 00101 rs1 000 00000 0001011 * * dcache.cipa rs1 (clean then invalidate, physical address) * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | @@ -113,7 +118,7 @@ asm volatile(ALTERNATIVE( \ * 0000000 11001 00000 000 00000 0001011 */ #define THEAD_inval_A0 ".long 0x0265000b" -#define THEAD_clean_A0 ".long 0x0245000b" +#define THEAD_clean_A0 ".long 0x0255000b" #define THEAD_flush_A0 ".long 0x0275000b" #define THEAD_SYNC_S ".long 0x0190000b" diff --git a/arch/riscv/include/asm/hugetlb.h b/arch/riscv/include/asm/hugetlb.h index 34e24f078cc1b3e00cdc9655269e55d90f906e9a..4c5b0e929890fadcebb3caace0afe97dfa46d8bf 100644 --- a/arch/riscv/include/asm/hugetlb.h +++ b/arch/riscv/include/asm/hugetlb.h @@ -18,7 +18,8 @@ void huge_pte_clear(struct mm_struct *mm, unsigned long addr, #define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT void set_huge_pte_at(struct mm_struct *mm, - unsigned long addr, pte_t *ptep, pte_t pte); + unsigned long addr, pte_t *ptep, pte_t pte, + unsigned long sz); #define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR pte_t huge_ptep_get_and_clear(struct mm_struct *mm, diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 2d8ee53b66c7b1377bead9b5b02226fcb0e533eb..1ebf20dfbaa6982c32777af62ad30fba7581a223 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -337,6 +337,15 @@ int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, void __kvm_riscv_switch_to(struct kvm_vcpu_arch *vcpu_arch); +void kvm_riscv_vcpu_setup_isa(struct kvm_vcpu *vcpu); +unsigned long kvm_riscv_vcpu_num_regs(struct kvm_vcpu *vcpu); +int kvm_riscv_vcpu_copy_reg_indices(struct kvm_vcpu *vcpu, + u64 __user *uindices); +int kvm_riscv_vcpu_get_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg); +int kvm_riscv_vcpu_set_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg); + int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq); int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq); void kvm_riscv_vcpu_flush_interrupts(struct kvm_vcpu *vcpu); diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h index ff994fdd6d0d1cc7c01079847dcd8242cb1790a5..27f5bccdd8b02faf9e97cd225b89f3ee84f56065 100644 --- a/arch/riscv/include/asm/kvm_vcpu_vector.h +++ b/arch/riscv/include/asm/kvm_vcpu_vector.h @@ -74,9 +74,7 @@ static inline void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu) #endif int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg, - unsigned long rtype); + const struct kvm_one_reg *reg); int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg, - unsigned long rtype); + const struct kvm_one_reg *reg); #endif diff --git a/arch/riscv/include/asm/page.h b/arch/riscv/include/asm/page.h index b55ba20903ecce8f0c148b7237574c8df7b2352e..5488ecc337b63fd5505c92165695131723d85bff 100644 --- a/arch/riscv/include/asm/page.h +++ b/arch/riscv/include/asm/page.h @@ -106,6 +106,7 @@ typedef struct page *pgtable_t; struct kernel_mapping { unsigned long page_offset; unsigned long virt_addr; + unsigned long virt_offset; uintptr_t phys_addr; uintptr_t size; /* Offset between linear mapping virtual address and kernel load address */ @@ -185,6 +186,8 @@ extern phys_addr_t __phys_addr_symbol(unsigned long x); #define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x)) +unsigned long kaslr_offset(void); + #endif /* __ASSEMBLY__ */ #define virt_addr_valid(vaddr) ({ \ diff --git a/arch/riscv/include/asm/patch.h b/arch/riscv/include/asm/patch.h index 63c98833d5105d325db88ab3a232462fc0bdae85..e88b52d39eac76bc8d5658a2f65bda8f9d260f08 100644 --- a/arch/riscv/include/asm/patch.h +++ b/arch/riscv/include/asm/patch.h @@ -7,6 +7,7 @@ #define _ASM_RISCV_PATCH_H int patch_text_nosync(void *addr, const void *insns, size_t len); +int patch_text_set_nosync(void *addr, u8 c, size_t len); int patch_text(void *addr, u32 *insns, int ninsns); extern int riscv_patch_in_stop_machine; diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h index cb89af3f070471cab0c4dec0850202ea2e07e90d..e55407ace0c3617631df5c887338bf7e43727e7f 100644 --- a/arch/riscv/include/asm/vendorid_list.h +++ b/arch/riscv/include/asm/vendorid_list.h @@ -5,6 +5,7 @@ #ifndef ASM_VENDOR_LIST_H #define ASM_VENDOR_LIST_H +#define ANDESTECH_VENDOR_ID 0x31e #define SIFIVE_VENDOR_ID 0x489 #define THEAD_VENDOR_ID 0x5b7 diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 930fdc4101cdab8eddbd31e2ff33fb27f17bc998..992c5e407104958532d7ba930d50447ebe56fa25 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -55,6 +55,7 @@ struct kvm_riscv_config { unsigned long marchid; unsigned long mimpid; unsigned long zicboz_block_size; + unsigned long satp_mode; }; /* CORE registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ @@ -124,6 +125,12 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_SSAIA, KVM_RISCV_ISA_EXT_V, KVM_RISCV_ISA_EXT_SVNAPOT, + KVM_RISCV_ISA_EXT_ZBA, + KVM_RISCV_ISA_EXT_ZBS, + KVM_RISCV_ISA_EXT_ZICNTR, + KVM_RISCV_ISA_EXT_ZICSR, + KVM_RISCV_ISA_EXT_ZIFENCEI, + KVM_RISCV_ISA_EXT_ZIHPM, KVM_RISCV_ISA_EXT_MAX, }; @@ -193,6 +200,15 @@ enum KVM_RISCV_SBI_EXT_ID { /* ISA Extension registers are mapped as type 7 */ #define KVM_REG_RISCV_ISA_EXT (0x07 << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_ISA_SINGLE (0x0 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_ISA_MULTI_EN (0x1 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_ISA_MULTI_DIS (0x2 << KVM_REG_RISCV_SUBTYPE_SHIFT) +#define KVM_REG_RISCV_ISA_MULTI_REG(__ext_id) \ + ((__ext_id) / __BITS_PER_LONG) +#define KVM_REG_RISCV_ISA_MULTI_MASK(__ext_id) \ + (1UL << ((__ext_id) % __BITS_PER_LONG)) +#define KVM_REG_RISCV_ISA_MULTI_REG_LAST \ + KVM_REG_RISCV_ISA_MULTI_REG(KVM_RISCV_ISA_EXT_MAX - 1) /* SBI extension registers are mapped as type 8 */ #define KVM_REG_RISCV_SBI_EXT (0x08 << KVM_REG_RISCV_TYPE_SHIFT) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 6d2d9afaabea8096cedc681f25fccabc49e91fd5..a38268b19c3d3d5b3e837634e05a7774fc4287d4 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -108,13 +108,18 @@ struct __riscv_v_ext_state { * In signal handler, datap will be set a correct user stack offset * and vector registers will be copied to the address of datap * pointer. - * - * In ptrace syscall, datap will be set to zero and the vector - * registers will be copied to the address right after this - * structure. */ }; +struct __riscv_v_regset_state { + unsigned long vstart; + unsigned long vl; + unsigned long vtype; + unsigned long vcsr; + unsigned long vlenb; + char vreg[]; +}; + /* * According to spec: The number of bits in a single vector register, * VLEN >= ELEN, which must be a power of 2, and must be no greater than diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 6ac56af42f4a0c847bcc258e7cb9e6df0ffbb7bb..95cf25d484052e88b39ca3a49b74cc2bb1453992 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -38,6 +38,7 @@ extra-y += vmlinux.lds obj-y += head.o obj-y += soc.o obj-$(CONFIG_RISCV_ALTERNATIVE) += alternative.o +obj-y += copy-unaligned.o obj-y += cpu.o obj-y += cpufeature.o obj-y += entry.o diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 6b75788c18e66c70a6ec6e21c8f91ef0d36c490d..319a1da0358b4924b706d1eaff6dc89343d6e54d 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -27,8 +27,6 @@ struct cpu_manufacturer_info_t { void (*patch_func)(struct alt_entry *begin, struct alt_entry *end, unsigned long archid, unsigned long impid, unsigned int stage); - void (*feature_probe_func)(unsigned int cpu, unsigned long archid, - unsigned long impid); }; static void riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info) @@ -43,8 +41,12 @@ static void riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info cpu_mfr_info->imp_id = sbi_get_mimpid(); #endif - cpu_mfr_info->feature_probe_func = NULL; switch (cpu_mfr_info->vendor_id) { +#ifdef CONFIG_ERRATA_ANDES + case ANDESTECH_VENDOR_ID: + cpu_mfr_info->patch_func = andes_errata_patch_func; + break; +#endif #ifdef CONFIG_ERRATA_SIFIVE case SIFIVE_VENDOR_ID: cpu_mfr_info->patch_func = sifive_errata_patch_func; @@ -53,7 +55,6 @@ static void riscv_fill_cpu_mfr_info(struct cpu_manufacturer_info_t *cpu_mfr_info #ifdef CONFIG_ERRATA_THEAD case THEAD_VENDOR_ID: cpu_mfr_info->patch_func = thead_errata_patch_func; - cpu_mfr_info->feature_probe_func = thead_feature_probe_func; break; #endif default: @@ -143,20 +144,6 @@ void riscv_alternative_fix_offsets(void *alt_ptr, unsigned int len, } } -/* Called on each CPU as it starts */ -void probe_vendor_features(unsigned int cpu) -{ - struct cpu_manufacturer_info_t cpu_mfr_info; - - riscv_fill_cpu_mfr_info(&cpu_mfr_info); - if (!cpu_mfr_info.feature_probe_func) - return; - - cpu_mfr_info.feature_probe_func(cpu, - cpu_mfr_info.arch_id, - cpu_mfr_info.imp_id); -} - /* * This is called very early in the boot process (directly after we run * a feature detect on the boot CPU). No need to worry about other CPUs @@ -211,7 +198,6 @@ void __init apply_boot_alternatives(void) /* If called on non-boot cpu things could go wrong */ WARN_ON(smp_processor_id() != 0); - probe_vendor_features(0); _apply_alternatives((struct alt_entry *)__alt_start, (struct alt_entry *)__alt_end, RISCV_ALTERNATIVES_BOOT); diff --git a/arch/riscv/kernel/copy-unaligned.S b/arch/riscv/kernel/copy-unaligned.S new file mode 100644 index 0000000000000000000000000000000000000000..cfdecfbaad627153ead8cde6fe9ca7a298c1aa40 --- /dev/null +++ b/arch/riscv/kernel/copy-unaligned.S @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023 Rivos Inc. */ + +#include +#include + + .text + +/* void __riscv_copy_words_unaligned(void *, const void *, size_t) */ +/* Performs a memcpy without aligning buffers, using word loads and stores. */ +/* Note: The size is truncated to a multiple of 8 * SZREG */ +ENTRY(__riscv_copy_words_unaligned) + andi a4, a2, ~((8*SZREG)-1) + beqz a4, 2f + add a3, a1, a4 +1: + REG_L a4, 0(a1) + REG_L a5, SZREG(a1) + REG_L a6, 2*SZREG(a1) + REG_L a7, 3*SZREG(a1) + REG_L t0, 4*SZREG(a1) + REG_L t1, 5*SZREG(a1) + REG_L t2, 6*SZREG(a1) + REG_L t3, 7*SZREG(a1) + REG_S a4, 0(a0) + REG_S a5, SZREG(a0) + REG_S a6, 2*SZREG(a0) + REG_S a7, 3*SZREG(a0) + REG_S t0, 4*SZREG(a0) + REG_S t1, 5*SZREG(a0) + REG_S t2, 6*SZREG(a0) + REG_S t3, 7*SZREG(a0) + addi a0, a0, 8*SZREG + addi a1, a1, 8*SZREG + bltu a1, a3, 1b + +2: + ret +END(__riscv_copy_words_unaligned) + +/* void __riscv_copy_bytes_unaligned(void *, const void *, size_t) */ +/* Performs a memcpy without aligning buffers, using only byte accesses. */ +/* Note: The size is truncated to a multiple of 8 */ +ENTRY(__riscv_copy_bytes_unaligned) + andi a4, a2, ~(8-1) + beqz a4, 2f + add a3, a1, a4 +1: + lb a4, 0(a1) + lb a5, 1(a1) + lb a6, 2(a1) + lb a7, 3(a1) + lb t0, 4(a1) + lb t1, 5(a1) + lb t2, 6(a1) + lb t3, 7(a1) + sb a4, 0(a0) + sb a5, 1(a0) + sb a6, 2(a0) + sb a7, 3(a0) + sb t0, 4(a0) + sb t1, 5(a0) + sb t2, 6(a0) + sb t3, 7(a0) + addi a0, a0, 8 + addi a1, a1, 8 + bltu a1, a3, 1b + +2: + ret +END(__riscv_copy_bytes_unaligned) diff --git a/arch/riscv/kernel/copy-unaligned.h b/arch/riscv/kernel/copy-unaligned.h new file mode 100644 index 0000000000000000000000000000000000000000..e3d70d35b70819203245a35324360657386a32dc --- /dev/null +++ b/arch/riscv/kernel/copy-unaligned.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Rivos, Inc. + */ +#ifndef __RISCV_KERNEL_COPY_UNALIGNED_H +#define __RISCV_KERNEL_COPY_UNALIGNED_H + +#include + +void __riscv_copy_words_unaligned(void *dst, const void *src, size_t size); +void __riscv_copy_bytes_unaligned(void *dst, const void *src, size_t size); + +#endif /* __RISCV_KERNEL_COPY_UNALIGNED_H */ diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index ef7b4fd9e87688f42a62345ba66b8ef0a2b61500..1cfbba65d11ae311d54729966e57fb3c9386d61c 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -18,12 +18,19 @@ #include #include #include +#include #include #include #include +#include "copy-unaligned.h" + #define NUM_ALPHA_EXTS ('z' - 'a' + 1) +#define MISALIGNED_ACCESS_JIFFIES_LG2 1 +#define MISALIGNED_BUFFER_SIZE 0x4000 +#define MISALIGNED_COPY_SIZE ((MISALIGNED_BUFFER_SIZE / 2) - 0x80) + unsigned long elf_hwcap __read_mostly; /* Host ISA bitmap */ @@ -549,6 +556,103 @@ unsigned long riscv_get_elf_hwcap(void) return hwcap; } +void check_unaligned_access(int cpu) +{ + u64 start_cycles, end_cycles; + u64 word_cycles; + u64 byte_cycles; + int ratio; + unsigned long start_jiffies, now; + struct page *page; + void *dst; + void *src; + long speed = RISCV_HWPROBE_MISALIGNED_SLOW; + + page = alloc_pages(GFP_NOWAIT, get_order(MISALIGNED_BUFFER_SIZE)); + if (!page) { + pr_warn("Can't alloc pages to measure memcpy performance"); + return; + } + + /* Make an unaligned destination buffer. */ + dst = (void *)((unsigned long)page_address(page) | 0x1); + /* Unalign src as well, but differently (off by 1 + 2 = 3). */ + src = dst + (MISALIGNED_BUFFER_SIZE / 2); + src += 2; + word_cycles = -1ULL; + /* Do a warmup. */ + __riscv_copy_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); + preempt_disable(); + start_jiffies = jiffies; + while ((now = jiffies) == start_jiffies) + cpu_relax(); + + /* + * For a fixed amount of time, repeatedly try the function, and take + * the best time in cycles as the measurement. + */ + while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { + start_cycles = get_cycles64(); + /* Ensure the CSR read can't reorder WRT to the copy. */ + mb(); + __riscv_copy_words_unaligned(dst, src, MISALIGNED_COPY_SIZE); + /* Ensure the copy ends before the end time is snapped. */ + mb(); + end_cycles = get_cycles64(); + if ((end_cycles - start_cycles) < word_cycles) + word_cycles = end_cycles - start_cycles; + } + + byte_cycles = -1ULL; + __riscv_copy_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); + start_jiffies = jiffies; + while ((now = jiffies) == start_jiffies) + cpu_relax(); + + while (time_before(jiffies, now + (1 << MISALIGNED_ACCESS_JIFFIES_LG2))) { + start_cycles = get_cycles64(); + mb(); + __riscv_copy_bytes_unaligned(dst, src, MISALIGNED_COPY_SIZE); + mb(); + end_cycles = get_cycles64(); + if ((end_cycles - start_cycles) < byte_cycles) + byte_cycles = end_cycles - start_cycles; + } + + preempt_enable(); + + /* Don't divide by zero. */ + if (!word_cycles || !byte_cycles) { + pr_warn("cpu%d: rdtime lacks granularity needed to measure unaligned access speed\n", + cpu); + + goto out; + } + + if (word_cycles < byte_cycles) + speed = RISCV_HWPROBE_MISALIGNED_FAST; + + ratio = div_u64((byte_cycles * 100), word_cycles); + pr_info("cpu%d: Ratio of byte access time to unaligned word access is %d.%02d, unaligned accesses are %s\n", + cpu, + ratio / 100, + ratio % 100, + (speed == RISCV_HWPROBE_MISALIGNED_FAST) ? "fast" : "slow"); + + per_cpu(misaligned_access_speed, cpu) = speed; + +out: + __free_pages(page, get_order(MISALIGNED_BUFFER_SIZE)); +} + +static int check_unaligned_access_boot_cpu(void) +{ + check_unaligned_access(0); + return 0; +} + +arch_initcall(check_unaligned_access_boot_cpu); + #ifdef CONFIG_RISCV_ALTERNATIVE /* * Alternative patch sites consider 48 bits when determining when to patch diff --git a/arch/riscv/kernel/elf_kexec.c b/arch/riscv/kernel/elf_kexec.c index f4099059ed8f2507913ecfbb3713249feda33373..e60fbd8660c4a531c6deef3938c45afe2e158839 100644 --- a/arch/riscv/kernel/elf_kexec.c +++ b/arch/riscv/kernel/elf_kexec.c @@ -98,7 +98,13 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len, kbuf.image = image; kbuf.buf_min = lowest_paddr; kbuf.buf_max = ULONG_MAX; - kbuf.buf_align = PAGE_SIZE; + + /* + * Current riscv boot protocol requires 2MB alignment for + * RV64 and 4MB alignment for RV32 + * + */ + kbuf.buf_align = PMD_SIZE; kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE); kbuf.top_down = false; diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h index 15616155008cc83d1009d386db3b3c2f278d5b81..ea1a10355ce908a0f75d7f76ba99c72bfc507a77 100644 --- a/arch/riscv/kernel/image-vars.h +++ b/arch/riscv/kernel/image-vars.h @@ -27,6 +27,7 @@ __efistub__start = _start; __efistub__start_kernel = _start_kernel; __efistub__end = _end; __efistub__edata = _edata; +__efistub___init_text_end = __init_text_end; __efistub_screen_info = screen_info; #endif diff --git a/arch/riscv/kernel/patch.c b/arch/riscv/kernel/patch.c index 575e71d6c8ae2d0b4e9b0935f117299884272a80..13ee7bf589a15e1fed201415dbe352494c1ba526 100644 --- a/arch/riscv/kernel/patch.c +++ b/arch/riscv/kernel/patch.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -53,12 +54,51 @@ static void patch_unmap(int fixmap) } NOKPROBE_SYMBOL(patch_unmap); -static int patch_insn_write(void *addr, const void *insn, size_t len) +static int __patch_insn_set(void *addr, u8 c, size_t len) +{ + void *waddr = addr; + bool across_pages = (((uintptr_t)addr & ~PAGE_MASK) + len) > PAGE_SIZE; + + /* + * Only two pages can be mapped at a time for writing. + */ + if (len + offset_in_page(addr) > 2 * PAGE_SIZE) + return -EINVAL; + /* + * Before reaching here, it was expected to lock the text_mutex + * already, so we don't need to give another lock here and could + * ensure that it was safe between each cores. + */ + lockdep_assert_held(&text_mutex); + + if (across_pages) + patch_map(addr + PAGE_SIZE, FIX_TEXT_POKE1); + + waddr = patch_map(addr, FIX_TEXT_POKE0); + + memset(waddr, c, len); + + patch_unmap(FIX_TEXT_POKE0); + + if (across_pages) + patch_unmap(FIX_TEXT_POKE1); + + return 0; +} +NOKPROBE_SYMBOL(__patch_insn_set); + +static int __patch_insn_write(void *addr, const void *insn, size_t len) { void *waddr = addr; bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE; int ret; + /* + * Only two pages can be mapped at a time for writing. + */ + if (len + offset_in_page(addr) > 2 * PAGE_SIZE) + return -EINVAL; + /* * Before reaching here, it was expected to lock the text_mutex * already, so we don't need to give another lock here and could @@ -74,7 +114,7 @@ static int patch_insn_write(void *addr, const void *insn, size_t len) lockdep_assert_held(&text_mutex); if (across_pages) - patch_map(addr + len, FIX_TEXT_POKE1); + patch_map(addr + PAGE_SIZE, FIX_TEXT_POKE1); waddr = patch_map(addr, FIX_TEXT_POKE0); @@ -87,15 +127,79 @@ static int patch_insn_write(void *addr, const void *insn, size_t len) return ret; } -NOKPROBE_SYMBOL(patch_insn_write); +NOKPROBE_SYMBOL(__patch_insn_write); #else -static int patch_insn_write(void *addr, const void *insn, size_t len) +static int __patch_insn_set(void *addr, u8 c, size_t len) +{ + memset(addr, c, len); + + return 0; +} +NOKPROBE_SYMBOL(__patch_insn_set); + +static int __patch_insn_write(void *addr, const void *insn, size_t len) { return copy_to_kernel_nofault(addr, insn, len); } -NOKPROBE_SYMBOL(patch_insn_write); +NOKPROBE_SYMBOL(__patch_insn_write); #endif /* CONFIG_MMU */ +static int patch_insn_set(void *addr, u8 c, size_t len) +{ + size_t patched = 0; + size_t size; + int ret = 0; + + /* + * __patch_insn_set() can only work on 2 pages at a time so call it in a + * loop with len <= 2 * PAGE_SIZE. + */ + while (patched < len && !ret) { + size = min_t(size_t, PAGE_SIZE * 2 - offset_in_page(addr + patched), len - patched); + ret = __patch_insn_set(addr + patched, c, size); + + patched += size; + } + + return ret; +} +NOKPROBE_SYMBOL(patch_insn_set); + +int patch_text_set_nosync(void *addr, u8 c, size_t len) +{ + u32 *tp = addr; + int ret; + + ret = patch_insn_set(tp, c, len); + + if (!ret) + flush_icache_range((uintptr_t)tp, (uintptr_t)tp + len); + + return ret; +} +NOKPROBE_SYMBOL(patch_text_set_nosync); + +static int patch_insn_write(void *addr, const void *insn, size_t len) +{ + size_t patched = 0; + size_t size; + int ret = 0; + + /* + * Copy the instructions to the destination address, two pages at a time + * because __patch_insn_write() can only handle len <= 2 * PAGE_SIZE. + */ + while (patched < len && !ret) { + size = min_t(size_t, PAGE_SIZE * 2 - offset_in_page(addr + patched), len - patched); + ret = __patch_insn_write(addr + patched, insn + patched, size); + + patched += size; + } + + return ret; +} +NOKPROBE_SYMBOL(patch_insn_write); + int patch_text_nosync(void *addr, const void *insns, size_t len) { u32 *tp = addr; diff --git a/arch/riscv/kernel/pi/Makefile b/arch/riscv/kernel/pi/Makefile index 7b593d44c712aa7a06ddc5a66da73eb7e3184083..07915dc9279e9219fc2c7abc54ba749d4a48597e 100644 --- a/arch/riscv/kernel/pi/Makefile +++ b/arch/riscv/kernel/pi/Makefile @@ -35,5 +35,5 @@ $(obj)/string.o: $(srctree)/lib/string.c FORCE $(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE $(call if_changed_rule,cc_o_c) -obj-y := cmdline_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o +obj-y := cmdline_early.pi.o fdt_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o extra-y := $(patsubst %.pi.o,%.o,$(obj-y)) diff --git a/arch/riscv/kernel/pi/cmdline_early.c b/arch/riscv/kernel/pi/cmdline_early.c index 05652d13c746232550803882de481278b56e7063..68e786c84c949b23b9fa529686fc21ce89e94d64 100644 --- a/arch/riscv/kernel/pi/cmdline_early.c +++ b/arch/riscv/kernel/pi/cmdline_early.c @@ -14,6 +14,7 @@ static char early_cmdline[COMMAND_LINE_SIZE]; * LLVM complain because the function is actually unused in this file). */ u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa); +bool set_nokaslr_from_cmdline(uintptr_t dtb_pa); static char *get_early_cmdline(uintptr_t dtb_pa) { @@ -60,3 +61,15 @@ u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa) return match_noXlvl(cmdline); } + +static bool match_nokaslr(char *cmdline) +{ + return strstr(cmdline, "nokaslr"); +} + +bool set_nokaslr_from_cmdline(uintptr_t dtb_pa) +{ + char *cmdline = get_early_cmdline(dtb_pa); + + return match_nokaslr(cmdline); +} diff --git a/arch/riscv/kernel/pi/fdt_early.c b/arch/riscv/kernel/pi/fdt_early.c new file mode 100644 index 0000000000000000000000000000000000000000..899610e042ab4d96f9967bcfcf6a1acba8865ed1 --- /dev/null +++ b/arch/riscv/kernel/pi/fdt_early.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include + +/* + * Declare the functions that are exported (but prefixed) here so that LLVM + * does not complain it lacks the 'static' keyword (which, if added, makes + * LLVM complain because the function is actually unused in this file). + */ +u64 get_kaslr_seed(uintptr_t dtb_pa); + +u64 get_kaslr_seed(uintptr_t dtb_pa) +{ + int node, len; + fdt64_t *prop; + u64 ret; + + node = fdt_path_offset((void *)dtb_pa, "/chosen"); + if (node < 0) + return 0; + + prop = fdt_getprop_w((void *)dtb_pa, node, "kaslr-seed", &len); + if (!prop || len != sizeof(u64)) + return 0; + + ret = fdt64_to_cpu(*prop); + *prop = 0; + return ret; +} diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 487303e3ef229346c6cf5515add1e3b998ac982c..2afe460de16a62ba21cf3c7db4a96c5ec8f7d3a5 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -25,6 +25,9 @@ enum riscv_regset { #ifdef CONFIG_FPU REGSET_F, #endif +#ifdef CONFIG_RISCV_ISA_V + REGSET_V, +#endif }; static int riscv_gpr_get(struct task_struct *target, @@ -81,6 +84,71 @@ static int riscv_fpr_set(struct task_struct *target, } #endif +#ifdef CONFIG_RISCV_ISA_V +static int riscv_vr_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct __riscv_v_ext_state *vstate = &target->thread.vstate; + struct __riscv_v_regset_state ptrace_vstate; + + if (!riscv_v_vstate_query(task_pt_regs(target))) + return -EINVAL; + + /* + * Ensure the vector registers have been saved to the memory before + * copying them to membuf. + */ + if (target == current) + riscv_v_vstate_save(current, task_pt_regs(current)); + + ptrace_vstate.vstart = vstate->vstart; + ptrace_vstate.vl = vstate->vl; + ptrace_vstate.vtype = vstate->vtype; + ptrace_vstate.vcsr = vstate->vcsr; + ptrace_vstate.vlenb = vstate->vlenb; + + /* Copy vector header from vstate. */ + membuf_write(&to, &ptrace_vstate, sizeof(struct __riscv_v_regset_state)); + + /* Copy all the vector registers from vstate. */ + return membuf_write(&to, vstate->datap, riscv_v_vsize); +} + +static int riscv_vr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + struct __riscv_v_ext_state *vstate = &target->thread.vstate; + struct __riscv_v_regset_state ptrace_vstate; + + if (!riscv_v_vstate_query(task_pt_regs(target))) + return -EINVAL; + + /* Copy rest of the vstate except datap */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0, + sizeof(struct __riscv_v_regset_state)); + if (unlikely(ret)) + return ret; + + if (vstate->vlenb != ptrace_vstate.vlenb) + return -EINVAL; + + vstate->vstart = ptrace_vstate.vstart; + vstate->vl = ptrace_vstate.vl; + vstate->vtype = ptrace_vstate.vtype; + vstate->vcsr = ptrace_vstate.vcsr; + + /* Copy all the vector registers. */ + pos = 0; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap, + 0, riscv_v_vsize); + return ret; +} +#endif + static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { .core_note_type = NT_PRSTATUS, @@ -100,6 +168,17 @@ static const struct user_regset riscv_user_regset[] = { .set = riscv_fpr_set, }, #endif +#ifdef CONFIG_RISCV_ISA_V + [REGSET_V] = { + .core_note_type = NT_RISCV_VECTOR, + .align = 16, + .n = ((32 * RISCV_MAX_VLENB) + + sizeof(struct __riscv_v_regset_state)) / sizeof(__u32), + .size = sizeof(__u32), + .regset_get = riscv_vr_get, + .set = riscv_vr_set, + }, +#endif }; static const struct user_regset_view riscv_user_native_view = { diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 32c2e1eb71bd95211f20099c7d4467f422d39deb..e600aab116a4093a5a45285f3e14e32e917f54ba 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -347,3 +348,27 @@ void free_initmem(void) free_initmem_default(POISON_FREE_INITMEM); } + +static int dump_kernel_offset(struct notifier_block *self, + unsigned long v, void *p) +{ + pr_emerg("Kernel Offset: 0x%lx from 0x%lx\n", + kernel_map.virt_offset, + KERNEL_LINK_ADDR); + + return 0; +} + +static struct notifier_block kernel_offset_notifier = { + .notifier_call = dump_kernel_offset +}; + +static int __init register_kernel_offset_dumper(void) +{ + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) + atomic_notifier_chain_register(&panic_notifier_list, + &kernel_offset_notifier); + + return 0; +} +device_initcall(register_kernel_offset_dumper); diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index f4d6acb38dd0dfd81653425f9717f976703ca232..1b8da4e40a4d6e979293e4734fb7d4b2e7407723 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -245,7 +246,7 @@ asmlinkage __visible void smp_callin(void) numa_add_cpu(curr_cpuid); set_cpu_online(curr_cpuid, 1); - probe_vendor_features(curr_cpuid); + check_unaligned_access(curr_cpuid); if (has_vector()) { if (riscv_v_setup_vsize()) diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index fee0671e2dc12d65e3faeb48f21d0493f06f556c..4c2067fc59fcbf004e0c7df753a6c31ad70ee8e0 100644 --- a/arch/riscv/kvm/Makefile +++ b/arch/riscv/kvm/Makefile @@ -19,6 +19,7 @@ kvm-y += vcpu_exit.o kvm-y += vcpu_fp.o kvm-y += vcpu_vector.o kvm-y += vcpu_insn.o +kvm-y += vcpu_onereg.o kvm-y += vcpu_switch.o kvm-y += vcpu_sbi.o kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o diff --git a/arch/riscv/kvm/aia.c b/arch/riscv/kvm/aia.c index 585a3b42c52c69cc34662d9740e4378fcc475147..74bb27440527b3bafa1418fc75337162e9bc3589 100644 --- a/arch/riscv/kvm/aia.c +++ b/arch/riscv/kvm/aia.c @@ -176,7 +176,7 @@ int kvm_riscv_vcpu_aia_get_csr(struct kvm_vcpu *vcpu, struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long)) - return -EINVAL; + return -ENOENT; *out_val = 0; if (kvm_riscv_aia_available()) @@ -192,7 +192,7 @@ int kvm_riscv_vcpu_aia_set_csr(struct kvm_vcpu *vcpu, struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr; if (reg_num >= sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long)) - return -EINVAL; + return -ENOENT; if (kvm_riscv_aia_available()) { ((unsigned long *)csr)[reg_num] = val; diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index f2eb47925806b6830acc9d32ebd456da81063608..068c7459387102c19f7759f445c878c510670530 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -406,12 +406,6 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) { } -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, - const struct kvm_memory_slot *memslot) -{ - kvm_flush_remote_tlbs(kvm); -} - void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *free) { } @@ -559,7 +553,7 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { int ret; - kvm_pfn_t pfn = pte_pfn(range->pte); + kvm_pfn_t pfn = pte_pfn(range->arg.pte); if (!kvm->arch.pgd) return false; diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index d12ef99901fc50334df504c04d9492304f513411..82229db1ce73f3b42de478e2af6cbc9a0756fca1 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -13,16 +13,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include -#include -#include #include const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = { @@ -46,79 +42,6 @@ const struct kvm_stats_header kvm_vcpu_stats_header = { sizeof(kvm_vcpu_stats_desc), }; -#define KVM_RISCV_BASE_ISA_MASK GENMASK(25, 0) - -#define KVM_ISA_EXT_ARR(ext) [KVM_RISCV_ISA_EXT_##ext] = RISCV_ISA_EXT_##ext - -/* Mapping between KVM ISA Extension ID & Host ISA extension ID */ -static const unsigned long kvm_isa_ext_arr[] = { - [KVM_RISCV_ISA_EXT_A] = RISCV_ISA_EXT_a, - [KVM_RISCV_ISA_EXT_C] = RISCV_ISA_EXT_c, - [KVM_RISCV_ISA_EXT_D] = RISCV_ISA_EXT_d, - [KVM_RISCV_ISA_EXT_F] = RISCV_ISA_EXT_f, - [KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h, - [KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i, - [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, - [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, - - KVM_ISA_EXT_ARR(SSAIA), - KVM_ISA_EXT_ARR(SSTC), - KVM_ISA_EXT_ARR(SVINVAL), - KVM_ISA_EXT_ARR(SVNAPOT), - KVM_ISA_EXT_ARR(SVPBMT), - KVM_ISA_EXT_ARR(ZBB), - KVM_ISA_EXT_ARR(ZIHINTPAUSE), - KVM_ISA_EXT_ARR(ZICBOM), - KVM_ISA_EXT_ARR(ZICBOZ), -}; - -static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext) -{ - unsigned long i; - - for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { - if (kvm_isa_ext_arr[i] == base_ext) - return i; - } - - return KVM_RISCV_ISA_EXT_MAX; -} - -static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext) -{ - switch (ext) { - case KVM_RISCV_ISA_EXT_H: - return false; - case KVM_RISCV_ISA_EXT_V: - return riscv_v_vstate_ctrl_user_allowed(); - default: - break; - } - - return true; -} - -static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) -{ - switch (ext) { - case KVM_RISCV_ISA_EXT_A: - case KVM_RISCV_ISA_EXT_C: - case KVM_RISCV_ISA_EXT_I: - case KVM_RISCV_ISA_EXT_M: - case KVM_RISCV_ISA_EXT_SSAIA: - case KVM_RISCV_ISA_EXT_SSTC: - case KVM_RISCV_ISA_EXT_SVINVAL: - case KVM_RISCV_ISA_EXT_SVNAPOT: - case KVM_RISCV_ISA_EXT_ZIHINTPAUSE: - case KVM_RISCV_ISA_EXT_ZBB: - return false; - default: - break; - } - - return true; -} - static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; @@ -176,7 +99,6 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) int rc; struct kvm_cpu_context *cntx; struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr; - unsigned long host_isa, i; /* Mark this VCPU never ran */ vcpu->arch.ran_atleast_once = false; @@ -184,12 +106,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) bitmap_zero(vcpu->arch.isa, RISCV_ISA_EXT_MAX); /* Setup ISA features available to VCPU */ - for (i = 0; i < ARRAY_SIZE(kvm_isa_ext_arr); i++) { - host_isa = kvm_isa_ext_arr[i]; - if (__riscv_isa_extension_available(NULL, host_isa) && - kvm_riscv_vcpu_isa_enable_allowed(i)) - set_bit(host_isa, vcpu->arch.isa); - } + kvm_riscv_vcpu_setup_isa(vcpu); /* Setup vendor, arch, and implementation details */ vcpu->arch.mvendorid = sbi_get_mvendorid(); @@ -294,450 +211,6 @@ vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) return VM_FAULT_SIGBUS; } -static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_CONFIG); - unsigned long reg_val; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - - switch (reg_num) { - case KVM_REG_RISCV_CONFIG_REG(isa): - reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK; - break; - case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOM)) - return -EINVAL; - reg_val = riscv_cbom_block_size; - break; - case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOZ)) - return -EINVAL; - reg_val = riscv_cboz_block_size; - break; - case KVM_REG_RISCV_CONFIG_REG(mvendorid): - reg_val = vcpu->arch.mvendorid; - break; - case KVM_REG_RISCV_CONFIG_REG(marchid): - reg_val = vcpu->arch.marchid; - break; - case KVM_REG_RISCV_CONFIG_REG(mimpid): - reg_val = vcpu->arch.mimpid; - break; - default: - return -EINVAL; - } - - if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - return 0; -} - -static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_CONFIG); - unsigned long i, isa_ext, reg_val; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - - if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - switch (reg_num) { - case KVM_REG_RISCV_CONFIG_REG(isa): - /* - * This ONE REG interface is only defined for - * single letter extensions. - */ - if (fls(reg_val) >= RISCV_ISA_EXT_BASE) - return -EINVAL; - - if (!vcpu->arch.ran_atleast_once) { - /* Ignore the enable/disable request for certain extensions */ - for (i = 0; i < RISCV_ISA_EXT_BASE; i++) { - isa_ext = kvm_riscv_vcpu_base2isa_ext(i); - if (isa_ext >= KVM_RISCV_ISA_EXT_MAX) { - reg_val &= ~BIT(i); - continue; - } - if (!kvm_riscv_vcpu_isa_enable_allowed(isa_ext)) - if (reg_val & BIT(i)) - reg_val &= ~BIT(i); - if (!kvm_riscv_vcpu_isa_disable_allowed(isa_ext)) - if (!(reg_val & BIT(i))) - reg_val |= BIT(i); - } - reg_val &= riscv_isa_extension_base(NULL); - /* Do not modify anything beyond single letter extensions */ - reg_val = (vcpu->arch.isa[0] & ~KVM_RISCV_BASE_ISA_MASK) | - (reg_val & KVM_RISCV_BASE_ISA_MASK); - vcpu->arch.isa[0] = reg_val; - kvm_riscv_vcpu_fp_reset(vcpu); - } else { - return -EOPNOTSUPP; - } - break; - case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): - return -EOPNOTSUPP; - case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): - return -EOPNOTSUPP; - case KVM_REG_RISCV_CONFIG_REG(mvendorid): - if (!vcpu->arch.ran_atleast_once) - vcpu->arch.mvendorid = reg_val; - else - return -EBUSY; - break; - case KVM_REG_RISCV_CONFIG_REG(marchid): - if (!vcpu->arch.ran_atleast_once) - vcpu->arch.marchid = reg_val; - else - return -EBUSY; - break; - case KVM_REG_RISCV_CONFIG_REG(mimpid): - if (!vcpu->arch.ran_atleast_once) - vcpu->arch.mimpid = reg_val; - else - return -EBUSY; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int kvm_riscv_vcpu_get_reg_core(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_CORE); - unsigned long reg_val; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - if (reg_num >= sizeof(struct kvm_riscv_core) / sizeof(unsigned long)) - return -EINVAL; - - if (reg_num == KVM_REG_RISCV_CORE_REG(regs.pc)) - reg_val = cntx->sepc; - else if (KVM_REG_RISCV_CORE_REG(regs.pc) < reg_num && - reg_num <= KVM_REG_RISCV_CORE_REG(regs.t6)) - reg_val = ((unsigned long *)cntx)[reg_num]; - else if (reg_num == KVM_REG_RISCV_CORE_REG(mode)) - reg_val = (cntx->sstatus & SR_SPP) ? - KVM_RISCV_MODE_S : KVM_RISCV_MODE_U; - else - return -EINVAL; - - if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - return 0; -} - -static int kvm_riscv_vcpu_set_reg_core(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_CORE); - unsigned long reg_val; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - if (reg_num >= sizeof(struct kvm_riscv_core) / sizeof(unsigned long)) - return -EINVAL; - - if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - if (reg_num == KVM_REG_RISCV_CORE_REG(regs.pc)) - cntx->sepc = reg_val; - else if (KVM_REG_RISCV_CORE_REG(regs.pc) < reg_num && - reg_num <= KVM_REG_RISCV_CORE_REG(regs.t6)) - ((unsigned long *)cntx)[reg_num] = reg_val; - else if (reg_num == KVM_REG_RISCV_CORE_REG(mode)) { - if (reg_val == KVM_RISCV_MODE_S) - cntx->sstatus |= SR_SPP; - else - cntx->sstatus &= ~SR_SPP; - } else - return -EINVAL; - - return 0; -} - -static int kvm_riscv_vcpu_general_get_csr(struct kvm_vcpu *vcpu, - unsigned long reg_num, - unsigned long *out_val) -{ - struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - - if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long)) - return -EINVAL; - - if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { - kvm_riscv_vcpu_flush_interrupts(vcpu); - *out_val = (csr->hvip >> VSIP_TO_HVIP_SHIFT) & VSIP_VALID_MASK; - *out_val |= csr->hvip & ~IRQ_LOCAL_MASK; - } else - *out_val = ((unsigned long *)csr)[reg_num]; - - return 0; -} - -static inline int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, - unsigned long reg_num, - unsigned long reg_val) -{ - struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; - - if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long)) - return -EINVAL; - - if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { - reg_val &= VSIP_VALID_MASK; - reg_val <<= VSIP_TO_HVIP_SHIFT; - } - - ((unsigned long *)csr)[reg_num] = reg_val; - - if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) - WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); - - return 0; -} - -static int kvm_riscv_vcpu_get_reg_csr(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - int rc; - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_CSR); - unsigned long reg_val, reg_subtype; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - - reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; - reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; - switch (reg_subtype) { - case KVM_REG_RISCV_CSR_GENERAL: - rc = kvm_riscv_vcpu_general_get_csr(vcpu, reg_num, ®_val); - break; - case KVM_REG_RISCV_CSR_AIA: - rc = kvm_riscv_vcpu_aia_get_csr(vcpu, reg_num, ®_val); - break; - default: - rc = -EINVAL; - break; - } - if (rc) - return rc; - - if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - return 0; -} - -static int kvm_riscv_vcpu_set_reg_csr(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - int rc; - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_CSR); - unsigned long reg_val, reg_subtype; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - - if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; - reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; - switch (reg_subtype) { - case KVM_REG_RISCV_CSR_GENERAL: - rc = kvm_riscv_vcpu_general_set_csr(vcpu, reg_num, reg_val); - break; - case KVM_REG_RISCV_CSR_AIA: - rc = kvm_riscv_vcpu_aia_set_csr(vcpu, reg_num, reg_val); - break; - default: - rc = -EINVAL; - break; - } - if (rc) - return rc; - - return 0; -} - -static int kvm_riscv_vcpu_get_reg_isa_ext(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_ISA_EXT); - unsigned long reg_val = 0; - unsigned long host_isa_ext; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - - if (reg_num >= KVM_RISCV_ISA_EXT_MAX || - reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) - return -EINVAL; - - host_isa_ext = kvm_isa_ext_arr[reg_num]; - if (__riscv_isa_extension_available(vcpu->arch.isa, host_isa_ext)) - reg_val = 1; /* Mark the given extension as available */ - - if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - return 0; -} - -static int kvm_riscv_vcpu_set_reg_isa_ext(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - unsigned long __user *uaddr = - (unsigned long __user *)(unsigned long)reg->addr; - unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | - KVM_REG_SIZE_MASK | - KVM_REG_RISCV_ISA_EXT); - unsigned long reg_val; - unsigned long host_isa_ext; - - if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) - return -EINVAL; - - if (reg_num >= KVM_RISCV_ISA_EXT_MAX || - reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) - return -EINVAL; - - if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) - return -EFAULT; - - host_isa_ext = kvm_isa_ext_arr[reg_num]; - if (!__riscv_isa_extension_available(NULL, host_isa_ext)) - return -EOPNOTSUPP; - - if (!vcpu->arch.ran_atleast_once) { - /* - * All multi-letter extension and a few single letter - * extension can be disabled - */ - if (reg_val == 1 && - kvm_riscv_vcpu_isa_enable_allowed(reg_num)) - set_bit(host_isa_ext, vcpu->arch.isa); - else if (!reg_val && - kvm_riscv_vcpu_isa_disable_allowed(reg_num)) - clear_bit(host_isa_ext, vcpu->arch.isa); - else - return -EINVAL; - kvm_riscv_vcpu_fp_reset(vcpu); - } else { - return -EOPNOTSUPP; - } - - return 0; -} - -static int kvm_riscv_vcpu_set_reg(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - switch (reg->id & KVM_REG_RISCV_TYPE_MASK) { - case KVM_REG_RISCV_CONFIG: - return kvm_riscv_vcpu_set_reg_config(vcpu, reg); - case KVM_REG_RISCV_CORE: - return kvm_riscv_vcpu_set_reg_core(vcpu, reg); - case KVM_REG_RISCV_CSR: - return kvm_riscv_vcpu_set_reg_csr(vcpu, reg); - case KVM_REG_RISCV_TIMER: - return kvm_riscv_vcpu_set_reg_timer(vcpu, reg); - case KVM_REG_RISCV_FP_F: - return kvm_riscv_vcpu_set_reg_fp(vcpu, reg, - KVM_REG_RISCV_FP_F); - case KVM_REG_RISCV_FP_D: - return kvm_riscv_vcpu_set_reg_fp(vcpu, reg, - KVM_REG_RISCV_FP_D); - case KVM_REG_RISCV_ISA_EXT: - return kvm_riscv_vcpu_set_reg_isa_ext(vcpu, reg); - case KVM_REG_RISCV_SBI_EXT: - return kvm_riscv_vcpu_set_reg_sbi_ext(vcpu, reg); - case KVM_REG_RISCV_VECTOR: - return kvm_riscv_vcpu_set_reg_vector(vcpu, reg, - KVM_REG_RISCV_VECTOR); - default: - break; - } - - return -EINVAL; -} - -static int kvm_riscv_vcpu_get_reg(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg) -{ - switch (reg->id & KVM_REG_RISCV_TYPE_MASK) { - case KVM_REG_RISCV_CONFIG: - return kvm_riscv_vcpu_get_reg_config(vcpu, reg); - case KVM_REG_RISCV_CORE: - return kvm_riscv_vcpu_get_reg_core(vcpu, reg); - case KVM_REG_RISCV_CSR: - return kvm_riscv_vcpu_get_reg_csr(vcpu, reg); - case KVM_REG_RISCV_TIMER: - return kvm_riscv_vcpu_get_reg_timer(vcpu, reg); - case KVM_REG_RISCV_FP_F: - return kvm_riscv_vcpu_get_reg_fp(vcpu, reg, - KVM_REG_RISCV_FP_F); - case KVM_REG_RISCV_FP_D: - return kvm_riscv_vcpu_get_reg_fp(vcpu, reg, - KVM_REG_RISCV_FP_D); - case KVM_REG_RISCV_ISA_EXT: - return kvm_riscv_vcpu_get_reg_isa_ext(vcpu, reg); - case KVM_REG_RISCV_SBI_EXT: - return kvm_riscv_vcpu_get_reg_sbi_ext(vcpu, reg); - case KVM_REG_RISCV_VECTOR: - return kvm_riscv_vcpu_get_reg_vector(vcpu, reg, - KVM_REG_RISCV_VECTOR); - default: - break; - } - - return -EINVAL; -} - long kvm_arch_vcpu_async_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -781,6 +254,24 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = kvm_riscv_vcpu_get_reg(vcpu, ®); break; } + case KVM_GET_REG_LIST: { + struct kvm_reg_list __user *user_list = argp; + struct kvm_reg_list reg_list; + unsigned int n; + + r = -EFAULT; + if (copy_from_user(®_list, user_list, sizeof(reg_list))) + break; + n = reg_list.n; + reg_list.n = kvm_riscv_vcpu_num_regs(vcpu); + if (copy_to_user(user_list, ®_list, sizeof(reg_list))) + break; + r = -E2BIG; + if (n < reg_list.n) + break; + r = kvm_riscv_vcpu_copy_reg_indices(vcpu, user_list->reg); + break; + } default: break; } diff --git a/arch/riscv/kvm/vcpu_fp.c b/arch/riscv/kvm/vcpu_fp.c index 9d8cbc42057a92b1bcffe3ef339fbfed253ef8ae..08ba48a395aa2a232bd00755f02355e6770d0ee7 100644 --- a/arch/riscv/kvm/vcpu_fp.c +++ b/arch/riscv/kvm/vcpu_fp.c @@ -96,7 +96,7 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu, reg_num <= KVM_REG_RISCV_FP_F_REG(f[31])) reg_val = &cntx->fp.f.f[reg_num]; else - return -EINVAL; + return -ENOENT; } else if ((rtype == KVM_REG_RISCV_FP_D) && riscv_isa_extension_available(vcpu->arch.isa, d)) { if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) { @@ -109,9 +109,9 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu, return -EINVAL; reg_val = &cntx->fp.d.f[reg_num]; } else - return -EINVAL; + return -ENOENT; } else - return -EINVAL; + return -ENOENT; if (copy_to_user(uaddr, reg_val, KVM_REG_SIZE(reg->id))) return -EFAULT; @@ -141,7 +141,7 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu, reg_num <= KVM_REG_RISCV_FP_F_REG(f[31])) reg_val = &cntx->fp.f.f[reg_num]; else - return -EINVAL; + return -ENOENT; } else if ((rtype == KVM_REG_RISCV_FP_D) && riscv_isa_extension_available(vcpu->arch.isa, d)) { if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) { @@ -154,9 +154,9 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu, return -EINVAL; reg_val = &cntx->fp.d.f[reg_num]; } else - return -EINVAL; + return -ENOENT; } else - return -EINVAL; + return -ENOENT; if (copy_from_user(reg_val, uaddr, KVM_REG_SIZE(reg->id))) return -EFAULT; diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c new file mode 100644 index 0000000000000000000000000000000000000000..b7e0e03c69b1e50c7fa937ca513129259678aad3 --- /dev/null +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -0,0 +1,1054 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * Copyright (C) 2023 Ventana Micro Systems Inc. + * + * Authors: + * Anup Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KVM_RISCV_BASE_ISA_MASK GENMASK(25, 0) + +#define KVM_ISA_EXT_ARR(ext) \ +[KVM_RISCV_ISA_EXT_##ext] = RISCV_ISA_EXT_##ext + +/* Mapping between KVM ISA Extension ID & Host ISA extension ID */ +static const unsigned long kvm_isa_ext_arr[] = { + /* Single letter extensions (alphabetically sorted) */ + [KVM_RISCV_ISA_EXT_A] = RISCV_ISA_EXT_a, + [KVM_RISCV_ISA_EXT_C] = RISCV_ISA_EXT_c, + [KVM_RISCV_ISA_EXT_D] = RISCV_ISA_EXT_d, + [KVM_RISCV_ISA_EXT_F] = RISCV_ISA_EXT_f, + [KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h, + [KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i, + [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, + [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, + /* Multi letter extensions (alphabetically sorted) */ + KVM_ISA_EXT_ARR(SSAIA), + KVM_ISA_EXT_ARR(SSTC), + KVM_ISA_EXT_ARR(SVINVAL), + KVM_ISA_EXT_ARR(SVNAPOT), + KVM_ISA_EXT_ARR(SVPBMT), + KVM_ISA_EXT_ARR(ZBA), + KVM_ISA_EXT_ARR(ZBB), + KVM_ISA_EXT_ARR(ZBS), + KVM_ISA_EXT_ARR(ZICBOM), + KVM_ISA_EXT_ARR(ZICBOZ), + KVM_ISA_EXT_ARR(ZICNTR), + KVM_ISA_EXT_ARR(ZICSR), + KVM_ISA_EXT_ARR(ZIFENCEI), + KVM_ISA_EXT_ARR(ZIHINTPAUSE), + KVM_ISA_EXT_ARR(ZIHPM), +}; + +static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext) +{ + unsigned long i; + + for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + if (kvm_isa_ext_arr[i] == base_ext) + return i; + } + + return KVM_RISCV_ISA_EXT_MAX; +} + +static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext) +{ + switch (ext) { + case KVM_RISCV_ISA_EXT_H: + return false; + case KVM_RISCV_ISA_EXT_V: + return riscv_v_vstate_ctrl_user_allowed(); + default: + break; + } + + return true; +} + +static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) +{ + switch (ext) { + case KVM_RISCV_ISA_EXT_A: + case KVM_RISCV_ISA_EXT_C: + case KVM_RISCV_ISA_EXT_I: + case KVM_RISCV_ISA_EXT_M: + case KVM_RISCV_ISA_EXT_SSAIA: + case KVM_RISCV_ISA_EXT_SSTC: + case KVM_RISCV_ISA_EXT_SVINVAL: + case KVM_RISCV_ISA_EXT_SVNAPOT: + case KVM_RISCV_ISA_EXT_ZBA: + case KVM_RISCV_ISA_EXT_ZBB: + case KVM_RISCV_ISA_EXT_ZBS: + case KVM_RISCV_ISA_EXT_ZICNTR: + case KVM_RISCV_ISA_EXT_ZICSR: + case KVM_RISCV_ISA_EXT_ZIFENCEI: + case KVM_RISCV_ISA_EXT_ZIHINTPAUSE: + case KVM_RISCV_ISA_EXT_ZIHPM: + return false; + default: + break; + } + + return true; +} + +void kvm_riscv_vcpu_setup_isa(struct kvm_vcpu *vcpu) +{ + unsigned long host_isa, i; + + for (i = 0; i < ARRAY_SIZE(kvm_isa_ext_arr); i++) { + host_isa = kvm_isa_ext_arr[i]; + if (__riscv_isa_extension_available(NULL, host_isa) && + kvm_riscv_vcpu_isa_enable_allowed(i)) + set_bit(host_isa, vcpu->arch.isa); + } +} + +static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_CONFIG); + unsigned long reg_val; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + + switch (reg_num) { + case KVM_REG_RISCV_CONFIG_REG(isa): + reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK; + break; + case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): + if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOM)) + return -ENOENT; + reg_val = riscv_cbom_block_size; + break; + case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): + if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOZ)) + return -ENOENT; + reg_val = riscv_cboz_block_size; + break; + case KVM_REG_RISCV_CONFIG_REG(mvendorid): + reg_val = vcpu->arch.mvendorid; + break; + case KVM_REG_RISCV_CONFIG_REG(marchid): + reg_val = vcpu->arch.marchid; + break; + case KVM_REG_RISCV_CONFIG_REG(mimpid): + reg_val = vcpu->arch.mimpid; + break; + case KVM_REG_RISCV_CONFIG_REG(satp_mode): + reg_val = satp_mode >> SATP_MODE_SHIFT; + break; + default: + return -ENOENT; + } + + if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + return 0; +} + +static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_CONFIG); + unsigned long i, isa_ext, reg_val; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + + if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + switch (reg_num) { + case KVM_REG_RISCV_CONFIG_REG(isa): + /* + * This ONE REG interface is only defined for + * single letter extensions. + */ + if (fls(reg_val) >= RISCV_ISA_EXT_BASE) + return -EINVAL; + + /* + * Return early (i.e. do nothing) if reg_val is the same + * value retrievable via kvm_riscv_vcpu_get_reg_config(). + */ + if (reg_val == (vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK)) + break; + + if (!vcpu->arch.ran_atleast_once) { + /* Ignore the enable/disable request for certain extensions */ + for (i = 0; i < RISCV_ISA_EXT_BASE; i++) { + isa_ext = kvm_riscv_vcpu_base2isa_ext(i); + if (isa_ext >= KVM_RISCV_ISA_EXT_MAX) { + reg_val &= ~BIT(i); + continue; + } + if (!kvm_riscv_vcpu_isa_enable_allowed(isa_ext)) + if (reg_val & BIT(i)) + reg_val &= ~BIT(i); + if (!kvm_riscv_vcpu_isa_disable_allowed(isa_ext)) + if (!(reg_val & BIT(i))) + reg_val |= BIT(i); + } + reg_val &= riscv_isa_extension_base(NULL); + /* Do not modify anything beyond single letter extensions */ + reg_val = (vcpu->arch.isa[0] & ~KVM_RISCV_BASE_ISA_MASK) | + (reg_val & KVM_RISCV_BASE_ISA_MASK); + vcpu->arch.isa[0] = reg_val; + kvm_riscv_vcpu_fp_reset(vcpu); + } else { + return -EBUSY; + } + break; + case KVM_REG_RISCV_CONFIG_REG(zicbom_block_size): + if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOM)) + return -ENOENT; + if (reg_val != riscv_cbom_block_size) + return -EINVAL; + break; + case KVM_REG_RISCV_CONFIG_REG(zicboz_block_size): + if (!riscv_isa_extension_available(vcpu->arch.isa, ZICBOZ)) + return -ENOENT; + if (reg_val != riscv_cboz_block_size) + return -EINVAL; + break; + case KVM_REG_RISCV_CONFIG_REG(mvendorid): + if (reg_val == vcpu->arch.mvendorid) + break; + if (!vcpu->arch.ran_atleast_once) + vcpu->arch.mvendorid = reg_val; + else + return -EBUSY; + break; + case KVM_REG_RISCV_CONFIG_REG(marchid): + if (reg_val == vcpu->arch.marchid) + break; + if (!vcpu->arch.ran_atleast_once) + vcpu->arch.marchid = reg_val; + else + return -EBUSY; + break; + case KVM_REG_RISCV_CONFIG_REG(mimpid): + if (reg_val == vcpu->arch.mimpid) + break; + if (!vcpu->arch.ran_atleast_once) + vcpu->arch.mimpid = reg_val; + else + return -EBUSY; + break; + case KVM_REG_RISCV_CONFIG_REG(satp_mode): + if (reg_val != (satp_mode >> SATP_MODE_SHIFT)) + return -EINVAL; + break; + default: + return -ENOENT; + } + + return 0; +} + +static int kvm_riscv_vcpu_get_reg_core(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_CORE); + unsigned long reg_val; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + if (reg_num >= sizeof(struct kvm_riscv_core) / sizeof(unsigned long)) + return -ENOENT; + + if (reg_num == KVM_REG_RISCV_CORE_REG(regs.pc)) + reg_val = cntx->sepc; + else if (KVM_REG_RISCV_CORE_REG(regs.pc) < reg_num && + reg_num <= KVM_REG_RISCV_CORE_REG(regs.t6)) + reg_val = ((unsigned long *)cntx)[reg_num]; + else if (reg_num == KVM_REG_RISCV_CORE_REG(mode)) + reg_val = (cntx->sstatus & SR_SPP) ? + KVM_RISCV_MODE_S : KVM_RISCV_MODE_U; + else + return -ENOENT; + + if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + return 0; +} + +static int kvm_riscv_vcpu_set_reg_core(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_CORE); + unsigned long reg_val; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + if (reg_num >= sizeof(struct kvm_riscv_core) / sizeof(unsigned long)) + return -ENOENT; + + if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + if (reg_num == KVM_REG_RISCV_CORE_REG(regs.pc)) + cntx->sepc = reg_val; + else if (KVM_REG_RISCV_CORE_REG(regs.pc) < reg_num && + reg_num <= KVM_REG_RISCV_CORE_REG(regs.t6)) + ((unsigned long *)cntx)[reg_num] = reg_val; + else if (reg_num == KVM_REG_RISCV_CORE_REG(mode)) { + if (reg_val == KVM_RISCV_MODE_S) + cntx->sstatus |= SR_SPP; + else + cntx->sstatus &= ~SR_SPP; + } else + return -ENOENT; + + return 0; +} + +static int kvm_riscv_vcpu_general_get_csr(struct kvm_vcpu *vcpu, + unsigned long reg_num, + unsigned long *out_val) +{ + struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; + + if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long)) + return -ENOENT; + + if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { + kvm_riscv_vcpu_flush_interrupts(vcpu); + *out_val = (csr->hvip >> VSIP_TO_HVIP_SHIFT) & VSIP_VALID_MASK; + *out_val |= csr->hvip & ~IRQ_LOCAL_MASK; + } else + *out_val = ((unsigned long *)csr)[reg_num]; + + return 0; +} + +static int kvm_riscv_vcpu_general_set_csr(struct kvm_vcpu *vcpu, + unsigned long reg_num, + unsigned long reg_val) +{ + struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; + + if (reg_num >= sizeof(struct kvm_riscv_csr) / sizeof(unsigned long)) + return -ENOENT; + + if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) { + reg_val &= VSIP_VALID_MASK; + reg_val <<= VSIP_TO_HVIP_SHIFT; + } + + ((unsigned long *)csr)[reg_num] = reg_val; + + if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) + WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0); + + return 0; +} + +static int kvm_riscv_vcpu_get_reg_csr(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + int rc; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_CSR); + unsigned long reg_val, reg_subtype; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + + reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; + reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; + switch (reg_subtype) { + case KVM_REG_RISCV_CSR_GENERAL: + rc = kvm_riscv_vcpu_general_get_csr(vcpu, reg_num, ®_val); + break; + case KVM_REG_RISCV_CSR_AIA: + rc = kvm_riscv_vcpu_aia_get_csr(vcpu, reg_num, ®_val); + break; + default: + rc = -ENOENT; + break; + } + if (rc) + return rc; + + if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + return 0; +} + +static int kvm_riscv_vcpu_set_reg_csr(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + int rc; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_CSR); + unsigned long reg_val, reg_subtype; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + + if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; + reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; + switch (reg_subtype) { + case KVM_REG_RISCV_CSR_GENERAL: + rc = kvm_riscv_vcpu_general_set_csr(vcpu, reg_num, reg_val); + break; + case KVM_REG_RISCV_CSR_AIA: + rc = kvm_riscv_vcpu_aia_set_csr(vcpu, reg_num, reg_val); + break; + default: + rc = -ENOENT; + break; + } + if (rc) + return rc; + + return 0; +} + +static int riscv_vcpu_get_isa_ext_single(struct kvm_vcpu *vcpu, + unsigned long reg_num, + unsigned long *reg_val) +{ + unsigned long host_isa_ext; + + if (reg_num >= KVM_RISCV_ISA_EXT_MAX || + reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) + return -ENOENT; + + host_isa_ext = kvm_isa_ext_arr[reg_num]; + if (!__riscv_isa_extension_available(NULL, host_isa_ext)) + return -ENOENT; + + *reg_val = 0; + if (__riscv_isa_extension_available(vcpu->arch.isa, host_isa_ext)) + *reg_val = 1; /* Mark the given extension as available */ + + return 0; +} + +static int riscv_vcpu_set_isa_ext_single(struct kvm_vcpu *vcpu, + unsigned long reg_num, + unsigned long reg_val) +{ + unsigned long host_isa_ext; + + if (reg_num >= KVM_RISCV_ISA_EXT_MAX || + reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) + return -ENOENT; + + host_isa_ext = kvm_isa_ext_arr[reg_num]; + if (!__riscv_isa_extension_available(NULL, host_isa_ext)) + return -ENOENT; + + if (reg_val == test_bit(host_isa_ext, vcpu->arch.isa)) + return 0; + + if (!vcpu->arch.ran_atleast_once) { + /* + * All multi-letter extension and a few single letter + * extension can be disabled + */ + if (reg_val == 1 && + kvm_riscv_vcpu_isa_enable_allowed(reg_num)) + set_bit(host_isa_ext, vcpu->arch.isa); + else if (!reg_val && + kvm_riscv_vcpu_isa_disable_allowed(reg_num)) + clear_bit(host_isa_ext, vcpu->arch.isa); + else + return -EINVAL; + kvm_riscv_vcpu_fp_reset(vcpu); + } else { + return -EBUSY; + } + + return 0; +} + +static int riscv_vcpu_get_isa_ext_multi(struct kvm_vcpu *vcpu, + unsigned long reg_num, + unsigned long *reg_val) +{ + unsigned long i, ext_id, ext_val; + + if (reg_num > KVM_REG_RISCV_ISA_MULTI_REG_LAST) + return -ENOENT; + + for (i = 0; i < BITS_PER_LONG; i++) { + ext_id = i + reg_num * BITS_PER_LONG; + if (ext_id >= KVM_RISCV_ISA_EXT_MAX) + break; + + ext_val = 0; + riscv_vcpu_get_isa_ext_single(vcpu, ext_id, &ext_val); + if (ext_val) + *reg_val |= KVM_REG_RISCV_ISA_MULTI_MASK(ext_id); + } + + return 0; +} + +static int riscv_vcpu_set_isa_ext_multi(struct kvm_vcpu *vcpu, + unsigned long reg_num, + unsigned long reg_val, bool enable) +{ + unsigned long i, ext_id; + + if (reg_num > KVM_REG_RISCV_ISA_MULTI_REG_LAST) + return -ENOENT; + + for_each_set_bit(i, ®_val, BITS_PER_LONG) { + ext_id = i + reg_num * BITS_PER_LONG; + if (ext_id >= KVM_RISCV_ISA_EXT_MAX) + break; + + riscv_vcpu_set_isa_ext_single(vcpu, ext_id, enable); + } + + return 0; +} + +static int kvm_riscv_vcpu_get_reg_isa_ext(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + int rc; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_ISA_EXT); + unsigned long reg_val, reg_subtype; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + + reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; + reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; + + reg_val = 0; + switch (reg_subtype) { + case KVM_REG_RISCV_ISA_SINGLE: + rc = riscv_vcpu_get_isa_ext_single(vcpu, reg_num, ®_val); + break; + case KVM_REG_RISCV_ISA_MULTI_EN: + case KVM_REG_RISCV_ISA_MULTI_DIS: + rc = riscv_vcpu_get_isa_ext_multi(vcpu, reg_num, ®_val); + if (!rc && reg_subtype == KVM_REG_RISCV_ISA_MULTI_DIS) + reg_val = ~reg_val; + break; + default: + rc = -ENOENT; + } + if (rc) + return rc; + + if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + return 0; +} + +static int kvm_riscv_vcpu_set_reg_isa_ext(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + KVM_REG_RISCV_ISA_EXT); + unsigned long reg_val, reg_subtype; + + if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) + return -EINVAL; + + reg_subtype = reg_num & KVM_REG_RISCV_SUBTYPE_MASK; + reg_num &= ~KVM_REG_RISCV_SUBTYPE_MASK; + + if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) + return -EFAULT; + + switch (reg_subtype) { + case KVM_REG_RISCV_ISA_SINGLE: + return riscv_vcpu_set_isa_ext_single(vcpu, reg_num, reg_val); + case KVM_REG_RISCV_SBI_MULTI_EN: + return riscv_vcpu_set_isa_ext_multi(vcpu, reg_num, reg_val, true); + case KVM_REG_RISCV_SBI_MULTI_DIS: + return riscv_vcpu_set_isa_ext_multi(vcpu, reg_num, reg_val, false); + default: + return -ENOENT; + } + + return 0; +} + +static int copy_config_reg_indices(const struct kvm_vcpu *vcpu, + u64 __user *uindices) +{ + int n = 0; + + for (int i = 0; i < sizeof(struct kvm_riscv_config)/sizeof(unsigned long); + i++) { + u64 size; + u64 reg; + + /* + * Avoid reporting config reg if the corresponding extension + * was not available. + */ + if (i == KVM_REG_RISCV_CONFIG_REG(zicbom_block_size) && + !riscv_isa_extension_available(vcpu->arch.isa, ZICBOM)) + continue; + else if (i == KVM_REG_RISCV_CONFIG_REG(zicboz_block_size) && + !riscv_isa_extension_available(vcpu->arch.isa, ZICBOZ)) + continue; + + size = IS_ENABLED(CONFIG_32BIT) ? KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CONFIG | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + + n++; + } + + return n; +} + +static unsigned long num_config_regs(const struct kvm_vcpu *vcpu) +{ + return copy_config_reg_indices(vcpu, NULL); +} + +static inline unsigned long num_core_regs(void) +{ + return sizeof(struct kvm_riscv_core) / sizeof(unsigned long); +} + +static int copy_core_reg_indices(u64 __user *uindices) +{ + int n = num_core_regs(); + + for (int i = 0; i < n; i++) { + u64 size = IS_ENABLED(CONFIG_32BIT) ? + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CORE | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + return n; +} + +static inline unsigned long num_csr_regs(const struct kvm_vcpu *vcpu) +{ + unsigned long n = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long); + + if (riscv_isa_extension_available(vcpu->arch.isa, SSAIA)) + n += sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long); + + return n; +} + +static int copy_csr_reg_indices(const struct kvm_vcpu *vcpu, + u64 __user *uindices) +{ + int n1 = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long); + int n2 = 0; + + /* copy general csr regs */ + for (int i = 0; i < n1; i++) { + u64 size = IS_ENABLED(CONFIG_32BIT) ? + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CSR | + KVM_REG_RISCV_CSR_GENERAL | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + /* copy AIA csr regs */ + if (riscv_isa_extension_available(vcpu->arch.isa, SSAIA)) { + n2 = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long); + + for (int i = 0; i < n2; i++) { + u64 size = IS_ENABLED(CONFIG_32BIT) ? + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_CSR | + KVM_REG_RISCV_CSR_AIA | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + } + + return n1 + n2; +} + +static inline unsigned long num_timer_regs(void) +{ + return sizeof(struct kvm_riscv_timer) / sizeof(u64); +} + +static int copy_timer_reg_indices(u64 __user *uindices) +{ + int n = num_timer_regs(); + + for (int i = 0; i < n; i++) { + u64 reg = KVM_REG_RISCV | KVM_REG_SIZE_U64 | + KVM_REG_RISCV_TIMER | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + return n; +} + +static inline unsigned long num_fp_f_regs(const struct kvm_vcpu *vcpu) +{ + const struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; + + if (riscv_isa_extension_available(vcpu->arch.isa, f)) + return sizeof(cntx->fp.f) / sizeof(u32); + else + return 0; +} + +static int copy_fp_f_reg_indices(const struct kvm_vcpu *vcpu, + u64 __user *uindices) +{ + int n = num_fp_f_regs(vcpu); + + for (int i = 0; i < n; i++) { + u64 reg = KVM_REG_RISCV | KVM_REG_SIZE_U32 | + KVM_REG_RISCV_FP_F | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + return n; +} + +static inline unsigned long num_fp_d_regs(const struct kvm_vcpu *vcpu) +{ + const struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; + + if (riscv_isa_extension_available(vcpu->arch.isa, d)) + return sizeof(cntx->fp.d.f) / sizeof(u64) + 1; + else + return 0; +} + +static int copy_fp_d_reg_indices(const struct kvm_vcpu *vcpu, + u64 __user *uindices) +{ + int i; + int n = num_fp_d_regs(vcpu); + u64 reg; + + /* copy fp.d.f indices */ + for (i = 0; i < n-1; i++) { + reg = KVM_REG_RISCV | KVM_REG_SIZE_U64 | + KVM_REG_RISCV_FP_D | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + /* copy fp.d.fcsr indices */ + reg = KVM_REG_RISCV | KVM_REG_SIZE_U32 | KVM_REG_RISCV_FP_D | i; + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + + return n; +} + +static int copy_isa_ext_reg_indices(const struct kvm_vcpu *vcpu, + u64 __user *uindices) +{ + unsigned int n = 0; + unsigned long isa_ext; + + for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + u64 size = IS_ENABLED(CONFIG_32BIT) ? + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_ISA_EXT | i; + + isa_ext = kvm_isa_ext_arr[i]; + if (!__riscv_isa_extension_available(NULL, isa_ext)) + continue; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + + n++; + } + + return n; +} + +static inline unsigned long num_isa_ext_regs(const struct kvm_vcpu *vcpu) +{ + return copy_isa_ext_reg_indices(vcpu, NULL);; +} + +static inline unsigned long num_sbi_ext_regs(void) +{ + /* + * number of KVM_REG_RISCV_SBI_SINGLE + + * 2 x (number of KVM_REG_RISCV_SBI_MULTI) + */ + return KVM_RISCV_SBI_EXT_MAX + 2*(KVM_REG_RISCV_SBI_MULTI_REG_LAST+1); +} + +static int copy_sbi_ext_reg_indices(u64 __user *uindices) +{ + int n; + + /* copy KVM_REG_RISCV_SBI_SINGLE */ + n = KVM_RISCV_SBI_EXT_MAX; + for (int i = 0; i < n; i++) { + u64 size = IS_ENABLED(CONFIG_32BIT) ? + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_SBI_EXT | + KVM_REG_RISCV_SBI_SINGLE | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + /* copy KVM_REG_RISCV_SBI_MULTI */ + n = KVM_REG_RISCV_SBI_MULTI_REG_LAST + 1; + for (int i = 0; i < n; i++) { + u64 size = IS_ENABLED(CONFIG_32BIT) ? + KVM_REG_SIZE_U32 : KVM_REG_SIZE_U64; + u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_SBI_EXT | + KVM_REG_RISCV_SBI_MULTI_EN | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + + reg = KVM_REG_RISCV | size | KVM_REG_RISCV_SBI_EXT | + KVM_REG_RISCV_SBI_MULTI_DIS | i; + + if (uindices) { + if (put_user(reg, uindices)) + return -EFAULT; + uindices++; + } + } + + return num_sbi_ext_regs(); +} + +/* + * kvm_riscv_vcpu_num_regs - how many registers do we present via KVM_GET/SET_ONE_REG + * + * This is for all registers. + */ +unsigned long kvm_riscv_vcpu_num_regs(struct kvm_vcpu *vcpu) +{ + unsigned long res = 0; + + res += num_config_regs(vcpu); + res += num_core_regs(); + res += num_csr_regs(vcpu); + res += num_timer_regs(); + res += num_fp_f_regs(vcpu); + res += num_fp_d_regs(vcpu); + res += num_isa_ext_regs(vcpu); + res += num_sbi_ext_regs(); + + return res; +} + +/* + * kvm_riscv_vcpu_copy_reg_indices - get indices of all registers. + */ +int kvm_riscv_vcpu_copy_reg_indices(struct kvm_vcpu *vcpu, + u64 __user *uindices) +{ + int ret; + + ret = copy_config_reg_indices(vcpu, uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_core_reg_indices(uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_csr_reg_indices(vcpu, uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_timer_reg_indices(uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_fp_f_reg_indices(vcpu, uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_fp_d_reg_indices(vcpu, uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_isa_ext_reg_indices(vcpu, uindices); + if (ret < 0) + return ret; + uindices += ret; + + ret = copy_sbi_ext_reg_indices(uindices); + if (ret < 0) + return ret; + + return 0; +} + +int kvm_riscv_vcpu_set_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + switch (reg->id & KVM_REG_RISCV_TYPE_MASK) { + case KVM_REG_RISCV_CONFIG: + return kvm_riscv_vcpu_set_reg_config(vcpu, reg); + case KVM_REG_RISCV_CORE: + return kvm_riscv_vcpu_set_reg_core(vcpu, reg); + case KVM_REG_RISCV_CSR: + return kvm_riscv_vcpu_set_reg_csr(vcpu, reg); + case KVM_REG_RISCV_TIMER: + return kvm_riscv_vcpu_set_reg_timer(vcpu, reg); + case KVM_REG_RISCV_FP_F: + return kvm_riscv_vcpu_set_reg_fp(vcpu, reg, + KVM_REG_RISCV_FP_F); + case KVM_REG_RISCV_FP_D: + return kvm_riscv_vcpu_set_reg_fp(vcpu, reg, + KVM_REG_RISCV_FP_D); + case KVM_REG_RISCV_ISA_EXT: + return kvm_riscv_vcpu_set_reg_isa_ext(vcpu, reg); + case KVM_REG_RISCV_SBI_EXT: + return kvm_riscv_vcpu_set_reg_sbi_ext(vcpu, reg); + case KVM_REG_RISCV_VECTOR: + return kvm_riscv_vcpu_set_reg_vector(vcpu, reg); + default: + break; + } + + return -ENOENT; +} + +int kvm_riscv_vcpu_get_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg) +{ + switch (reg->id & KVM_REG_RISCV_TYPE_MASK) { + case KVM_REG_RISCV_CONFIG: + return kvm_riscv_vcpu_get_reg_config(vcpu, reg); + case KVM_REG_RISCV_CORE: + return kvm_riscv_vcpu_get_reg_core(vcpu, reg); + case KVM_REG_RISCV_CSR: + return kvm_riscv_vcpu_get_reg_csr(vcpu, reg); + case KVM_REG_RISCV_TIMER: + return kvm_riscv_vcpu_get_reg_timer(vcpu, reg); + case KVM_REG_RISCV_FP_F: + return kvm_riscv_vcpu_get_reg_fp(vcpu, reg, + KVM_REG_RISCV_FP_F); + case KVM_REG_RISCV_FP_D: + return kvm_riscv_vcpu_get_reg_fp(vcpu, reg, + KVM_REG_RISCV_FP_D); + case KVM_REG_RISCV_ISA_EXT: + return kvm_riscv_vcpu_get_reg_isa_ext(vcpu, reg); + case KVM_REG_RISCV_SBI_EXT: + return kvm_riscv_vcpu_get_reg_sbi_ext(vcpu, reg); + case KVM_REG_RISCV_VECTOR: + return kvm_riscv_vcpu_get_reg_vector(vcpu, reg); + default: + break; + } + + return -ENOENT; +} diff --git a/arch/riscv/kvm/vcpu_sbi.c b/arch/riscv/kvm/vcpu_sbi.c index 7b46e04fb6675cbe9fea91955ee5b74060462614..9cd97091c7233089ff71207c6da8280f2bcba0c6 100644 --- a/arch/riscv/kvm/vcpu_sbi.c +++ b/arch/riscv/kvm/vcpu_sbi.c @@ -140,8 +140,10 @@ static int riscv_vcpu_set_sbi_ext_single(struct kvm_vcpu *vcpu, const struct kvm_riscv_sbi_extension_entry *sext = NULL; struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; - if (reg_num >= KVM_RISCV_SBI_EXT_MAX || - (reg_val != 1 && reg_val != 0)) + if (reg_num >= KVM_RISCV_SBI_EXT_MAX) + return -ENOENT; + + if (reg_val != 1 && reg_val != 0) return -EINVAL; for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { @@ -175,7 +177,7 @@ static int riscv_vcpu_get_sbi_ext_single(struct kvm_vcpu *vcpu, struct kvm_vcpu_sbi_context *scontext = &vcpu->arch.sbi_context; if (reg_num >= KVM_RISCV_SBI_EXT_MAX) - return -EINVAL; + return -ENOENT; for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) { if (sbi_ext[i].ext_idx == reg_num) { @@ -206,7 +208,7 @@ static int riscv_vcpu_set_sbi_ext_multi(struct kvm_vcpu *vcpu, unsigned long i, ext_id; if (reg_num > KVM_REG_RISCV_SBI_MULTI_REG_LAST) - return -EINVAL; + return -ENOENT; for_each_set_bit(i, ®_val, BITS_PER_LONG) { ext_id = i + reg_num * BITS_PER_LONG; @@ -226,7 +228,7 @@ static int riscv_vcpu_get_sbi_ext_multi(struct kvm_vcpu *vcpu, unsigned long i, ext_id, ext_val; if (reg_num > KVM_REG_RISCV_SBI_MULTI_REG_LAST) - return -EINVAL; + return -ENOENT; for (i = 0; i < BITS_PER_LONG; i++) { ext_id = i + reg_num * BITS_PER_LONG; @@ -272,7 +274,7 @@ int kvm_riscv_vcpu_set_reg_sbi_ext(struct kvm_vcpu *vcpu, case KVM_REG_RISCV_SBI_MULTI_DIS: return riscv_vcpu_set_sbi_ext_multi(vcpu, reg_num, reg_val, false); default: - return -EINVAL; + return -ENOENT; } return 0; @@ -307,7 +309,7 @@ int kvm_riscv_vcpu_get_reg_sbi_ext(struct kvm_vcpu *vcpu, reg_val = ~reg_val; break; default: - rc = -EINVAL; + rc = -ENOENT; } if (rc) return rc; diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index 3ac2ff6a65dacbeb1e5c030602bef10962ecbb21..75486b25ac45aa3d3e2a9e91701fc0f76e1c59bd 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -170,7 +170,7 @@ int kvm_riscv_vcpu_get_reg_timer(struct kvm_vcpu *vcpu, if (KVM_REG_SIZE(reg->id) != sizeof(u64)) return -EINVAL; if (reg_num >= sizeof(struct kvm_riscv_timer) / sizeof(u64)) - return -EINVAL; + return -ENOENT; switch (reg_num) { case KVM_REG_RISCV_TIMER_REG(frequency): @@ -187,7 +187,7 @@ int kvm_riscv_vcpu_get_reg_timer(struct kvm_vcpu *vcpu, KVM_RISCV_TIMER_STATE_OFF; break; default: - return -EINVAL; + return -ENOENT; } if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) @@ -211,14 +211,15 @@ int kvm_riscv_vcpu_set_reg_timer(struct kvm_vcpu *vcpu, if (KVM_REG_SIZE(reg->id) != sizeof(u64)) return -EINVAL; if (reg_num >= sizeof(struct kvm_riscv_timer) / sizeof(u64)) - return -EINVAL; + return -ENOENT; if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) return -EFAULT; switch (reg_num) { case KVM_REG_RISCV_TIMER_REG(frequency): - ret = -EOPNOTSUPP; + if (reg_val != riscv_timebase) + return -EINVAL; break; case KVM_REG_RISCV_TIMER_REG(time): gt->time_delta = reg_val - get_cycles64(); @@ -233,7 +234,7 @@ int kvm_riscv_vcpu_set_reg_timer(struct kvm_vcpu *vcpu, ret = kvm_riscv_vcpu_timer_cancel(t); break; default: - ret = -EINVAL; + ret = -ENOENT; break; } diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c index edd2eecbddc2ed14666e330590272dccd52ab54a..b430cbb695214da2a500d46c58c7113faa04237c 100644 --- a/arch/riscv/kvm/vcpu_vector.c +++ b/arch/riscv/kvm/vcpu_vector.c @@ -91,95 +91,93 @@ void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu) } #endif -static void *kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, - unsigned long reg_num, - size_t reg_size) +static int kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, + unsigned long reg_num, + size_t reg_size, + void **reg_addr) { struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; - void *reg_val; size_t vlenb = riscv_v_vsize / 32; if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) { if (reg_size != sizeof(unsigned long)) - return NULL; + return -EINVAL; switch (reg_num) { case KVM_REG_RISCV_VECTOR_CSR_REG(vstart): - reg_val = &cntx->vector.vstart; + *reg_addr = &cntx->vector.vstart; break; case KVM_REG_RISCV_VECTOR_CSR_REG(vl): - reg_val = &cntx->vector.vl; + *reg_addr = &cntx->vector.vl; break; case KVM_REG_RISCV_VECTOR_CSR_REG(vtype): - reg_val = &cntx->vector.vtype; + *reg_addr = &cntx->vector.vtype; break; case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr): - reg_val = &cntx->vector.vcsr; + *reg_addr = &cntx->vector.vcsr; break; case KVM_REG_RISCV_VECTOR_CSR_REG(datap): default: - return NULL; + return -ENOENT; } } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) { if (reg_size != vlenb) - return NULL; - reg_val = cntx->vector.datap - + (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb; + return -EINVAL; + *reg_addr = cntx->vector.datap + + (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb; } else { - return NULL; + return -ENOENT; } - return reg_val; + return 0; } int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg, - unsigned long rtype) + const struct kvm_one_reg *reg) { unsigned long *isa = vcpu->arch.isa; unsigned long __user *uaddr = (unsigned long __user *)(unsigned long)reg->addr; unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | - rtype); - void *reg_val = NULL; + KVM_REG_RISCV_VECTOR); size_t reg_size = KVM_REG_SIZE(reg->id); + void *reg_addr; + int rc; - if (rtype == KVM_REG_RISCV_VECTOR && - riscv_isa_extension_available(isa, v)) { - reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size); - } + if (!riscv_isa_extension_available(isa, v)) + return -ENOENT; - if (!reg_val) - return -EINVAL; + rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, ®_addr); + if (rc) + return rc; - if (copy_to_user(uaddr, reg_val, reg_size)) + if (copy_to_user(uaddr, reg_addr, reg_size)) return -EFAULT; return 0; } int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu, - const struct kvm_one_reg *reg, - unsigned long rtype) + const struct kvm_one_reg *reg) { unsigned long *isa = vcpu->arch.isa; unsigned long __user *uaddr = (unsigned long __user *)(unsigned long)reg->addr; unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | - rtype); - void *reg_val = NULL; + KVM_REG_RISCV_VECTOR); size_t reg_size = KVM_REG_SIZE(reg->id); + void *reg_addr; + int rc; - if (rtype == KVM_REG_RISCV_VECTOR && - riscv_isa_extension_available(isa, v)) { - reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size); - } + if (!riscv_isa_extension_available(isa, v)) + return -ENOENT; - if (!reg_val) - return -EINVAL; + rc = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size, ®_addr); + if (rc) + return rc; - if (copy_from_user(reg_val, uaddr, reg_size)) + if (copy_from_user(reg_addr, uaddr, reg_size)) return -EFAULT; return 0; diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c index 7270b4d8c05b4d4801b4cd21d61ce8cc8964a004..b76e7e192eb183460c3caf897b36f5a0ec59b30b 100644 --- a/arch/riscv/mm/dma-noncoherent.c +++ b/arch/riscv/mm/dma-noncoherent.c @@ -9,26 +9,93 @@ #include #include #include +#include static bool noncoherent_supported __ro_after_init; int dma_cache_alignment __ro_after_init = ARCH_DMA_MINALIGN; EXPORT_SYMBOL_GPL(dma_cache_alignment); -void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, - enum dma_data_direction dir) +struct riscv_nonstd_cache_ops noncoherent_cache_ops __ro_after_init = { + .wback = NULL, + .inv = NULL, + .wback_inv = NULL, +}; + +static inline void arch_dma_cache_wback(phys_addr_t paddr, size_t size) +{ + void *vaddr = phys_to_virt(paddr); + +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (unlikely(noncoherent_cache_ops.wback)) { + noncoherent_cache_ops.wback(paddr, size); + return; + } +#endif + ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); +} + +static inline void arch_dma_cache_inv(phys_addr_t paddr, size_t size) +{ + void *vaddr = phys_to_virt(paddr); + +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (unlikely(noncoherent_cache_ops.inv)) { + noncoherent_cache_ops.inv(paddr, size); + return; + } +#endif + + ALT_CMO_OP(inval, vaddr, size, riscv_cbom_block_size); +} + +static inline void arch_dma_cache_wback_inv(phys_addr_t paddr, size_t size) { void *vaddr = phys_to_virt(paddr); +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (unlikely(noncoherent_cache_ops.wback_inv)) { + noncoherent_cache_ops.wback_inv(paddr, size); + return; + } +#endif + + ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); +} + +static inline bool arch_sync_dma_clean_before_fromdevice(void) +{ + return true; +} + +static inline bool arch_sync_dma_cpu_needs_post_dma_flush(void) +{ + return true; +} + +void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, + enum dma_data_direction dir) +{ switch (dir) { case DMA_TO_DEVICE: - ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); + arch_dma_cache_wback(paddr, size); break; + case DMA_FROM_DEVICE: - ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); - break; + if (!arch_sync_dma_clean_before_fromdevice()) { + arch_dma_cache_inv(paddr, size); + break; + } + fallthrough; + case DMA_BIDIRECTIONAL: - ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); + /* Skip the invalidate here if it's done later */ + if (IS_ENABLED(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) && + arch_sync_dma_cpu_needs_post_dma_flush()) + arch_dma_cache_wback(paddr, size); + else + arch_dma_cache_wback_inv(paddr, size); break; + default: break; } @@ -37,15 +104,17 @@ void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, enum dma_data_direction dir) { - void *vaddr = phys_to_virt(paddr); - switch (dir) { case DMA_TO_DEVICE: break; + case DMA_FROM_DEVICE: case DMA_BIDIRECTIONAL: - ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); + /* FROM_DEVICE invalidate needed if speculative CPU prefetch only */ + if (arch_sync_dma_cpu_needs_post_dma_flush()) + arch_dma_cache_inv(paddr, size); break; + default: break; } @@ -55,6 +124,13 @@ void arch_dma_prep_coherent(struct page *page, size_t size) { void *flush_addr = page_address(page); +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (unlikely(noncoherent_cache_ops.wback_inv)) { + noncoherent_cache_ops.wback_inv(page_to_phys(page), size); + return; + } +#endif + ALT_CMO_OP(flush, flush_addr, size, riscv_cbom_block_size); } @@ -86,3 +162,12 @@ void __init riscv_set_dma_cache_alignment(void) if (!noncoherent_supported) dma_cache_alignment = 1; } + +void riscv_noncoherent_register_cache_ops(const struct riscv_nonstd_cache_ops *ops) +{ + if (!ops) + return; + + noncoherent_cache_ops = *ops; +} +EXPORT_SYMBOL_GPL(riscv_noncoherent_register_cache_ops); diff --git a/arch/riscv/mm/hugetlbpage.c b/arch/riscv/mm/hugetlbpage.c index 96225a8533ad8002e39518f87c6bab9d0e8ffb5a..e4a2ace92dbeba5b522ebca0f0fe9dfa28a9655a 100644 --- a/arch/riscv/mm/hugetlbpage.c +++ b/arch/riscv/mm/hugetlbpage.c @@ -180,7 +180,8 @@ pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags) void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, - pte_t pte) + pte_t pte, + unsigned long sz) { int i, pte_num; diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 943c18d6ef4d40eb4cc940f980b4c01b59be0e55..0798bd861dcb9adfcec5e39b2c0713e8d3b3f1c0 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1014,11 +1014,45 @@ static void __init pt_ops_set_late(void) #endif } +#ifdef CONFIG_RANDOMIZE_BASE +extern bool __init __pi_set_nokaslr_from_cmdline(uintptr_t dtb_pa); +extern u64 __init __pi_get_kaslr_seed(uintptr_t dtb_pa); + +static int __init print_nokaslr(char *p) +{ + pr_info("Disabled KASLR"); + return 0; +} +early_param("nokaslr", print_nokaslr); + +unsigned long kaslr_offset(void) +{ + return kernel_map.virt_offset; +} +#endif + asmlinkage void __init setup_vm(uintptr_t dtb_pa) { pmd_t __maybe_unused fix_bmap_spmd, fix_bmap_epmd; - kernel_map.virt_addr = KERNEL_LINK_ADDR; +#ifdef CONFIG_RANDOMIZE_BASE + if (!__pi_set_nokaslr_from_cmdline(dtb_pa)) { + u64 kaslr_seed = __pi_get_kaslr_seed(dtb_pa); + u32 kernel_size = (uintptr_t)(&_end) - (uintptr_t)(&_start); + u32 nr_pos; + + /* + * Compute the number of positions available: we are limited + * by the early page table that only has one PUD and we must + * be aligned on PMD_SIZE. + */ + nr_pos = (PUD_SIZE - kernel_size) / PMD_SIZE; + + kernel_map.virt_offset = (kaslr_seed % nr_pos) * PMD_SIZE; + } +#endif + + kernel_map.virt_addr = KERNEL_LINK_ADDR + kernel_map.virt_offset; kernel_map.page_offset = _AC(CONFIG_PAGE_OFFSET, UL); #ifdef CONFIG_XIP_KERNEL diff --git a/arch/riscv/mm/pmem.c b/arch/riscv/mm/pmem.c index 089df92ae8760a4e27d3c53c0ef05e5f4035eae3..c5fc5ec96f6d4b1fc71b2949ed970c561b7fcb14 100644 --- a/arch/riscv/mm/pmem.c +++ b/arch/riscv/mm/pmem.c @@ -7,15 +7,28 @@ #include #include +#include void arch_wb_cache_pmem(void *addr, size_t size) { +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (unlikely(noncoherent_cache_ops.wback)) { + noncoherent_cache_ops.wback(virt_to_phys(addr), size); + return; + } +#endif ALT_CMO_OP(clean, addr, size, riscv_cbom_block_size); } EXPORT_SYMBOL_GPL(arch_wb_cache_pmem); void arch_invalidate_pmem(void *addr, size_t size) { +#ifdef CONFIG_RISCV_NONSTANDARD_CACHE_OPS + if (unlikely(noncoherent_cache_ops.inv)) { + noncoherent_cache_ops.inv(virt_to_phys(addr), size); + return; + } +#endif ALT_CMO_OP(inval, addr, size, riscv_cbom_block_size); } EXPORT_SYMBOL_GPL(arch_invalidate_pmem); diff --git a/arch/riscv/net/bpf_jit.h b/arch/riscv/net/bpf_jit.h index d21c6c92a683d9cca04a89ee3cb86c467cfcd14a..a5ce1ab76ecee0cc2581c8bb78a86ece03776993 100644 --- a/arch/riscv/net/bpf_jit.h +++ b/arch/riscv/net/bpf_jit.h @@ -68,6 +68,7 @@ static inline bool is_creg(u8 reg) struct rv_jit_context { struct bpf_prog *prog; u16 *insns; /* RV insns */ + u16 *ro_insns; int ninsns; int prologue_len; int epilogue_offset; @@ -85,7 +86,9 @@ static inline int ninsns_rvoff(int ninsns) struct rv_jit_data { struct bpf_binary_header *header; + struct bpf_binary_header *ro_header; u8 *image; + u8 *ro_image; struct rv_jit_context ctx; }; diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c index 8423f4ddf8f5f5ba99ffe3bacd699483f1cd3cbf..ecd3ae6f411679b454963a4f68611c9b38064fbf 100644 --- a/arch/riscv/net/bpf_jit_comp64.c +++ b/arch/riscv/net/bpf_jit_comp64.c @@ -144,7 +144,11 @@ static bool in_auipc_jalr_range(s64 val) /* Emit fixed-length instructions for address */ static int emit_addr(u8 rd, u64 addr, bool extra_pass, struct rv_jit_context *ctx) { - u64 ip = (u64)(ctx->insns + ctx->ninsns); + /* + * Use the ro_insns(RX) to calculate the offset as the BPF program will + * finally run from this memory region. + */ + u64 ip = (u64)(ctx->ro_insns + ctx->ninsns); s64 off = addr - ip; s64 upper = (off + (1 << 11)) >> 12; s64 lower = off & 0xfff; @@ -464,8 +468,12 @@ static int emit_call(u64 addr, bool fixed_addr, struct rv_jit_context *ctx) s64 off = 0; u64 ip; - if (addr && ctx->insns) { - ip = (u64)(long)(ctx->insns + ctx->ninsns); + if (addr && ctx->insns && ctx->ro_insns) { + /* + * Use the ro_insns(RX) to calculate the offset as the BPF + * program will finally run from this memory region. + */ + ip = (u64)(long)(ctx->ro_insns + ctx->ninsns); off = addr - ip; } @@ -578,9 +586,10 @@ static int add_exception_handler(const struct bpf_insn *insn, { struct exception_table_entry *ex; unsigned long pc; - off_t offset; + off_t ins_offset; + off_t fixup_offset; - if (!ctx->insns || !ctx->prog->aux->extable || + if (!ctx->insns || !ctx->ro_insns || !ctx->prog->aux->extable || (BPF_MODE(insn->code) != BPF_PROBE_MEM && BPF_MODE(insn->code) != BPF_PROBE_MEMSX)) return 0; @@ -594,12 +603,17 @@ static int add_exception_handler(const struct bpf_insn *insn, return -EINVAL; ex = &ctx->prog->aux->extable[ctx->nexentries]; - pc = (unsigned long)&ctx->insns[ctx->ninsns - insn_len]; + pc = (unsigned long)&ctx->ro_insns[ctx->ninsns - insn_len]; - offset = pc - (long)&ex->insn; - if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN)) + /* + * This is the relative offset of the instruction that may fault from + * the exception table itself. This will be written to the exception + * table and if this instruction faults, the destination register will + * be set to '0' and the execution will jump to the next instruction. + */ + ins_offset = pc - (long)&ex->insn; + if (WARN_ON_ONCE(ins_offset >= 0 || ins_offset < INT_MIN)) return -ERANGE; - ex->insn = offset; /* * Since the extable follows the program, the fixup offset is always @@ -608,12 +622,25 @@ static int add_exception_handler(const struct bpf_insn *insn, * bits. We don't need to worry about buildtime or runtime sort * modifying the upper bits because the table is already sorted, and * isn't part of the main exception table. + * + * The fixup_offset is set to the next instruction from the instruction + * that may fault. The execution will jump to this after handling the + * fault. */ - offset = (long)&ex->fixup - (pc + insn_len * sizeof(u16)); - if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset)) + fixup_offset = (long)&ex->fixup - (pc + insn_len * sizeof(u16)); + if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, fixup_offset)) return -ERANGE; - ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | + /* + * The offsets above have been calculated using the RO buffer but we + * need to use the R/W buffer for writes. + * switch ex to rw buffer for writing. + */ + ex = (void *)ctx->insns + ((void *)ex - (void *)ctx->ro_insns); + + ex->insn = ins_offset; + + ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, fixup_offset) | FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg); ex->type = EX_TYPE_BPF; @@ -1007,6 +1034,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, ctx.ninsns = 0; ctx.insns = NULL; + ctx.ro_insns = NULL; ret = __arch_prepare_bpf_trampoline(im, m, tlinks, func_addr, flags, &ctx); if (ret < 0) return ret; @@ -1015,7 +1043,15 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, return -EFBIG; ctx.ninsns = 0; + /* + * The bpf_int_jit_compile() uses a RW buffer (ctx.insns) to write the + * JITed instructions and later copies it to a RX region (ctx.ro_insns). + * It also uses ctx.ro_insns to calculate offsets for jumps etc. As the + * trampoline image uses the same memory area for writing and execution, + * both ctx.insns and ctx.ro_insns can be set to image. + */ ctx.insns = image; + ctx.ro_insns = image; ret = __arch_prepare_bpf_trampoline(im, m, tlinks, func_addr, flags, &ctx); if (ret < 0) return ret; diff --git a/arch/riscv/net/bpf_jit_core.c b/arch/riscv/net/bpf_jit_core.c index 7a26a3e1c73cfe8c75c0090c546f83d185572e2c..7b70ccb7fec345847b2dc2ab4664472f0d9ca574 100644 --- a/arch/riscv/net/bpf_jit_core.c +++ b/arch/riscv/net/bpf_jit_core.c @@ -8,6 +8,8 @@ #include #include +#include +#include #include "bpf_jit.h" /* Number of iterations to try until offsets converge. */ @@ -117,16 +119,24 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) sizeof(struct exception_table_entry); prog_size = sizeof(*ctx->insns) * ctx->ninsns; - jit_data->header = - bpf_jit_binary_alloc(prog_size + extable_size, - &jit_data->image, - sizeof(u32), - bpf_fill_ill_insns); - if (!jit_data->header) { + jit_data->ro_header = + bpf_jit_binary_pack_alloc(prog_size + extable_size, + &jit_data->ro_image, sizeof(u32), + &jit_data->header, &jit_data->image, + bpf_fill_ill_insns); + if (!jit_data->ro_header) { prog = orig_prog; goto out_offset; } + /* + * Use the image(RW) for writing the JITed instructions. But also save + * the ro_image(RX) for calculating the offsets in the image. The RW + * image will be later copied to the RX image from where the program + * will run. The bpf_jit_binary_pack_finalize() will do this copy in the + * final step. + */ + ctx->ro_insns = (u16 *)jit_data->ro_image; ctx->insns = (u16 *)jit_data->image; /* * Now, when the image is allocated, the image can @@ -138,14 +148,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) if (i == NR_JIT_ITERATIONS) { pr_err("bpf-jit: image did not converge in <%d passes!\n", i); - if (jit_data->header) - bpf_jit_binary_free(jit_data->header); prog = orig_prog; - goto out_offset; + goto out_free_hdr; } if (extable_size) - prog->aux->extable = (void *)ctx->insns + prog_size; + prog->aux->extable = (void *)ctx->ro_insns + prog_size; skip_init_ctx: pass++; @@ -154,23 +162,33 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) bpf_jit_build_prologue(ctx); if (build_body(ctx, extra_pass, NULL)) { - bpf_jit_binary_free(jit_data->header); prog = orig_prog; - goto out_offset; + goto out_free_hdr; } bpf_jit_build_epilogue(ctx); if (bpf_jit_enable > 1) bpf_jit_dump(prog->len, prog_size, pass, ctx->insns); - prog->bpf_func = (void *)ctx->insns; + prog->bpf_func = (void *)ctx->ro_insns; prog->jited = 1; prog->jited_len = prog_size; - bpf_flush_icache(jit_data->header, ctx->insns + ctx->ninsns); - if (!prog->is_func || extra_pass) { - bpf_jit_binary_lock_ro(jit_data->header); + if (WARN_ON(bpf_jit_binary_pack_finalize(prog, jit_data->ro_header, + jit_data->header))) { + /* ro_header has been freed */ + jit_data->ro_header = NULL; + prog = orig_prog; + goto out_offset; + } + /* + * The instructions have now been copied to the ROX region from + * where they will execute. + * Write any modified data cache blocks out to memory and + * invalidate the corresponding blocks in the instruction cache. + */ + bpf_flush_icache(jit_data->ro_header, ctx->ro_insns + ctx->ninsns); for (i = 0; i < prog->len; i++) ctx->offset[i] = ninsns_rvoff(ctx->offset[i]); bpf_prog_fill_jited_linfo(prog, ctx->offset); @@ -185,6 +203,14 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); return prog; + +out_free_hdr: + if (jit_data->header) { + bpf_arch_text_copy(&jit_data->ro_header->size, &jit_data->header->size, + sizeof(jit_data->header->size)); + bpf_jit_binary_pack_free(jit_data->ro_header, jit_data->header); + } + goto out_offset; } u64 bpf_jit_alloc_exec_limit(void) @@ -204,3 +230,51 @@ void bpf_jit_free_exec(void *addr) { return vfree(addr); } + +void *bpf_arch_text_copy(void *dst, void *src, size_t len) +{ + int ret; + + mutex_lock(&text_mutex); + ret = patch_text_nosync(dst, src, len); + mutex_unlock(&text_mutex); + + if (ret) + return ERR_PTR(-EINVAL); + + return dst; +} + +int bpf_arch_text_invalidate(void *dst, size_t len) +{ + int ret; + + mutex_lock(&text_mutex); + ret = patch_text_set_nosync(dst, 0, len); + mutex_unlock(&text_mutex); + + return ret; +} + +void bpf_jit_free(struct bpf_prog *prog) +{ + if (prog->jited) { + struct rv_jit_data *jit_data = prog->aux->jit_data; + struct bpf_binary_header *hdr; + + /* + * If we fail the final pass of JIT (from jit_subprogs), + * the program may not be finalized yet. Call finalize here + * before freeing it. + */ + if (jit_data) { + bpf_jit_binary_pack_finalize(prog, jit_data->ro_header, jit_data->header); + kfree(jit_data); + } + hdr = bpf_jit_binary_pack_hdr(prog); + bpf_jit_binary_pack_free(hdr, NULL); + WARN_ON_ONCE(!bpf_prog_kallsyms_verify_off(prog)); + } + + bpf_prog_unlock_free(prog); +} diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c index 8753cb0339e559e8486dab3b54320d4379fe81a2..7b7521762633f9a75a1a4a09070436c7e2782ffc 100644 --- a/arch/s390/boot/ipl_parm.c +++ b/arch/s390/boot/ipl_parm.c @@ -19,7 +19,6 @@ struct parmarea parmarea __section(".parmarea") = { }; char __bootdata(early_command_line)[COMMAND_LINE_SIZE]; -int __bootdata(noexec_disabled); unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL; struct ipl_parameter_block __bootdata_preserved(ipl_block); @@ -290,12 +289,6 @@ void parse_boot_command_line(void) zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG; } - if (!strcmp(param, "noexec")) { - rc = kstrtobool(val, &enabled); - if (!rc && !enabled) - noexec_disabled = 1; - } - if (!strcmp(param, "facilities") && val) modify_fac_list(val); diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index b9681cb2275392d2293850592f1a381d5ad526ba..d3e48bd9c3944f17799e955cce5c78e246bdac14 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -53,10 +53,8 @@ static void detect_facilities(void) } if (test_facility(78)) machine.has_edat2 = 1; - if (!noexec_disabled && test_facility(130)) { + if (test_facility(130)) machine.has_nx = 1; - __ctl_set_bit(0, 20); - } } static void setup_lpp(void) diff --git a/arch/s390/boot/vmem.c b/arch/s390/boot/vmem.c index c67f59db7a51240a4fdca8064bebe051b64a736e..01257ce3b89c00f6fc124e2d4c3d7a331ee4250c 100644 --- a/arch/s390/boot/vmem.c +++ b/arch/s390/boot/vmem.c @@ -287,7 +287,9 @@ static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long e if (kasan_pte_populate_zero_shadow(pte, mode)) continue; entry = __pte(_pa(addr, PAGE_SIZE, mode)); - entry = set_pte_bit(entry, PAGE_KERNEL_EXEC); + entry = set_pte_bit(entry, PAGE_KERNEL); + if (!machine.has_nx) + entry = clear_pte_bit(entry, __pgprot(_PAGE_NOEXEC)); set_pte(pte, entry); pages++; } @@ -311,7 +313,9 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e continue; if (can_large_pmd(pmd, addr, next)) { entry = __pmd(_pa(addr, _SEGMENT_SIZE, mode)); - entry = set_pmd_bit(entry, SEGMENT_KERNEL_EXEC); + entry = set_pmd_bit(entry, SEGMENT_KERNEL); + if (!machine.has_nx) + entry = clear_pmd_bit(entry, __pgprot(_SEGMENT_ENTRY_NOEXEC)); set_pmd(pmd, entry); pages++; continue; @@ -342,7 +346,9 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e continue; if (can_large_pud(pud, addr, next)) { entry = __pud(_pa(addr, _REGION3_SIZE, mode)); - entry = set_pud_bit(entry, REGION3_KERNEL_EXEC); + entry = set_pud_bit(entry, REGION3_KERNEL); + if (!machine.has_nx) + entry = clear_pud_bit(entry, __pgprot(_REGION_ENTRY_NOEXEC)); set_pud(pud, entry); pages++; continue; diff --git a/arch/s390/configs/btf.config b/arch/s390/configs/btf.config index 39227b4511aff8f781d8aa72657fce051a9d981f..eb7f84f5925cf36a9bc21b7d398a5c56d498b6d8 100644 --- a/arch/s390/configs/btf.config +++ b/arch/s390/configs/btf.config @@ -1 +1,2 @@ +# Help: Enable BTF debug info CONFIG_DEBUG_INFO_BTF=y diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index af2fbe48e16cd9fc22eecdc1be964c8fae4cbb5c..438cd92e60801bd3c12cd1f891c5a9009fe115f8 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -40,23 +40,25 @@ CONFIG_SCHED_AUTOGROUP=y CONFIG_EXPERT=y # CONFIG_SYSFS_SYSCALL is not set CONFIG_PROFILING=y +CONFIG_KEXEC_FILE=y +CONFIG_KEXEC_SIG=y +CONFIG_CRASH_DUMP=y CONFIG_LIVEPATCH=y CONFIG_MARCH_ZEC12=y CONFIG_TUNE_ZEC12=y CONFIG_NR_CPUS=512 CONFIG_NUMA=y CONFIG_HZ_100=y -CONFIG_KEXEC_FILE=y -CONFIG_KEXEC_SIG=y +CONFIG_CERT_STORE=y CONFIG_EXPOLINE=y CONFIG_EXPOLINE_AUTO=y CONFIG_CHSC_SCH=y CONFIG_VFIO_CCW=m CONFIG_VFIO_AP=m -CONFIG_CRASH_DUMP=y CONFIG_PROTECTED_VIRTUALIZATION_GUEST=y CONFIG_CMM=m CONFIG_APPLDATA_BASE=y +CONFIG_S390_HYPFS_FS=y CONFIG_KVM=m CONFIG_S390_UNWIND_SELFTEST=m CONFIG_S390_KPROBES_SANITY_TEST=m @@ -434,6 +436,7 @@ CONFIG_SCSI_DH_EMC=m CONFIG_SCSI_DH_ALUA=m CONFIG_MD=y CONFIG_BLK_DEV_MD=y +# CONFIG_MD_BITMAP_FILE is not set CONFIG_MD_LINEAR=m CONFIG_MD_MULTIPATH=m CONFIG_MD_FAULTY=m @@ -577,6 +580,7 @@ CONFIG_SOFT_WATCHDOG=m CONFIG_DIAG288_WATCHDOG=m # CONFIG_DRM_DEBUG_MODESET_LOCK is not set CONFIG_FB=y +# CONFIG_FB_DEVICE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID_SUPPORT is not set @@ -647,6 +651,7 @@ CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_INODE64=y +CONFIG_TMPFS_QUOTA=y CONFIG_HUGETLBFS=y CONFIG_ECRYPT_FS=m CONFIG_CRAMFS=m @@ -703,6 +708,7 @@ CONFIG_IMA_WRITE_POLICY=y CONFIG_IMA_APPRAISE=y CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" CONFIG_INIT_STACK_NONE=y +CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_CRYPTO_USER=m # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set CONFIG_CRYPTO_PCRYPT=m @@ -825,9 +831,9 @@ CONFIG_LOCK_STAT=y CONFIG_DEBUG_ATOMIC_SLEEP=y CONFIG_DEBUG_LOCKING_API_SELFTESTS=y CONFIG_DEBUG_IRQFLAGS=y +CONFIG_DEBUG_LIST=y CONFIG_DEBUG_SG=y CONFIG_DEBUG_NOTIFIERS=y -CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_DEBUG_CREDENTIALS=y CONFIG_RCU_TORTURE_TEST=m CONFIG_RCU_REF_SCALE_TEST=m diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index 3f263b767a4c4198f098a017dd83db427ecbcc37..1b8150e50f6a65a9d0dadbbf6bb1ee7e2785f8d3 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -38,23 +38,25 @@ CONFIG_SCHED_AUTOGROUP=y CONFIG_EXPERT=y # CONFIG_SYSFS_SYSCALL is not set CONFIG_PROFILING=y +CONFIG_KEXEC_FILE=y +CONFIG_KEXEC_SIG=y +CONFIG_CRASH_DUMP=y CONFIG_LIVEPATCH=y CONFIG_MARCH_ZEC12=y CONFIG_TUNE_ZEC12=y CONFIG_NR_CPUS=512 CONFIG_NUMA=y CONFIG_HZ_100=y -CONFIG_KEXEC_FILE=y -CONFIG_KEXEC_SIG=y +CONFIG_CERT_STORE=y CONFIG_EXPOLINE=y CONFIG_EXPOLINE_AUTO=y CONFIG_CHSC_SCH=y CONFIG_VFIO_CCW=m CONFIG_VFIO_AP=m -CONFIG_CRASH_DUMP=y CONFIG_PROTECTED_VIRTUALIZATION_GUEST=y CONFIG_CMM=m CONFIG_APPLDATA_BASE=y +CONFIG_S390_HYPFS_FS=y CONFIG_KVM=m CONFIG_S390_UNWIND_SELFTEST=m CONFIG_S390_KPROBES_SANITY_TEST=m @@ -424,6 +426,7 @@ CONFIG_SCSI_DH_EMC=m CONFIG_SCSI_DH_ALUA=m CONFIG_MD=y CONFIG_BLK_DEV_MD=y +# CONFIG_MD_BITMAP_FILE is not set CONFIG_MD_LINEAR=m CONFIG_MD_MULTIPATH=m CONFIG_MD_FAULTY=m @@ -566,6 +569,7 @@ CONFIG_WATCHDOG_NOWAYOUT=y CONFIG_SOFT_WATCHDOG=m CONFIG_DIAG288_WATCHDOG=m CONFIG_FB=y +# CONFIG_FB_DEVICE is not set CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y # CONFIG_HID_SUPPORT is not set @@ -632,6 +636,7 @@ CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_INODE64=y +CONFIG_TMPFS_QUOTA=y CONFIG_HUGETLBFS=y CONFIG_CONFIGFS_FS=m CONFIG_ECRYPT_FS=m @@ -687,6 +692,7 @@ CONFIG_IMA_WRITE_POLICY=y CONFIG_IMA_APPRAISE=y CONFIG_LSM="yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" CONFIG_INIT_STACK_NONE=y +CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_CRYPTO_FIPS=y CONFIG_CRYPTO_USER=m # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set @@ -781,7 +787,6 @@ CONFIG_PTDUMP_DEBUGFS=y CONFIG_DEBUG_MEMORY_INIT=y CONFIG_PANIC_ON_OOPS=y CONFIG_TEST_LOCKUP=m -CONFIG_BUG_ON_DATA_CORRUPTION=y CONFIG_RCU_TORTURE_TEST=m CONFIG_RCU_REF_SCALE_TEST=m CONFIG_RCU_CPU_STALL_TIMEOUT=60 diff --git a/arch/s390/configs/kasan.config b/arch/s390/configs/kasan.config index 700a8b25c3ffc057f59e099590574ddf15ce0bd1..84c2b551e9927bb26b3a68a5cafa969be2c857b7 100644 --- a/arch/s390/configs/kasan.config +++ b/arch/s390/configs/kasan.config @@ -1,3 +1,4 @@ +# Help: Enable KASan for debugging CONFIG_KASAN=y CONFIG_KASAN_INLINE=y CONFIG_KASAN_VMALLOC=y diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig index e62fb20151028fd53cbf28b065694ee81b778f01..b831083b4edd8b796b949333b5ca279c96eda237 100644 --- a/arch/s390/configs/zfcpdump_defconfig +++ b/arch/s390/configs/zfcpdump_defconfig @@ -8,6 +8,7 @@ CONFIG_BPF_SYSCALL=y # CONFIG_NET_NS is not set CONFIG_BLK_DEV_INITRD=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CRASH_DUMP=y CONFIG_MARCH_ZEC12=y CONFIG_TUNE_ZEC12=y # CONFIG_COMPAT is not set @@ -15,9 +16,8 @@ CONFIG_NR_CPUS=2 CONFIG_HZ_100=y # CONFIG_CHSC_SCH is not set # CONFIG_SCM_BUS is not set -CONFIG_CRASH_DUMP=y # CONFIG_PFAULT is not set -# CONFIG_S390_HYPFS_FS is not set +# CONFIG_S390_HYPFS is not set # CONFIG_VIRTUALIZATION is not set # CONFIG_S390_GUEST is not set # CONFIG_SECCOMP is not set diff --git a/arch/s390/include/asm/airq.h b/arch/s390/include/asm/airq.h index e82e5626e139ed634aa8b9a4e7e685774c5ad82a..c4c28c2609a51a0a46eb5ec3a3efff877d322356 100644 --- a/arch/s390/include/asm/airq.h +++ b/arch/s390/include/asm/airq.h @@ -18,7 +18,6 @@ struct airq_struct { struct hlist_node list; /* Handler queueing. */ void (*handler)(struct airq_struct *airq, struct tpi_info *tpi_info); u8 *lsi_ptr; /* Local-Summary-Indicator pointer */ - u8 lsi_mask; /* Local-Summary-Indicator mask */ u8 isc; /* Interrupt-subclass */ u8 flags; }; diff --git a/arch/s390/include/asm/dma.h b/arch/s390/include/asm/dma.h index c260adb259979eed4edf671f805e2a5c87f88752..7fe3e31956d74075d52aacf918ecba8f935f138a 100644 --- a/arch/s390/include/asm/dma.h +++ b/arch/s390/include/asm/dma.h @@ -9,6 +9,6 @@ * to DMA. It _is_ used for the s390 memory zone split at 2GB caused * by the 31 bit heritage. */ -#define MAX_DMA_ADDRESS 0x80000000 +#define MAX_DMA_ADDRESS __va(0x80000000) #endif /* _ASM_S390_DMA_H */ diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h index f07267875a198760adfb149721c3497044fe478a..deb198a610395bc410bd09a30bdddbfb33b41d65 100644 --- a/arch/s390/include/asm/hugetlb.h +++ b/arch/s390/include/asm/hugetlb.h @@ -16,6 +16,8 @@ #define hugepages_supported() (MACHINE_HAS_EDAT1) void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte, unsigned long sz); +void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte); pte_t huge_ptep_get(pte_t *ptep); pte_t huge_ptep_get_and_clear(struct mm_struct *mm, @@ -65,7 +67,7 @@ static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, int changed = !pte_same(huge_ptep_get(ptep), pte); if (changed) { huge_ptep_get_and_clear(vma->vm_mm, addr, ptep); - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + __set_huge_pte_at(vma->vm_mm, addr, ptep, pte); } return changed; } @@ -74,7 +76,7 @@ static inline void huge_ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { pte_t pte = huge_ptep_get_and_clear(mm, addr, ptep); - set_huge_pte_at(mm, addr, ptep, pte_wrprotect(pte)); + __set_huge_pte_at(mm, addr, ptep, pte_wrprotect(pte)); } static inline pte_t mk_huge_pte(struct page *page, pgprot_t pgprot) diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 91bfecb913210c24d7d42f723c7e46a44b710ab7..427f9528a7b694820b7f0697940f6d7d06934dfe 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -817,6 +817,8 @@ struct kvm_s390_cpu_model { __u64 *fac_list; u64 cpuid; unsigned short ibc; + /* subset of available UV-features for pv-guests enabled by user space */ + struct kvm_s390_vm_cpu_uv_feat uv_feat_guest; }; typedef int (*crypto_hook)(struct kvm_vcpu *vcpu); diff --git a/arch/s390/include/asm/sections.h b/arch/s390/include/asm/sections.h index 3fecaa4e8b74ddaa9ba3aa6d5414ff7c262242cf..0486e6ef62bf0f3343385b5d831a9267b01988fa 100644 --- a/arch/s390/include/asm/sections.h +++ b/arch/s390/include/asm/sections.h @@ -23,7 +23,7 @@ */ #define __bootdata_preserved(var) __section(".boot.preserved.data." #var) var -extern unsigned long __samode31, __eamode31; -extern unsigned long __stext_amode31, __etext_amode31; +extern char *__samode31, *__eamode31; +extern char *__stext_amode31, *__etext_amode31; #endif diff --git a/arch/s390/include/asm/set_memory.h b/arch/s390/include/asm/set_memory.h index 7a3eefd7a24236453015178ac896fada8c2a7c76..06fbabe2f66c982aeae972ec0539c044a85f9ebd 100644 --- a/arch/s390/include/asm/set_memory.h +++ b/arch/s390/include/asm/set_memory.h @@ -24,43 +24,41 @@ enum { #define SET_MEMORY_INV BIT(_SET_MEMORY_INV_BIT) #define SET_MEMORY_DEF BIT(_SET_MEMORY_DEF_BIT) -int __set_memory(unsigned long addr, int numpages, unsigned long flags); - -static inline int set_memory_ro(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_RO); -} - -static inline int set_memory_rw(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_RW); -} - -static inline int set_memory_nx(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_NX); -} - -static inline int set_memory_x(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_X); -} +int __set_memory(unsigned long addr, unsigned long numpages, unsigned long flags); #define set_memory_rox set_memory_rox -static inline int set_memory_rox(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_RO | SET_MEMORY_X); -} -static inline int set_memory_rwnx(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_RW | SET_MEMORY_NX); +/* + * Generate two variants of each set_memory() function: + * + * set_memory_yy(unsigned long addr, int numpages); + * __set_memory_yy(void *start, void *end); + * + * The second variant exists for both convenience to avoid the usual + * (unsigned long) casts, but unlike the first variant it can also be used + * for areas larger than 8TB, which may happen at memory initialization. + */ +#define __SET_MEMORY_FUNC(fname, flags) \ +static inline int fname(unsigned long addr, int numpages) \ +{ \ + return __set_memory(addr, numpages, (flags)); \ +} \ + \ +static inline int __##fname(void *start, void *end) \ +{ \ + unsigned long numpages; \ + \ + numpages = (end - start) >> PAGE_SHIFT; \ + return __set_memory((unsigned long)start, numpages, (flags)); \ } -static inline int set_memory_4k(unsigned long addr, int numpages) -{ - return __set_memory(addr, numpages, SET_MEMORY_4K); -} +__SET_MEMORY_FUNC(set_memory_ro, SET_MEMORY_RO) +__SET_MEMORY_FUNC(set_memory_rw, SET_MEMORY_RW) +__SET_MEMORY_FUNC(set_memory_nx, SET_MEMORY_NX) +__SET_MEMORY_FUNC(set_memory_x, SET_MEMORY_X) +__SET_MEMORY_FUNC(set_memory_rox, SET_MEMORY_RO | SET_MEMORY_X) +__SET_MEMORY_FUNC(set_memory_rwnx, SET_MEMORY_RW | SET_MEMORY_NX) +__SET_MEMORY_FUNC(set_memory_4k, SET_MEMORY_4K) int set_direct_map_invalid_noflush(struct page *page); int set_direct_map_default_noflush(struct page *page); diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index b30fe91166e3312c9c8579b12882e55a252b143e..25cadc2b9cff277e46ec688bd20b288fb095f77e 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h @@ -72,7 +72,6 @@ extern unsigned int zlib_dfltcc_support; #define ZLIB_DFLTCC_INFLATE_ONLY 3 #define ZLIB_DFLTCC_FULL_DEBUG 4 -extern int noexec_disabled; extern unsigned long ident_map_size; extern unsigned long max_mappable; diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index d2cd42bb2c26ef6d0957d7e899fd37575b1f15be..0e7bd3873907f7bd6a3fe575908d874c7c672764 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -99,6 +99,8 @@ enum uv_cmds_inst { enum uv_feat_ind { BIT_UV_FEAT_MISC = 0, BIT_UV_FEAT_AIV = 1, + BIT_UV_FEAT_AP = 4, + BIT_UV_FEAT_AP_INTR = 5, }; struct uv_cb_header { @@ -159,7 +161,15 @@ struct uv_cb_cgc { u64 guest_handle; u64 conf_base_stor_origin; u64 conf_virt_stor_origin; - u64 reserved30; + u8 reserved30[6]; + union { + struct { + u16 : 14; + u16 ap_instr_intr : 1; + u16 ap_allow_instr : 1; + }; + u16 raw; + } flags; u64 guest_stor_origin; u64 guest_stor_len; u64 guest_sca; @@ -397,6 +407,13 @@ struct uv_info { extern struct uv_info uv_info; +static inline bool uv_has_feature(u8 feature_bit) +{ + if (feature_bit >= sizeof(uv_info.uv_feature_indications) * 8) + return false; + return test_bit_inv(feature_bit, &uv_info.uv_feature_indications); +} + #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST extern int prot_virt_guest; diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h index a73cf01a1606671bf77a995c665f90ca7428c9ab..abe926d43cbe0a06342f8c53f202cdd707ea1693 100644 --- a/arch/s390/include/uapi/asm/kvm.h +++ b/arch/s390/include/uapi/asm/kvm.h @@ -159,6 +159,22 @@ struct kvm_s390_vm_cpu_subfunc { __u8 reserved[1728]; }; +#define KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST 6 +#define KVM_S390_VM_CPU_MACHINE_UV_FEAT_GUEST 7 + +#define KVM_S390_VM_CPU_UV_FEAT_NR_BITS 64 +struct kvm_s390_vm_cpu_uv_feat { + union { + struct { + __u64 : 4; + __u64 ap : 1; /* bit 4 */ + __u64 ap_intr : 1; /* bit 5 */ + __u64 : 58; + }; + __u64 feat; + }; +}; + /* kvm attributes for crypto */ #define KVM_S390_VM_CRYPTO_ENABLE_AES_KW 0 #define KVM_S390_VM_CRYPTO_ENABLE_DEA_KW 1 diff --git a/arch/s390/kernel/cert_store.c b/arch/s390/kernel/cert_store.c index 3986a044eb367f5ab08f3f8b0e9ac58f834e1236..554447768bddc7d0d27aa16ddbf0727c6ef95d4e 100644 --- a/arch/s390/kernel/cert_store.c +++ b/arch/s390/kernel/cert_store.c @@ -432,15 +432,16 @@ static char *get_key_description(struct vcssb *vcssb, const struct vce *vce) char *desc; cs_token = vcssb->cs_token; - /* Description string contains "%64s:%04u:%08u\0". */ + /* Description string contains "%64s:%05u:%010u\0". */ name_len = sizeof(vce->vce_hdr.vc_name); - len = name_len + 1 + 4 + 1 + 8 + 1; + len = name_len + 1 + 5 + 1 + 10 + 1; desc = kmalloc(len, GFP_KERNEL); if (!desc) return NULL; memcpy(desc, vce->vce_hdr.vc_name, name_len); - sprintf(desc + name_len, ":%04u:%08u", vce->vce_hdr.vc_index, cs_token); + snprintf(desc + name_len, len - name_len, ":%05u:%010u", + vce->vce_hdr.vc_index, cs_token); return desc; } diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 2dd5976a55ac221ec2aafd38c05587984112a6ac..442ce0489e1a1e4515fc0576549cb87da23e2d40 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c @@ -44,7 +44,6 @@ early_param(#param, ignore_decompressor_param_##param) decompressor_handled_param(mem); decompressor_handled_param(vmalloc); decompressor_handled_param(dfltcc); -decompressor_handled_param(noexec); decompressor_handled_param(facilities); decompressor_handled_param(nokaslr); #if IS_ENABLED(CONFIG_KVM) @@ -233,10 +232,8 @@ static __init void detect_machine_facilities(void) S390_lowcore.machine_flags |= MACHINE_FLAG_VX; __ctl_set_bit(0, 17); } - if (test_facility(130) && !noexec_disabled) { + if (test_facility(130)) S390_lowcore.machine_flags |= MACHINE_FLAG_NX; - __ctl_set_bit(0, 20); - } if (test_facility(133)) S390_lowcore.machine_flags |= MACHINE_FLAG_GS; if (test_facility(139) && (tod_clock_base.tod >> 63)) { diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 12a2bd4fc88cb99f9116176c0948fc04170a2f31..ce65fc01671f93c3905bb94dd472cfa1b14ec2cd 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c @@ -216,8 +216,8 @@ void arch_crash_save_vmcoreinfo(void) VMCOREINFO_SYMBOL(lowcore_ptr); VMCOREINFO_SYMBOL(high_memory); VMCOREINFO_LENGTH(lowcore_ptr, NR_CPUS); - vmcoreinfo_append_str("SAMODE31=%lx\n", __samode31); - vmcoreinfo_append_str("EAMODE31=%lx\n", __eamode31); + vmcoreinfo_append_str("SAMODE31=%lx\n", (unsigned long)__samode31); + vmcoreinfo_append_str("EAMODE31=%lx\n", (unsigned long)__eamode31); vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset()); abs_lc = get_abs_lowcore(); abs_lc->vmcore_info = paddr_vmcoreinfo_note(); diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index c744104e4a9c9a28483f053c8b2d1751b94e33f7..de6ad0fb2328a1b551105151da26bdb6b59c5fb2 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -97,10 +97,10 @@ EXPORT_SYMBOL(console_irq); * relocated above 2 GB, because it has to use 31 bit addresses. * Such code and data is part of the .amode31 section. */ -unsigned long __amode31_ref __samode31 = (unsigned long)&_samode31; -unsigned long __amode31_ref __eamode31 = (unsigned long)&_eamode31; -unsigned long __amode31_ref __stext_amode31 = (unsigned long)&_stext_amode31; -unsigned long __amode31_ref __etext_amode31 = (unsigned long)&_etext_amode31; +char __amode31_ref *__samode31 = _samode31; +char __amode31_ref *__eamode31 = _eamode31; +char __amode31_ref *__stext_amode31 = _stext_amode31; +char __amode31_ref *__etext_amode31 = _etext_amode31; struct exception_table_entry __amode31_ref *__start_amode31_ex_table = _start_amode31_ex_table; struct exception_table_entry __amode31_ref *__stop_amode31_ex_table = _stop_amode31_ex_table; @@ -145,7 +145,6 @@ static u32 __amode31_ref *__ctl_duald = __ctl_duald_amode31; static u32 __amode31_ref *__ctl_linkage_stack = __ctl_linkage_stack_amode31; static u32 __amode31_ref *__ctl_duct = __ctl_duct_amode31; -int __bootdata(noexec_disabled); unsigned long __bootdata_preserved(max_mappable); unsigned long __bootdata(ident_map_size); struct physmem_info __bootdata(physmem_info); @@ -771,15 +770,15 @@ static void __init setup_memory(void) static void __init relocate_amode31_section(void) { unsigned long amode31_size = __eamode31 - __samode31; - long amode31_offset = physmem_info.reserved[RR_AMODE31].start - __samode31; - long *ptr; + long amode31_offset, *ptr; + amode31_offset = physmem_info.reserved[RR_AMODE31].start - (unsigned long)__samode31; pr_info("Relocating AMODE31 section of size 0x%08lx\n", amode31_size); /* Move original AMODE31 section to the new one */ - memmove((void *)physmem_info.reserved[RR_AMODE31].start, (void *)__samode31, amode31_size); + memmove((void *)physmem_info.reserved[RR_AMODE31].start, __samode31, amode31_size); /* Zero out the old AMODE31 section to catch invalid accesses within it */ - memset((void *)__samode31, 0, amode31_size); + memset(__samode31, 0, amode31_size); /* Update all AMODE31 region references */ for (ptr = _start_amode31_refs; ptr != _end_amode31_refs; ptr++) diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index b771f1b4cdd1591fabbbeb8efa13ff5179e86a9a..fc07bc39e69839698e2ca4a9db424b9d0fe70aba 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -258,7 +258,7 @@ static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_str * shared page from a different protected VM will automatically also * transfer its ownership. */ - if (test_bit_inv(BIT_UV_FEAT_MISC, &uv_info.uv_feature_indications)) + if (uv_has_feature(BIT_UV_FEAT_MISC)) return false; if (uvcb->cmd == UVC_CMD_UNPIN_PAGE_SHARED) return false; diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 341abafb96e49aa43358c8b02a7ce43a4acf34d2..b16352083ff987277a498532aaf5a7d78bf9b856 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -228,6 +228,21 @@ static int handle_itdb(struct kvm_vcpu *vcpu) #define per_event(vcpu) (vcpu->arch.sie_block->iprcc & PGM_PER) +static bool should_handle_per_event(const struct kvm_vcpu *vcpu) +{ + if (!guestdbg_enabled(vcpu) || !per_event(vcpu)) + return false; + if (guestdbg_sstep_enabled(vcpu) && + vcpu->arch.sie_block->iprcc != PGM_PER) { + /* + * __vcpu_run() will exit after delivering the concurrently + * indicated condition. + */ + return false; + } + return true; +} + static int handle_prog(struct kvm_vcpu *vcpu) { psw_t psw; @@ -242,7 +257,7 @@ static int handle_prog(struct kvm_vcpu *vcpu) if (kvm_s390_pv_cpu_is_protected(vcpu)) return -EOPNOTSUPP; - if (guestdbg_enabled(vcpu) && per_event(vcpu)) { + if (should_handle_per_event(vcpu)) { rc = kvm_s390_handle_per_event(vcpu); if (rc) return rc; @@ -571,6 +586,19 @@ static int handle_pv_notification(struct kvm_vcpu *vcpu) return handle_instruction(vcpu); } +static bool should_handle_per_ifetch(const struct kvm_vcpu *vcpu, int rc) +{ + /* Process PER, also if the instruction is processed in user space. */ + if (!(vcpu->arch.sie_block->icptstatus & 0x02)) + return false; + if (rc != 0 && rc != -EOPNOTSUPP) + return false; + if (guestdbg_sstep_enabled(vcpu) && vcpu->arch.local_int.pending_irqs) + /* __vcpu_run() will exit after delivering the interrupt. */ + return false; + return true; +} + int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) { int rc, per_rc = 0; @@ -605,8 +633,8 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) rc = handle_partial_execution(vcpu); break; case ICPT_KSS: - rc = kvm_s390_skey_check_enable(vcpu); - break; + /* Instruction will be redriven, skip the PER check. */ + return kvm_s390_skey_check_enable(vcpu); case ICPT_MCHKREQ: case ICPT_INT_ENABLE: /* @@ -633,9 +661,7 @@ int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu) return -EOPNOTSUPP; } - /* process PER, also if the instruction is processed in user space */ - if (vcpu->arch.sie_block->icptstatus & 0x02 && - (!rc || rc == -EOPNOTSUPP)) + if (should_handle_per_ifetch(vcpu, rc)) per_rc = kvm_s390_handle_per_ifetch_icpt(vcpu); return per_rc ? per_rc : rc; } diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 9bd0a873f3b15f984bb06e0ceebcc6b7343665fc..c1b47d608a2b206debef90c4f59e293ca33011e4 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -1392,6 +1392,7 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; int rc = 0; + bool delivered = false; unsigned long irq_type; unsigned long irqs; @@ -1465,6 +1466,19 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) WARN_ONCE(1, "Unknown pending irq type %ld", irq_type); clear_bit(irq_type, &li->pending_irqs); } + delivered |= !rc; + } + + /* + * We delivered at least one interrupt and modified the PC. Force a + * singlestep event now. + */ + if (delivered && guestdbg_sstep_enabled(vcpu)) { + struct kvm_debug_exit_arch *debug_exit = &vcpu->run->debug.arch; + + debug_exit->addr = vcpu->arch.sie_block->gpsw.addr; + debug_exit->type = KVM_SINGLESTEP; + vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING; } set_intercept_indicators(vcpu); @@ -3398,7 +3412,6 @@ static void gib_alert_irq_handler(struct airq_struct *airq, static struct airq_struct gib_alert_irq = { .handler = gib_alert_irq_handler, - .lsi_ptr = &gib_alert_irq.lsi_mask, }; void kvm_s390_gib_destroy(void) @@ -3438,6 +3451,8 @@ int __init kvm_s390_gib_init(u8 nisc) rc = -EIO; goto out_free_gib; } + /* adapter interrupts used for AP (applicable here) don't use the LSI */ + *gib_alert_irq.lsi_ptr = 0xff; gib->nisc = nisc; gib_origin = virt_to_phys(gib); diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index d1e768bcfe1d380fc6653c6376c0ad4f366c0a9a..b3f17e014cab5d3d912c5393eac5498f3e135591 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -1531,6 +1531,39 @@ static int kvm_s390_set_processor_subfunc(struct kvm *kvm, return 0; } +#define KVM_S390_VM_CPU_UV_FEAT_GUEST_MASK \ +( \ + ((struct kvm_s390_vm_cpu_uv_feat){ \ + .ap = 1, \ + .ap_intr = 1, \ + }) \ + .feat \ +) + +static int kvm_s390_set_uv_feat(struct kvm *kvm, struct kvm_device_attr *attr) +{ + struct kvm_s390_vm_cpu_uv_feat __user *ptr = (void __user *)attr->addr; + unsigned long data, filter; + + filter = uv_info.uv_feature_indications & KVM_S390_VM_CPU_UV_FEAT_GUEST_MASK; + if (get_user(data, &ptr->feat)) + return -EFAULT; + if (!bitmap_subset(&data, &filter, KVM_S390_VM_CPU_UV_FEAT_NR_BITS)) + return -EINVAL; + + mutex_lock(&kvm->lock); + if (kvm->created_vcpus) { + mutex_unlock(&kvm->lock); + return -EBUSY; + } + kvm->arch.model.uv_feat_guest.feat = data; + mutex_unlock(&kvm->lock); + + VM_EVENT(kvm, 3, "SET: guest UV-feat: 0x%16.16lx", data); + + return 0; +} + static int kvm_s390_set_cpu_model(struct kvm *kvm, struct kvm_device_attr *attr) { int ret = -ENXIO; @@ -1545,6 +1578,9 @@ static int kvm_s390_set_cpu_model(struct kvm *kvm, struct kvm_device_attr *attr) case KVM_S390_VM_CPU_PROCESSOR_SUBFUNC: ret = kvm_s390_set_processor_subfunc(kvm, attr); break; + case KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST: + ret = kvm_s390_set_uv_feat(kvm, attr); + break; } return ret; } @@ -1777,6 +1813,33 @@ static int kvm_s390_get_machine_subfunc(struct kvm *kvm, return 0; } +static int kvm_s390_get_processor_uv_feat(struct kvm *kvm, struct kvm_device_attr *attr) +{ + struct kvm_s390_vm_cpu_uv_feat __user *dst = (void __user *)attr->addr; + unsigned long feat = kvm->arch.model.uv_feat_guest.feat; + + if (put_user(feat, &dst->feat)) + return -EFAULT; + VM_EVENT(kvm, 3, "GET: guest UV-feat: 0x%16.16lx", feat); + + return 0; +} + +static int kvm_s390_get_machine_uv_feat(struct kvm *kvm, struct kvm_device_attr *attr) +{ + struct kvm_s390_vm_cpu_uv_feat __user *dst = (void __user *)attr->addr; + unsigned long feat; + + BUILD_BUG_ON(sizeof(*dst) != sizeof(uv_info.uv_feature_indications)); + + feat = uv_info.uv_feature_indications & KVM_S390_VM_CPU_UV_FEAT_GUEST_MASK; + if (put_user(feat, &dst->feat)) + return -EFAULT; + VM_EVENT(kvm, 3, "GET: guest UV-feat: 0x%16.16lx", feat); + + return 0; +} + static int kvm_s390_get_cpu_model(struct kvm *kvm, struct kvm_device_attr *attr) { int ret = -ENXIO; @@ -1800,6 +1863,12 @@ static int kvm_s390_get_cpu_model(struct kvm *kvm, struct kvm_device_attr *attr) case KVM_S390_VM_CPU_MACHINE_SUBFUNC: ret = kvm_s390_get_machine_subfunc(kvm, attr); break; + case KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST: + ret = kvm_s390_get_processor_uv_feat(kvm, attr); + break; + case KVM_S390_VM_CPU_MACHINE_UV_FEAT_GUEST: + ret = kvm_s390_get_machine_uv_feat(kvm, attr); + break; } return ret; } @@ -1952,6 +2021,8 @@ static int kvm_s390_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr) case KVM_S390_VM_CPU_MACHINE_FEAT: case KVM_S390_VM_CPU_MACHINE_SUBFUNC: case KVM_S390_VM_CPU_PROCESSOR_SUBFUNC: + case KVM_S390_VM_CPU_MACHINE_UV_FEAT_GUEST: + case KVM_S390_VM_CPU_PROCESSOR_UV_FEAT_GUEST: ret = 0; break; default: @@ -2406,7 +2477,7 @@ static int kvm_s390_cpus_to_pv(struct kvm *kvm, u16 *rc, u16 *rrc) struct kvm_vcpu *vcpu; /* Disable the GISA if the ultravisor does not support AIV. */ - if (!test_bit_inv(BIT_UV_FEAT_AIV, &uv_info.uv_feature_indications)) + if (!uv_has_feature(BIT_UV_FEAT_AIV)) kvm_s390_gisa_disable(kvm); kvm_for_each_vcpu(i, vcpu, kvm) { @@ -3296,6 +3367,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.model.cpuid = kvm_s390_get_initial_cpuid(); kvm->arch.model.ibc = sclp.ibc & 0x0fff; + kvm->arch.model.uv_feat_guest.feat = 0; + kvm_s390_crypto_init(kvm); if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) { @@ -4611,7 +4684,7 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu) if (!kvm_is_ucontrol(vcpu->kvm)) { rc = kvm_s390_deliver_pending_interrupts(vcpu); - if (rc) + if (rc || guestdbg_exit_pending(vcpu)) return rc; } @@ -4738,7 +4811,7 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) do { rc = vcpu_pre_run(vcpu); - if (rc) + if (rc || guestdbg_exit_pending(vcpu)) break; kvm_vcpu_srcu_read_unlock(vcpu); @@ -5383,6 +5456,7 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp, { struct kvm_vcpu *vcpu = filp->private_data; void __user *argp = (void __user *)arg; + int rc; switch (ioctl) { case KVM_S390_IRQ: { @@ -5390,7 +5464,8 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp, if (copy_from_user(&s390irq, argp, sizeof(s390irq))) return -EFAULT; - return kvm_s390_inject_vcpu(vcpu, &s390irq); + rc = kvm_s390_inject_vcpu(vcpu, &s390irq); + break; } case KVM_S390_INTERRUPT: { struct kvm_s390_interrupt s390int; @@ -5400,10 +5475,25 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp, return -EFAULT; if (s390int_to_s390irq(&s390int, &s390irq)) return -EINVAL; - return kvm_s390_inject_vcpu(vcpu, &s390irq); + rc = kvm_s390_inject_vcpu(vcpu, &s390irq); + break; } + default: + rc = -ENOIOCTLCMD; + break; } - return -ENOIOCTLCMD; + + /* + * To simplify single stepping of userspace-emulated instructions, + * KVM_EXIT_S390_SIEIC exit sets KVM_GUESTDBG_EXIT_PENDING (see + * should_handle_per_ifetch()). However, if userspace emulation injects + * an interrupt, it needs to be cleared, so that KVM_EXIT_DEBUG happens + * after (and not before) the interrupt delivery. + */ + if (!rc) + vcpu->guest_debug &= ~KVM_GUESTDBG_EXIT_PENDING; + + return rc; } static int kvm_s390_handle_pv_vcpu_dump(struct kvm_vcpu *vcpu, diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 8d3f39a8a11eaaba65c1bd651430b82c4b776e0a..75e81ba26d04747adc06bac7dd3ed0502424d35c 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -285,7 +285,8 @@ static int kvm_s390_pv_deinit_vm_fast(struct kvm *kvm, u16 *rc, u16 *rrc) WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY VM FAST: rc %x rrc %x", uvcb.header.rc, uvcb.header.rrc); - WARN_ONCE(cc, "protvirt destroy vm fast failed handle %llx rc %x rrc %x", + WARN_ONCE(cc && uvcb.header.rc != 0x104, + "protvirt destroy vm fast failed handle %llx rc %x rrc %x", kvm_s390_pv_get_handle(kvm), uvcb.header.rc, uvcb.header.rrc); /* Intended memory leak on "impossible" error */ if (!cc) @@ -575,12 +576,14 @@ int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc) uvcb.conf_base_stor_origin = virt_to_phys((void *)kvm->arch.pv.stor_base); uvcb.conf_virt_stor_origin = (u64)kvm->arch.pv.stor_var; + uvcb.flags.ap_allow_instr = kvm->arch.model.uv_feat_guest.ap; + uvcb.flags.ap_instr_intr = kvm->arch.model.uv_feat_guest.ap_intr; cc = uv_call_sched(0, (u64)&uvcb); *rc = uvcb.header.rc; *rrc = uvcb.header.rrc; - KVM_UV_EVENT(kvm, 3, "PROTVIRT CREATE VM: handle %llx len %llx rc %x rrc %x", - uvcb.guest_handle, uvcb.guest_stor_len, *rc, *rrc); + KVM_UV_EVENT(kvm, 3, "PROTVIRT CREATE VM: handle %llx len %llx rc %x rrc %x flags %04x", + uvcb.guest_handle, uvcb.guest_stor_len, *rc, *rrc, uvcb.flags.raw); /* Outputs */ kvm->arch.pv.handle = uvcb.guest_handle; diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index afa5db750d9242cd623b559ab59eb5bafb28f669..b51666967aa1fd337fc3e5ed7f73f2d30375467b 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c @@ -290,8 +290,8 @@ static int pt_dump_init(void) max_addr = (S390_lowcore.kernel_asce & _REGION_ENTRY_TYPE_MASK) >> 2; max_addr = 1UL << (max_addr * 11 + 31); address_markers[IDENTITY_AFTER_END_NR].start_address = ident_map_size; - address_markers[AMODE31_START_NR].start_address = __samode31; - address_markers[AMODE31_END_NR].start_address = __eamode31; + address_markers[AMODE31_START_NR].start_address = (unsigned long)__samode31; + address_markers[AMODE31_END_NR].start_address = (unsigned long)__eamode31; address_markers[MODULES_NR].start_address = MODULES_VADDR; address_markers[MODULES_END_NR].start_address = MODULES_END; address_markers[ABS_LOWCORE_NR].start_address = __abs_lowcore; diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 099c4824dd8a2b696bc42d13a1d73e929d29080d..b678295931c315ceae5abdfb92bb8525f16eee34 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -598,7 +598,7 @@ void do_secure_storage_access(struct pt_regs *regs) * reliable without the misc UV feature so we need to check * for that as well. */ - if (test_bit_inv(BIT_UV_FEAT_MISC, &uv_info.uv_feature_indications) && + if (uv_has_feature(BIT_UV_FEAT_MISC) && !test_bit_inv(61, ®s->int_parm_long)) { /* * When this happens, userspace did something that it diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index c718f2a0de9485ecb695b972509e99dd2eb0fbfa..297a6d897d5a0c0e2e00f271ae23d918c4c6862a 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c @@ -142,7 +142,7 @@ static void clear_huge_pte_skeys(struct mm_struct *mm, unsigned long rste) __storage_key_init_range(paddr, paddr + size - 1); } -void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, +void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte) { unsigned long rste; @@ -163,6 +163,12 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, set_pte(ptep, __pte(rste)); } +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte, unsigned long sz) +{ + __set_huge_pte_at(mm, addr, ptep, pte); +} + pte_t huge_ptep_get(pte_t *ptep) { return __rste_to_pte(pte_val(*ptep)); diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 8d94e29adcdbcbc776d781be7c9bc57321b4fed6..8b94d2212d33325c2c98ab73c483e33543b50fca 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -98,7 +98,7 @@ void __init paging_init(void) sparse_init(); zone_dma_bits = 31; memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); - max_zone_pfns[ZONE_DMA] = PFN_DOWN(MAX_DMA_ADDRESS); + max_zone_pfns[ZONE_DMA] = virt_to_pfn(MAX_DMA_ADDRESS); max_zone_pfns[ZONE_NORMAL] = max_low_pfn; free_area_init(max_zone_pfns); } @@ -107,7 +107,7 @@ void mark_rodata_ro(void) { unsigned long size = __end_ro_after_init - __start_ro_after_init; - set_memory_ro((unsigned long)__start_ro_after_init, size >> PAGE_SHIFT); + __set_memory_ro(__start_ro_after_init, __end_ro_after_init); pr_info("Write protected read-only-after-init data: %luk\n", size >> 10); debug_checkwx(); } diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index e5ec76271b160e4b398df9fc4918a6b7ea02b31b..b87e96c64b61d218e2a05570892ec30537d9985f 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c @@ -373,7 +373,7 @@ static int change_page_attr_alias(unsigned long addr, unsigned long end, return rc; } -int __set_memory(unsigned long addr, int numpages, unsigned long flags) +int __set_memory(unsigned long addr, unsigned long numpages, unsigned long flags) { unsigned long end; int rc; diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index e44243b9c0a4c6945b7a5cd868468c2252f64b09..6957d2ed97bf0e18414cf5234887bcbed2f6fae4 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -5,7 +5,6 @@ #include #include -#include #include #include #include @@ -291,14 +290,9 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr, static void try_free_pmd_table(pud_t *pud, unsigned long start) { - const unsigned long end = start + PUD_SIZE; pmd_t *pmd; int i; - /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ - if (end > VMALLOC_START) - return; - pmd = pmd_offset(pud, start); for (i = 0; i < PTRS_PER_PMD; i++, pmd++) if (!pmd_none(*pmd)) @@ -363,14 +357,9 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end, static void try_free_pud_table(p4d_t *p4d, unsigned long start) { - const unsigned long end = start + P4D_SIZE; pud_t *pud; int i; - /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ - if (end > VMALLOC_START) - return; - pud = pud_offset(p4d, start); for (i = 0; i < PTRS_PER_PUD; i++, pud++) { if (!pud_none(*pud)) @@ -413,14 +402,9 @@ static int modify_p4d_table(pgd_t *pgd, unsigned long addr, unsigned long end, static void try_free_p4d_table(pgd_t *pgd, unsigned long start) { - const unsigned long end = start + PGDIR_SIZE; p4d_t *p4d; int i; - /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ - if (end > VMALLOC_START) - return; - p4d = p4d_offset(pgd, start); for (i = 0; i < PTRS_PER_P4D; i++, p4d++) { if (!p4d_none(*p4d)) @@ -440,6 +424,9 @@ static int modify_pagetable(unsigned long start, unsigned long end, bool add, if (WARN_ON_ONCE(!PAGE_ALIGNED(start | end))) return -EINVAL; + /* Don't mess with any tables not fully in 1:1 mapping & vmemmap area */ + if (WARN_ON_ONCE(end > VMALLOC_START)) + return -EINVAL; for (addr = start; addr < end; addr = next) { next = pgd_addr_end(addr, end); pgd = pgd_offset_k(addr); @@ -650,122 +637,29 @@ void vmem_unmap_4k_page(unsigned long addr) mutex_unlock(&vmem_mutex); } -static int __init memblock_region_cmp(const void *a, const void *b) -{ - const struct memblock_region *r1 = a; - const struct memblock_region *r2 = b; - - if (r1->base < r2->base) - return -1; - if (r1->base > r2->base) - return 1; - return 0; -} - -static void __init memblock_region_swap(void *a, void *b, int size) -{ - swap(*(struct memblock_region *)a, *(struct memblock_region *)b); -} - -#ifdef CONFIG_KASAN -#define __sha(x) ((unsigned long)kasan_mem_to_shadow((void *)x)) - -static inline int set_memory_kasan(unsigned long start, unsigned long end) -{ - start = PAGE_ALIGN_DOWN(__sha(start)); - end = PAGE_ALIGN(__sha(end)); - return set_memory_rwnx(start, (end - start) >> PAGE_SHIFT); -} -#endif - -/* - * map whole physical memory to virtual memory (identity mapping) - * we reserve enough space in the vmalloc area for vmemmap to hotplug - * additional memory segments. - */ void __init vmem_map_init(void) { - struct memblock_region memory_rwx_regions[] = { - { - .base = 0, - .size = sizeof(struct lowcore), - .flags = MEMBLOCK_NONE, -#ifdef CONFIG_NUMA - .nid = NUMA_NO_NODE, -#endif - }, - { - .base = __pa(_stext), - .size = _etext - _stext, - .flags = MEMBLOCK_NONE, -#ifdef CONFIG_NUMA - .nid = NUMA_NO_NODE, -#endif - }, - { - .base = __pa(_sinittext), - .size = _einittext - _sinittext, - .flags = MEMBLOCK_NONE, -#ifdef CONFIG_NUMA - .nid = NUMA_NO_NODE, -#endif - }, - { - .base = __stext_amode31, - .size = __etext_amode31 - __stext_amode31, - .flags = MEMBLOCK_NONE, -#ifdef CONFIG_NUMA - .nid = NUMA_NO_NODE, -#endif - }, - }; - struct memblock_type memory_rwx = { - .regions = memory_rwx_regions, - .cnt = ARRAY_SIZE(memory_rwx_regions), - .max = ARRAY_SIZE(memory_rwx_regions), - }; - phys_addr_t base, end; - u64 i; - + __set_memory_rox(_stext, _etext); + __set_memory_ro(_etext, __end_rodata); + __set_memory_rox(_sinittext, _einittext); + __set_memory_rox(__stext_amode31, __etext_amode31); /* - * Set RW+NX attribute on all memory, except regions enumerated with - * memory_rwx exclude type. These regions need different attributes, - * which are enforced afterwards. - * - * __for_each_mem_range() iterate and exclude types should be sorted. - * The relative location of _stext and _sinittext is hardcoded in the - * linker script. However a location of __stext_amode31 and the kernel - * image itself are chosen dynamically. Thus, sort the exclude type. + * If the BEAR-enhancement facility is not installed the first + * prefix page is used to return to the previous context with + * an LPSWE instruction and therefore must be executable. */ - sort(&memory_rwx_regions, - ARRAY_SIZE(memory_rwx_regions), sizeof(memory_rwx_regions[0]), - memblock_region_cmp, memblock_region_swap); - __for_each_mem_range(i, &memblock.memory, &memory_rwx, - NUMA_NO_NODE, MEMBLOCK_NONE, &base, &end, NULL) { - set_memory_rwnx((unsigned long)__va(base), - (end - base) >> PAGE_SHIFT); + if (!static_key_enabled(&cpu_has_bear)) + set_memory_x(0, 1); + if (debug_pagealloc_enabled()) { + /* + * Use RELOC_HIDE() as long as __va(0) translates to NULL, + * since performing pointer arithmetic on a NULL pointer + * has undefined behavior and generates compiler warnings. + */ + __set_memory_4k(__va(0), RELOC_HIDE(__va(0), ident_map_size)); } - -#ifdef CONFIG_KASAN - for_each_mem_range(i, &base, &end) - set_memory_kasan(base, end); -#endif - set_memory_rox((unsigned long)_stext, - (unsigned long)(_etext - _stext) >> PAGE_SHIFT); - set_memory_ro((unsigned long)_etext, - (unsigned long)(__end_rodata - _etext) >> PAGE_SHIFT); - set_memory_rox((unsigned long)_sinittext, - (unsigned long)(_einittext - _sinittext) >> PAGE_SHIFT); - set_memory_rox(__stext_amode31, - (__etext_amode31 - __stext_amode31) >> PAGE_SHIFT); - - /* lowcore must be executable for LPSWE */ - if (static_key_enabled(&cpu_has_bear)) - set_memory_nx(0, 1); - set_memory_nx(PAGE_SIZE, 1); - if (debug_pagealloc_enabled()) - set_memory_4k(0, ident_map_size >> PAGE_SHIFT); - + if (MACHINE_HAS_NX) + ctl_set_bit(0, 20); pr_info("Write protected kernel read-only data: %luk\n", (unsigned long)(__end_rodata - _stext) >> 10); } diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c index 5e9371fbf3d5f3c39f6e98126c0ba8c1d28eb777..2861e3360affc3e464021917911b091f185ed5da 100644 --- a/arch/s390/net/bpf_jit_comp.c +++ b/arch/s390/net/bpf_jit_comp.c @@ -2088,6 +2088,7 @@ struct bpf_tramp_jit { */ int r14_off; /* Offset of saved %r14 */ int run_ctx_off; /* Offset of struct bpf_tramp_run_ctx */ + int tccnt_off; /* Offset of saved tailcall counter */ int do_fexit; /* do_fexit: label */ }; @@ -2258,12 +2259,16 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, tjit->r14_off = alloc_stack(tjit, sizeof(u64)); tjit->run_ctx_off = alloc_stack(tjit, sizeof(struct bpf_tramp_run_ctx)); + tjit->tccnt_off = alloc_stack(tjit, sizeof(u64)); /* The caller has already reserved STACK_FRAME_OVERHEAD bytes. */ tjit->stack_size -= STACK_FRAME_OVERHEAD; tjit->orig_stack_args_off = tjit->stack_size + STACK_FRAME_OVERHEAD; /* aghi %r15,-stack_size */ EMIT4_IMM(0xa70b0000, REG_15, -tjit->stack_size); + /* mvc tccnt_off(4,%r15),stack_size+STK_OFF_TCCNT(%r15) */ + _EMIT6(0xd203f000 | tjit->tccnt_off, + 0xf000 | (tjit->stack_size + STK_OFF_TCCNT)); /* stmg %r2,%rN,fwd_reg_args_off(%r15) */ if (nr_reg_args) EMIT6_DISP_LH(0xeb000000, 0x0024, REG_2, @@ -2400,6 +2405,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, (nr_stack_args * sizeof(u64) - 1) << 16 | tjit->stack_args_off, 0xf000 | tjit->orig_stack_args_off); + /* mvc STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ + _EMIT6(0xd203f000 | STK_OFF_TCCNT, 0xf000 | tjit->tccnt_off); /* lgr %r1,%r8 */ EMIT4(0xb9040000, REG_1, REG_8); /* %r1() */ @@ -2456,6 +2463,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, if (flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET)) EMIT6_DISP_LH(0xe3000000, 0x0004, REG_2, REG_0, REG_15, tjit->retval_off); + /* mvc stack_size+STK_OFF_TCCNT(4,%r15),tccnt_off(%r15) */ + _EMIT6(0xd203f000 | (tjit->stack_size + STK_OFF_TCCNT), + 0xf000 | tjit->tccnt_off); /* aghi %r15,stack_size */ EMIT4_IMM(0xa70b0000, REG_15, tjit->stack_size); /* Emit an expoline for the following indirect jump. */ @@ -2503,7 +2513,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, return -E2BIG; } - return ret; + return tjit.common.prg; } bool bpf_jit_supports_subprog_tailcalls(void) diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index 151792162152cdf47b76a13fc1dcfbe9ae952522..645cccf3da88eccf69e769ec4cd07c2056deb5ef 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -531,7 +531,7 @@ static int __init ap325rxa_devices_setup(void) device_initialize(&ap325rxa_ceu_device.dev); dma_declare_coherent_memory(&ap325rxa_ceu_device.dev, ceu_dma_membase, ceu_dma_membase, - ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(&ap325rxa_ceu_device); diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 310513646c9b3f2c452949f708f8fa5aa6ff8381..3be293335de545125ff8420cea36a28ee7873c51 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -1454,15 +1454,13 @@ static int __init arch_setup(void) device_initialize(&ecovec_ceu_devices[0]->dev); dma_declare_coherent_memory(&ecovec_ceu_devices[0]->dev, ceu0_dma_membase, ceu0_dma_membase, - ceu0_dma_membase + - CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(ecovec_ceu_devices[0]); device_initialize(&ecovec_ceu_devices[1]->dev); dma_declare_coherent_memory(&ecovec_ceu_devices[1]->dev, ceu1_dma_membase, ceu1_dma_membase, - ceu1_dma_membase + - CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(ecovec_ceu_devices[1]); gpiod_add_lookup_table(&cn12_power_gpiod_table); diff --git a/arch/sh/boards/mach-kfr2r09/setup.c b/arch/sh/boards/mach-kfr2r09/setup.c index a18e80394aedca6bb27d133374094bc739b4c0ea..6b775eae85c021bf0a7d02f64523f31a801dde61 100644 --- a/arch/sh/boards/mach-kfr2r09/setup.c +++ b/arch/sh/boards/mach-kfr2r09/setup.c @@ -603,7 +603,7 @@ static int __init kfr2r09_devices_setup(void) device_initialize(&kfr2r09_ceu_device.dev); dma_declare_coherent_memory(&kfr2r09_ceu_device.dev, ceu_dma_membase, ceu_dma_membase, - ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(&kfr2r09_ceu_device); diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index f60061283c4827440c18c97d14a39d925e99a876..773ee767d0c4e0299484c6b4910b0d40d8b7d2e9 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -604,7 +604,7 @@ static int __init migor_devices_setup(void) device_initialize(&migor_ceu_device.dev); dma_declare_coherent_memory(&migor_ceu_device.dev, ceu_dma_membase, ceu_dma_membase, - ceu_dma_membase + CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(&migor_ceu_device); diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index b60a2626e18b274f7965c3623b9cb96e34f7152b..6495f9354065435628f4f8af8994c3436a7e17f8 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -940,15 +940,13 @@ static int __init devices_setup(void) device_initialize(&ms7724se_ceu_devices[0]->dev); dma_declare_coherent_memory(&ms7724se_ceu_devices[0]->dev, ceu0_dma_membase, ceu0_dma_membase, - ceu0_dma_membase + - CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(ms7724se_ceu_devices[0]); device_initialize(&ms7724se_ceu_devices[1]->dev); dma_declare_coherent_memory(&ms7724se_ceu_devices[1]->dev, ceu1_dma_membase, ceu1_dma_membase, - ceu1_dma_membase + - CEU_BUFFER_MEMORY_SIZE - 1); + CEU_BUFFER_MEMORY_SIZE); platform_device_add(ms7724se_ceu_devices[1]); return platform_add_devices(ms7724se_devices, diff --git a/arch/sh/drivers/push-switch.c b/arch/sh/drivers/push-switch.c index c95f48ff3f6fb4cee52a0ae0871619be47408f51..6ecba5f521eb6c276a34961e8b10b3bcc5f14bfe 100644 --- a/arch/sh/drivers/push-switch.c +++ b/arch/sh/drivers/push-switch.c @@ -101,8 +101,8 @@ static int switch_drv_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_switch); platform_set_drvdata(pdev, NULL); - flush_work(&psw->work); timer_shutdown_sync(&psw->debounce); + flush_work(&psw->work); free_irq(irq, pdev); kfree(psw); diff --git a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap.c index c33b3daa4ad1a3e6db7e319e229fe08db204dfe8..33d20f34560fd5cb5d69dfe09ba08a16c9e0b496 100644 --- a/arch/sh/mm/ioremap.c +++ b/arch/sh/mm/ioremap.c @@ -72,8 +72,8 @@ __ioremap_29bit(phys_addr_t offset, unsigned long size, pgprot_t prot) #define __ioremap_29bit(offset, size, prot) NULL #endif /* CONFIG_29BIT */ -void __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size, - unsigned long prot) +void __iomem __ref *ioremap_prot(phys_addr_t phys_addr, size_t size, + unsigned long prot) { void __iomem *mapped; pgprot_t pgprot = __pgprot(prot); diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index 595ca0be286b5abb1a998c255a31460ba5d61d21..43b0ae4c2c2112d4d4d3cb3c60e787b175172dea 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -2,6 +2,5 @@ generated-y += syscall_table_32.h generated-y += syscall_table_64.h generic-y += agp.h -generic-y += export.h generic-y += kvm_para.h generic-y += mcs_spinlock.h diff --git a/arch/sparc/include/asm/hugetlb.h b/arch/sparc/include/asm/hugetlb.h index 0a26cca24232c0811a0bda5c347c9db8dd206fa3..c714ca6a05aa04b154d0b64f52cb3f7d4c20452b 100644 --- a/arch/sparc/include/asm/hugetlb.h +++ b/arch/sparc/include/asm/hugetlb.h @@ -14,6 +14,8 @@ extern struct pud_huge_patch_entry __pud_huge_patch, __pud_huge_patch_end; #define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pte, unsigned long sz); +void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t pte); #define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR @@ -32,7 +34,7 @@ static inline void huge_ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { pte_t old_pte = *ptep; - set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); + __set_huge_pte_at(mm, addr, ptep, pte_wrprotect(old_pte)); } #define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS @@ -42,7 +44,7 @@ static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma, { int changed = !pte_same(*ptep, pte); if (changed) { - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + __set_huge_pte_at(vma->vm_mm, addr, ptep, pte); flush_tlb_page(vma, addr); } return changed; diff --git a/arch/sparc/include/asm/ide.h b/arch/sparc/include/asm/ide.h deleted file mode 100644 index 904cc6cbc1550cd8824a55898380be79d066fe11..0000000000000000000000000000000000000000 --- a/arch/sparc/include/asm/ide.h +++ /dev/null @@ -1,97 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* ide.h: SPARC PCI specific IDE glue. - * - * Copyright (C) 1997 David S. Miller (davem@davemloft.net) - * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * Adaptation from sparc64 version to sparc by Pete Zaitcev. - */ - -#ifndef _SPARC_IDE_H -#define _SPARC_IDE_H - -#ifdef __KERNEL__ - -#include -#ifdef CONFIG_SPARC64 -#include -#include -#include -#else -#include -#include -#endif - -#define __ide_insl(data_reg, buffer, wcount) \ - __ide_insw(data_reg, buffer, (wcount)<<1) -#define __ide_outsl(data_reg, buffer, wcount) \ - __ide_outsw(data_reg, buffer, (wcount)<<1) - -/* On sparc, I/O ports and MMIO registers are accessed identically. */ -#define __ide_mm_insw __ide_insw -#define __ide_mm_insl __ide_insl -#define __ide_mm_outsw __ide_outsw -#define __ide_mm_outsl __ide_outsl - -static inline void __ide_insw(void __iomem *port, void *dst, u32 count) -{ -#if defined(CONFIG_SPARC64) && defined(DCACHE_ALIASING_POSSIBLE) - unsigned long end = (unsigned long)dst + (count << 1); -#endif - u16 *ps = dst; - u32 *pi; - - if(((unsigned long)ps) & 0x2) { - *ps++ = __raw_readw(port); - count--; - } - pi = (u32 *)ps; - while(count >= 2) { - u32 w; - - w = __raw_readw(port) << 16; - w |= __raw_readw(port); - *pi++ = w; - count -= 2; - } - ps = (u16 *)pi; - if(count) - *ps++ = __raw_readw(port); - -#if defined(CONFIG_SPARC64) && defined(DCACHE_ALIASING_POSSIBLE) - __flush_dcache_range((unsigned long)dst, end); -#endif -} - -static inline void __ide_outsw(void __iomem *port, const void *src, u32 count) -{ -#if defined(CONFIG_SPARC64) && defined(DCACHE_ALIASING_POSSIBLE) - unsigned long end = (unsigned long)src + (count << 1); -#endif - const u16 *ps = src; - const u32 *pi; - - if(((unsigned long)src) & 0x2) { - __raw_writew(*ps++, port); - count--; - } - pi = (const u32 *)ps; - while(count >= 2) { - u32 w; - - w = *pi++; - __raw_writew((w >> 16), port); - __raw_writew(w, port); - count -= 2; - } - ps = (const u16 *)pi; - if(count) - __raw_writew(*ps, port); - -#if defined(CONFIG_SPARC64) && defined(DCACHE_ALIASING_POSSIBLE) - __flush_dcache_range((unsigned long)src, end); -#endif -} - -#endif /* __KERNEL__ */ - -#endif /* _SPARC_IDE_H */ diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index a269ad2fe6dfeae29115a8757f07ad5396ea8ada..a3fdee4cd6fa318575d0115c4f0e8af8f94c2b59 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -8,6 +8,7 @@ * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) */ +#include #include #include #include @@ -30,7 +31,6 @@ #include #include -#include #define curptr g6 diff --git a/arch/sparc/kernel/head_32.S b/arch/sparc/kernel/head_32.S index 6044b82b976751a8ccbe43ac424bafe8de36af42..964c61b5cd03665f27190a7afc0374211336c6d3 100644 --- a/arch/sparc/kernel/head_32.S +++ b/arch/sparc/kernel/head_32.S @@ -11,6 +11,7 @@ * CompactPCI platform by Eric Brower, 1999. */ +#include #include #include @@ -25,7 +26,6 @@ #include /* TI_UWINMASK */ #include #include /* PGDIR_SHIFT */ -#include .data /* The following are used with the prom_vector node-ops to figure out diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S index 72a5bdc833ea22f0800663c354cba188fad83243..cf0549134234e42bc78cc96db9ab501c8bc222ad 100644 --- a/arch/sparc/kernel/head_64.S +++ b/arch/sparc/kernel/head_64.S @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include /* This section from from _start to sparc64_boot_end should fit into * 0x0000000000404000 to 0x0000000000408000. diff --git a/arch/sparc/lib/U1memcpy.S b/arch/sparc/lib/U1memcpy.S index a6f4ee3918977c35ff9744effb7701702b318b5d..635398ec7540ee3d344151ddc4c6f07ad41e11f9 100644 --- a/arch/sparc/lib/U1memcpy.S +++ b/arch/sparc/lib/U1memcpy.S @@ -6,10 +6,10 @@ */ #ifdef __KERNEL__ +#include #include #include #include -#include #define GLOBAL_SPARE g7 #else #define GLOBAL_SPARE g5 diff --git a/arch/sparc/lib/VISsave.S b/arch/sparc/lib/VISsave.S index 9c8eb2017d5b4be09e5465c3fb25e7d77fe671d3..31a0c336c1857f9b5beba29bab203a26f43c5884 100644 --- a/arch/sparc/lib/VISsave.S +++ b/arch/sparc/lib/VISsave.S @@ -7,6 +7,7 @@ * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) */ +#include #include #include @@ -14,7 +15,6 @@ #include #include #include -#include /* On entry: %o5=current FPRS value, %g7 is callers address */ /* May clobber %o5, %g1, %g2, %g3, %g7, %icc, %xcc */ diff --git a/arch/sparc/lib/ashldi3.S b/arch/sparc/lib/ashldi3.S index 2d72de88af905737dc8fb7fac35cd0917a3611e4..2a9e7c4fb260f575949ef4f8ee307f263f7b3179 100644 --- a/arch/sparc/lib/ashldi3.S +++ b/arch/sparc/lib/ashldi3.S @@ -6,8 +6,8 @@ * Copyright (C) 1999 David S. Miller (davem@redhat.com) */ +#include #include -#include .text ENTRY(__ashldi3) diff --git a/arch/sparc/lib/ashrdi3.S b/arch/sparc/lib/ashrdi3.S index 05dfda9f500519636502efe5471a168fe78d07bd..8fd0b311722fd169e9ec681accd54efdbcc77920 100644 --- a/arch/sparc/lib/ashrdi3.S +++ b/arch/sparc/lib/ashrdi3.S @@ -6,8 +6,8 @@ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) */ +#include #include -#include .text ENTRY(__ashrdi3) diff --git a/arch/sparc/lib/atomic_64.S b/arch/sparc/lib/atomic_64.S index 8245d4a97301c37a66a933a629c43ab4d7536b3f..4f8cab2fb9cdb8ba89466c0ed3af9d22e1c88003 100644 --- a/arch/sparc/lib/atomic_64.S +++ b/arch/sparc/lib/atomic_64.S @@ -4,10 +4,10 @@ * Copyright (C) 1999, 2007 2012 David S. Miller (davem@davemloft.net) */ +#include #include #include #include -#include .text diff --git a/arch/sparc/lib/bitops.S b/arch/sparc/lib/bitops.S index 9d647f977618c5541662403010642a89b1d77c6f..9c91cbb310e7b828c34824860e312eb5d6b65ac0 100644 --- a/arch/sparc/lib/bitops.S +++ b/arch/sparc/lib/bitops.S @@ -4,10 +4,10 @@ * Copyright (C) 2000, 2007 David S. Miller (davem@davemloft.net) */ +#include #include #include #include -#include .text diff --git a/arch/sparc/lib/blockops.S b/arch/sparc/lib/blockops.S index 76ddd1ff6833ecce6ac7f14f187fe16ae3dc9708..5b92959a4d4891a8b325f50cae31012cbb7ab36c 100644 --- a/arch/sparc/lib/blockops.S +++ b/arch/sparc/lib/blockops.S @@ -5,9 +5,9 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ +#include #include #include -#include /* Zero out 64 bytes of memory at (buf + offset). * Assumes %g1 contains zero. diff --git a/arch/sparc/lib/bzero.S b/arch/sparc/lib/bzero.S index 87fec4cbe10c50eb5b570dfebc7d941940bbe760..2bfa44a6b25e51401ac33adab1536a89d1e98b25 100644 --- a/arch/sparc/lib/bzero.S +++ b/arch/sparc/lib/bzero.S @@ -5,8 +5,8 @@ * Copyright (C) 2005 David S. Miller */ +#include #include -#include .text diff --git a/arch/sparc/lib/checksum_32.S b/arch/sparc/lib/checksum_32.S index 781e39b3c009f9b27e886f6a6e07e6d2f5545108..84ad709cbecb4459c50ee9ea279585613c92c0bd 100644 --- a/arch/sparc/lib/checksum_32.S +++ b/arch/sparc/lib/checksum_32.S @@ -14,8 +14,8 @@ * BSD4.4 portable checksum routine */ +#include #include -#include #define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5) \ ldd [buf + offset + 0x00], t0; \ diff --git a/arch/sparc/lib/checksum_64.S b/arch/sparc/lib/checksum_64.S index 9700ef1730df63fb91bc5b8c14d9a91587143b5f..32b626f3fe4d3816dd9971a797c47705e2c17286 100644 --- a/arch/sparc/lib/checksum_64.S +++ b/arch/sparc/lib/checksum_64.S @@ -14,7 +14,7 @@ * BSD4.4 portable checksum routine */ -#include +#include .text csum_partial_fix_alignment: diff --git a/arch/sparc/lib/clear_page.S b/arch/sparc/lib/clear_page.S index 302d3454a9942c0b5002a9759caa1f64d1a951b2..e63458194f5a05d5cff94ba767877696d312cf3a 100644 --- a/arch/sparc/lib/clear_page.S +++ b/arch/sparc/lib/clear_page.S @@ -5,13 +5,13 @@ * Copyright (C) 1997 Jakub Jelinek (jakub@redhat.com) */ +#include #include #include #include #include #include #include -#include /* What we used to do was lock a TLB entry into a specific * TLB slot, clear the page with interrupts disabled, then diff --git a/arch/sparc/lib/copy_in_user.S b/arch/sparc/lib/copy_in_user.S index 66e90bf528e2bf3a4f9ba710418eee539d38c816..e23e6a69ff920013bb36c124567e75ef071f1f54 100644 --- a/arch/sparc/lib/copy_in_user.S +++ b/arch/sparc/lib/copy_in_user.S @@ -4,9 +4,9 @@ * Copyright (C) 1999, 2000, 2004 David S. Miller (davem@redhat.com) */ +#include #include #include -#include #define XCC xcc diff --git a/arch/sparc/lib/copy_page.S b/arch/sparc/lib/copy_page.S index 5ebcfd479f4f88c48d0f1b5cf8593b47cb9a8c07..7a041f3ebc58201d09b28e6ca3715a4fbea96183 100644 --- a/arch/sparc/lib/copy_page.S +++ b/arch/sparc/lib/copy_page.S @@ -5,13 +5,13 @@ * Copyright (C) 1997 Jakub Jelinek (jakub@redhat.com) */ +#include #include #include #include #include #include #include -#include /* What we used to do was lock a TLB entry into a specific * TLB slot, clear the page with interrupts disabled, then diff --git a/arch/sparc/lib/copy_user.S b/arch/sparc/lib/copy_user.S index 954572c78539aeff5a84fedd211d3dfb4465bfea..7bb2ef68881d297e0f622a0c8a753418b171fa93 100644 --- a/arch/sparc/lib/copy_user.S +++ b/arch/sparc/lib/copy_user.S @@ -12,11 +12,11 @@ * Returns 0 if successful, otherwise count of bytes not copied yet */ +#include #include #include #include #include -#include /* Work around cpp -rob */ #define ALLOC #alloc diff --git a/arch/sparc/lib/csum_copy.S b/arch/sparc/lib/csum_copy.S index d839956407a7749739928a3f53e18f48bc0dc440..f968e83bc93b7f7c58543f69bdbbe62b3c5e443d 100644 --- a/arch/sparc/lib/csum_copy.S +++ b/arch/sparc/lib/csum_copy.S @@ -4,7 +4,7 @@ * Copyright (C) 2005 David S. Miller */ -#include +#include #ifdef __KERNEL__ #define GLOBAL_SPARE %g7 diff --git a/arch/sparc/lib/divdi3.S b/arch/sparc/lib/divdi3.S index a7389409d9fafd3f84b0a5dc06e545c19f335efe..4ba901acd572d4a11a6330989b446ab652042fcc 100644 --- a/arch/sparc/lib/divdi3.S +++ b/arch/sparc/lib/divdi3.S @@ -5,7 +5,7 @@ This file is part of GNU CC. */ -#include +#include .text .align 4 .globl __divdi3 diff --git a/arch/sparc/lib/ffs.S b/arch/sparc/lib/ffs.S index 5a11d864fa052036ea3f04e16dcfb219844256d3..3a9ad8ffdfe8717a309763b8a8e1d7943c0331ad 100644 --- a/arch/sparc/lib/ffs.S +++ b/arch/sparc/lib/ffs.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include #include -#include .register %g2,#scratch diff --git a/arch/sparc/lib/fls.S b/arch/sparc/lib/fls.S index 06b8d300bcae276807d9b9d164325e0beaf6d86d..ccf97fb7d8cd97de556f1d48303315bfd35da78a 100644 --- a/arch/sparc/lib/fls.S +++ b/arch/sparc/lib/fls.S @@ -5,8 +5,8 @@ * and onward. */ +#include #include -#include .text .register %g2, #scratch diff --git a/arch/sparc/lib/fls64.S b/arch/sparc/lib/fls64.S index c83e22ae9586400bdd776ddff6fc7cd673b71c37..87005b67d3780639ddf17711efb6dd9c42323a98 100644 --- a/arch/sparc/lib/fls64.S +++ b/arch/sparc/lib/fls64.S @@ -5,8 +5,8 @@ * and onward. */ +#include #include -#include .text .register %g2, #scratch diff --git a/arch/sparc/lib/hweight.S b/arch/sparc/lib/hweight.S index 0ddbbb03182232fe199f5222248617c72a2f23b7..eebee59b0655bd586496c4767328c911cd3aaef6 100644 --- a/arch/sparc/lib/hweight.S +++ b/arch/sparc/lib/hweight.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include #include -#include .text .align 32 diff --git a/arch/sparc/lib/ipcsum.S b/arch/sparc/lib/ipcsum.S index 531d89c9d5d9a38129e2dab3d43e1127e572f6a3..7fa8fd4b795acd3c4b575eb3557b4102a56e249e 100644 --- a/arch/sparc/lib/ipcsum.S +++ b/arch/sparc/lib/ipcsum.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include #include -#include .text ENTRY(ip_fast_csum) /* %o0 = iph, %o1 = ihl */ diff --git a/arch/sparc/lib/locks.S b/arch/sparc/lib/locks.S index 9a1289a3fb28a7c81e055ed0171a19151398eb1b..47a39f4384a2e4814fa963cc1b320070a6788064 100644 --- a/arch/sparc/lib/locks.S +++ b/arch/sparc/lib/locks.S @@ -7,11 +7,11 @@ * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) */ +#include #include #include #include #include -#include .text .align 4 diff --git a/arch/sparc/lib/lshrdi3.S b/arch/sparc/lib/lshrdi3.S index 509ca6682da8d113935c6f6fdc9402aa51e5ca3f..09bf581a0ba5c76e893fdd47cd95e27fb6cd6063 100644 --- a/arch/sparc/lib/lshrdi3.S +++ b/arch/sparc/lib/lshrdi3.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include #include -#include ENTRY(__lshrdi3) cmp %o2, 0 diff --git a/arch/sparc/lib/mcount.S b/arch/sparc/lib/mcount.S index deba6fa0bc78bfd48ffdb7b96219d1072109ca06..f7f7910eb41e37a6aa9b34f84db6b8e1b312e3e3 100644 --- a/arch/sparc/lib/mcount.S +++ b/arch/sparc/lib/mcount.S @@ -6,8 +6,8 @@ * This can also be tweaked for kernel stack overflow detection. */ +#include #include -#include /* * This is the main variant and is called by C code. GCC's -pg option diff --git a/arch/sparc/lib/memcmp.S b/arch/sparc/lib/memcmp.S index a18076ef5af1479c5eba57643c7220f72ef7e94b..c87e8000feba1579469b738687807b0782825ed4 100644 --- a/arch/sparc/lib/memcmp.S +++ b/arch/sparc/lib/memcmp.S @@ -5,9 +5,9 @@ * Copyright (C) 2000, 2008 David S. Miller (davem@davemloft.net) */ +#include #include #include -#include .text ENTRY(memcmp) diff --git a/arch/sparc/lib/memcpy.S b/arch/sparc/lib/memcpy.S index ee823d8c92157d0842e586839f1a61a9802a008b..57b1ae0f592471109f87edef3a277c07bf78d529 100644 --- a/arch/sparc/lib/memcpy.S +++ b/arch/sparc/lib/memcpy.S @@ -8,7 +8,8 @@ * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ -#include +#include + #define FUNC(x) \ .globl x; \ .type x,@function; \ diff --git a/arch/sparc/lib/memmove.S b/arch/sparc/lib/memmove.S index 3132b6316144f3e40b281bbe7a9748363ef64d4f..543dda7b9dacc3cf9295dcda38922b93f9108cb5 100644 --- a/arch/sparc/lib/memmove.S +++ b/arch/sparc/lib/memmove.S @@ -5,8 +5,8 @@ * Copyright (C) 1996, 1997, 1998, 1999 Jakub Jelinek (jj@ultra.linux.cz) */ +#include #include -#include .text ENTRY(memmove) /* o0=dst o1=src o2=len */ diff --git a/arch/sparc/lib/memscan_32.S b/arch/sparc/lib/memscan_32.S index c4c2d5b3a2e95a93418811d3bcb148a8df5b2ac2..5386a3a20019103083037b599aad155d82e7dd6b 100644 --- a/arch/sparc/lib/memscan_32.S +++ b/arch/sparc/lib/memscan_32.S @@ -5,7 +5,7 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ -#include +#include /* In essence, this is just a fancy strlen. */ diff --git a/arch/sparc/lib/memscan_64.S b/arch/sparc/lib/memscan_64.S index 36dd638905c34566ca421c38a2fc40f918deaefd..70a4f21057f2e7114529ba319ca226cc9ce21818 100644 --- a/arch/sparc/lib/memscan_64.S +++ b/arch/sparc/lib/memscan_64.S @@ -6,7 +6,7 @@ * Copyright (C) 1998 David S. Miller (davem@redhat.com) */ - #include +#include #define HI_MAGIC 0x8080808080808080 #define LO_MAGIC 0x0101010101010101 diff --git a/arch/sparc/lib/memset.S b/arch/sparc/lib/memset.S index eaff68213fdf58b86fb297d2ab844ad858da5dc0..a33419dbb464c29403f90a85c2b1b1bb665b0237 100644 --- a/arch/sparc/lib/memset.S +++ b/arch/sparc/lib/memset.S @@ -9,8 +9,8 @@ * clear_user. */ +#include #include -#include /* Work around cpp -rob */ #define ALLOC #alloc diff --git a/arch/sparc/lib/muldi3.S b/arch/sparc/lib/muldi3.S index 53054dee66d6713db5783d25296107a916f0b3dc..7e1e8cd30a22e6c6918a5344b6d071737ad77bf5 100644 --- a/arch/sparc/lib/muldi3.S +++ b/arch/sparc/lib/muldi3.S @@ -5,7 +5,7 @@ This file is part of GNU CC. */ -#include +#include .text .align 4 .globl __muldi3 diff --git a/arch/sparc/lib/multi3.S b/arch/sparc/lib/multi3.S index 2f187b299345ff7527d1ce643937bfc523a6f280..5bb4c122a2cf0d17c0b4d4263896065ae80bfe0e 100644 --- a/arch/sparc/lib/multi3.S +++ b/arch/sparc/lib/multi3.S @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include #include -#include .text .align 4 diff --git a/arch/sparc/lib/strlen.S b/arch/sparc/lib/strlen.S index dd111bbad5df9063337ba7f2c9b72903399416f1..27478b3f16476908a04af4832af93be3c12d504d 100644 --- a/arch/sparc/lib/strlen.S +++ b/arch/sparc/lib/strlen.S @@ -6,9 +6,9 @@ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include #include #include -#include #define LO_MAGIC 0x01010101 #define HI_MAGIC 0x80808080 diff --git a/arch/sparc/lib/strncmp_32.S b/arch/sparc/lib/strncmp_32.S index 794733f036b6c66653b582b120e5e2f615aa89e9..387bbf62154884a805f30f19aa3bc6462a85d331 100644 --- a/arch/sparc/lib/strncmp_32.S +++ b/arch/sparc/lib/strncmp_32.S @@ -4,8 +4,8 @@ * generic strncmp routine. */ +#include #include -#include .text ENTRY(strncmp) diff --git a/arch/sparc/lib/strncmp_64.S b/arch/sparc/lib/strncmp_64.S index 3d37d65f674c36b21d707e9223ed3193eaf5a92c..76c1207ecf5af56609c87603a5e5a17afa291066 100644 --- a/arch/sparc/lib/strncmp_64.S +++ b/arch/sparc/lib/strncmp_64.S @@ -5,9 +5,9 @@ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include #include #include -#include .text ENTRY(strncmp) diff --git a/arch/sparc/lib/xor.S b/arch/sparc/lib/xor.S index f6af7c7ee6fc16a6ad7640e1be0986c086b62363..35461e3b2a9b1447af8030e77381a1ebfe261311 100644 --- a/arch/sparc/lib/xor.S +++ b/arch/sparc/lib/xor.S @@ -9,12 +9,12 @@ * Copyright (C) 2006 David S. Miller */ +#include #include #include #include #include #include -#include /* * Requirements: diff --git a/arch/sparc/mm/hugetlbpage.c b/arch/sparc/mm/hugetlbpage.c index d7018823206c13647fa5ddde4103fa9ff18f5cd7..b432500c13a5d8794b2296dec7ac45973b33dbad 100644 --- a/arch/sparc/mm/hugetlbpage.c +++ b/arch/sparc/mm/hugetlbpage.c @@ -328,7 +328,7 @@ pte_t *huge_pte_offset(struct mm_struct *mm, return pte_offset_huge(pmd, addr); } -void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, +void __set_huge_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep, pte_t entry) { unsigned int nptes, orig_shift, shift; @@ -364,6 +364,12 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, orig_shift); } +void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t entry, unsigned long sz) +{ + __set_huge_pte_at(mm, addr, ptep, entry); +} + pte_t huge_ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { diff --git a/arch/sparc/mm/tlb.c b/arch/sparc/mm/tlb.c index 0d41c94ec3ac0697420a24f1a1a73b7fb1c5a8c3..b44d79d778c718164e45813be89e3089ebf24efc 100644 --- a/arch/sparc/mm/tlb.c +++ b/arch/sparc/mm/tlb.c @@ -128,6 +128,7 @@ void tlb_batch_add(struct mm_struct *mm, unsigned long vaddr, goto no_cache_flush; /* A real file page? */ + folio = page_folio(page); mapping = folio_flush_mapping(folio); if (!mapping) goto no_cache_flush; diff --git a/arch/um/Kbuild b/arch/um/Kbuild index a4e40e534e6a84db241abfe5076962a90f8a71bd..6cf0c1e5927b273626b5bf964952c524e3a207c3 100644 --- a/arch/um/Kbuild +++ b/arch/um/Kbuild @@ -1 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only + +obj-y += kernel/ drivers/ os-Linux/ diff --git a/arch/um/Makefile b/arch/um/Makefile index da4d5256af2f00aff819d5a007d5f69844c3c403..82f05f250634807c9f78774bca9213dfd5de2038 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -18,15 +18,10 @@ else endif ARCH_DIR := arch/um -OS := $(shell uname -s) # We require bash because the vmlinux link and loader script cpp use bash # features. SHELL := /bin/bash -core-y += $(ARCH_DIR)/kernel/ \ - $(ARCH_DIR)/drivers/ \ - $(ARCH_DIR)/os-$(OS)/ - MODE_INCLUDE += -I$(srctree)/$(ARCH_DIR)/include/shared/skas HEADER_ARCH := $(SUBARCH) @@ -78,7 +73,7 @@ USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -I%,,$(KBUILD_CFLAGS))) \ -idirafter $(objtree)/include -D__KERNEL__ -D__UM_HOST__ #This will adjust *FLAGS accordingly to the platform. -include $(srctree)/$(ARCH_DIR)/Makefile-os-$(OS) +include $(srctree)/$(ARCH_DIR)/Makefile-os-Linux KBUILD_CPPFLAGS += -I$(srctree)/$(HOST_DIR)/include \ -I$(srctree)/$(HOST_DIR)/include/uapi \ @@ -155,4 +150,4 @@ archclean: @find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ -o -name '*.gcov' \) -type f -print | xargs rm -f -export HEADER_ARCH SUBARCH USER_CFLAGS CFLAGS_NO_HARDENING OS DEV_NULL_PATH +export HEADER_ARCH SUBARCH USER_CFLAGS CFLAGS_NO_HARDENING DEV_NULL_PATH diff --git a/arch/um/configs/i386_defconfig b/arch/um/configs/i386_defconfig index 630be793759e2cccc762beb14f808a859d98f935..e543cbac879259694ef64e9b65dc81595d40e536 100644 --- a/arch/um/configs/i386_defconfig +++ b/arch/um/configs/i386_defconfig @@ -34,6 +34,7 @@ CONFIG_TTY_CHAN=y CONFIG_XTERM_CHAN=y CONFIG_CON_CHAN="pts" CONFIG_SSL_CHAN="pts" +CONFIG_SOUND=m CONFIG_UML_SOUND=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y diff --git a/arch/um/configs/x86_64_defconfig b/arch/um/configs/x86_64_defconfig index 8540d33702726d73537ae2dab89813c7403d451c..939cb12318cae53ece1f7823c69c9ac4db91ba07 100644 --- a/arch/um/configs/x86_64_defconfig +++ b/arch/um/configs/x86_64_defconfig @@ -32,6 +32,7 @@ CONFIG_TTY_CHAN=y CONFIG_XTERM_CHAN=y CONFIG_CON_CHAN="pts" CONFIG_SSL_CHAN="pts" +CONFIG_SOUND=m CONFIG_UML_SOUND=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y diff --git a/arch/um/drivers/Kconfig b/arch/um/drivers/Kconfig index 36911b1fddcf00653eae02ff2c645ef4938b9a52..b94b2618e7d8411d7b77020d428819d6378055fd 100644 --- a/arch/um/drivers/Kconfig +++ b/arch/um/drivers/Kconfig @@ -111,24 +111,14 @@ config SSL_CHAN config UML_SOUND tristate "Sound support" + depends on SOUND + select SOUND_OSS_CORE help This option enables UML sound support. If enabled, it will pull in - soundcore and the UML hostaudio relay, which acts as a intermediary + the UML hostaudio relay, which acts as a intermediary between the host's dsp and mixer devices and the UML sound system. It is safe to say 'Y' here. -config SOUND - tristate - default UML_SOUND - -config SOUND_OSS_CORE - bool - default UML_SOUND - -config HOSTAUDIO - tristate - default UML_SOUND - endmenu menu "UML Network Devices" diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile index a461a950f0518d55fc5ad95175520c6b45e8efe4..0e6af81096fd596d661686319ccb102abf01ba27 100644 --- a/arch/um/drivers/Makefile +++ b/arch/um/drivers/Makefile @@ -54,7 +54,7 @@ obj-$(CONFIG_UML_NET) += net.o obj-$(CONFIG_MCONSOLE) += mconsole.o obj-$(CONFIG_MMAPPER) += mmapper_kern.o obj-$(CONFIG_BLK_DEV_UBD) += ubd.o -obj-$(CONFIG_HOSTAUDIO) += hostaudio.o +obj-$(CONFIG_UML_SOUND) += hostaudio.o obj-$(CONFIG_NULL_CHAN) += null.o obj-$(CONFIG_PORT_CHAN) += port.o obj-$(CONFIG_PTY_CHAN) += pty.o diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c index 5b064d360cb7495a6392edee68d9d4f5b5572854..c42b793bce6506162ee5cb72262e3bb80188791d 100644 --- a/arch/um/drivers/hostaudio_kern.c +++ b/arch/um/drivers/hostaudio_kern.c @@ -310,7 +310,7 @@ static const struct file_operations hostmixer_fops = { .release = hostmixer_release, }; -struct { +static struct { int dev_audio; int dev_mixer; } module_data; diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c index efa8b730409051346b8fb8b845a7587af5066839..c52b3ff3c092cff658866a9d2e9a9c832575b974 100644 --- a/arch/um/drivers/port_kern.c +++ b/arch/um/drivers/port_kern.c @@ -144,7 +144,7 @@ static void port_work_proc(struct work_struct *unused) local_irq_restore(flags); } -DECLARE_WORK(port_work, port_work_proc); +static DECLARE_WORK(port_work, port_work_proc); static irqreturn_t port_interrupt(int irq, void *data) { diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c index 2d9769237f08e885482ef007c455eb14618659c5..0a6151ee957259913123d5eeea12cc0f24f5a4f9 100644 --- a/arch/um/drivers/slirp_kern.c +++ b/arch/um/drivers/slirp_kern.c @@ -15,7 +15,7 @@ struct slirp_init { struct arg_list_dummy_wrapper argw; /* XXX should be simpler... */ }; -void slirp_init(struct net_device *dev, void *data) +static void slirp_init(struct net_device *dev, void *data) { struct uml_net_private *private; struct slirp_data *spri; diff --git a/arch/um/drivers/virt-pci.c b/arch/um/drivers/virt-pci.c index 7699ca5f35d48ab8fd3fc4df3dbcc162ba2ae19e..ffe2ee8a02465bc649914353becacd59c0dc6188 100644 --- a/arch/um/drivers/virt-pci.c +++ b/arch/um/drivers/virt-pci.c @@ -544,6 +544,7 @@ static void um_pci_irq_vq_cb(struct virtqueue *vq) } } +#ifdef CONFIG_OF /* Copied from arch/x86/kernel/devicetree.c */ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus) { @@ -562,6 +563,7 @@ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus) } return NULL; } +#endif static int um_pci_init_vqs(struct um_pci_device *dev) { diff --git a/arch/um/drivers/xterm_kern.c b/arch/um/drivers/xterm_kern.c index 50f11b7b47744e9bfed119a4c51391a65c2a1cc5..8011e51993d5c4e815d7145d00a1edeb2d8b5fde 100644 --- a/arch/um/drivers/xterm_kern.c +++ b/arch/um/drivers/xterm_kern.c @@ -9,6 +9,7 @@ #include #include #include +#include "xterm.h" struct xterm_wait { struct completion ready; diff --git a/arch/um/include/shared/irq_kern.h b/arch/um/include/shared/irq_kern.h index f2dc817abb7cb8e918f1e34d4bbe12c2ffd0a826..44357fa6ee29c37af8d03e866ad3da3656de605f 100644 --- a/arch/um/include/shared/irq_kern.h +++ b/arch/um/include/shared/irq_kern.h @@ -76,4 +76,5 @@ static inline bool um_irq_timetravel_handler_used(void) } void um_free_irq(int irq, void *dev_id); +void free_irqs(void); #endif diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index a8873d9bc28b8cb2d913cb06b3bf5f9a8469f7b9..635d44606bfeea706fd4937aca93567e6183e1eb 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -23,8 +23,6 @@ #include -extern void free_irqs(void); - /* When epoll triggers we do not know why it did so * we can also have different IRQs for read and write. * This is why we keep a small irq_reg array for each fd - diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 982b777eadc7a16cc4b4b52f300b9c816f0cfd2f..66bfabae8814919c61902da3cdc77a45d9efa775 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1945,6 +1945,7 @@ config EFI select UCS2_STRING select EFI_RUNTIME_WRAPPERS select ARCH_USE_MEMREMAP_PROT + select EFI_RUNTIME_MAP if KEXEC_CORE help This enables the kernel to use EFI runtime services that are available (such as the EFI variable services). @@ -2020,7 +2021,6 @@ config EFI_MAX_FAKE_MEM config EFI_RUNTIME_MAP bool "Export EFI runtime maps to sysfs" if EXPERT depends on EFI - default KEXEC_CORE help Export EFI runtime memory regions to /sys/firmware/efi/runtime-map. That memory map is required by the 2nd kernel to set up EFI virtual diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 95315d3474a292cc3418ea4a9f43dedcdbd5dc14..5bfe5caaa444b3faf5f6b4c06d98e22bc7e76586 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -335,9 +335,5 @@ define archhelp echo ' bzdisk/fdimage*/hdimage/isoimage also accept:' echo ' FDARGS="..." arguments for the booted kernel' echo ' FDINITRD=file initrd for the booted kernel' - echo '' - echo ' kvm_guest.config - Enable Kconfig items for running this kernel as a KVM guest' - echo ' xen.config - Enable Kconfig items for running this kernel as a Xen guest' - echo ' x86_debug.config - Enable tip tree debugging options for testing' endef diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c index bcc956c17872bbd3939b33c59889d39cfb3641ac..08f93b0401bbd3498b0754e20780e2d585e064f1 100644 --- a/arch/x86/boot/compressed/ident_map_64.c +++ b/arch/x86/boot/compressed/ident_map_64.c @@ -59,6 +59,14 @@ static void *alloc_pgt_page(void *context) return NULL; } + /* Consumed more tables than expected? */ + if (pages->pgt_buf_offset == BOOT_PGT_SIZE_WARN) { + debug_putstr("pgt_buf running low in " __FILE__ "\n"); + debug_putstr("Need to raise BOOT_PGT_SIZE?\n"); + debug_putaddr(pages->pgt_buf_offset); + debug_putaddr(pages->pgt_buf_size); + } + entry = pages->pgt_buf + pages->pgt_buf_offset; pages->pgt_buf_offset += PAGE_SIZE; diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index 6c2826417b3376cb70d53df8006f5998a0e33d9c..93c60c0c9d4a7a25d91aef2d948d648e08006abe 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -294,7 +294,7 @@ static void __xen_pv_evtchn_do_upcall(struct pt_regs *regs) inc_irq_stat(irq_hv_callback_count); - xen_hvm_evtchn_do_upcall(); + xen_evtchn_do_upcall(); set_irq_regs(old_regs); } diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c index abadd5f234254ce053c08d49381eb3ec9410958e..e24976593a298a5600dda460c9e81275928343e0 100644 --- a/arch/x86/events/amd/core.c +++ b/arch/x86/events/amd/core.c @@ -534,8 +534,12 @@ static void amd_pmu_cpu_reset(int cpu) /* Clear enable bits i.e. PerfCntrGlobalCtl.PerfCntrEn */ wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0); - /* Clear overflow bits i.e. PerfCntrGLobalStatus.PerfCntrOvfl */ - wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, amd_pmu_global_cntr_mask); + /* + * Clear freeze and overflow bits i.e. PerfCntrGLobalStatus.LbrFreeze + * and PerfCntrGLobalStatus.PerfCntrOvfl + */ + wrmsrl(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, + GLOBAL_STATUS_LBRS_FROZEN | amd_pmu_global_cntr_mask); } static int amd_pmu_cpu_prepare(int cpu) @@ -570,6 +574,7 @@ static void amd_pmu_cpu_starting(int cpu) int i, nb_id; cpuc->perf_ctr_virt_mask = AMD64_EVENTSEL_HOSTONLY; + amd_pmu_cpu_reset(cpu); if (!x86_pmu.amd_nb_constraints) return; @@ -591,8 +596,6 @@ static void amd_pmu_cpu_starting(int cpu) cpuc->amd_nb->nb_id = nb_id; cpuc->amd_nb->refcnt++; - - amd_pmu_cpu_reset(cpu); } static void amd_pmu_cpu_dead(int cpu) @@ -601,6 +604,7 @@ static void amd_pmu_cpu_dead(int cpu) kfree(cpuhw->lbr_sel); cpuhw->lbr_sel = NULL; + amd_pmu_cpu_reset(cpu); if (!x86_pmu.amd_nb_constraints) return; @@ -613,8 +617,6 @@ static void amd_pmu_cpu_dead(int cpu) cpuhw->amd_nb = NULL; } - - amd_pmu_cpu_reset(cpu); } static inline void amd_pmu_set_global_ctl(u64 ctl) @@ -884,7 +886,7 @@ static int amd_pmu_v2_handle_irq(struct pt_regs *regs) struct hw_perf_event *hwc; struct perf_event *event; int handled = 0, idx; - u64 status, mask; + u64 reserved, status, mask; bool pmu_enabled; /* @@ -909,6 +911,14 @@ static int amd_pmu_v2_handle_irq(struct pt_regs *regs) status &= ~GLOBAL_STATUS_LBRS_FROZEN; } + reserved = status & ~amd_pmu_global_cntr_mask; + if (reserved) + pr_warn_once("Reserved PerfCntrGlobalStatus bits are set (0x%llx), please consider updating microcode\n", + reserved); + + /* Clear any reserved bits set by buggy microcode */ + status &= amd_pmu_global_cntr_mask; + for (idx = 0; idx < x86_pmu.num_counters; idx++) { if (!test_bit(idx, cpuc->active_mask)) continue; diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c index 4d349986f76a1d88d75f8c83a92658bfcd9d5577..8250f0f59c2bbe4865703e5b6c3d009bcc03fdab 100644 --- a/arch/x86/events/intel/uncore_snbep.c +++ b/arch/x86/events/intel/uncore_snbep.c @@ -6474,8 +6474,18 @@ void spr_uncore_cpu_init(void) type = uncore_find_type_by_id(uncore_msr_uncores, UNCORE_SPR_CHA); if (type) { + /* + * The value from the discovery table (stored in the type->num_boxes + * of UNCORE_SPR_CHA) is incorrect on some SPR variants because of a + * firmware bug. Using the value from SPR_MSR_UNC_CBO_CONFIG to replace it. + */ rdmsrl(SPR_MSR_UNC_CBO_CONFIG, num_cbo); - type->num_boxes = num_cbo; + /* + * The MSR doesn't work on the EMR XCC, but the firmware bug doesn't impact + * the EMR XCC. Don't let the value from the MSR replace the existing value. + */ + if (num_cbo) + type->num_boxes = num_cbo; } spr_uncore_iio_free_running.num_boxes = uncore_type_max_boxes(uncore_msr_uncores, UNCORE_SPR_IIO); } diff --git a/arch/x86/hyperv/hv_apic.c b/arch/x86/hyperv/hv_apic.c index 187e13b15e9a81961a1b220366e5fa9e9ade9e7c..97bfe5f0531f39633ee7bf9ca6fbdfb3d2524387 100644 --- a/arch/x86/hyperv/hv_apic.c +++ b/arch/x86/hyperv/hv_apic.c @@ -175,8 +175,11 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector, (exclude_self && weight == 1 && cpumask_test_cpu(this_cpu, mask))) return true; - if (!hv_hypercall_pg) - return false; + /* A fully enlightened TDX VM uses GHCI rather than hv_hypercall_pg. */ + if (!hv_hypercall_pg) { + if (ms_hyperv.paravisor_present || !hv_isolation_type_tdx()) + return false; + } if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR)) return false; @@ -229,9 +232,15 @@ static bool __send_ipi_one(int cpu, int vector) trace_hyperv_send_ipi_one(cpu, vector); - if (!hv_hypercall_pg || (vp == VP_INVAL)) + if (vp == VP_INVAL) return false; + /* A fully enlightened TDX VM uses GHCI rather than hv_hypercall_pg. */ + if (!hv_hypercall_pg) { + if (ms_hyperv.paravisor_present || !hv_isolation_type_tdx()) + return false; + } + if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR)) return false; diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c index 507d98331e7c469439207c80b23d0d3a679d11b5..21556ad87f4ba86a076c828b2a88daf76a02c038 100644 --- a/arch/x86/hyperv/hv_init.c +++ b/arch/x86/hyperv/hv_init.c @@ -7,6 +7,8 @@ * Author : K. Y. Srinivasan */ +#define pr_fmt(fmt) "Hyper-V: " fmt + #include #include #include @@ -19,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +55,7 @@ static int hyperv_init_ghcb(void) void *ghcb_va; void **ghcb_base; - if (!hv_isolation_type_snp()) + if (!ms_hyperv.paravisor_present || !hv_isolation_type_snp()) return 0; if (!hv_ghcb_pg) @@ -80,7 +83,7 @@ static int hyperv_init_ghcb(void) static int hv_cpu_init(unsigned int cpu) { union hv_vp_assist_msr_contents msr = { 0 }; - struct hv_vp_assist_page **hvp = &hv_vp_assist_page[cpu]; + struct hv_vp_assist_page **hvp; int ret; ret = hv_common_cpu_init(cpu); @@ -90,6 +93,7 @@ static int hv_cpu_init(unsigned int cpu) if (!hv_vp_assist_page) return 0; + hvp = &hv_vp_assist_page[cpu]; if (hv_root_partition) { /* * For root partition we get the hypervisor provided VP assist @@ -107,8 +111,21 @@ static int hv_cpu_init(unsigned int cpu) * in hv_cpu_die(), otherwise a CPU may not be stopped in the * case of CPU offlining and the VM will hang. */ - if (!*hvp) + if (!*hvp) { *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO); + + /* + * Hyper-V should never specify a VM that is a Confidential + * VM and also running in the root partition. Root partition + * is blocked to run in Confidential VM. So only decrypt assist + * page in non-root partition here. + */ + if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) { + WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1)); + memset(*hvp, 0, PAGE_SIZE); + } + } + if (*hvp) msr.pfn = vmalloc_to_pfn(*hvp); @@ -176,7 +193,7 @@ void set_hv_tscchange_cb(void (*cb)(void)) struct hv_tsc_emulation_control emu_ctrl = {.enabled = 1}; if (!hv_reenlightenment_available()) { - pr_warn("Hyper-V: reenlightenment support is unavailable\n"); + pr_warn("reenlightenment support is unavailable\n"); return; } @@ -379,6 +396,40 @@ static void __init hv_get_partition_id(void) local_irq_restore(flags); } +#if IS_ENABLED(CONFIG_HYPERV_VTL_MODE) +static u8 __init get_vtl(void) +{ + u64 control = HV_HYPERCALL_REP_COMP_1 | HVCALL_GET_VP_REGISTERS; + struct hv_get_vp_registers_input *input; + struct hv_get_vp_registers_output *output; + unsigned long flags; + u64 ret; + + local_irq_save(flags); + input = *this_cpu_ptr(hyperv_pcpu_input_arg); + output = (struct hv_get_vp_registers_output *)input; + + memset(input, 0, struct_size(input, element, 1)); + input->header.partitionid = HV_PARTITION_ID_SELF; + input->header.vpindex = HV_VP_INDEX_SELF; + input->header.inputvtl = 0; + input->element[0].name0 = HV_X64_REGISTER_VSM_VP_STATUS; + + ret = hv_do_hypercall(control, input, output); + if (hv_result_success(ret)) { + ret = output->as64.low & HV_X64_VTL_MASK; + } else { + pr_err("Failed to get VTL(error: %lld) exiting...\n", ret); + BUG(); + } + + local_irq_restore(flags); + return ret; +} +#else +static inline u8 get_vtl(void) { return 0; } +#endif + /* * This function is to be invoked early in the boot sequence after the * hypervisor has been detected. @@ -399,14 +450,24 @@ void __init hyperv_init(void) if (hv_common_init()) return; - hv_vp_assist_page = kcalloc(num_possible_cpus(), - sizeof(*hv_vp_assist_page), GFP_KERNEL); + /* + * The VP assist page is useless to a TDX guest: the only use we + * would have for it is lazy EOI, which can not be used with TDX. + */ + if (hv_isolation_type_tdx()) + hv_vp_assist_page = NULL; + else + hv_vp_assist_page = kcalloc(num_possible_cpus(), + sizeof(*hv_vp_assist_page), + GFP_KERNEL); if (!hv_vp_assist_page) { ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED; - goto common_free; + + if (!hv_isolation_type_tdx()) + goto common_free; } - if (hv_isolation_type_snp()) { + if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) { /* Negotiate GHCB Version. */ if (!hv_ghcb_negotiate_protocol()) hv_ghcb_terminate(SEV_TERM_SET_GEN, @@ -426,12 +487,32 @@ void __init hyperv_init(void) * Setup the hypercall page and enable hypercalls. * 1. Register the guest ID * 2. Enable the hypercall and register the hypercall page + * + * A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg: + * when the hypercall input is a page, such a VM must pass a decrypted + * page to Hyper-V, e.g. hv_post_message() uses the per-CPU page + * hyperv_pcpu_input_arg, which is decrypted if no paravisor is present. + * + * A TDX VM with the paravisor uses hv_hypercall_pg for most hypercalls, + * which are handled by the paravisor and the VM must use an encrypted + * input page: in such a VM, the hyperv_pcpu_input_arg is encrypted and + * used in the hypercalls, e.g. see hv_mark_gpa_visibility() and + * hv_arch_irq_unmask(). Such a VM uses TDX GHCI for two hypercalls: + * 1. HVCALL_SIGNAL_EVENT: see vmbus_set_event() and _hv_do_fast_hypercall8(). + * 2. HVCALL_POST_MESSAGE: the input page must be a decrypted page, i.e. + * hv_post_message() in such a VM can't use the encrypted hyperv_pcpu_input_arg; + * instead, hv_post_message() uses the post_msg_page, which is decrypted + * in such a VM and is only used in such a VM. */ guest_id = hv_generate_guest_id(LINUX_VERSION_CODE); wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id); - /* Hyper-V requires to write guest os id via ghcb in SNP IVM. */ - hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id); + /* With the paravisor, the VM must also write the ID via GHCB/GHCI */ + hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, guest_id); + + /* A TDX VM with no paravisor only uses TDX GHCI rather than hv_hypercall_pg */ + if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present) + goto skip_hypercall_pg_init; hv_hypercall_pg = __vmalloc_node_range(PAGE_SIZE, 1, VMALLOC_START, VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_ROX, @@ -472,6 +553,7 @@ void __init hyperv_init(void) wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64); } +skip_hypercall_pg_init: /* * Some versions of Hyper-V that provide IBT in guest VMs have a bug * in that there's no ENDBR64 instruction at the entry to the @@ -488,7 +570,7 @@ void __init hyperv_init(void) if (cpu_feature_enabled(X86_FEATURE_IBT) && *(u32 *)hv_hypercall_pg != gen_endbr()) { setup_clear_cpu_cap(X86_FEATURE_IBT); - pr_warn("Hyper-V: Disabling IBT because of Hyper-V bug\n"); + pr_warn("Disabling IBT because of Hyper-V bug\n"); } #endif @@ -527,11 +609,17 @@ void __init hyperv_init(void) /* Query the VMs extended capability once, so that it can be cached. */ hv_query_ext_cap(0); + /* Find the VTL */ + ms_hyperv.vtl = get_vtl(); + + if (ms_hyperv.vtl > 0) /* non default VTL */ + hv_vtl_early_init(); + return; clean_guest_os_id: wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); - hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0); + hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0); cpuhp_remove_state(cpuhp); free_ghcb_page: free_percpu(hv_ghcb_pg); @@ -552,7 +640,7 @@ void hyperv_cleanup(void) /* Reset our OS id */ wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0); - hv_ghcb_msr_write(HV_X64_MSR_GUEST_OS_ID, 0); + hv_ivm_msr_write(HV_X64_MSR_GUEST_OS_ID, 0); /* * Reset hypercall page reference before reset the page, @@ -615,6 +703,9 @@ bool hv_is_hyperv_initialized(void) if (x86_hyper_type != X86_HYPER_MS_HYPERV) return false; + /* A TDX VM with no paravisor uses TDX GHCI call rather than hv_hypercall_pg */ + if (hv_isolation_type_tdx() && !ms_hyperv.paravisor_present) + return true; /* * Verify that earlier initialization succeeded by checking * that the hypercall page is setup diff --git a/arch/x86/hyperv/hv_vtl.c b/arch/x86/hyperv/hv_vtl.c index 36a5622180104f29e9c3ab5fc99c8d1bca7a3c6a..999f5ac82fe900d73d319c562319ce9bd77cde98 100644 --- a/arch/x86/hyperv/hv_vtl.c +++ b/arch/x86/hyperv/hv_vtl.c @@ -215,7 +215,7 @@ static int hv_vtl_wakeup_secondary_cpu(int apicid, unsigned long start_eip) return hv_vtl_bringup_vcpu(vp_id, start_eip); } -static int __init hv_vtl_early_init(void) +int __init hv_vtl_early_init(void) { /* * `boot_cpu_has` returns the runtime feature support, @@ -230,4 +230,3 @@ static int __init hv_vtl_early_init(void) return 0; } -early_initcall(hv_vtl_early_init); diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c index 28be6df88063974d61eeab55cd97c89b216318a3..8c6bf07f7d2b805dd95251571920b7a1a91fea45 100644 --- a/arch/x86/hyperv/ivm.c +++ b/arch/x86/hyperv/ivm.c @@ -18,6 +18,11 @@ #include #include #include +#include +#include +#include +#include +#include #ifdef CONFIG_AMD_MEM_ENCRYPT @@ -56,8 +61,10 @@ union hv_ghcb { } hypercall; } __packed __aligned(HV_HYP_PAGE_SIZE); +/* Only used in an SNP VM with the paravisor */ static u16 hv_ghcb_version __ro_after_init; +/* Functions only used in an SNP VM with the paravisor go here. */ u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size) { union hv_ghcb *hv_ghcb; @@ -175,7 +182,7 @@ bool hv_ghcb_negotiate_protocol(void) return true; } -void hv_ghcb_msr_write(u64 msr, u64 value) +static void hv_ghcb_msr_write(u64 msr, u64 value) { union hv_ghcb *hv_ghcb; void **ghcb_base; @@ -203,9 +210,8 @@ void hv_ghcb_msr_write(u64 msr, u64 value) local_irq_restore(flags); } -EXPORT_SYMBOL_GPL(hv_ghcb_msr_write); -void hv_ghcb_msr_read(u64 msr, u64 *value) +static void hv_ghcb_msr_read(u64 msr, u64 *value) { union hv_ghcb *hv_ghcb; void **ghcb_base; @@ -235,7 +241,217 @@ void hv_ghcb_msr_read(u64 msr, u64 *value) | ((u64)lower_32_bits(hv_ghcb->ghcb.save.rdx) << 32); local_irq_restore(flags); } -EXPORT_SYMBOL_GPL(hv_ghcb_msr_read); + +/* Only used in a fully enlightened SNP VM, i.e. without the paravisor */ +static u8 ap_start_input_arg[PAGE_SIZE] __bss_decrypted __aligned(PAGE_SIZE); +static u8 ap_start_stack[PAGE_SIZE] __aligned(PAGE_SIZE); +static DEFINE_PER_CPU(struct sev_es_save_area *, hv_sev_vmsa); + +/* Functions only used in an SNP VM without the paravisor go here. */ + +#define hv_populate_vmcb_seg(seg, gdtr_base) \ +do { \ + if (seg.selector) { \ + seg.base = 0; \ + seg.limit = HV_AP_SEGMENT_LIMIT; \ + seg.attrib = *(u16 *)(gdtr_base + seg.selector + 5); \ + seg.attrib = (seg.attrib & 0xFF) | ((seg.attrib >> 4) & 0xF00); \ + } \ +} while (0) \ + +static int snp_set_vmsa(void *va, bool vmsa) +{ + u64 attrs; + + /* + * Running at VMPL0 allows the kernel to change the VMSA bit for a page + * using the RMPADJUST instruction. However, for the instruction to + * succeed it must target the permissions of a lesser privileged + * (higher numbered) VMPL level, so use VMPL1 (refer to the RMPADJUST + * instruction in the AMD64 APM Volume 3). + */ + attrs = 1; + if (vmsa) + attrs |= RMPADJUST_VMSA_PAGE_BIT; + + return rmpadjust((unsigned long)va, RMP_PG_SIZE_4K, attrs); +} + +static void snp_cleanup_vmsa(struct sev_es_save_area *vmsa) +{ + int err; + + err = snp_set_vmsa(vmsa, false); + if (err) + pr_err("clear VMSA page failed (%u), leaking page\n", err); + else + free_page((unsigned long)vmsa); +} + +int hv_snp_boot_ap(int cpu, unsigned long start_ip) +{ + struct sev_es_save_area *vmsa = (struct sev_es_save_area *) + __get_free_page(GFP_KERNEL | __GFP_ZERO); + struct sev_es_save_area *cur_vmsa; + struct desc_ptr gdtr; + u64 ret, retry = 5; + struct hv_enable_vp_vtl *start_vp_input; + unsigned long flags; + + if (!vmsa) + return -ENOMEM; + + native_store_gdt(&gdtr); + + vmsa->gdtr.base = gdtr.address; + vmsa->gdtr.limit = gdtr.size; + + asm volatile("movl %%es, %%eax;" : "=a" (vmsa->es.selector)); + hv_populate_vmcb_seg(vmsa->es, vmsa->gdtr.base); + + asm volatile("movl %%cs, %%eax;" : "=a" (vmsa->cs.selector)); + hv_populate_vmcb_seg(vmsa->cs, vmsa->gdtr.base); + + asm volatile("movl %%ss, %%eax;" : "=a" (vmsa->ss.selector)); + hv_populate_vmcb_seg(vmsa->ss, vmsa->gdtr.base); + + asm volatile("movl %%ds, %%eax;" : "=a" (vmsa->ds.selector)); + hv_populate_vmcb_seg(vmsa->ds, vmsa->gdtr.base); + + vmsa->efer = native_read_msr(MSR_EFER); + + asm volatile("movq %%cr4, %%rax;" : "=a" (vmsa->cr4)); + asm volatile("movq %%cr3, %%rax;" : "=a" (vmsa->cr3)); + asm volatile("movq %%cr0, %%rax;" : "=a" (vmsa->cr0)); + + vmsa->xcr0 = 1; + vmsa->g_pat = HV_AP_INIT_GPAT_DEFAULT; + vmsa->rip = (u64)secondary_startup_64_no_verify; + vmsa->rsp = (u64)&ap_start_stack[PAGE_SIZE]; + + /* + * Set the SNP-specific fields for this VMSA: + * VMPL level + * SEV_FEATURES (matches the SEV STATUS MSR right shifted 2 bits) + */ + vmsa->vmpl = 0; + vmsa->sev_features = sev_status >> 2; + + ret = snp_set_vmsa(vmsa, true); + if (!ret) { + pr_err("RMPADJUST(%llx) failed: %llx\n", (u64)vmsa, ret); + free_page((u64)vmsa); + return ret; + } + + local_irq_save(flags); + start_vp_input = (struct hv_enable_vp_vtl *)ap_start_input_arg; + memset(start_vp_input, 0, sizeof(*start_vp_input)); + start_vp_input->partition_id = -1; + start_vp_input->vp_index = cpu; + start_vp_input->target_vtl.target_vtl = ms_hyperv.vtl; + *(u64 *)&start_vp_input->vp_context = __pa(vmsa) | 1; + + do { + ret = hv_do_hypercall(HVCALL_START_VP, + start_vp_input, NULL); + } while (hv_result(ret) == HV_STATUS_TIME_OUT && retry--); + + local_irq_restore(flags); + + if (!hv_result_success(ret)) { + pr_err("HvCallStartVirtualProcessor failed: %llx\n", ret); + snp_cleanup_vmsa(vmsa); + vmsa = NULL; + } + + cur_vmsa = per_cpu(hv_sev_vmsa, cpu); + /* Free up any previous VMSA page */ + if (cur_vmsa) + snp_cleanup_vmsa(cur_vmsa); + + /* Record the current VMSA page */ + per_cpu(hv_sev_vmsa, cpu) = vmsa; + + return ret; +} + +#else +static inline void hv_ghcb_msr_write(u64 msr, u64 value) {} +static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {} +#endif /* CONFIG_AMD_MEM_ENCRYPT */ + +#ifdef CONFIG_INTEL_TDX_GUEST +static void hv_tdx_msr_write(u64 msr, u64 val) +{ + struct tdx_hypercall_args args = { + .r10 = TDX_HYPERCALL_STANDARD, + .r11 = EXIT_REASON_MSR_WRITE, + .r12 = msr, + .r13 = val, + }; + + u64 ret = __tdx_hypercall(&args); + + WARN_ONCE(ret, "Failed to emulate MSR write: %lld\n", ret); +} + +static void hv_tdx_msr_read(u64 msr, u64 *val) +{ + struct tdx_hypercall_args args = { + .r10 = TDX_HYPERCALL_STANDARD, + .r11 = EXIT_REASON_MSR_READ, + .r12 = msr, + }; + + u64 ret = __tdx_hypercall_ret(&args); + + if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret)) + *val = 0; + else + *val = args.r11; +} + +u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2) +{ + struct tdx_hypercall_args args = { }; + + args.r10 = control; + args.rdx = param1; + args.r8 = param2; + + (void)__tdx_hypercall_ret(&args); + + return args.r11; +} + +#else +static inline void hv_tdx_msr_write(u64 msr, u64 value) {} +static inline void hv_tdx_msr_read(u64 msr, u64 *value) {} +#endif /* CONFIG_INTEL_TDX_GUEST */ + +#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST) +void hv_ivm_msr_write(u64 msr, u64 value) +{ + if (!ms_hyperv.paravisor_present) + return; + + if (hv_isolation_type_tdx()) + hv_tdx_msr_write(msr, value); + else if (hv_isolation_type_snp()) + hv_ghcb_msr_write(msr, value); +} + +void hv_ivm_msr_read(u64 msr, u64 *value) +{ + if (!ms_hyperv.paravisor_present) + return; + + if (hv_isolation_type_tdx()) + hv_tdx_msr_read(msr, value); + else if (hv_isolation_type_snp()) + hv_ghcb_msr_read(msr, value); +} /* * hv_mark_gpa_visibility - Set pages visible to host via hvcall. @@ -358,13 +574,34 @@ static bool hv_is_private_mmio(u64 addr) void __init hv_vtom_init(void) { + enum hv_isolation_type type = hv_get_isolation_type(); + + switch (type) { + case HV_ISOLATION_TYPE_VBS: + fallthrough; /* * By design, a VM using vTOM doesn't see the SEV setting, * so SEV initialization is bypassed and sev_status isn't set. * Set it here to indicate a vTOM VM. + * + * Note: if CONFIG_AMD_MEM_ENCRYPT is not set, sev_status is + * defined as 0ULL, to which we can't assigned a value. */ - sev_status = MSR_AMD64_SNP_VTOM; - cc_vendor = CC_VENDOR_AMD; +#ifdef CONFIG_AMD_MEM_ENCRYPT + case HV_ISOLATION_TYPE_SNP: + sev_status = MSR_AMD64_SNP_VTOM; + cc_vendor = CC_VENDOR_AMD; + break; +#endif + + case HV_ISOLATION_TYPE_TDX: + cc_vendor = CC_VENDOR_INTEL; + break; + + default: + panic("hv_vtom_init: unsupported isolation type %d\n", type); + } + cc_set_mask(ms_hyperv.shared_gpa_boundary); physical_mask &= ms_hyperv.shared_gpa_boundary - 1; @@ -377,7 +614,7 @@ void __init hv_vtom_init(void) mtrr_overwrite_state(NULL, 0, MTRR_TYPE_WRBACK); } -#endif /* CONFIG_AMD_MEM_ENCRYPT */ +#endif /* defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST) */ enum hv_isolation_type hv_get_isolation_type(void) { @@ -405,10 +642,20 @@ bool hv_is_isolation_supported(void) DEFINE_STATIC_KEY_FALSE(isolation_type_snp); /* - * hv_isolation_type_snp - Check system runs in the AMD SEV-SNP based + * hv_isolation_type_snp - Check if the system runs in an AMD SEV-SNP based * isolation VM. */ bool hv_isolation_type_snp(void) { return static_branch_unlikely(&isolation_type_snp); } + +DEFINE_STATIC_KEY_FALSE(isolation_type_tdx); +/* + * hv_isolation_type_tdx - Check if the system runs in an Intel TDX based + * isolated VM. + */ +bool hv_isolation_type_tdx(void) +{ + return static_branch_unlikely(&isolation_type_tdx); +} diff --git a/arch/x86/include/asm/audit.h b/arch/x86/include/asm/audit.h index 36aec57ea7a36b25a47a3dbc33a99d8033c66a65..fa918f01333e0f00fbd9119fe4da8ca741d20269 100644 --- a/arch/x86/include/asm/audit.h +++ b/arch/x86/include/asm/audit.h @@ -4,4 +4,11 @@ int ia32_classify_syscall(unsigned int syscall); +extern unsigned ia32_dir_class[]; +extern unsigned ia32_write_class[]; +extern unsigned ia32_read_class[]; +extern unsigned ia32_chattr_class[]; +extern unsigned ia32_signal_class[]; + + #endif /* _ASM_X86_AUDIT_H */ diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h index 4ae14339cb8cc72da4df2442a363ca2f728573c6..b3a7cfb0d99e01a8f54fa540983ca9a4b4010f50 100644 --- a/arch/x86/include/asm/boot.h +++ b/arch/x86/include/asm/boot.h @@ -40,23 +40,40 @@ #ifdef CONFIG_X86_64 # define BOOT_STACK_SIZE 0x4000 +/* + * Used by decompressor's startup_32() to allocate page tables for identity + * mapping of the 4G of RAM in 4-level paging mode: + * - 1 level4 table; + * - 1 level3 table; + * - 4 level2 table that maps everything with 2M pages; + * + * The additional level5 table needed for 5-level paging is allocated from + * trampoline_32bit memory. + */ # define BOOT_INIT_PGT_SIZE (6*4096) -# ifdef CONFIG_RANDOMIZE_BASE + /* - * Assuming all cross the 512GB boundary: - * 1 page for level4 - * (2+2)*4 pages for kernel, param, cmd_line, and randomized kernel - * 2 pages for first 2M (video RAM: CONFIG_X86_VERBOSE_BOOTUP). - * Total is 19 pages. + * Total number of page tables kernel_add_identity_map() can allocate, + * including page tables consumed by startup_32(). + * + * Worst-case scenario: + * - 5-level paging needs 1 level5 table; + * - KASLR needs to map kernel, boot_params, cmdline and randomized kernel, + * assuming all of them cross 256T boundary: + * + 4*2 level4 table; + * + 4*2 level3 table; + * + 4*2 level2 table; + * - X86_VERBOSE_BOOTUP needs to map the first 2M (video RAM): + * + 1 level4 table; + * + 1 level3 table; + * + 1 level2 table; + * Total: 28 tables + * + * Add 4 spare table in case decompressor touches anything beyond what is + * accounted above. Warn if it happens. */ -# ifdef CONFIG_X86_VERBOSE_BOOTUP -# define BOOT_PGT_SIZE (19*4096) -# else /* !CONFIG_X86_VERBOSE_BOOTUP */ -# define BOOT_PGT_SIZE (17*4096) -# endif -# else /* !CONFIG_RANDOMIZE_BASE */ -# define BOOT_PGT_SIZE BOOT_INIT_PGT_SIZE -# endif +# define BOOT_PGT_SIZE_WARN (28*4096) +# define BOOT_PGT_SIZE (32*4096) #else /* !CONFIG_X86_64 */ # define BOOT_STACK_SIZE 0x1000 diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 2061ed1c398f6afbc88b7627e7a3ed7306295301..58cb9495e40f426323cd65569cd53145e7b2f9e7 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -439,6 +439,7 @@ #define X86_FEATURE_SEV_ES (19*32+ 3) /* AMD Secure Encrypted Virtualization - Encrypted State */ #define X86_FEATURE_V_TSC_AUX (19*32+ 9) /* "" Virtual TSC_AUX */ #define X86_FEATURE_SME_COHERENT (19*32+10) /* "" AMD hardware-enforced cache coherency */ +#define X86_FEATURE_DEBUG_SWAP (19*32+14) /* AMD SEV-ES full debug state swap support */ /* AMD-defined Extended Feature 2 EAX, CPUID level 0x80000021 (EAX), word 20 */ #define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* "" No Nested Data Breakpoints */ diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index b0994ae3bc23f84df03331b9ffc36b3e1c9d62f9..c4555b269a1b247430f50dcf5934855823e1eba6 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -91,19 +91,6 @@ static inline void efi_fpu_end(void) #ifdef CONFIG_X86_32 #define EFI_X86_KERNEL_ALLOC_LIMIT (SZ_512M - 1) - -#define arch_efi_call_virt_setup() \ -({ \ - efi_fpu_begin(); \ - firmware_restrict_branch_speculation_start(); \ -}) - -#define arch_efi_call_virt_teardown() \ -({ \ - firmware_restrict_branch_speculation_end(); \ - efi_fpu_end(); \ -}) - #else /* !CONFIG_X86_32 */ #define EFI_X86_KERNEL_ALLOC_LIMIT EFI_ALLOC_LIMIT @@ -116,14 +103,6 @@ extern bool efi_disable_ibt_for_runtime; __efi_call(__VA_ARGS__); \ }) -#define arch_efi_call_virt_setup() \ -({ \ - efi_sync_low_kernel_mappings(); \ - efi_fpu_begin(); \ - firmware_restrict_branch_speculation_start(); \ - efi_enter_mm(); \ -}) - #undef arch_efi_call_virt #define arch_efi_call_virt(p, f, args...) ({ \ u64 ret, ibt = ibt_save(efi_disable_ibt_for_runtime); \ @@ -132,13 +111,6 @@ extern bool efi_disable_ibt_for_runtime; ret; \ }) -#define arch_efi_call_virt_teardown() \ -({ \ - efi_leave_mm(); \ - firmware_restrict_branch_speculation_end(); \ - efi_fpu_end(); \ -}) - #ifdef CONFIG_KASAN /* * CONFIG_KASAN may redefine memset to __memset. __memset function is present @@ -168,8 +140,8 @@ extern void efi_delete_dummy_variable(void); extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr); extern void efi_free_boot_services(void); -void efi_enter_mm(void); -void efi_leave_mm(void); +void arch_efi_call_virt_setup(void); +void arch_efi_call_virt_teardown(void); /* kexec external ABI */ struct efi_setup_data { diff --git a/arch/x86/include/asm/hyperv-tlfs.h b/arch/x86/include/asm/hyperv-tlfs.h index cea95dcd27c2e559983b9ea9cab15b83a1f26cd0..2ff26f53cd62446e6260025b7a7094fee1d5b765 100644 --- a/arch/x86/include/asm/hyperv-tlfs.h +++ b/arch/x86/include/asm/hyperv-tlfs.h @@ -169,7 +169,8 @@ enum hv_isolation_type { HV_ISOLATION_TYPE_NONE = 0, HV_ISOLATION_TYPE_VBS = 1, - HV_ISOLATION_TYPE_SNP = 2 + HV_ISOLATION_TYPE_SNP = 2, + HV_ISOLATION_TYPE_TDX = 3 }; /* Hyper-V specific model specific registers (MSRs) */ @@ -301,6 +302,13 @@ enum hv_isolation_type { #define HV_X64_MSR_TIME_REF_COUNT HV_REGISTER_TIME_REF_COUNT #define HV_X64_MSR_REFERENCE_TSC HV_REGISTER_REFERENCE_TSC +/* + * Registers are only accessible via HVCALL_GET_VP_REGISTERS hvcall and + * there is not associated MSR address. + */ +#define HV_X64_REGISTER_VSM_VP_STATUS 0x000D0003 +#define HV_X64_VTL_MASK GENMASK(3, 0) + /* Hyper-V memory host visibility */ enum hv_mem_host_visibility { VMBUS_PAGE_NOT_VISIBLE = 0, diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h index 3be6a98751f02b10c05772ec6cdcf0359f9577aa..c9f6a6c5de3cf6c72f53a62e25c0c548df85b28e 100644 --- a/arch/x86/include/asm/kexec.h +++ b/arch/x86/include/asm/kexec.h @@ -205,8 +205,6 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image); #endif #endif -typedef void crash_vmclear_fn(void); -extern crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss; extern void kdump_nmi_shootdown_cpus(void); #ifdef CONFIG_CRASH_HOTPLUG diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 3bc146dfd38da5bbf98bbe33353398ac8a625fa0..17715cb8731d5d2c7d60caeebf6e340158888ef5 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -288,13 +288,13 @@ struct kvm_kernel_irq_routing_entry; * kvm_mmu_page_role tracks the properties of a shadow page (where shadow page * also includes TDP pages) to determine whether or not a page can be used in * the given MMU context. This is a subset of the overall kvm_cpu_role to - * minimize the size of kvm_memory_slot.arch.gfn_track, i.e. allows allocating - * 2 bytes per gfn instead of 4 bytes per gfn. + * minimize the size of kvm_memory_slot.arch.gfn_write_track, i.e. allows + * allocating 2 bytes per gfn instead of 4 bytes per gfn. * * Upper-level shadow pages having gptes are tracked for write-protection via - * gfn_track. As above, gfn_track is a 16 bit counter, so KVM must not create - * more than 2^16-1 upper-level shadow pages at a single gfn, otherwise - * gfn_track will overflow and explosions will ensure. + * gfn_write_track. As above, gfn_write_track is a 16 bit counter, so KVM must + * not create more than 2^16-1 upper-level shadow pages at a single gfn, + * otherwise gfn_write_track will overflow and explosions will ensue. * * A unique shadow page (SP) for a gfn is created if and only if an existing SP * cannot be reused. The ability to reuse a SP is tracked by its role, which @@ -746,7 +746,6 @@ struct kvm_vcpu_arch { u64 smi_count; bool at_instruction_boundary; bool tpr_access_reporting; - bool xsaves_enabled; bool xfd_no_write_intercept; u64 ia32_xss; u64 microcode_version; @@ -831,6 +830,25 @@ struct kvm_vcpu_arch { struct kvm_cpuid_entry2 *cpuid_entries; struct kvm_hypervisor_cpuid kvm_cpuid; + /* + * FIXME: Drop this macro and use KVM_NR_GOVERNED_FEATURES directly + * when "struct kvm_vcpu_arch" is no longer defined in an + * arch/x86/include/asm header. The max is mostly arbitrary, i.e. + * can be increased as necessary. + */ +#define KVM_MAX_NR_GOVERNED_FEATURES BITS_PER_LONG + + /* + * Track whether or not the guest is allowed to use features that are + * governed by KVM, where "governed" means KVM needs to manage state + * and/or explicitly enable the feature in hardware. Typically, but + * not always, governed features can be used by the guest if and only + * if both KVM and userspace want to expose the feature to the guest. + */ + struct { + DECLARE_BITMAP(enabled, KVM_MAX_NR_GOVERNED_FEATURES); + } governed_features; + u64 reserved_gpa_bits; int maxphyaddr; @@ -1005,7 +1023,7 @@ struct kvm_lpage_info { struct kvm_arch_memory_slot { struct kvm_rmap_head *rmap[KVM_NR_PAGE_SIZES]; struct kvm_lpage_info *lpage_info[KVM_NR_PAGE_SIZES - 1]; - unsigned short *gfn_track[KVM_PAGE_TRACK_MAX]; + unsigned short *gfn_write_track; }; /* @@ -1247,8 +1265,9 @@ struct kvm_arch { * create an NX huge page (without hanging the guest). */ struct list_head possible_nx_huge_pages; - struct kvm_page_track_notifier_node mmu_sp_tracker; +#ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING struct kvm_page_track_notifier_head track_notifier_head; +#endif /* * Protects marking pages unsync during page faults, as TDP MMU page * faults only take mmu_lock for read. For simplicity, the unsync @@ -1400,7 +1419,6 @@ struct kvm_arch { * the thread holds the MMU lock in write mode. */ spinlock_t tdp_mmu_pages_lock; - struct workqueue_struct *tdp_mmu_zap_wq; #endif /* CONFIG_X86_64 */ /* @@ -1655,8 +1673,8 @@ struct kvm_x86_ops { u64 (*get_l2_tsc_offset)(struct kvm_vcpu *vcpu); u64 (*get_l2_tsc_multiplier)(struct kvm_vcpu *vcpu); - void (*write_tsc_offset)(struct kvm_vcpu *vcpu, u64 offset); - void (*write_tsc_multiplier)(struct kvm_vcpu *vcpu, u64 multiplier); + void (*write_tsc_offset)(struct kvm_vcpu *vcpu); + void (*write_tsc_multiplier)(struct kvm_vcpu *vcpu); /* * Retrieve somewhat arbitrary exit information. Intended to @@ -1795,8 +1813,8 @@ static inline struct kvm *kvm_arch_alloc_vm(void) #define __KVM_HAVE_ARCH_VM_FREE void kvm_arch_free_vm(struct kvm *kvm); -#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLB -static inline int kvm_arch_flush_remote_tlb(struct kvm *kvm) +#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS +static inline int kvm_arch_flush_remote_tlbs(struct kvm *kvm) { if (kvm_x86_ops.flush_remote_tlbs && !static_call(kvm_x86_flush_remote_tlbs)(kvm)) @@ -1805,6 +1823,8 @@ static inline int kvm_arch_flush_remote_tlb(struct kvm *kvm) return -ENOTSUPP; } +#define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE + #define kvm_arch_pmi_in_guest(vcpu) \ ((vcpu) && (vcpu)->arch.handling_intr_from_guest) @@ -1814,7 +1834,7 @@ void kvm_mmu_vendor_module_exit(void); void kvm_mmu_destroy(struct kvm_vcpu *vcpu); int kvm_mmu_create(struct kvm_vcpu *vcpu); -int kvm_mmu_init_vm(struct kvm *kvm); +void kvm_mmu_init_vm(struct kvm *kvm); void kvm_mmu_uninit_vm(struct kvm *kvm); void kvm_mmu_after_set_cpuid(struct kvm_vcpu *vcpu); @@ -1833,7 +1853,6 @@ void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, const struct kvm_memory_slot *memslot); void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm, const struct kvm_memory_slot *memslot); -void kvm_mmu_zap_all(struct kvm *kvm); void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen); void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned long kvm_nr_mmu_pages); diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h index eb186bc57f6a907d31591249268a588da59bca0e..3d040741044b4142ce4c7c2f79608ac18b409c69 100644 --- a/arch/x86/include/asm/kvm_page_track.h +++ b/arch/x86/include/asm/kvm_page_track.h @@ -2,11 +2,9 @@ #ifndef _ASM_X86_KVM_PAGE_TRACK_H #define _ASM_X86_KVM_PAGE_TRACK_H -enum kvm_page_track_mode { - KVM_PAGE_TRACK_WRITE, - KVM_PAGE_TRACK_MAX, -}; +#include +#ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING /* * The notifier represented by @kvm_page_track_notifier_node is linked into * the head which will be notified when guest is triggering the track event. @@ -26,54 +24,39 @@ struct kvm_page_track_notifier_node { * It is called when guest is writing the write-tracked page * and write emulation is finished at that time. * - * @vcpu: the vcpu where the write access happened. * @gpa: the physical address written by guest. * @new: the data was written to the address. * @bytes: the written length. * @node: this node */ - void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, - int bytes, struct kvm_page_track_notifier_node *node); + void (*track_write)(gpa_t gpa, const u8 *new, int bytes, + struct kvm_page_track_notifier_node *node); + /* - * It is called when memory slot is being moved or removed - * users can drop write-protection for the pages in that memory slot + * Invoked when a memory region is removed from the guest. Or in KVM + * terms, when a memslot is deleted. * - * @kvm: the kvm where memory slot being moved or removed - * @slot: the memory slot being moved or removed - * @node: this node + * @gfn: base gfn of the region being removed + * @nr_pages: number of pages in the to-be-removed region + * @node: this node */ - void (*track_flush_slot)(struct kvm *kvm, struct kvm_memory_slot *slot, - struct kvm_page_track_notifier_node *node); + void (*track_remove_region)(gfn_t gfn, unsigned long nr_pages, + struct kvm_page_track_notifier_node *node); }; -int kvm_page_track_init(struct kvm *kvm); -void kvm_page_track_cleanup(struct kvm *kvm); +int kvm_page_track_register_notifier(struct kvm *kvm, + struct kvm_page_track_notifier_node *n); +void kvm_page_track_unregister_notifier(struct kvm *kvm, + struct kvm_page_track_notifier_node *n); -bool kvm_page_track_write_tracking_enabled(struct kvm *kvm); -int kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot); - -void kvm_page_track_free_memslot(struct kvm_memory_slot *slot); -int kvm_page_track_create_memslot(struct kvm *kvm, - struct kvm_memory_slot *slot, - unsigned long npages); - -void kvm_slot_page_track_add_page(struct kvm *kvm, - struct kvm_memory_slot *slot, gfn_t gfn, - enum kvm_page_track_mode mode); -void kvm_slot_page_track_remove_page(struct kvm *kvm, - struct kvm_memory_slot *slot, gfn_t gfn, - enum kvm_page_track_mode mode); -bool kvm_slot_page_track_is_active(struct kvm *kvm, - const struct kvm_memory_slot *slot, - gfn_t gfn, enum kvm_page_track_mode mode); +int kvm_write_track_add_gfn(struct kvm *kvm, gfn_t gfn); +int kvm_write_track_remove_gfn(struct kvm *kvm, gfn_t gfn); +#else +/* + * Allow defining a node in a structure even if page tracking is disabled, e.g. + * to play nice with testing headers via direct inclusion from the command line. + */ +struct kvm_page_track_notifier_node {}; +#endif /* CONFIG_KVM_EXTERNAL_WRITE_TRACKING */ -void -kvm_page_track_register_notifier(struct kvm *kvm, - struct kvm_page_track_notifier_node *n); -void -kvm_page_track_unregister_notifier(struct kvm *kvm, - struct kvm_page_track_notifier_node *n); -void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, - int bytes); -void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot); #endif diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h index 97a3de7892d3ff376c3058d759141b07630c0760..571fe4d2d2328c3b4899faf8085e0181403ed003 100644 --- a/arch/x86/include/asm/linkage.h +++ b/arch/x86/include/asm/linkage.h @@ -8,6 +8,14 @@ #undef notrace #define notrace __attribute__((no_instrument_function)) +#ifdef CONFIG_64BIT +/* + * The generic version tends to create spurious ENDBR instructions under + * certain conditions. + */ +#define _THIS_IP_ ({ unsigned long __here; asm ("lea 0(%%rip), %0" : "=r" (__here)); __here; }) +#endif + #ifdef CONFIG_X86_32 #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) #endif /* CONFIG_X86_32 */ @@ -97,6 +105,13 @@ CFI_POST_PADDING \ SYM_FUNC_END(__cfi_##name) +/* UML needs to be able to override memcpy() and friends for KASAN. */ +#ifdef CONFIG_UML +# define SYM_FUNC_ALIAS_MEMFUNC SYM_FUNC_ALIAS_WEAK +#else +# define SYM_FUNC_ALIAS_MEMFUNC SYM_FUNC_ALIAS +#endif + /* SYM_TYPED_FUNC_START -- use for indirectly called globals, w/ CFI type */ #define SYM_TYPED_FUNC_START(name) \ SYM_TYPED_START(name, SYM_L_GLOBAL, SYM_F_ALIGN) \ diff --git a/arch/x86/include/asm/mman.h b/arch/x86/include/asm/mman.h new file mode 100644 index 0000000000000000000000000000000000000000..12b820259b9f3b68fe4d54270a0ad0ff6889e266 --- /dev/null +++ b/arch/x86/include/asm/mman.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MMAN_H__ +#define __ASM_MMAN_H__ + +#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS +#define arch_calc_vm_prot_bits(prot, key) ( \ + ((key) & 0x1 ? VM_PKEY_BIT0 : 0) | \ + ((key) & 0x2 ? VM_PKEY_BIT1 : 0) | \ + ((key) & 0x4 ? VM_PKEY_BIT2 : 0) | \ + ((key) & 0x8 ? VM_PKEY_BIT3 : 0)) +#endif + +#include + +#endif /* __ASM_MMAN_H__ */ diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h index 416901d406f8a09d98580315759782a6c559d68e..8dac45a2c7fcf236708272ac7379d3d706c13493 100644 --- a/arch/x86/include/asm/mmu_context.h +++ b/arch/x86/include/asm/mmu_context.h @@ -186,8 +186,7 @@ do { \ #else #define deactivate_mm(tsk, mm) \ do { \ - if (!tsk->vfork_done) \ - shstk_free(tsk); \ + shstk_free(tsk); \ load_gs_index(0); \ loadsegment(fs, 0); \ } while (0) diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index fa83d88e4c99dd61679d02f65cf460fadb2acca4..896445edc6a8e924910bd2b04e43e7fee248ee52 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -26,6 +26,7 @@ union hv_ghcb; DECLARE_STATIC_KEY_FALSE(isolation_type_snp); +DECLARE_STATIC_KEY_FALSE(isolation_type_tdx); typedef int (*hyperv_fill_flush_list_func)( struct hv_guest_mapping_flush_list *flush, @@ -40,6 +41,7 @@ static inline unsigned char hv_get_nmi_reason(void) #if IS_ENABLED(CONFIG_HYPERV) extern int hyperv_init_cpuhp; +extern bool hyperv_paravisor_present; extern void *hv_hypercall_pg; @@ -47,10 +49,25 @@ extern u64 hv_current_partition_id; extern union hv_ghcb * __percpu *hv_ghcb_pg; +bool hv_isolation_type_snp(void); +bool hv_isolation_type_tdx(void); +u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2); + +/* + * DEFAULT INIT GPAT and SEGMENT LIMIT value in struct VMSA + * to start AP in enlightened SEV guest. + */ +#define HV_AP_INIT_GPAT_DEFAULT 0x0007040600070406ULL +#define HV_AP_SEGMENT_LIMIT 0xffffffff + int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages); int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id); int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags); +/* + * If the hypercall involves no input or output parameters, the hypervisor + * ignores the corresponding GPA pointer. + */ static inline u64 hv_do_hypercall(u64 control, void *input, void *output) { u64 input_address = input ? virt_to_phys(input) : 0; @@ -58,6 +75,19 @@ static inline u64 hv_do_hypercall(u64 control, void *input, void *output) u64 hv_status; #ifdef CONFIG_X86_64 + if (hv_isolation_type_tdx() && !hyperv_paravisor_present) + return hv_tdx_hypercall(control, input_address, output_address); + + if (hv_isolation_type_snp() && !hyperv_paravisor_present) { + __asm__ __volatile__("mov %4, %%r8\n" + "vmmcall" + : "=a" (hv_status), ASM_CALL_CONSTRAINT, + "+c" (control), "+d" (input_address) + : "r" (output_address) + : "cc", "memory", "r8", "r9", "r10", "r11"); + return hv_status; + } + if (!hv_hypercall_pg) return U64_MAX; @@ -101,7 +131,16 @@ static inline u64 _hv_do_fast_hypercall8(u64 control, u64 input1) u64 hv_status; #ifdef CONFIG_X86_64 - { + if (hv_isolation_type_tdx() && !hyperv_paravisor_present) + return hv_tdx_hypercall(control, input1, 0); + + if (hv_isolation_type_snp() && !hyperv_paravisor_present) { + __asm__ __volatile__( + "vmmcall" + : "=a" (hv_status), ASM_CALL_CONSTRAINT, + "+c" (control), "+d" (input1) + :: "cc", "r8", "r9", "r10", "r11"); + } else { __asm__ __volatile__(CALL_NOSPEC : "=a" (hv_status), ASM_CALL_CONSTRAINT, "+c" (control), "+d" (input1) @@ -146,7 +185,17 @@ static inline u64 _hv_do_fast_hypercall16(u64 control, u64 input1, u64 input2) u64 hv_status; #ifdef CONFIG_X86_64 - { + if (hv_isolation_type_tdx() && !hyperv_paravisor_present) + return hv_tdx_hypercall(control, input1, input2); + + if (hv_isolation_type_snp() && !hyperv_paravisor_present) { + __asm__ __volatile__("mov %4, %%r8\n" + "vmmcall" + : "=a" (hv_status), ASM_CALL_CONSTRAINT, + "+c" (control), "+d" (input1) + : "r" (input2) + : "cc", "r8", "r9", "r10", "r11"); + } else { __asm__ __volatile__("mov %4, %%r8\n" CALL_NOSPEC : "=a" (hv_status), ASM_CALL_CONSTRAINT, @@ -225,20 +274,24 @@ int hv_map_ioapic_interrupt(int ioapic_id, bool level, int vcpu, int vector, int hv_unmap_ioapic_interrupt(int ioapic_id, struct hv_interrupt_entry *entry); #ifdef CONFIG_AMD_MEM_ENCRYPT -void hv_ghcb_msr_write(u64 msr, u64 value); -void hv_ghcb_msr_read(u64 msr, u64 *value); bool hv_ghcb_negotiate_protocol(void); void __noreturn hv_ghcb_terminate(unsigned int set, unsigned int reason); -void hv_vtom_init(void); +int hv_snp_boot_ap(int cpu, unsigned long start_ip); #else -static inline void hv_ghcb_msr_write(u64 msr, u64 value) {} -static inline void hv_ghcb_msr_read(u64 msr, u64 *value) {} static inline bool hv_ghcb_negotiate_protocol(void) { return false; } static inline void hv_ghcb_terminate(unsigned int set, unsigned int reason) {} -static inline void hv_vtom_init(void) {} +static inline int hv_snp_boot_ap(int cpu, unsigned long start_ip) { return 0; } #endif -extern bool hv_isolation_type_snp(void); +#if defined(CONFIG_AMD_MEM_ENCRYPT) || defined(CONFIG_INTEL_TDX_GUEST) +void hv_vtom_init(void); +void hv_ivm_msr_write(u64 msr, u64 value); +void hv_ivm_msr_read(u64 msr, u64 *value); +#else +static inline void hv_vtom_init(void) {} +static inline void hv_ivm_msr_write(u64 msr, u64 value) {} +static inline void hv_ivm_msr_read(u64 msr, u64 *value) {} +#endif static inline bool hv_is_synic_reg(unsigned int reg) { @@ -287,8 +340,10 @@ static inline u64 hv_get_non_nested_register(unsigned int reg) { return 0; } #ifdef CONFIG_HYPERV_VTL_MODE void __init hv_vtl_init_platform(void); +int __init hv_vtl_early_init(void); #else static inline void __init hv_vtl_init_platform(void) {} +static inline int __init hv_vtl_early_init(void) { return 0; } #endif #include diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 4acbcddddc29f21fc9f21117b653c842430d943c..772d03487520e595ba458ae9032f8a0a2dc00101 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -9,13 +9,6 @@ struct paravirt_patch_site { u8 type; /* type of this instruction */ u8 len; /* length of original instruction */ }; - -/* Lazy mode for batching updates / context switch */ -enum paravirt_lazy_mode { - PARAVIRT_LAZY_NONE, - PARAVIRT_LAZY_MMU, - PARAVIRT_LAZY_CPU, -}; #endif #ifdef CONFIG_PARAVIRT @@ -549,14 +542,6 @@ int paravirt_disable_iospace(void); __PVOP_VCALL(op, PVOP_CALL_ARG1(arg1), PVOP_CALL_ARG2(arg2), \ PVOP_CALL_ARG3(arg3), PVOP_CALL_ARG4(arg4)) -enum paravirt_lazy_mode paravirt_get_lazy_mode(void); -void paravirt_start_context_switch(struct task_struct *prev); -void paravirt_end_context_switch(struct task_struct *next); - -void paravirt_enter_lazy_mmu(void); -void paravirt_leave_lazy_mmu(void); -void paravirt_flush_lazy_mmu(void); - void _paravirt_nop(void); void paravirt_BUG(void); unsigned long paravirt_ret0(void); diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index d6ad98ca1288d22f7b69c05382a404b47427e611..e02b179ec6598900d56dec3196287f46b7818a5a 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -955,6 +955,14 @@ static inline int pte_same(pte_t a, pte_t b) return a.pte == b.pte; } +static inline pte_t pte_next_pfn(pte_t pte) +{ + if (__pte_needs_invert(pte_val(pte))) + return __pte(pte_val(pte) - (1UL << PFN_PTE_SHIFT)); + return __pte(pte_val(pte) + (1UL << PFN_PTE_SHIFT)); +} +#define pte_next_pfn pte_next_pfn + static inline int pte_present(pte_t a) { return pte_flags(a) & (_PAGE_PRESENT | _PAGE_PROTNONE); diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 0086920cda061f83f2050822646bfadbc0f474bc..a3669a7774edbbb9f622f2e882b9089ce39f2202 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -683,13 +683,11 @@ extern u16 get_llc_id(unsigned int cpu); #ifdef CONFIG_CPU_SUP_AMD extern u32 amd_get_nodes_per_socket(void); extern u32 amd_get_highest_perf(void); -extern bool cpu_has_ibpb_brtype_microcode(void); extern void amd_clear_divider(void); extern void amd_check_microcode(void); #else static inline u32 amd_get_nodes_per_socket(void) { return 0; } static inline u32 amd_get_highest_perf(void) { return 0; } -static inline bool cpu_has_ibpb_brtype_microcode(void) { return false; } static inline void amd_clear_divider(void) { } static inline void amd_check_microcode(void) { } #endif diff --git a/arch/x86/include/asm/reboot.h b/arch/x86/include/asm/reboot.h index 9177b4354c3f50b2c1ff7f5f454abcace0a98a60..6536873f8fc0a2fc748070ab1834d1b97db70386 100644 --- a/arch/x86/include/asm/reboot.h +++ b/arch/x86/include/asm/reboot.h @@ -25,7 +25,14 @@ void __noreturn machine_real_restart(unsigned int type); #define MRR_BIOS 0 #define MRR_APM 1 +#if IS_ENABLED(CONFIG_KVM_INTEL) || IS_ENABLED(CONFIG_KVM_AMD) +typedef void (cpu_emergency_virt_cb)(void); +void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback); +void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback); void cpu_emergency_disable_virtualization(void); +#else +static inline void cpu_emergency_disable_virtualization(void) {} +#endif /* CONFIG_KVM_INTEL || CONFIG_KVM_AMD */ typedef void (*nmi_shootdown_cb)(int, struct pt_regs*); void nmi_shootdown_cpus(nmi_shootdown_cb callback); diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index e7c7379d6ac7b0ff25379e6bc46e129000346cc3..19bf955b67e0da097aa7e4187c0feecbf10268e4 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -288,6 +288,7 @@ static_assert((X2AVIC_MAX_PHYSICAL_ID & AVIC_PHYSICAL_MAX_INDEX_MASK) == X2AVIC_ #define AVIC_HPA_MASK ~((0xFFFULL << 52) | 0xFFF) +#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5) struct vmcb_seg { u16 selector; @@ -345,7 +346,7 @@ struct vmcb_save_area { u64 last_excp_from; u64 last_excp_to; u8 reserved_0x298[72]; - u32 spec_ctrl; /* Guest version of SPEC_CTRL at 0x2E0 */ + u64 spec_ctrl; /* Guest version of SPEC_CTRL at 0x2E0 */ } __packed; /* Save area definition for SEV-ES and SEV-SNP guests */ @@ -512,7 +513,7 @@ struct ghcb { } __packed; -#define EXPECTED_VMCB_SAVE_AREA_SIZE 740 +#define EXPECTED_VMCB_SAVE_AREA_SIZE 744 #define EXPECTED_GHCB_SAVE_AREA_SIZE 1032 #define EXPECTED_SEV_ES_SAVE_AREA_SIZE 1648 #define EXPECTED_VMCB_CONTROL_AREA_SIZE 1024 diff --git a/arch/x86/include/asm/virtext.h b/arch/x86/include/asm/virtext.h deleted file mode 100644 index 3b12e6b99412370b6d71a3845500a5cdba46eb1f..0000000000000000000000000000000000000000 --- a/arch/x86/include/asm/virtext.h +++ /dev/null @@ -1,154 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* CPU virtualization extensions handling - * - * This should carry the code for handling CPU virtualization extensions - * that needs to live in the kernel core. - * - * Author: Eduardo Habkost - * - * Copyright (C) 2008, Red Hat Inc. - * - * Contains code from KVM, Copyright (C) 2006 Qumranet, Inc. - */ -#ifndef _ASM_X86_VIRTEX_H -#define _ASM_X86_VIRTEX_H - -#include - -#include -#include -#include - -/* - * VMX functions: - */ - -static inline int cpu_has_vmx(void) -{ - unsigned long ecx = cpuid_ecx(1); - return test_bit(5, &ecx); /* CPUID.1:ECX.VMX[bit 5] -> VT */ -} - - -/** - * cpu_vmxoff() - Disable VMX on the current CPU - * - * Disable VMX and clear CR4.VMXE (even if VMXOFF faults) - * - * Note, VMXOFF causes a #UD if the CPU is !post-VMXON, but it's impossible to - * atomically track post-VMXON state, e.g. this may be called in NMI context. - * Eat all faults as all other faults on VMXOFF faults are mode related, i.e. - * faults are guaranteed to be due to the !post-VMXON check unless the CPU is - * magically in RM, VM86, compat mode, or at CPL>0. - */ -static inline int cpu_vmxoff(void) -{ - asm_volatile_goto("1: vmxoff\n\t" - _ASM_EXTABLE(1b, %l[fault]) - ::: "cc", "memory" : fault); - - cr4_clear_bits(X86_CR4_VMXE); - return 0; - -fault: - cr4_clear_bits(X86_CR4_VMXE); - return -EIO; -} - -static inline int cpu_vmx_enabled(void) -{ - return __read_cr4() & X86_CR4_VMXE; -} - -/** Disable VMX if it is enabled on the current CPU - * - * You shouldn't call this if cpu_has_vmx() returns 0. - */ -static inline void __cpu_emergency_vmxoff(void) -{ - if (cpu_vmx_enabled()) - cpu_vmxoff(); -} - -/** Disable VMX if it is supported and enabled on the current CPU - */ -static inline void cpu_emergency_vmxoff(void) -{ - if (cpu_has_vmx()) - __cpu_emergency_vmxoff(); -} - - - - -/* - * SVM functions: - */ - -/** Check if the CPU has SVM support - * - * You can use the 'msg' arg to get a message describing the problem, - * if the function returns zero. Simply pass NULL if you are not interested - * on the messages; gcc should take care of not generating code for - * the messages on this case. - */ -static inline int cpu_has_svm(const char **msg) -{ - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && - boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) { - if (msg) - *msg = "not amd or hygon"; - return 0; - } - - if (boot_cpu_data.extended_cpuid_level < SVM_CPUID_FUNC) { - if (msg) - *msg = "can't execute cpuid_8000000a"; - return 0; - } - - if (!boot_cpu_has(X86_FEATURE_SVM)) { - if (msg) - *msg = "svm not available"; - return 0; - } - return 1; -} - - -/** Disable SVM on the current CPU - * - * You should call this only if cpu_has_svm() returned true. - */ -static inline void cpu_svm_disable(void) -{ - uint64_t efer; - - wrmsrl(MSR_VM_HSAVE_PA, 0); - rdmsrl(MSR_EFER, efer); - if (efer & EFER_SVME) { - /* - * Force GIF=1 prior to disabling SVM to ensure INIT and NMI - * aren't blocked, e.g. if a fatal error occurred between CLGI - * and STGI. Note, STGI may #UD if SVM is disabled from NMI - * context between reading EFER and executing STGI. In that - * case, GIF must already be set, otherwise the NMI would have - * been blocked, so just eat the fault. - */ - asm_volatile_goto("1: stgi\n\t" - _ASM_EXTABLE(1b, %l[fault]) - ::: "memory" : fault); -fault: - wrmsrl(MSR_EFER, efer & ~EFER_SVME); - } -} - -/** Makes sure SVM is disabled, if it is supported on the CPU - */ -static inline void cpu_emergency_svm_disable(void) -{ - if (cpu_has_svm(NULL)) - cpu_svm_disable(); -} - -#endif /* _ASM_X86_VIRTEX_H */ diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 0d02c4aafa6f49b2e981ac975610095844a815d3..0e73616b82f3469f47877e0cc56ca6b79f034173 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -71,7 +71,7 @@ #define SECONDARY_EXEC_RDSEED_EXITING VMCS_CONTROL_BIT(RDSEED_EXITING) #define SECONDARY_EXEC_ENABLE_PML VMCS_CONTROL_BIT(PAGE_MOD_LOGGING) #define SECONDARY_EXEC_PT_CONCEAL_VMX VMCS_CONTROL_BIT(PT_CONCEAL_VMX) -#define SECONDARY_EXEC_XSAVES VMCS_CONTROL_BIT(XSAVES) +#define SECONDARY_EXEC_ENABLE_XSAVES VMCS_CONTROL_BIT(XSAVES) #define SECONDARY_EXEC_MODE_BASED_EPT_EXEC VMCS_CONTROL_BIT(MODE_BASED_EPT_EXEC) #define SECONDARY_EXEC_PT_USE_GPA VMCS_CONTROL_BIT(PT_USE_GPA) #define SECONDARY_EXEC_TSC_SCALING VMCS_CONTROL_BIT(TSC_SCALING) diff --git a/arch/x86/include/asm/xen/hypervisor.h b/arch/x86/include/asm/xen/hypervisor.h index 5fc35f889cd1a29d86f245da3c7e1f993f3cdf4d..7048dfacc04b2413acba1054fb4412c7048b8249 100644 --- a/arch/x86/include/asm/xen/hypervisor.h +++ b/arch/x86/include/asm/xen/hypervisor.h @@ -36,6 +36,7 @@ extern struct shared_info *HYPERVISOR_shared_info; extern struct start_info *xen_start_info; +#include #include #define XEN_SIGNATURE "XenVMMXenVMM" @@ -63,4 +64,40 @@ void __init xen_pvh_init(struct boot_params *boot_params); void __init mem_map_via_hcall(struct boot_params *boot_params_p); #endif +/* Lazy mode for batching updates / context switch */ +enum xen_lazy_mode { + XEN_LAZY_NONE, + XEN_LAZY_MMU, + XEN_LAZY_CPU, +}; + +DECLARE_PER_CPU(enum xen_lazy_mode, xen_lazy_mode); +DECLARE_PER_CPU(unsigned int, xen_lazy_nesting); + +static inline void enter_lazy(enum xen_lazy_mode mode) +{ + enum xen_lazy_mode old_mode = this_cpu_read(xen_lazy_mode); + + if (mode == old_mode) { + this_cpu_inc(xen_lazy_nesting); + return; + } + + BUG_ON(old_mode != XEN_LAZY_NONE); + + this_cpu_write(xen_lazy_mode, mode); +} + +static inline void leave_lazy(enum xen_lazy_mode mode) +{ + BUG_ON(this_cpu_read(xen_lazy_mode) != mode); + + if (this_cpu_read(xen_lazy_nesting) == 0) + this_cpu_write(xen_lazy_mode, XEN_LAZY_NONE); + else + this_cpu_dec(xen_lazy_nesting); +} + +enum xen_lazy_mode xen_get_lazy_mode(void); + #endif /* _ASM_X86_XEN_HYPERVISOR_H */ diff --git a/arch/x86/include/uapi/asm/mman.h b/arch/x86/include/uapi/asm/mman.h index 8148bdddbd2ca5059d36642f74d856f070d07150..46cdc941f9586ac227c02a16705cf4397b629423 100644 --- a/arch/x86/include/uapi/asm/mman.h +++ b/arch/x86/include/uapi/asm/mman.h @@ -5,14 +5,6 @@ #define MAP_32BIT 0x40 /* only give out 32bit addresses */ #define MAP_ABOVE4G 0x80 /* only map above 4GB */ -#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS -#define arch_calc_vm_prot_bits(prot, key) ( \ - ((key) & 0x1 ? VM_PKEY_BIT0 : 0) | \ - ((key) & 0x2 ? VM_PKEY_BIT1 : 0) | \ - ((key) & 0x4 ? VM_PKEY_BIT2 : 0) | \ - ((key) & 0x8 ? VM_PKEY_BIT3 : 0)) -#endif - /* Flags for map_shadow_stack(2) */ #define SHADOW_STACK_SET_TOKEN (1ULL << 0) /* Set up a restore token in the shadow stack */ diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index a5ead6a6d233b2ef4223d5ba54579a69f3042f6f..517ee01503be1e9fab72b3ecd80adfca7ce99d8a 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -720,13 +720,8 @@ void __init_or_module noinline apply_returns(s32 *start, s32 *end) { s32 *s; - /* - * Do not patch out the default return thunks if those needed are the - * ones generated by the compiler. - */ - if (cpu_feature_enabled(X86_FEATURE_RETHUNK) && - (x86_return_thunk == __x86_return_thunk)) - return; + if (cpu_feature_enabled(X86_FEATURE_RETHUNK)) + static_call_force_reinit(); for (s = start; s < end; s++) { void *dest = NULL, *addr = (void *)s + *s; diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index d9f5d7492f8371df1004159297a4b4ad48c78e63..205cee5676298fb6b04c592e02d9a7585a9a6072 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -1533,7 +1533,7 @@ static void __init build_socket_tables(void) { struct uv_gam_range_entry *gre = uv_gre_table; int nums, numn, nump; - int cpu, i, lnid; + int i, lnid, apicid; int minsock = _min_socket; int maxsock = _max_socket; int minpnode = _min_pnode; @@ -1584,15 +1584,14 @@ static void __init build_socket_tables(void) /* Set socket -> node values: */ lnid = NUMA_NO_NODE; - for_each_possible_cpu(cpu) { - int nid = cpu_to_node(cpu); - int apicid, sockid; + for (apicid = 0; apicid < ARRAY_SIZE(__apicid_to_node); apicid++) { + int nid = __apicid_to_node[apicid]; + int sockid; - if (lnid == nid) + if ((nid == NUMA_NO_NODE) || (lnid == nid)) continue; lnid = nid; - apicid = per_cpu(x86_cpu_to_apicid, cpu); sockid = apicid >> uv_cpuid.socketid_shift; if (_socket_to_node[sockid - minsock] == SOCK_EMPTY) diff --git a/arch/x86/kernel/audit_64.c b/arch/x86/kernel/audit_64.c index 44c3601cfdc45d65d75e4a440f1a0a01a83df0c2..190c120f4285a38ec59bbcd748c45d60f5b81e3f 100644 --- a/arch/x86/kernel/audit_64.c +++ b/arch/x86/kernel/audit_64.c @@ -63,11 +63,6 @@ int audit_classify_syscall(int abi, unsigned syscall) static int __init audit_classes_init(void) { #ifdef CONFIG_IA32_EMULATION - extern __u32 ia32_dir_class[]; - extern __u32 ia32_write_class[]; - extern __u32 ia32_read_class[]; - extern __u32 ia32_chattr_class[]; - extern __u32 ia32_signal_class[]; audit_register_class(AUDIT_CLASS_WRITE_32, ia32_write_class); audit_register_class(AUDIT_CLASS_READ_32, ia32_read_class); audit_register_class(AUDIT_CLASS_DIR_WRITE_32, ia32_dir_class); diff --git a/arch/x86/kernel/callthunks.c b/arch/x86/kernel/callthunks.c index c06bfc086565dff236c72c0ab9758c3239c63bcc..faa9f22998488758c5513091b491e99932cd6980 100644 --- a/arch/x86/kernel/callthunks.c +++ b/arch/x86/kernel/callthunks.c @@ -272,7 +272,6 @@ void __init callthunks_patch_builtin_calls(void) pr_info("Setting up call depth tracking\n"); mutex_lock(&text_mutex); callthunks_setup(&cs, &builtin_coretext); - static_call_force_reinit(); thunks_initialized = true; mutex_unlock(&text_mutex); } diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index dd8379d844452b17dceaa37ff2c6c45e65e1f0e1..03ef962a6992db0281697ea0d02b89e97f677dd5 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -766,6 +766,15 @@ static void early_init_amd(struct cpuinfo_x86 *c) if (cpu_has(c, X86_FEATURE_TOPOEXT)) smp_num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1; + + if (!cpu_has(c, X86_FEATURE_HYPERVISOR) && !cpu_has(c, X86_FEATURE_IBPB_BRTYPE)) { + if (c->x86 == 0x17 && boot_cpu_has(X86_FEATURE_AMD_IBPB)) + setup_force_cpu_cap(X86_FEATURE_IBPB_BRTYPE); + else if (c->x86 >= 0x19 && !wrmsrl_safe(MSR_IA32_PRED_CMD, PRED_CMD_SBPB)) { + setup_force_cpu_cap(X86_FEATURE_IBPB_BRTYPE); + setup_force_cpu_cap(X86_FEATURE_SBPB); + } + } } static void init_amd_k8(struct cpuinfo_x86 *c) @@ -1301,25 +1310,6 @@ void amd_check_microcode(void) on_each_cpu(zenbleed_check_cpu, NULL, 1); } -bool cpu_has_ibpb_brtype_microcode(void) -{ - switch (boot_cpu_data.x86) { - /* Zen1/2 IBPB flushes branch type predictions too. */ - case 0x17: - return boot_cpu_has(X86_FEATURE_AMD_IBPB); - case 0x19: - /* Poke the MSR bit on Zen3/4 to check its presence. */ - if (!wrmsrl_safe(MSR_IA32_PRED_CMD, PRED_CMD_SBPB)) { - setup_force_cpu_cap(X86_FEATURE_SBPB); - return true; - } else { - return false; - } - default: - return false; - } -} - /* * Issue a DIV 0/1 insn to clear any division data from previous DIV * operations. diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index f081d26616ac1ce9aebf720ce3430bece3a02025..10499bcd4e39624827e9641df81eda074bc3cfdc 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -2404,26 +2404,15 @@ early_param("spec_rstack_overflow", srso_parse_cmdline); static void __init srso_select_mitigation(void) { - bool has_microcode; + bool has_microcode = boot_cpu_has(X86_FEATURE_IBPB_BRTYPE); if (!boot_cpu_has_bug(X86_BUG_SRSO) || cpu_mitigations_off()) goto pred_cmd; - /* - * The first check is for the kernel running as a guest in order - * for guests to verify whether IBPB is a viable mitigation. - */ - has_microcode = boot_cpu_has(X86_FEATURE_IBPB_BRTYPE) || cpu_has_ibpb_brtype_microcode(); if (!has_microcode) { pr_warn("IBPB-extending microcode not applied!\n"); pr_warn(SRSO_NOTICE); } else { - /* - * Enable the synthetic (even if in a real CPUID leaf) - * flags for guests. - */ - setup_force_cpu_cap(X86_FEATURE_IBPB_BRTYPE); - /* * Zen1/2 with SMT off aren't vulnerable after the right * IBPB microcode has been applied. @@ -2444,7 +2433,7 @@ static void __init srso_select_mitigation(void) switch (srso_cmd) { case SRSO_CMD_OFF: - return; + goto pred_cmd; case SRSO_CMD_MICROCODE: if (has_microcode) { @@ -2717,7 +2706,7 @@ static ssize_t srso_show_state(char *buf) return sysfs_emit(buf, "%s%s\n", srso_strings[srso_mitigation], - (cpu_has_ibpb_brtype_microcode() ? "" : ", no microcode")); + boot_cpu_has(X86_FEATURE_IBPB_BRTYPE) ? "" : ", no microcode"); } static ssize_t gds_show_state(char *buf) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 6d75fab10161bf660065720197d37026b82f7c6d..4e5ffc8b0e469d142c28667359a1f4719f942fd7 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1280,11 +1280,11 @@ static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { VULNBL_INTEL_STEPPINGS(BROADWELL_G, X86_STEPPING_ANY, SRBDS), VULNBL_INTEL_STEPPINGS(BROADWELL_X, X86_STEPPING_ANY, MMIO), VULNBL_INTEL_STEPPINGS(BROADWELL, X86_STEPPING_ANY, SRBDS), - VULNBL_INTEL_STEPPINGS(SKYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), VULNBL_INTEL_STEPPINGS(SKYLAKE_X, X86_STEPPING_ANY, MMIO | RETBLEED | GDS), - VULNBL_INTEL_STEPPINGS(SKYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED), - VULNBL_INTEL_STEPPINGS(KABYLAKE_L, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED | GDS), - VULNBL_INTEL_STEPPINGS(KABYLAKE, X86_STEPPING_ANY, SRBDS | MMIO | RETBLEED | GDS), + VULNBL_INTEL_STEPPINGS(SKYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), + VULNBL_INTEL_STEPPINGS(SKYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), + VULNBL_INTEL_STEPPINGS(KABYLAKE_L, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), + VULNBL_INTEL_STEPPINGS(KABYLAKE, X86_STEPPING_ANY, MMIO | RETBLEED | GDS | SRBDS), VULNBL_INTEL_STEPPINGS(CANNONLAKE_L, X86_STEPPING_ANY, RETBLEED), VULNBL_INTEL_STEPPINGS(ICELAKE_L, X86_STEPPING_ANY, MMIO | MMIO_SBDS | RETBLEED | GDS), VULNBL_INTEL_STEPPINGS(ICELAKE_D, X86_STEPPING_ANY, MMIO | GDS), @@ -1303,7 +1303,7 @@ static const struct x86_cpu_id cpu_vuln_blacklist[] __initconst = { VULNBL_AMD(0x15, RETBLEED), VULNBL_AMD(0x16, RETBLEED), VULNBL_AMD(0x17, RETBLEED | SMT_RSB | SRSO), - VULNBL_HYGON(0x18, RETBLEED | SMT_RSB), + VULNBL_HYGON(0x18, RETBLEED | SMT_RSB | SRSO), VULNBL_AMD(0x19, SRSO), {} }; diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index 0100468e72caac2187099336d86c811b47981901..e6bba12c759cb793a2a3eb0ff367bb51a6b502f0 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Is Linux running as the root partition? */ bool hv_root_partition; @@ -39,6 +40,10 @@ bool hv_root_partition; bool hv_nested; struct ms_hyperv_info ms_hyperv; +/* Used in modules via hv_do_hypercall(): see arch/x86/include/asm/mshyperv.h */ +bool hyperv_paravisor_present __ro_after_init; +EXPORT_SYMBOL_GPL(hyperv_paravisor_present); + #if IS_ENABLED(CONFIG_HYPERV) static inline unsigned int hv_get_nested_reg(unsigned int reg) { @@ -65,8 +70,8 @@ u64 hv_get_non_nested_register(unsigned int reg) { u64 value; - if (hv_is_synic_reg(reg) && hv_isolation_type_snp()) - hv_ghcb_msr_read(reg, &value); + if (hv_is_synic_reg(reg) && ms_hyperv.paravisor_present) + hv_ivm_msr_read(reg, &value); else rdmsrl(reg, value); return value; @@ -75,8 +80,8 @@ EXPORT_SYMBOL_GPL(hv_get_non_nested_register); void hv_set_non_nested_register(unsigned int reg, u64 value) { - if (hv_is_synic_reg(reg) && hv_isolation_type_snp()) { - hv_ghcb_msr_write(reg, value); + if (hv_is_synic_reg(reg) && ms_hyperv.paravisor_present) { + hv_ivm_msr_write(reg, value); /* Write proxy bit via wrmsl instruction */ if (hv_is_sint_reg(reg)) @@ -295,6 +300,15 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus) native_smp_prepare_cpus(max_cpus); + /* + * Override wakeup_secondary_cpu_64 callback for SEV-SNP + * enlightened guest. + */ + if (!ms_hyperv.paravisor_present && hv_isolation_type_snp()) { + apic->wakeup_secondary_cpu_64 = hv_snp_boot_ap; + return; + } + #ifdef CONFIG_X86_64 for_each_present_cpu(i) { if (i == 0) @@ -313,6 +327,26 @@ static void __init hv_smp_prepare_cpus(unsigned int max_cpus) } #endif +/* + * When a fully enlightened TDX VM runs on Hyper-V, the firmware sets the + * HW_REDUCED flag: refer to acpi_tb_create_local_fadt(). Consequently ttyS0 + * interrupts can't work because request_irq() -> ... -> irq_to_desc() returns + * NULL for ttyS0. This happens because mp_config_acpi_legacy_irqs() sees a + * nr_legacy_irqs() of 0, so it doesn't initialize the array 'mp_irqs[]', and + * later setup_IO_APIC_irqs() -> find_irq_entry() fails to find the legacy irqs + * from the array and hence doesn't create the necessary irq description info. + * + * Clone arch/x86/kernel/acpi/boot.c: acpi_generic_reduced_hw_init() here, + * except don't change 'legacy_pic', which keeps its default value + * 'default_legacy_pic'. This way, mp_config_acpi_legacy_irqs() sees a non-zero + * nr_legacy_irqs() and eventually serial console interrupts works properly. + */ +static void __init reduced_hw_init(void) +{ + x86_init.timers.timer_init = x86_init_noop; + x86_init.irqs.pre_vector_init = x86_init_noop; +} + static void __init ms_hyperv_init_platform(void) { int hv_max_functions_eax; @@ -399,11 +433,33 @@ static void __init ms_hyperv_init_platform(void) ms_hyperv.shared_gpa_boundary = BIT_ULL(ms_hyperv.shared_gpa_boundary_bits); + hyperv_paravisor_present = !!ms_hyperv.paravisor_present; + pr_info("Hyper-V: Isolation Config: Group A 0x%x, Group B 0x%x\n", ms_hyperv.isolation_config_a, ms_hyperv.isolation_config_b); - if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) + + if (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP) { static_branch_enable(&isolation_type_snp); + } else if (hv_get_isolation_type() == HV_ISOLATION_TYPE_TDX) { + static_branch_enable(&isolation_type_tdx); + + /* A TDX VM must use x2APIC and doesn't use lazy EOI. */ + ms_hyperv.hints &= ~HV_X64_APIC_ACCESS_RECOMMENDED; + + if (!ms_hyperv.paravisor_present) { + /* To be supported: more work is required. */ + ms_hyperv.features &= ~HV_MSR_REFERENCE_TSC_AVAILABLE; + + /* HV_REGISTER_CRASH_CTL is unsupported. */ + ms_hyperv.misc_features &= ~HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE; + + /* Don't trust Hyper-V's TLB-flushing hypercalls. */ + ms_hyperv.hints &= ~HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED; + + x86_init.acpi.reduced_hw_early_init = reduced_hw_init; + } + } } if (hv_max_functions_eax >= HYPERV_CPUID_NESTED_FEATURES) { @@ -473,7 +529,7 @@ static void __init ms_hyperv_init_platform(void) #if IS_ENABLED(CONFIG_HYPERV) if ((hv_get_isolation_type() == HV_ISOLATION_TYPE_VBS) || - (hv_get_isolation_type() == HV_ISOLATION_TYPE_SNP)) + ms_hyperv.paravisor_present) hv_vtom_init(); /* * Setup the hook to get control post apic initialization. @@ -497,7 +553,8 @@ static void __init ms_hyperv_init_platform(void) # ifdef CONFIG_SMP smp_ops.smp_prepare_boot_cpu = hv_smp_prepare_boot_cpu; - if (hv_root_partition) + if (hv_root_partition || + (!ms_hyperv.paravisor_present && hv_isolation_type_snp())) smp_ops.smp_prepare_cpus = hv_smp_prepare_cpus; # endif @@ -560,6 +617,22 @@ static bool __init ms_hyperv_msi_ext_dest_id(void) return eax & HYPERV_VS_PROPERTIES_EAX_EXTENDED_IOAPIC_RTE; } +#ifdef CONFIG_AMD_MEM_ENCRYPT +static void hv_sev_es_hcall_prepare(struct ghcb *ghcb, struct pt_regs *regs) +{ + /* RAX and CPL are already in the GHCB */ + ghcb_set_rcx(ghcb, regs->cx); + ghcb_set_rdx(ghcb, regs->dx); + ghcb_set_r8(ghcb, regs->r8); +} + +static bool hv_sev_es_hcall_finish(struct ghcb *ghcb, struct pt_regs *regs) +{ + /* No checking of the return state needed */ + return true; +} +#endif + const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { .name = "Microsoft Hyper-V", .detect = ms_hyperv_platform, @@ -567,4 +640,8 @@ const __initconst struct hypervisor_x86 x86_hyper_ms_hyperv = { .init.x2apic_available = ms_hyperv_x2apic_available, .init.msi_ext_dest_id = ms_hyperv_msi_ext_dest_id, .init.init_platform = ms_hyperv_init_platform, +#ifdef CONFIG_AMD_MEM_ENCRYPT + .runtime.sev_es_hcall_prepare = hv_sev_es_hcall_prepare, + .runtime.sev_es_hcall_finish = hv_sev_es_hcall_finish, +#endif }; diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c index 91fa70e5100413655b4b99629ad72b2843a4836b..279148e7245965dc59198fa3c2705b26aff23f92 100644 --- a/arch/x86/kernel/cpu/sgx/encl.c +++ b/arch/x86/kernel/cpu/sgx/encl.c @@ -235,6 +235,21 @@ static struct sgx_epc_page *sgx_encl_eldu(struct sgx_encl_page *encl_page, return epc_page; } +/* + * Ensure the SECS page is not swapped out. Must be called with encl->lock + * to protect the enclave states including SECS and ensure the SECS page is + * not swapped out again while being used. + */ +static struct sgx_epc_page *sgx_encl_load_secs(struct sgx_encl *encl) +{ + struct sgx_epc_page *epc_page = encl->secs.epc_page; + + if (!epc_page) + epc_page = sgx_encl_eldu(&encl->secs, NULL); + + return epc_page; +} + static struct sgx_encl_page *__sgx_encl_load_page(struct sgx_encl *encl, struct sgx_encl_page *entry) { @@ -248,11 +263,9 @@ static struct sgx_encl_page *__sgx_encl_load_page(struct sgx_encl *encl, return entry; } - if (!(encl->secs.epc_page)) { - epc_page = sgx_encl_eldu(&encl->secs, NULL); - if (IS_ERR(epc_page)) - return ERR_CAST(epc_page); - } + epc_page = sgx_encl_load_secs(encl); + if (IS_ERR(epc_page)) + return ERR_CAST(epc_page); epc_page = sgx_encl_eldu(entry, encl->secs.epc_page); if (IS_ERR(epc_page)) @@ -339,6 +352,13 @@ static vm_fault_t sgx_encl_eaug_page(struct vm_area_struct *vma, mutex_lock(&encl->lock); + epc_page = sgx_encl_load_secs(encl); + if (IS_ERR(epc_page)) { + if (PTR_ERR(epc_page) == -EBUSY) + vmret = VM_FAULT_NOPAGE; + goto err_out_unlock; + } + epc_page = sgx_alloc_epc_page(encl_page, false); if (IS_ERR(epc_page)) { if (PTR_ERR(epc_page) == -EBUSY) diff --git a/arch/x86/kernel/cpu/sgx/virt.c b/arch/x86/kernel/cpu/sgx/virt.c index c3e37eaec8ecde12ca45bcfe40f2c3785940f8e3..7aaa3652e31d15022a9d956944cd14c099ea0740 100644 --- a/arch/x86/kernel/cpu/sgx/virt.c +++ b/arch/x86/kernel/cpu/sgx/virt.c @@ -204,6 +204,7 @@ static int sgx_vepc_release(struct inode *inode, struct file *file) continue; xa_erase(&vepc->page_array, index); + cond_resched(); } /* @@ -222,6 +223,7 @@ static int sgx_vepc_release(struct inode *inode, struct file *file) list_add_tail(&epc_page->list, &secs_pages); xa_erase(&vepc->page_array, index); + cond_resched(); } /* @@ -243,6 +245,7 @@ static int sgx_vepc_release(struct inode *inode, struct file *file) if (sgx_vepc_free_page(epc_page)) list_add_tail(&epc_page->list, &secs_pages); + cond_resched(); } if (!list_empty(&secs_pages)) diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index 587c7743fd21a7c8603609701b1d730fe61228e1..c92d88680dbf16ba25826fb7d26b48bcfb72c695 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -48,38 +48,12 @@ struct crash_memmap_data { unsigned int type; }; -/* - * This is used to VMCLEAR all VMCSs loaded on the - * processor. And when loading kvm_intel module, the - * callback function pointer will be assigned. - * - * protected by rcu. - */ -crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss = NULL; -EXPORT_SYMBOL_GPL(crash_vmclear_loaded_vmcss); - -static inline void cpu_crash_vmclear_loaded_vmcss(void) -{ - crash_vmclear_fn *do_vmclear_operation = NULL; - - rcu_read_lock(); - do_vmclear_operation = rcu_dereference(crash_vmclear_loaded_vmcss); - if (do_vmclear_operation) - do_vmclear_operation(); - rcu_read_unlock(); -} - #if defined(CONFIG_SMP) && defined(CONFIG_X86_LOCAL_APIC) static void kdump_nmi_callback(int cpu, struct pt_regs *regs) { crash_save_cpu(regs, cpu); - /* - * VMCLEAR VMCSs loaded on all cpus if needed. - */ - cpu_crash_vmclear_loaded_vmcss(); - /* * Disable Intel PT to stop its logging */ @@ -133,11 +107,6 @@ void native_machine_crash_shutdown(struct pt_regs *regs) crash_smp_send_stop(); - /* - * VMCLEAR VMCSs loaded on this cpu if needed. - */ - cpu_crash_vmclear_loaded_vmcss(); - cpu_emergency_disable_virtualization(); /* diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 41dac93b8ea4e30f75e65cc1408b253dfa4f78c5..cadf68737e6bc78f12c1c3a444528895d1eba2f9 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c @@ -71,7 +71,7 @@ static unsigned short xsave_cpuid_features[] __initdata = { [XFEATURE_ZMM_Hi256] = X86_FEATURE_AVX512F, [XFEATURE_Hi16_ZMM] = X86_FEATURE_AVX512F, [XFEATURE_PT_UNIMPLEMENTED_SO_FAR] = X86_FEATURE_INTEL_PT, - [XFEATURE_PKRU] = X86_FEATURE_PKU, + [XFEATURE_PKRU] = X86_FEATURE_OSPKE, [XFEATURE_PASID] = X86_FEATURE_ENQCMD, [XFEATURE_CET_USER] = X86_FEATURE_SHSTK, [XFEATURE_XTILE_CFG] = X86_FEATURE_AMX_TILE, diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c index 4d8aff05a509ec4ca413b08e3d26ca812f5d94e7..30a55207c000139338c5fe305ae38b7f6a9b8524 100644 --- a/arch/x86/kernel/i8259.c +++ b/arch/x86/kernel/i8259.c @@ -231,9 +231,7 @@ struct irq_chip i8259A_chip = { }; static char irq_trigger[2]; -/** - * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ - */ +/* ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ */ static void restore_ELCR(char *trigger) { outb(trigger[0], PIC_ELCR1); diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c index 3a43a2dee658189764c440240c0621225e75c4fb..9c9faa1634fb9ab50907a27f25c6766de8bdd5b8 100644 --- a/arch/x86/kernel/kgdb.c +++ b/arch/x86/kernel/kgdb.c @@ -695,7 +695,6 @@ void kgdb_arch_exit(void) } /** - * * kgdb_skipexception - Bail out of KGDB when we've been triggered. * @exception: Exception vector number * @regs: Current &struct pt_regs. diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 975f98d5eee5dcf46d0b40d63199eb21c5c39c7a..97f1436c1a20348a567ea439aeaa41881448d625 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -143,66 +143,7 @@ int paravirt_disable_iospace(void) return request_resource(&ioport_resource, &reserve_ioports); } -static DEFINE_PER_CPU(enum paravirt_lazy_mode, paravirt_lazy_mode) = PARAVIRT_LAZY_NONE; - -static inline void enter_lazy(enum paravirt_lazy_mode mode) -{ - BUG_ON(this_cpu_read(paravirt_lazy_mode) != PARAVIRT_LAZY_NONE); - - this_cpu_write(paravirt_lazy_mode, mode); -} - -static void leave_lazy(enum paravirt_lazy_mode mode) -{ - BUG_ON(this_cpu_read(paravirt_lazy_mode) != mode); - - this_cpu_write(paravirt_lazy_mode, PARAVIRT_LAZY_NONE); -} - -void paravirt_enter_lazy_mmu(void) -{ - enter_lazy(PARAVIRT_LAZY_MMU); -} - -void paravirt_leave_lazy_mmu(void) -{ - leave_lazy(PARAVIRT_LAZY_MMU); -} - -void paravirt_flush_lazy_mmu(void) -{ - preempt_disable(); - - if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) { - arch_leave_lazy_mmu_mode(); - arch_enter_lazy_mmu_mode(); - } - - preempt_enable(); -} - #ifdef CONFIG_PARAVIRT_XXL -void paravirt_start_context_switch(struct task_struct *prev) -{ - BUG_ON(preemptible()); - - if (this_cpu_read(paravirt_lazy_mode) == PARAVIRT_LAZY_MMU) { - arch_leave_lazy_mmu_mode(); - set_ti_thread_flag(task_thread_info(prev), TIF_LAZY_MMU_UPDATES); - } - enter_lazy(PARAVIRT_LAZY_CPU); -} - -void paravirt_end_context_switch(struct task_struct *next) -{ - BUG_ON(preemptible()); - - leave_lazy(PARAVIRT_LAZY_CPU); - - if (test_and_clear_ti_thread_flag(task_thread_info(next), TIF_LAZY_MMU_UPDATES)) - arch_enter_lazy_mmu_mode(); -} - static noinstr void pv_native_write_cr2(unsigned long val) { native_write_cr2(val); @@ -229,14 +170,6 @@ static noinstr void pv_native_safe_halt(void) } #endif -enum paravirt_lazy_mode paravirt_get_lazy_mode(void) -{ - if (in_interrupt()) - return PARAVIRT_LAZY_NONE; - - return this_cpu_read(paravirt_lazy_mode); -} - struct pv_info pv_info = { .name = "bare hardware", #ifdef CONFIG_PARAVIRT_XXL diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 9f0909142a0ae38e322b31d7e124589e408f21a0..b6f4e8399fca20ddae6b147fa4209a97b188ec25 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -257,13 +257,6 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) if (!ret && unlikely(test_tsk_thread_flag(current, TIF_IO_BITMAP))) io_bitmap_share(p); - /* - * If copy_thread() if failing, don't leak the shadow stack possibly - * allocated in shstk_alloc_thread_stack() above. - */ - if (ret) - shstk_free(p); - return ret; } diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 3adbe97015c1344ce23b896356c89fe1dd7d0de8..830425e6d38e2f75fb9ea41f7dc042e175d98846 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -530,9 +529,54 @@ static inline void kb_wait(void) static inline void nmi_shootdown_cpus_on_restart(void); +#if IS_ENABLED(CONFIG_KVM_INTEL) || IS_ENABLED(CONFIG_KVM_AMD) +/* RCU-protected callback to disable virtualization prior to reboot. */ +static cpu_emergency_virt_cb __rcu *cpu_emergency_virt_callback; + +void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback) +{ + if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback))) + return; + + rcu_assign_pointer(cpu_emergency_virt_callback, callback); +} +EXPORT_SYMBOL_GPL(cpu_emergency_register_virt_callback); + +void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback) +{ + if (WARN_ON_ONCE(rcu_access_pointer(cpu_emergency_virt_callback) != callback)) + return; + + rcu_assign_pointer(cpu_emergency_virt_callback, NULL); + synchronize_rcu(); +} +EXPORT_SYMBOL_GPL(cpu_emergency_unregister_virt_callback); + +/* + * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized during + * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT if + * GIF=0, i.e. if the crash occurred between CLGI and STGI. + */ +void cpu_emergency_disable_virtualization(void) +{ + cpu_emergency_virt_cb *callback; + + /* + * IRQs must be disabled as KVM enables virtualization in hardware via + * function call IPIs, i.e. IRQs need to be disabled to guarantee + * virtualization stays disabled. + */ + lockdep_assert_irqs_disabled(); + + rcu_read_lock(); + callback = rcu_dereference(cpu_emergency_virt_callback); + if (callback) + callback(); + rcu_read_unlock(); +} + static void emergency_reboot_disable_virtualization(void) { - /* Just make sure we won't change CPUs while doing this */ local_irq_disable(); /* @@ -545,7 +589,7 @@ static void emergency_reboot_disable_virtualization(void) * Do the NMI shootdown even if virtualization is off on _this_ CPU, as * other CPUs may have virtualization enabled. */ - if (cpu_has_vmx() || cpu_has_svm(NULL)) { + if (rcu_access_pointer(cpu_emergency_virt_callback)) { /* Safely force _this_ CPU out of VMX/SVM operation. */ cpu_emergency_disable_virtualization(); @@ -553,7 +597,9 @@ static void emergency_reboot_disable_virtualization(void) nmi_shootdown_cpus_on_restart(); } } - +#else +static void emergency_reboot_disable_virtualization(void) { } +#endif /* CONFIG_KVM_INTEL || CONFIG_KVM_AMD */ void __attribute__((weak)) mach_reboot_fixups(void) { @@ -787,21 +833,9 @@ void machine_crash_shutdown(struct pt_regs *regs) } #endif - /* This is the CPU performing the emergency shutdown work. */ int crashing_cpu = -1; -/* - * Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized during - * reboot. VMX blocks INIT if the CPU is post-VMXON, and SVM blocks INIT if - * GIF=0, i.e. if the crash occurred between CLGI and STGI. - */ -void cpu_emergency_disable_virtualization(void) -{ - cpu_emergency_vmxoff(); - cpu_emergency_svm_disable(); -} - #if defined(CONFIG_SMP) static nmi_shootdown_cb shootdown_callback; diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index b9145a63da77d83586eb6fe608bfa0ff617ddd9b..b098b1fa2470816a35d7784d93c0e7ae0fc83f6d 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -358,15 +358,11 @@ static void __init add_early_ima_buffer(u64 phys_addr) #if defined(CONFIG_HAVE_IMA_KEXEC) && !defined(CONFIG_OF_FLATTREE) int __init ima_free_kexec_buffer(void) { - int rc; - if (!ima_kexec_buffer_size) return -ENOENT; - rc = memblock_phys_free(ima_kexec_buffer_phys, - ima_kexec_buffer_size); - if (rc) - return rc; + memblock_free_late(ima_kexec_buffer_phys, + ima_kexec_buffer_size); ima_kexec_buffer_phys = 0; ima_kexec_buffer_size = 0; diff --git a/arch/x86/kernel/sev-shared.c b/arch/x86/kernel/sev-shared.c index 2eabccde94fb31b77b738e4b32d614fd35b5b5c8..dcf325b7b022956ab241f92ac4b9d55bdd8eaba2 100644 --- a/arch/x86/kernel/sev-shared.c +++ b/arch/x86/kernel/sev-shared.c @@ -256,7 +256,7 @@ static int __sev_cpuid_hv(u32 fn, int reg_idx, u32 *reg) return 0; } -static int sev_cpuid_hv(struct cpuid_leaf *leaf) +static int __sev_cpuid_hv_msr(struct cpuid_leaf *leaf) { int ret; @@ -279,6 +279,45 @@ static int sev_cpuid_hv(struct cpuid_leaf *leaf) return ret; } +static int __sev_cpuid_hv_ghcb(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) +{ + u32 cr4 = native_read_cr4(); + int ret; + + ghcb_set_rax(ghcb, leaf->fn); + ghcb_set_rcx(ghcb, leaf->subfn); + + if (cr4 & X86_CR4_OSXSAVE) + /* Safe to read xcr0 */ + ghcb_set_xcr0(ghcb, xgetbv(XCR_XFEATURE_ENABLED_MASK)); + else + /* xgetbv will cause #UD - use reset value for xcr0 */ + ghcb_set_xcr0(ghcb, 1); + + ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_CPUID, 0, 0); + if (ret != ES_OK) + return ret; + + if (!(ghcb_rax_is_valid(ghcb) && + ghcb_rbx_is_valid(ghcb) && + ghcb_rcx_is_valid(ghcb) && + ghcb_rdx_is_valid(ghcb))) + return ES_VMM_ERROR; + + leaf->eax = ghcb->save.rax; + leaf->ebx = ghcb->save.rbx; + leaf->ecx = ghcb->save.rcx; + leaf->edx = ghcb->save.rdx; + + return ES_OK; +} + +static int sev_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) +{ + return ghcb ? __sev_cpuid_hv_ghcb(ghcb, ctxt, leaf) + : __sev_cpuid_hv_msr(leaf); +} + /* * This may be called early while still running on the initial identity * mapping. Use RIP-relative addressing to obtain the correct address @@ -388,19 +427,20 @@ snp_cpuid_get_validated_func(struct cpuid_leaf *leaf) return false; } -static void snp_cpuid_hv(struct cpuid_leaf *leaf) +static void snp_cpuid_hv(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) { - if (sev_cpuid_hv(leaf)) + if (sev_cpuid_hv(ghcb, ctxt, leaf)) sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_CPUID_HV); } -static int snp_cpuid_postprocess(struct cpuid_leaf *leaf) +static int snp_cpuid_postprocess(struct ghcb *ghcb, struct es_em_ctxt *ctxt, + struct cpuid_leaf *leaf) { struct cpuid_leaf leaf_hv = *leaf; switch (leaf->fn) { case 0x1: - snp_cpuid_hv(&leaf_hv); + snp_cpuid_hv(ghcb, ctxt, &leaf_hv); /* initial APIC ID */ leaf->ebx = (leaf_hv.ebx & GENMASK(31, 24)) | (leaf->ebx & GENMASK(23, 0)); @@ -419,7 +459,7 @@ static int snp_cpuid_postprocess(struct cpuid_leaf *leaf) break; case 0xB: leaf_hv.subfn = 0; - snp_cpuid_hv(&leaf_hv); + snp_cpuid_hv(ghcb, ctxt, &leaf_hv); /* extended APIC ID */ leaf->edx = leaf_hv.edx; @@ -467,7 +507,7 @@ static int snp_cpuid_postprocess(struct cpuid_leaf *leaf) } break; case 0x8000001E: - snp_cpuid_hv(&leaf_hv); + snp_cpuid_hv(ghcb, ctxt, &leaf_hv); /* extended APIC ID */ leaf->eax = leaf_hv.eax; @@ -488,7 +528,7 @@ static int snp_cpuid_postprocess(struct cpuid_leaf *leaf) * Returns -EOPNOTSUPP if feature not enabled. Any other non-zero return value * should be treated as fatal by caller. */ -static int snp_cpuid(struct cpuid_leaf *leaf) +static int snp_cpuid(struct ghcb *ghcb, struct es_em_ctxt *ctxt, struct cpuid_leaf *leaf) { const struct snp_cpuid_table *cpuid_table = snp_cpuid_get_table(); @@ -522,7 +562,7 @@ static int snp_cpuid(struct cpuid_leaf *leaf) return 0; } - return snp_cpuid_postprocess(leaf); + return snp_cpuid_postprocess(ghcb, ctxt, leaf); } /* @@ -544,14 +584,14 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code) leaf.fn = fn; leaf.subfn = subfn; - ret = snp_cpuid(&leaf); + ret = snp_cpuid(NULL, NULL, &leaf); if (!ret) goto cpuid_done; if (ret != -EOPNOTSUPP) goto fail; - if (sev_cpuid_hv(&leaf)) + if (__sev_cpuid_hv_msr(&leaf)) goto fail; cpuid_done: @@ -848,14 +888,15 @@ static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt) return ret; } -static int vc_handle_cpuid_snp(struct pt_regs *regs) +static int vc_handle_cpuid_snp(struct ghcb *ghcb, struct es_em_ctxt *ctxt) { + struct pt_regs *regs = ctxt->regs; struct cpuid_leaf leaf; int ret; leaf.fn = regs->ax; leaf.subfn = regs->cx; - ret = snp_cpuid(&leaf); + ret = snp_cpuid(ghcb, ctxt, &leaf); if (!ret) { regs->ax = leaf.eax; regs->bx = leaf.ebx; @@ -874,7 +915,7 @@ static enum es_result vc_handle_cpuid(struct ghcb *ghcb, enum es_result ret; int snp_cpuid_ret; - snp_cpuid_ret = vc_handle_cpuid_snp(regs); + snp_cpuid_ret = vc_handle_cpuid_snp(ghcb, ctxt); if (!snp_cpuid_ret) return ES_OK; if (snp_cpuid_ret != -EOPNOTSUPP) diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c index 2787826d9f607798da1d2b2fca4d8550a601eb0c..d8c1e3be74c0b6edba5c88e7d6e921da11f44a2a 100644 --- a/arch/x86/kernel/sev.c +++ b/arch/x86/kernel/sev.c @@ -868,8 +868,7 @@ void snp_set_memory_private(unsigned long vaddr, unsigned long npages) void snp_accept_memory(phys_addr_t start, phys_addr_t end) { - unsigned long vaddr; - unsigned int npages; + unsigned long vaddr, npages; if (!cc_platform_has(CC_ATTR_GUEST_SEV_SNP)) return; diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c index fd689921a1dbad3c4036132d977d9b4bee438b98..59e15dd8d0f86665ef0949b1c7072b9d25205c5e 100644 --- a/arch/x86/kernel/shstk.c +++ b/arch/x86/kernel/shstk.c @@ -205,10 +205,21 @@ unsigned long shstk_alloc_thread_stack(struct task_struct *tsk, unsigned long cl return 0; /* - * For CLONE_VM, except vfork, the child needs a separate shadow + * For CLONE_VFORK the child will share the parents shadow stack. + * Make sure to clear the internal tracking of the thread shadow + * stack so the freeing logic run for child knows to leave it alone. + */ + if (clone_flags & CLONE_VFORK) { + shstk->base = 0; + shstk->size = 0; + return 0; + } + + /* + * For !CLONE_VM the child will use a copy of the parents shadow * stack. */ - if ((clone_flags & (CLONE_VFORK | CLONE_VM)) != CLONE_VM) + if (!(clone_flags & CLONE_VM)) return 0; size = adjust_shstk_size(stack_size); @@ -408,7 +419,25 @@ void shstk_free(struct task_struct *tsk) if (!tsk->mm || tsk->mm != current->mm) return; + /* + * If shstk->base is NULL, then this task is not managing its + * own shadow stack (CLONE_VFORK). So skip freeing it. + */ + if (!shstk->base) + return; + + /* + * shstk->base is NULL for CLONE_VFORK child tasks, and so is + * normal. But size = 0 on a shstk->base is not normal and + * indicated an attempt to free the thread shadow stack twice. + * Warn about it. + */ + if (WARN_ON(!shstk->size)) + return; + unmap_shadow_stack(shstk->base, shstk->size); + + shstk->size = 0; } static int wrss_control(bool enable) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index d7667a29acf3ac47df65177b425faa6ae3b43a17..48e0406187314f1342f6dcffa4552edf9f0291ec 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -579,7 +579,6 @@ static bool match_llc(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o) } -#if defined(CONFIG_SCHED_SMT) || defined(CONFIG_SCHED_CLUSTER) || defined(CONFIG_SCHED_MC) static inline int x86_sched_itmt_flags(void) { return sysctl_sched_itmt_enabled ? SD_ASYM_PACKING : 0; @@ -603,7 +602,14 @@ static int x86_cluster_flags(void) return cpu_cluster_flags() | x86_sched_itmt_flags(); } #endif -#endif + +static int x86_die_flags(void) +{ + if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) + return x86_sched_itmt_flags(); + + return 0; +} /* * Set if a package/die has multiple NUMA nodes inside. @@ -640,7 +646,7 @@ static void __init build_sched_topology(void) */ if (!x86_has_numa_in_package) { x86_topology[i++] = (struct sched_domain_topology_level){ - cpu_cpu_mask, SD_INIT_NAME(DIE) + cpu_cpu_mask, x86_die_flags, SD_INIT_NAME(DIE) }; } @@ -1250,7 +1256,7 @@ bool smp_park_other_cpus_in_init(void) if (this_cpu) return false; - for_each_present_cpu(cpu) { + for_each_cpu_and(cpu, &cpus_booted_once_mask, cpu_present_mask) { if (cpu == this_cpu) continue; apicid = apic->cpu_present_to_apicid(cpu); diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S index 83d41c2601d7b5b5ca8714b71c3c0224e077401f..f15fb71f280e242de7ec7ed1b978d09617ebc812 100644 --- a/arch/x86/kernel/vmlinux.lds.S +++ b/arch/x86/kernel/vmlinux.lds.S @@ -156,7 +156,7 @@ SECTIONS ALIGN_ENTRY_TEXT_END *(.gnu.warning) - } :text =0xcccc + } :text = 0xcccccccc /* End of text section, which should occupy whole number of pages */ _etext = .; diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index 89ca7f4c1464337bd42646caa24d37c3df2464d3..ed90f148140dfe093bed15a033b6e322a3cb5f2a 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -101,7 +101,7 @@ config X86_SGX_KVM config KVM_AMD tristate "KVM for AMD processors support" - depends on KVM + depends on KVM && (CPU_SUP_AMD || CPU_SUP_HYGON) help Provides support for KVM on AMD processors equipped with the AMD-V (SVM) extensions. @@ -138,6 +138,19 @@ config KVM_XEN If in doubt, say "N". +config KVM_PROVE_MMU + bool "Prove KVM MMU correctness" + depends on DEBUG_KERNEL + depends on KVM + depends on EXPERT + help + Enables runtime assertions in KVM's MMU that are too costly to enable + in anything remotely resembling a production environment, e.g. this + gates code that verifies a to-be-freed page table doesn't have any + present SPTEs. + + If in doubt, say "N". + config KVM_EXTERNAL_WRITE_TRACKING bool diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index d3432687c9e6315d0a521a5babce201a527a3a0b..0544e30b4946d1e5fb40f6737335ac00d58a84b2 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include "linux/lockdep.h" #include #include #include @@ -84,6 +85,18 @@ static inline struct kvm_cpuid_entry2 *cpuid_entry2_find( struct kvm_cpuid_entry2 *e; int i; + /* + * KVM has a semi-arbitrary rule that querying the guest's CPUID model + * with IRQs disabled is disallowed. The CPUID model can legitimately + * have over one hundred entries, i.e. the lookup is slow, and IRQs are + * typically disabled in KVM only when KVM is in a performance critical + * path, e.g. the core VM-Enter/VM-Exit run loop. Nothing will break + * if this rule is violated, this assertion is purely to flag potential + * performance issues. If this fires, consider moving the lookup out + * of the hotpath, e.g. by caching information during CPUID updates. + */ + lockdep_assert_irqs_enabled(); + for (i = 0; i < nent; i++) { e = &entries[i]; @@ -312,6 +325,27 @@ static void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic = vcpu->arch.apic; struct kvm_cpuid_entry2 *best; + bool allow_gbpages; + + BUILD_BUG_ON(KVM_NR_GOVERNED_FEATURES > KVM_MAX_NR_GOVERNED_FEATURES); + bitmap_zero(vcpu->arch.governed_features.enabled, + KVM_MAX_NR_GOVERNED_FEATURES); + + /* + * If TDP is enabled, let the guest use GBPAGES if they're supported in + * hardware. The hardware page walker doesn't let KVM disable GBPAGES, + * i.e. won't treat them as reserved, and KVM doesn't redo the GVA->GPA + * walk for performance and complexity reasons. Not to mention KVM + * _can't_ solve the problem because GVA->GPA walks aren't visible to + * KVM once a TDP translation is installed. Mimic hardware behavior so + * that KVM's is at least consistent, i.e. doesn't randomly inject #PF. + * If TDP is disabled, honor *only* guest CPUID as KVM has full control + * and can install smaller shadow pages if the host lacks 1GiB support. + */ + allow_gbpages = tdp_enabled ? boot_cpu_has(X86_FEATURE_GBPAGES) : + guest_cpuid_has(vcpu, X86_FEATURE_GBPAGES); + if (allow_gbpages) + kvm_governed_feature_set(vcpu, X86_FEATURE_GBPAGES); best = kvm_find_cpuid_entry(vcpu, 1); if (best && apic) { @@ -647,7 +681,8 @@ void kvm_set_cpu_caps(void) ); kvm_cpu_cap_init_kvm_defined(CPUID_7_1_EDX, - F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(PREFETCHITI) + F(AVX_VNNI_INT8) | F(AVX_NE_CONVERT) | F(PREFETCHITI) | + F(AMX_COMPLEX) ); kvm_cpu_cap_mask(CPUID_D_1_EAX, @@ -1154,6 +1189,9 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) cpuid_entry_override(entry, CPUID_8000_0001_EDX); cpuid_entry_override(entry, CPUID_8000_0001_ECX); break; + case 0x80000005: + /* Pass host L1 cache and TLB info. */ + break; case 0x80000006: /* Drop reserved bits, pass host L2 cache and TLB info. */ entry->edx &= ~GENMASK(17, 16); diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index b1658c0de847cd38c0560c9bbb947a1a267c2c7e..284fa4704553da1345731927225084e7daeeba4b 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -232,4 +232,50 @@ static __always_inline bool guest_pv_has(struct kvm_vcpu *vcpu, return vcpu->arch.pv_cpuid.features & (1u << kvm_feature); } +enum kvm_governed_features { +#define KVM_GOVERNED_FEATURE(x) KVM_GOVERNED_##x, +#include "governed_features.h" + KVM_NR_GOVERNED_FEATURES +}; + +static __always_inline int kvm_governed_feature_index(unsigned int x86_feature) +{ + switch (x86_feature) { +#define KVM_GOVERNED_FEATURE(x) case x: return KVM_GOVERNED_##x; +#include "governed_features.h" + default: + return -1; + } +} + +static __always_inline bool kvm_is_governed_feature(unsigned int x86_feature) +{ + return kvm_governed_feature_index(x86_feature) >= 0; +} + +static __always_inline void kvm_governed_feature_set(struct kvm_vcpu *vcpu, + unsigned int x86_feature) +{ + BUILD_BUG_ON(!kvm_is_governed_feature(x86_feature)); + + __set_bit(kvm_governed_feature_index(x86_feature), + vcpu->arch.governed_features.enabled); +} + +static __always_inline void kvm_governed_feature_check_and_set(struct kvm_vcpu *vcpu, + unsigned int x86_feature) +{ + if (kvm_cpu_cap_has(x86_feature) && guest_cpuid_has(vcpu, x86_feature)) + kvm_governed_feature_set(vcpu, x86_feature); +} + +static __always_inline bool guest_can_use(struct kvm_vcpu *vcpu, + unsigned int x86_feature) +{ + BUILD_BUG_ON(!kvm_is_governed_feature(x86_feature)); + + return test_bit(kvm_governed_feature_index(x86_feature), + vcpu->arch.governed_features.enabled); +} + #endif diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 936a397a08cd2840d9aea8e477272e342c3f53a6..2673cd5c46cb486b9e78a20c589462552f0b4747 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -1799,13 +1799,11 @@ static int writeback(struct x86_emulate_ctxt *ctxt, struct operand *op) op->addr.mem, &op->val, op->bytes); - break; case OP_MEM_STR: return segmented_write(ctxt, op->addr.mem, op->data, op->bytes * op->count); - break; case OP_XMM: kvm_write_sse_reg(op->addr.xmm, &op->vec_val); break; diff --git a/arch/x86/kvm/governed_features.h b/arch/x86/kvm/governed_features.h new file mode 100644 index 0000000000000000000000000000000000000000..423a73395c102ca908453016e416dbdfb7fc2b5b --- /dev/null +++ b/arch/x86/kvm/governed_features.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(KVM_GOVERNED_FEATURE) || defined(KVM_GOVERNED_X86_FEATURE) +BUILD_BUG() +#endif + +#define KVM_GOVERNED_X86_FEATURE(x) KVM_GOVERNED_FEATURE(X86_FEATURE_##x) + +KVM_GOVERNED_X86_FEATURE(GBPAGES) +KVM_GOVERNED_X86_FEATURE(XSAVES) +KVM_GOVERNED_X86_FEATURE(VMX) +KVM_GOVERNED_X86_FEATURE(NRIPS) +KVM_GOVERNED_X86_FEATURE(TSCRATEMSR) +KVM_GOVERNED_X86_FEATURE(V_VMSAVE_VMLOAD) +KVM_GOVERNED_X86_FEATURE(LBRV) +KVM_GOVERNED_X86_FEATURE(PAUSEFILTER) +KVM_GOVERNED_X86_FEATURE(PFTHRESHOLD) +KVM_GOVERNED_X86_FEATURE(VGIF) +KVM_GOVERNED_X86_FEATURE(VNMI) + +#undef KVM_GOVERNED_X86_FEATURE +#undef KVM_GOVERNED_FEATURE diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index b28fd020066f6e4e4ddc0750a1b341dc9508d958..7c2dac6824e262e0e82f1ed1d7bd564598006713 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1293,7 +1293,6 @@ static bool hv_check_msr_access(struct kvm_vcpu_hv *hv_vcpu, u32 msr) case HV_X64_MSR_VP_ASSIST_PAGE: return hv_vcpu->cpuid_cache.features_eax & HV_MSR_APIC_ACCESS_AVAILABLE; - break; case HV_X64_MSR_TSC_FREQUENCY: case HV_X64_MSR_APIC_FREQUENCY: return hv_vcpu->cpuid_cache.features_eax & diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index ab65f3a47dfde5bbdde5dd0b3ed25c3a08006ab0..be7aeb9b8ea3b152b269870e5a737642a492e192 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -213,7 +213,6 @@ struct x86_emulate_ops { bool (*get_cpuid)(struct x86_emulate_ctxt *ctxt, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, bool exact_only); - bool (*guest_has_long_mode)(struct x86_emulate_ctxt *ctxt); bool (*guest_has_movbe)(struct x86_emulate_ctxt *ctxt); bool (*guest_has_fxsr)(struct x86_emulate_ctxt *ctxt); bool (*guest_has_rdpid)(struct x86_emulate_ctxt *ctxt); diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index a983a16163b137524e8295a38d0c3709e788edde..dcd60b39e794d95ab4b1b4046a69ec5cf1a9dffc 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -376,7 +376,8 @@ void kvm_recalculate_apic_map(struct kvm *kvm) struct kvm_vcpu *vcpu; unsigned long i; u32 max_id = 255; /* enough space for any xAPIC ID */ - bool xapic_id_mismatch = false; + bool xapic_id_mismatch; + int r; /* Read kvm->arch.apic_map_dirty before kvm->arch.apic_map. */ if (atomic_read_acquire(&kvm->arch.apic_map_dirty) == CLEAN) @@ -386,9 +387,14 @@ void kvm_recalculate_apic_map(struct kvm *kvm) "Dirty APIC map without an in-kernel local APIC"); mutex_lock(&kvm->arch.apic_map_lock); + +retry: /* - * Read kvm->arch.apic_map_dirty before kvm->arch.apic_map - * (if clean) or the APIC registers (if dirty). + * Read kvm->arch.apic_map_dirty before kvm->arch.apic_map (if clean) + * or the APIC registers (if dirty). Note, on retry the map may have + * not yet been marked dirty by whatever task changed a vCPU's x2APIC + * ID, i.e. the map may still show up as in-progress. In that case + * this task still needs to retry and complete its calculation. */ if (atomic_cmpxchg_acquire(&kvm->arch.apic_map_dirty, DIRTY, UPDATE_IN_PROGRESS) == CLEAN) { @@ -397,6 +403,15 @@ void kvm_recalculate_apic_map(struct kvm *kvm) return; } + /* + * Reset the mismatch flag between attempts so that KVM does the right + * thing if a vCPU changes its xAPIC ID, but do NOT reset max_id, i.e. + * keep max_id strictly increasing. Disallowing max_id from shrinking + * ensures KVM won't get stuck in an infinite loop, e.g. if the vCPU + * with the highest x2APIC ID is toggling its APIC on and off. + */ + xapic_id_mismatch = false; + kvm_for_each_vcpu(i, vcpu, kvm) if (kvm_apic_present(vcpu)) max_id = max(max_id, kvm_x2apic_id(vcpu->arch.apic)); @@ -415,9 +430,15 @@ void kvm_recalculate_apic_map(struct kvm *kvm) if (!kvm_apic_present(vcpu)) continue; - if (kvm_recalculate_phys_map(new, vcpu, &xapic_id_mismatch)) { + r = kvm_recalculate_phys_map(new, vcpu, &xapic_id_mismatch); + if (r) { kvfree(new); new = NULL; + if (r == -E2BIG) { + cond_resched(); + goto retry; + } + goto out; } diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 92d5a1924fc18ec07bd6a95bdf6cfe328fbfed23..253fb2093d5dadcbe7994a004c26e2ad54a054a7 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -121,6 +121,8 @@ void kvm_mmu_unload(struct kvm_vcpu *vcpu); void kvm_mmu_free_obsolete_roots(struct kvm_vcpu *vcpu); void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu); void kvm_mmu_sync_prev_roots(struct kvm_vcpu *vcpu); +void kvm_mmu_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, + int bytes); static inline int kvm_mmu_reload(struct kvm_vcpu *vcpu) { diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index ec169f5c7dce21d5f730638ba86ebc99f3050146..f7901cb4d2fa4b67a4dae93648a169fd2931585b 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -25,6 +25,7 @@ #include "kvm_cache_regs.h" #include "smm.h" #include "kvm_emulate.h" +#include "page_track.h" #include "cpuid.h" #include "spte.h" @@ -53,7 +54,7 @@ #include #include #include -#include + #include "trace.h" extern bool itlb_multihit_kvm_mitigation; @@ -115,11 +116,6 @@ static int max_huge_page_level __read_mostly; static int tdp_root_level __read_mostly; static int max_tdp_level __read_mostly; -#ifdef MMU_DEBUG -bool dbg = 0; -module_param(dbg, bool, 0644); -#endif - #define PTE_PREFETCH_NUM 8 #include @@ -278,16 +274,12 @@ static inline bool kvm_available_flush_remote_tlbs_range(void) return kvm_x86_ops.flush_remote_tlbs_range; } -void kvm_flush_remote_tlbs_range(struct kvm *kvm, gfn_t start_gfn, - gfn_t nr_pages) +int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, gfn_t gfn, u64 nr_pages) { - int ret = -EOPNOTSUPP; + if (!kvm_x86_ops.flush_remote_tlbs_range) + return -EOPNOTSUPP; - if (kvm_x86_ops.flush_remote_tlbs_range) - ret = static_call(kvm_x86_flush_remote_tlbs_range)(kvm, start_gfn, - nr_pages); - if (ret) - kvm_flush_remote_tlbs(kvm); + return static_call(kvm_x86_flush_remote_tlbs_range)(kvm, gfn, nr_pages); } static gfn_t kvm_mmu_page_get_gfn(struct kvm_mmu_page *sp, int index); @@ -490,7 +482,7 @@ static u64 __get_spte_lockless(u64 *sptep) */ static void mmu_spte_set(u64 *sptep, u64 new_spte) { - WARN_ON(is_shadow_present_pte(*sptep)); + WARN_ON_ONCE(is_shadow_present_pte(*sptep)); __set_spte(sptep, new_spte); } @@ -502,7 +494,7 @@ static u64 mmu_spte_update_no_track(u64 *sptep, u64 new_spte) { u64 old_spte = *sptep; - WARN_ON(!is_shadow_present_pte(new_spte)); + WARN_ON_ONCE(!is_shadow_present_pte(new_spte)); check_spte_writable_invariants(new_spte); if (!is_shadow_present_pte(old_spte)) { @@ -515,7 +507,7 @@ static u64 mmu_spte_update_no_track(u64 *sptep, u64 new_spte) else old_spte = __update_clear_spte_slow(sptep, new_spte); - WARN_ON(spte_to_pfn(old_spte) != spte_to_pfn(new_spte)); + WARN_ON_ONCE(spte_to_pfn(old_spte) != spte_to_pfn(new_spte)); return old_spte; } @@ -597,7 +589,7 @@ static u64 mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) * by a refcounted page, the refcount is elevated. */ page = kvm_pfn_to_refcounted_page(pfn); - WARN_ON(page && !page_count(page)); + WARN_ON_ONCE(page && !page_count(page)); if (is_accessed_spte(old_spte)) kvm_set_pfn_accessed(pfn); @@ -812,7 +804,7 @@ static void update_gfn_disallow_lpage_count(const struct kvm_memory_slot *slot, for (i = PG_LEVEL_2M; i <= KVM_MAX_HUGEPAGE_LEVEL; ++i) { linfo = lpage_info_slot(gfn, slot, i); linfo->disallow_lpage += count; - WARN_ON(linfo->disallow_lpage < 0); + WARN_ON_ONCE(linfo->disallow_lpage < 0); } } @@ -839,8 +831,7 @@ static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp) /* the non-leaf shadow pages are keeping readonly. */ if (sp->role.level > PG_LEVEL_4K) - return kvm_slot_page_track_add_page(kvm, slot, gfn, - KVM_PAGE_TRACK_WRITE); + return __kvm_write_track_add_gfn(kvm, slot, gfn); kvm_mmu_gfn_disallow_lpage(slot, gfn); @@ -886,8 +877,7 @@ static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp) slots = kvm_memslots_for_spte_role(kvm, sp->role); slot = __gfn_to_memslot(slots, gfn); if (sp->role.level > PG_LEVEL_4K) - return kvm_slot_page_track_remove_page(kvm, slot, gfn, - KVM_PAGE_TRACK_WRITE); + return __kvm_write_track_remove_gfn(kvm, slot, gfn); kvm_mmu_gfn_allow_lpage(slot, gfn); } @@ -941,10 +931,8 @@ static int pte_list_add(struct kvm_mmu_memory_cache *cache, u64 *spte, int count = 0; if (!rmap_head->val) { - rmap_printk("%p %llx 0->1\n", spte, *spte); rmap_head->val = (unsigned long)spte; } else if (!(rmap_head->val & 1)) { - rmap_printk("%p %llx 1->many\n", spte, *spte); desc = kvm_mmu_memory_cache_alloc(cache); desc->sptes[0] = (u64 *)rmap_head->val; desc->sptes[1] = spte; @@ -953,7 +941,6 @@ static int pte_list_add(struct kvm_mmu_memory_cache *cache, u64 *spte, rmap_head->val = (unsigned long)desc | 1; ++count; } else { - rmap_printk("%p %llx many->many\n", spte, *spte); desc = (struct pte_list_desc *)(rmap_head->val & ~1ul); count = desc->tail_count + desc->spte_count; @@ -973,7 +960,8 @@ static int pte_list_add(struct kvm_mmu_memory_cache *cache, u64 *spte, return count; } -static void pte_list_desc_remove_entry(struct kvm_rmap_head *rmap_head, +static void pte_list_desc_remove_entry(struct kvm *kvm, + struct kvm_rmap_head *rmap_head, struct pte_list_desc *desc, int i) { struct pte_list_desc *head_desc = (struct pte_list_desc *)(rmap_head->val & ~1ul); @@ -984,7 +972,7 @@ static void pte_list_desc_remove_entry(struct kvm_rmap_head *rmap_head, * when adding an entry and the previous head is full, and heads are * removed (this flow) when they become empty. */ - BUG_ON(j < 0); + KVM_BUG_ON_DATA_CORRUPTION(j < 0, kvm); /* * Replace the to-be-freed SPTE with the last valid entry from the head @@ -1009,35 +997,34 @@ static void pte_list_desc_remove_entry(struct kvm_rmap_head *rmap_head, mmu_free_pte_list_desc(head_desc); } -static void pte_list_remove(u64 *spte, struct kvm_rmap_head *rmap_head) +static void pte_list_remove(struct kvm *kvm, u64 *spte, + struct kvm_rmap_head *rmap_head) { struct pte_list_desc *desc; int i; - if (!rmap_head->val) { - pr_err("%s: %p 0->BUG\n", __func__, spte); - BUG(); - } else if (!(rmap_head->val & 1)) { - rmap_printk("%p 1->0\n", spte); - if ((u64 *)rmap_head->val != spte) { - pr_err("%s: %p 1->BUG\n", __func__, spte); - BUG(); - } + if (KVM_BUG_ON_DATA_CORRUPTION(!rmap_head->val, kvm)) + return; + + if (!(rmap_head->val & 1)) { + if (KVM_BUG_ON_DATA_CORRUPTION((u64 *)rmap_head->val != spte, kvm)) + return; + rmap_head->val = 0; } else { - rmap_printk("%p many->many\n", spte); desc = (struct pte_list_desc *)(rmap_head->val & ~1ul); while (desc) { for (i = 0; i < desc->spte_count; ++i) { if (desc->sptes[i] == spte) { - pte_list_desc_remove_entry(rmap_head, desc, i); + pte_list_desc_remove_entry(kvm, rmap_head, + desc, i); return; } } desc = desc->more; } - pr_err("%s: %p many->many\n", __func__, spte); - BUG(); + + KVM_BUG_ON_DATA_CORRUPTION(true, kvm); } } @@ -1045,7 +1032,7 @@ static void kvm_zap_one_rmap_spte(struct kvm *kvm, struct kvm_rmap_head *rmap_head, u64 *sptep) { mmu_spte_clear_track_bits(kvm, sptep); - pte_list_remove(sptep, rmap_head); + pte_list_remove(kvm, sptep, rmap_head); } /* Return true if at least one SPTE was zapped, false otherwise */ @@ -1120,7 +1107,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte) slot = __gfn_to_memslot(slots, gfn); rmap_head = gfn_to_rmap(gfn, sp->role.level, slot); - pte_list_remove(spte, rmap_head); + pte_list_remove(kvm, spte, rmap_head); } /* @@ -1212,7 +1199,7 @@ static void drop_large_spte(struct kvm *kvm, u64 *sptep, bool flush) struct kvm_mmu_page *sp; sp = sptep_to_sp(sptep); - WARN_ON(sp->role.level == PG_LEVEL_4K); + WARN_ON_ONCE(sp->role.level == PG_LEVEL_4K); drop_spte(kvm, sptep); @@ -1241,8 +1228,6 @@ static bool spte_write_protect(u64 *sptep, bool pt_protect) !(pt_protect && is_mmu_writable_spte(spte))) return false; - rmap_printk("spte %p %llx\n", sptep, *sptep); - if (pt_protect) spte &= ~shadow_mmu_writable_mask; spte = spte & ~PT_WRITABLE_MASK; @@ -1267,9 +1252,7 @@ static bool spte_clear_dirty(u64 *sptep) { u64 spte = *sptep; - rmap_printk("spte %p %llx\n", sptep, *sptep); - - MMU_WARN_ON(!spte_ad_enabled(spte)); + KVM_MMU_WARN_ON(!spte_ad_enabled(spte)); spte &= ~shadow_dirty_mask; return mmu_spte_update(sptep, spte); } @@ -1475,14 +1458,11 @@ static bool kvm_set_pte_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head, u64 new_spte; kvm_pfn_t new_pfn; - WARN_ON(pte_huge(pte)); + WARN_ON_ONCE(pte_huge(pte)); new_pfn = pte_pfn(pte); restart: for_each_rmap_spte(rmap_head, &iter, sptep) { - rmap_printk("spte %p %llx gfn %llx (%d)\n", - sptep, *sptep, gfn, level); - need_flush = true; if (pte_write(pte)) { @@ -1588,7 +1568,7 @@ static __always_inline bool kvm_handle_gfn_range(struct kvm *kvm, for_each_slot_rmap_range(range->slot, PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL, range->start, range->end - 1, &iterator) ret |= handler(kvm, iterator.rmap, range->slot, iterator.gfn, - iterator.level, range->pte); + iterator.level, range->arg.pte); return ret; } @@ -1710,21 +1690,19 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) return young; } -#ifdef MMU_DEBUG -static int is_empty_shadow_page(u64 *spt) +static void kvm_mmu_check_sptes_at_free(struct kvm_mmu_page *sp) { - u64 *pos; - u64 *end; +#ifdef CONFIG_KVM_PROVE_MMU + int i; - for (pos = spt, end = pos + SPTE_ENT_PER_PAGE; pos != end; pos++) - if (is_shadow_present_pte(*pos)) { - printk(KERN_ERR "%s: %p %llx\n", __func__, - pos, *pos); - return 0; - } - return 1; -} + for (i = 0; i < SPTE_ENT_PER_PAGE; i++) { + if (KVM_MMU_WARN_ON(is_shadow_present_pte(sp->spt[i]))) + pr_err_ratelimited("SPTE %llx (@ %p) for gfn %llx shadow-present at free", + sp->spt[i], &sp->spt[i], + kvm_mmu_page_get_gfn(sp, i)); + } #endif +} /* * This value is the sum of all of the kvm instances's @@ -1752,7 +1730,8 @@ static void kvm_unaccount_mmu_page(struct kvm *kvm, struct kvm_mmu_page *sp) static void kvm_mmu_free_shadow_page(struct kvm_mmu_page *sp) { - MMU_WARN_ON(!is_empty_shadow_page(sp->spt)); + kvm_mmu_check_sptes_at_free(sp); + hlist_del(&sp->hash_link); list_del(&sp->link); free_page((unsigned long)sp->spt); @@ -1775,16 +1754,16 @@ static void mmu_page_add_parent_pte(struct kvm_mmu_memory_cache *cache, pte_list_add(cache, parent_pte, &sp->parent_ptes); } -static void mmu_page_remove_parent_pte(struct kvm_mmu_page *sp, +static void mmu_page_remove_parent_pte(struct kvm *kvm, struct kvm_mmu_page *sp, u64 *parent_pte) { - pte_list_remove(parent_pte, &sp->parent_ptes); + pte_list_remove(kvm, parent_pte, &sp->parent_ptes); } -static void drop_parent_pte(struct kvm_mmu_page *sp, +static void drop_parent_pte(struct kvm *kvm, struct kvm_mmu_page *sp, u64 *parent_pte) { - mmu_page_remove_parent_pte(sp, parent_pte); + mmu_page_remove_parent_pte(kvm, sp, parent_pte); mmu_spte_clear_no_track(parent_pte); } @@ -1840,7 +1819,7 @@ static int mmu_pages_add(struct kvm_mmu_pages *pvec, struct kvm_mmu_page *sp, static inline void clear_unsync_child_bit(struct kvm_mmu_page *sp, int idx) { --sp->unsync_children; - WARN_ON((int)sp->unsync_children < 0); + WARN_ON_ONCE((int)sp->unsync_children < 0); __clear_bit(idx, sp->unsync_child_bitmap); } @@ -1898,7 +1877,7 @@ static int mmu_unsync_walk(struct kvm_mmu_page *sp, static void kvm_unlink_unsync_page(struct kvm *kvm, struct kvm_mmu_page *sp) { - WARN_ON(!sp->unsync); + WARN_ON_ONCE(!sp->unsync); trace_kvm_mmu_sync_page(sp); sp->unsync = 0; --kvm->stat.mmu_unsync; @@ -2073,11 +2052,11 @@ static int mmu_pages_first(struct kvm_mmu_pages *pvec, if (pvec->nr == 0) return 0; - WARN_ON(pvec->page[0].idx != INVALID_INDEX); + WARN_ON_ONCE(pvec->page[0].idx != INVALID_INDEX); sp = pvec->page[0].sp; level = sp->role.level; - WARN_ON(level == PG_LEVEL_4K); + WARN_ON_ONCE(level == PG_LEVEL_4K); parents->parent[level-2] = sp; @@ -2099,7 +2078,7 @@ static void mmu_pages_clear_parents(struct mmu_page_path *parents) if (!sp) return; - WARN_ON(idx == INVALID_INDEX); + WARN_ON_ONCE(idx == INVALID_INDEX); clear_unsync_child_bit(sp, idx); level++; } while (!sp->unsync_children); @@ -2220,7 +2199,7 @@ static struct kvm_mmu_page *kvm_mmu_find_shadow_page(struct kvm *kvm, if (ret < 0) break; - WARN_ON(!list_empty(&invalid_list)); + WARN_ON_ONCE(!list_empty(&invalid_list)); if (ret > 0) kvm_flush_remote_tlbs(kvm); } @@ -2499,7 +2478,7 @@ static void validate_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep, if (child->role.access == direct_access) return; - drop_parent_pte(child, sptep); + drop_parent_pte(vcpu->kvm, child, sptep); kvm_flush_remote_tlbs_sptep(vcpu->kvm, sptep); } } @@ -2517,7 +2496,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, drop_spte(kvm, spte); } else { child = spte_to_child_sp(pte); - drop_parent_pte(child, spte); + drop_parent_pte(kvm, child, spte); /* * Recursively zap nested TDP SPs, parentless SPs are @@ -2548,13 +2527,13 @@ static int kvm_mmu_page_unlink_children(struct kvm *kvm, return zapped; } -static void kvm_mmu_unlink_parents(struct kvm_mmu_page *sp) +static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp) { u64 *sptep; struct rmap_iterator iter; while ((sptep = rmap_get_first(&sp->parent_ptes, &iter))) - drop_parent_pte(sp, sptep); + drop_parent_pte(kvm, sp, sptep); } static int mmu_zap_unsync_children(struct kvm *kvm, @@ -2593,7 +2572,7 @@ static bool __kvm_mmu_prepare_zap_page(struct kvm *kvm, ++kvm->stat.mmu_shadow_zapped; *nr_zapped = mmu_zap_unsync_children(kvm, sp, invalid_list); *nr_zapped += kvm_mmu_page_unlink_children(kvm, sp, invalid_list); - kvm_mmu_unlink_parents(sp); + kvm_mmu_unlink_parents(kvm, sp); /* Zapping children means active_mmu_pages has become unstable. */ list_unstable = *nr_zapped; @@ -2675,7 +2654,7 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm, kvm_flush_remote_tlbs(kvm); list_for_each_entry_safe(sp, nsp, invalid_list, link) { - WARN_ON(!sp->role.invalid || sp->root_count); + WARN_ON_ONCE(!sp->role.invalid || sp->root_count); kvm_mmu_free_shadow_page(sp); } } @@ -2775,12 +2754,9 @@ int kvm_mmu_unprotect_page(struct kvm *kvm, gfn_t gfn) LIST_HEAD(invalid_list); int r; - pgprintk("%s: looking for gfn %llx\n", __func__, gfn); r = 0; write_lock(&kvm->mmu_lock); for_each_gfn_valid_sp_with_gptes(kvm, sp, gfn) { - pgprintk("%s: gfn %llx role %x\n", __func__, gfn, - sp->role.word); r = 1; kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list); } @@ -2831,7 +2807,7 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, * track machinery is used to write-protect upper-level shadow pages, * i.e. this guards the role.level == 4K assertion below! */ - if (kvm_slot_page_track_is_active(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE)) + if (kvm_gfn_is_write_tracked(kvm, slot, gfn)) return -EPERM; /* @@ -2873,7 +2849,7 @@ int mmu_try_to_unsync_pages(struct kvm *kvm, const struct kvm_memory_slot *slot, continue; } - WARN_ON(sp->role.level != PG_LEVEL_4K); + WARN_ON_ONCE(sp->role.level != PG_LEVEL_4K); kvm_unsync_page(kvm, sp); } if (locked) @@ -2938,9 +2914,6 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, bool prefetch = !fault || fault->prefetch; bool write_fault = fault && fault->write; - pgprintk("%s: spte %llx write_fault %d gfn %llx\n", __func__, - *sptep, write_fault, gfn); - if (unlikely(is_noslot_pfn(pfn))) { vcpu->stat.pf_mmio_spte_created++; mark_mmio_spte(vcpu, sptep, gfn, pte_access); @@ -2957,11 +2930,9 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, u64 pte = *sptep; child = spte_to_child_sp(pte); - drop_parent_pte(child, sptep); + drop_parent_pte(vcpu->kvm, child, sptep); flush = true; } else if (pfn != spte_to_pfn(*sptep)) { - pgprintk("hfn old %llx new %llx\n", - spte_to_pfn(*sptep), pfn); drop_spte(vcpu->kvm, sptep); flush = true; } else @@ -2986,8 +2957,6 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, if (flush) kvm_flush_remote_tlbs_gfn(vcpu->kvm, gfn, level); - pgprintk("%s: setting spte %llx\n", __func__, *sptep); - if (!was_rmapped) { WARN_ON_ONCE(ret == RET_PF_SPURIOUS); rmap_add(vcpu, slot, sptep, gfn, pte_access); @@ -3033,7 +3002,7 @@ static void __direct_pte_prefetch(struct kvm_vcpu *vcpu, u64 *spte, *start = NULL; int i; - WARN_ON(!sp->role.direct); + WARN_ON_ONCE(!sp->role.direct); i = spte_index(sptep) & ~(PTE_PREFETCH_NUM - 1); spte = sp->spt + i; @@ -3574,12 +3543,8 @@ static void mmu_free_root_page(struct kvm *kvm, hpa_t *root_hpa, if (!VALID_PAGE(*root_hpa)) return; - /* - * The "root" may be a special root, e.g. a PAE entry, treat it as a - * SPTE to ensure any non-PA bits are dropped. - */ - sp = spte_to_child_sp(*root_hpa); - if (WARN_ON(!sp)) + sp = root_to_sp(*root_hpa); + if (WARN_ON_ONCE(!sp)) return; if (is_tdp_mmu_page(sp)) @@ -3624,7 +3589,9 @@ void kvm_mmu_free_roots(struct kvm *kvm, struct kvm_mmu *mmu, &invalid_list); if (free_active_root) { - if (to_shadow_page(mmu->root.hpa)) { + if (kvm_mmu_is_dummy_root(mmu->root.hpa)) { + /* Nothing to cleanup for dummy roots. */ + } else if (root_to_sp(mmu->root.hpa)) { mmu_free_root_page(kvm, &mmu->root.hpa, &invalid_list); } else if (mmu->pae_root) { for (i = 0; i < 4; ++i) { @@ -3648,6 +3615,7 @@ EXPORT_SYMBOL_GPL(kvm_mmu_free_roots); void kvm_mmu_free_guest_mode_roots(struct kvm *kvm, struct kvm_mmu *mmu) { unsigned long roots_to_free = 0; + struct kvm_mmu_page *sp; hpa_t root_hpa; int i; @@ -3662,8 +3630,8 @@ void kvm_mmu_free_guest_mode_roots(struct kvm *kvm, struct kvm_mmu *mmu) if (!VALID_PAGE(root_hpa)) continue; - if (!to_shadow_page(root_hpa) || - to_shadow_page(root_hpa)->role.guest_mode) + sp = root_to_sp(root_hpa); + if (!sp || sp->role.guest_mode) roots_to_free |= KVM_MMU_ROOT_PREVIOUS(i); } @@ -3671,19 +3639,6 @@ void kvm_mmu_free_guest_mode_roots(struct kvm *kvm, struct kvm_mmu *mmu) } EXPORT_SYMBOL_GPL(kvm_mmu_free_guest_mode_roots); - -static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn) -{ - int ret = 0; - - if (!kvm_vcpu_is_visible_gfn(vcpu, root_gfn)) { - kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); - ret = 1; - } - - return ret; -} - static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, int quadrant, u8 level) { @@ -3821,8 +3776,10 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) root_pgd = kvm_mmu_get_guest_pgd(vcpu, mmu); root_gfn = root_pgd >> PAGE_SHIFT; - if (mmu_check_root(vcpu, root_gfn)) - return 1; + if (!kvm_vcpu_is_visible_gfn(vcpu, root_gfn)) { + mmu->root.hpa = kvm_mmu_get_dummy_root(); + return 0; + } /* * On SVM, reading PDPTRs might access guest memory, which might fault @@ -3834,8 +3791,8 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) if (!(pdptrs[i] & PT_PRESENT_MASK)) continue; - if (mmu_check_root(vcpu, pdptrs[i] >> PAGE_SHIFT)) - return 1; + if (!kvm_vcpu_is_visible_gfn(vcpu, pdptrs[i] >> PAGE_SHIFT)) + pdptrs[i] = 0; } } @@ -4002,7 +3959,7 @@ static bool is_unsync_root(hpa_t root) { struct kvm_mmu_page *sp; - if (!VALID_PAGE(root)) + if (!VALID_PAGE(root) || kvm_mmu_is_dummy_root(root)) return false; /* @@ -4018,7 +3975,7 @@ static bool is_unsync_root(hpa_t root) * requirement isn't satisfied. */ smp_rmb(); - sp = to_shadow_page(root); + sp = root_to_sp(root); /* * PAE roots (somewhat arbitrarily) aren't backed by shadow pages, the @@ -4048,11 +4005,12 @@ void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu) if (vcpu->arch.mmu->cpu_role.base.level >= PT64_ROOT_4LEVEL) { hpa_t root = vcpu->arch.mmu->root.hpa; - sp = to_shadow_page(root); if (!is_unsync_root(root)) return; + sp = root_to_sp(root); + write_lock(&vcpu->kvm->mmu_lock); mmu_sync_children(vcpu, sp, true); write_unlock(&vcpu->kvm->mmu_lock); @@ -4194,7 +4152,7 @@ static int handle_mmio_page_fault(struct kvm_vcpu *vcpu, u64 addr, bool direct) return RET_PF_EMULATE; reserved = get_mmio_spte(vcpu, addr, &spte); - if (WARN_ON(reserved)) + if (WARN_ON_ONCE(reserved)) return -EINVAL; if (is_mmio_spte(spte)) { @@ -4232,7 +4190,7 @@ static bool page_fault_handle_page_track(struct kvm_vcpu *vcpu, * guest is writing the page which is write tracked which can * not be fixed by page fault handler. */ - if (kvm_slot_page_track_is_active(vcpu->kvm, fault->slot, fault->gfn, KVM_PAGE_TRACK_WRITE)) + if (kvm_gfn_is_write_tracked(vcpu->kvm, fault->slot, fault->gfn)) return true; return false; @@ -4382,7 +4340,7 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, static bool is_page_fault_stale(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { - struct kvm_mmu_page *sp = to_shadow_page(vcpu->arch.mmu->root.hpa); + struct kvm_mmu_page *sp = root_to_sp(vcpu->arch.mmu->root.hpa); /* Special roots, e.g. pae_root, are not backed by shadow pages. */ if (sp && is_obsolete_sp(vcpu->kvm, sp)) @@ -4407,6 +4365,10 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault { int r; + /* Dummy roots are used only for shadowing bad guest roots. */ + if (WARN_ON_ONCE(kvm_mmu_is_dummy_root(vcpu->arch.mmu->root.hpa))) + return RET_PF_RETRY; + if (page_fault_handle_page_track(vcpu, fault)) return RET_PF_EMULATE; @@ -4443,8 +4405,6 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault static int nonpaging_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { - pgprintk("%s: gva %lx error %x\n", __func__, fault->addr, fault->error_code); - /* This path builds a PAE pagetable, we can map 2mb pages at maximum. */ fault->max_level = PG_LEVEL_2M; return direct_page_fault(vcpu, fault); @@ -4562,9 +4522,19 @@ static void nonpaging_init_context(struct kvm_mmu *context) static inline bool is_root_usable(struct kvm_mmu_root_info *root, gpa_t pgd, union kvm_mmu_page_role role) { - return (role.direct || pgd == root->pgd) && - VALID_PAGE(root->hpa) && - role.word == to_shadow_page(root->hpa)->role.word; + struct kvm_mmu_page *sp; + + if (!VALID_PAGE(root->hpa)) + return false; + + if (!role.direct && pgd != root->pgd) + return false; + + sp = root_to_sp(root->hpa); + if (WARN_ON_ONCE(!sp)) + return false; + + return role.word == sp->role.word; } /* @@ -4634,11 +4604,10 @@ static bool fast_pgd_switch(struct kvm *kvm, struct kvm_mmu *mmu, gpa_t new_pgd, union kvm_mmu_page_role new_role) { /* - * For now, limit the caching to 64-bit hosts+VMs in order to avoid - * having to deal with PDPTEs. We may add support for 32-bit hosts/VMs - * later if necessary. + * Limit reuse to 64-bit hosts+VMs without "special" roots in order to + * avoid having to deal with PDPTEs and other complexities. */ - if (VALID_PAGE(mmu->root.hpa) && !to_shadow_page(mmu->root.hpa)) + if (VALID_PAGE(mmu->root.hpa) && !root_to_sp(mmu->root.hpa)) kvm_mmu_free_roots(kvm, mmu, KVM_MMU_ROOT_CURRENT); if (VALID_PAGE(mmu->root.hpa)) @@ -4684,9 +4653,12 @@ void kvm_mmu_new_pgd(struct kvm_vcpu *vcpu, gpa_t new_pgd) * If this is a direct root page, it doesn't have a write flooding * count. Otherwise, clear the write flooding count. */ - if (!new_role.direct) - __clear_sp_write_flooding_count( - to_shadow_page(vcpu->arch.mmu->root.hpa)); + if (!new_role.direct) { + struct kvm_mmu_page *sp = root_to_sp(vcpu->arch.mmu->root.hpa); + + if (!WARN_ON_ONCE(!sp)) + __clear_sp_write_flooding_count(sp); + } } EXPORT_SYMBOL_GPL(kvm_mmu_new_pgd); @@ -4808,28 +4780,13 @@ static void __reset_rsvds_bits_mask(struct rsvd_bits_validate *rsvd_check, } } -static bool guest_can_use_gbpages(struct kvm_vcpu *vcpu) -{ - /* - * If TDP is enabled, let the guest use GBPAGES if they're supported in - * hardware. The hardware page walker doesn't let KVM disable GBPAGES, - * i.e. won't treat them as reserved, and KVM doesn't redo the GVA->GPA - * walk for performance and complexity reasons. Not to mention KVM - * _can't_ solve the problem because GVA->GPA walks aren't visible to - * KVM once a TDP translation is installed. Mimic hardware behavior so - * that KVM's is at least consistent, i.e. doesn't randomly inject #PF. - */ - return tdp_enabled ? boot_cpu_has(X86_FEATURE_GBPAGES) : - guest_cpuid_has(vcpu, X86_FEATURE_GBPAGES); -} - static void reset_guest_rsvds_bits_mask(struct kvm_vcpu *vcpu, struct kvm_mmu *context) { __reset_rsvds_bits_mask(&context->guest_rsvd_check, vcpu->arch.reserved_gpa_bits, context->cpu_role.base.level, is_efer_nx(context), - guest_can_use_gbpages(vcpu), + guest_can_use(vcpu, X86_FEATURE_GBPAGES), is_cr4_pse(context), guest_cpuid_is_amd_or_hygon(vcpu)); } @@ -4906,7 +4863,8 @@ static void reset_shadow_zero_bits_mask(struct kvm_vcpu *vcpu, __reset_rsvds_bits_mask(shadow_zero_check, reserved_hpa_bits(), context->root_role.level, context->root_role.efer_nx, - guest_can_use_gbpages(vcpu), is_pse, is_amd); + guest_can_use(vcpu, X86_FEATURE_GBPAGES), + is_pse, is_amd); if (!shadow_me_mask) return; @@ -5467,8 +5425,8 @@ void kvm_mmu_after_set_cpuid(struct kvm_vcpu *vcpu) * physical address properties) in a single VM would require tracking * all relevant CPUID information in kvm_mmu_page_role. That is very * undesirable as it would increase the memory requirements for - * gfn_track (see struct kvm_mmu_page_role comments). For now that - * problem is swept under the rug; KVM's CPUID API is horrific and + * gfn_write_track (see struct kvm_mmu_page_role comments). For now + * that problem is swept under the rug; KVM's CPUID API is horrific and * it's all but impossible to solve it without introducing a new API. */ vcpu->arch.root_mmu.root_role.word = 0; @@ -5531,9 +5489,9 @@ void kvm_mmu_unload(struct kvm_vcpu *vcpu) struct kvm *kvm = vcpu->kvm; kvm_mmu_free_roots(kvm, &vcpu->arch.root_mmu, KVM_MMU_ROOTS_ALL); - WARN_ON(VALID_PAGE(vcpu->arch.root_mmu.root.hpa)); + WARN_ON_ONCE(VALID_PAGE(vcpu->arch.root_mmu.root.hpa)); kvm_mmu_free_roots(kvm, &vcpu->arch.guest_mmu, KVM_MMU_ROOTS_ALL); - WARN_ON(VALID_PAGE(vcpu->arch.guest_mmu.root.hpa)); + WARN_ON_ONCE(VALID_PAGE(vcpu->arch.guest_mmu.root.hpa)); vcpu_clear_mmio_info(vcpu, MMIO_GVA_ANY); } @@ -5546,16 +5504,21 @@ static bool is_obsolete_root(struct kvm *kvm, hpa_t root_hpa) /* * When freeing obsolete roots, treat roots as obsolete if they don't - * have an associated shadow page. This does mean KVM will get false + * have an associated shadow page, as it's impossible to determine if + * such roots are fresh or stale. This does mean KVM will get false * positives and free roots that don't strictly need to be freed, but * such false positives are relatively rare: * - * (a) only PAE paging and nested NPT has roots without shadow pages + * (a) only PAE paging and nested NPT have roots without shadow pages + * (or any shadow paging flavor with a dummy root, see note below) * (b) remote reloads due to a memslot update obsoletes _all_ roots * (c) KVM doesn't track previous roots for PAE paging, and the guest * is unlikely to zap an in-use PGD. + * + * Note! Dummy roots are unique in that they are obsoleted by memslot + * _creation_! See also FNAME(fetch). */ - sp = to_shadow_page(root_hpa); + sp = root_to_sp(root_hpa); return !sp || is_obsolete_sp(kvm, sp); } @@ -5634,9 +5597,6 @@ static bool detect_write_misaligned(struct kvm_mmu_page *sp, gpa_t gpa, { unsigned offset, pte_size, misaligned; - pgprintk("misaligned: gpa %llx bytes %d role %x\n", - gpa, bytes, sp->role.word); - offset = offset_in_page(gpa); pte_size = sp->role.has_4_byte_gpte ? 4 : 8; @@ -5684,9 +5644,8 @@ static u64 *get_written_sptes(struct kvm_mmu_page *sp, gpa_t gpa, int *nspte) return spte; } -static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, - const u8 *new, int bytes, - struct kvm_page_track_notifier_node *node) +void kvm_mmu_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, + int bytes) { gfn_t gfn = gpa >> PAGE_SHIFT; struct kvm_mmu_page *sp; @@ -5702,8 +5661,6 @@ static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, if (!READ_ONCE(vcpu->kvm->arch.indirect_shadow_pages)) return; - pgprintk("%s: gpa %llx bytes %d\n", __func__, gpa, bytes); - write_lock(&vcpu->kvm->mmu_lock); gentry = mmu_pte_write_fetch_gpte(vcpu, &gpa, &bytes); @@ -5742,7 +5699,18 @@ int noinline kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 err int r, emulation_type = EMULTYPE_PF; bool direct = vcpu->arch.mmu->root_role.direct; - if (WARN_ON(!VALID_PAGE(vcpu->arch.mmu->root.hpa))) + /* + * IMPLICIT_ACCESS is a KVM-defined flag used to correctly perform SMAP + * checks when emulating instructions that triggers implicit access. + * WARN if hardware generates a fault with an error code that collides + * with the KVM-defined value. Clear the flag and continue on, i.e. + * don't terminate the VM, as KVM can't possibly be relying on a flag + * that KVM doesn't know about. + */ + if (WARN_ON_ONCE(error_code & PFERR_IMPLICIT_ACCESS)) + error_code &= ~PFERR_IMPLICIT_ACCESS; + + if (WARN_ON_ONCE(!VALID_PAGE(vcpu->arch.mmu->root.hpa))) return RET_PF_RETRY; r = RET_PF_INVALID; @@ -6099,7 +6067,7 @@ static void kvm_zap_obsolete_pages(struct kvm *kvm) * pages. Skip the bogus page, otherwise we'll get stuck in an * infinite loop if the page gets put back on the list (again). */ - if (WARN_ON(sp->role.invalid)) + if (WARN_ON_ONCE(sp->role.invalid)) continue; /* @@ -6199,32 +6167,15 @@ static bool kvm_has_zapped_obsolete_pages(struct kvm *kvm) return unlikely(!list_empty_careful(&kvm->arch.zapped_obsolete_pages)); } -static void kvm_mmu_invalidate_zap_pages_in_memslot(struct kvm *kvm, - struct kvm_memory_slot *slot, - struct kvm_page_track_notifier_node *node) +void kvm_mmu_init_vm(struct kvm *kvm) { - kvm_mmu_zap_all_fast(kvm); -} - -int kvm_mmu_init_vm(struct kvm *kvm) -{ - struct kvm_page_track_notifier_node *node = &kvm->arch.mmu_sp_tracker; - int r; - INIT_LIST_HEAD(&kvm->arch.active_mmu_pages); INIT_LIST_HEAD(&kvm->arch.zapped_obsolete_pages); INIT_LIST_HEAD(&kvm->arch.possible_nx_huge_pages); spin_lock_init(&kvm->arch.mmu_unsync_pages_lock); - if (tdp_mmu_enabled) { - r = kvm_mmu_init_tdp_mmu(kvm); - if (r < 0) - return r; - } - - node->track_write = kvm_mmu_pte_write; - node->track_flush_slot = kvm_mmu_invalidate_zap_pages_in_memslot; - kvm_page_track_register_notifier(kvm, node); + if (tdp_mmu_enabled) + kvm_mmu_init_tdp_mmu(kvm); kvm->arch.split_page_header_cache.kmem_cache = mmu_page_header_cache; kvm->arch.split_page_header_cache.gfp_zero = __GFP_ZERO; @@ -6233,8 +6184,6 @@ int kvm_mmu_init_vm(struct kvm *kvm) kvm->arch.split_desc_cache.kmem_cache = pte_list_desc_cache; kvm->arch.split_desc_cache.gfp_zero = __GFP_ZERO; - - return 0; } static void mmu_free_vm_memory_caches(struct kvm *kvm) @@ -6246,10 +6195,6 @@ static void mmu_free_vm_memory_caches(struct kvm *kvm) void kvm_mmu_uninit_vm(struct kvm *kvm) { - struct kvm_page_track_notifier_node *node = &kvm->arch.mmu_sp_tracker; - - kvm_page_track_unregister_notifier(kvm, node); - if (tdp_mmu_enabled) kvm_mmu_uninit_tdp_mmu(kvm); @@ -6294,7 +6239,6 @@ static bool kvm_rmap_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_e void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) { bool flush; - int i; if (WARN_ON_ONCE(gfn_end <= gfn_start)) return; @@ -6305,11 +6249,8 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) flush = kvm_rmap_zap_gfn_range(kvm, gfn_start, gfn_end); - if (tdp_mmu_enabled) { - for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) - flush = kvm_tdp_mmu_zap_leafs(kvm, i, gfn_start, - gfn_end, true, flush); - } + if (tdp_mmu_enabled) + flush = kvm_tdp_mmu_zap_leafs(kvm, gfn_start, gfn_end, flush); if (flush) kvm_flush_remote_tlbs_range(kvm, gfn_start, gfn_end - gfn_start); @@ -6670,7 +6611,7 @@ static void kvm_rmap_zap_collapsible_sptes(struct kvm *kvm, */ if (walk_slot_rmaps(kvm, slot, kvm_mmu_zap_collapsible_spte, PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL - 1, true)) - kvm_arch_flush_remote_tlbs_memslot(kvm, slot); + kvm_flush_remote_tlbs_memslot(kvm, slot); } void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, @@ -6689,20 +6630,6 @@ void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, } } -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, - const struct kvm_memory_slot *memslot) -{ - /* - * All current use cases for flushing the TLBs for a specific memslot - * related to dirty logging, and many do the TLB flush out of mmu_lock. - * The interaction between the various operations on memslot must be - * serialized by slots_locks to ensure the TLB flush from one operation - * is observed by any other operation on the same memslot. - */ - lockdep_assert_held(&kvm->slots_lock); - kvm_flush_remote_tlbs_range(kvm, memslot->base_gfn, memslot->npages); -} - void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm, const struct kvm_memory_slot *memslot) { @@ -6732,7 +6659,7 @@ void kvm_mmu_slot_leaf_clear_dirty(struct kvm *kvm, */ } -void kvm_mmu_zap_all(struct kvm *kvm) +static void kvm_mmu_zap_all(struct kvm *kvm) { struct kvm_mmu_page *sp, *node; LIST_HEAD(invalid_list); @@ -6741,7 +6668,7 @@ void kvm_mmu_zap_all(struct kvm *kvm) write_lock(&kvm->mmu_lock); restart: list_for_each_entry_safe(sp, node, &kvm->arch.active_mmu_pages, link) { - if (WARN_ON(sp->role.invalid)) + if (WARN_ON_ONCE(sp->role.invalid)) continue; if (__kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list, &ign)) goto restart; @@ -6757,9 +6684,20 @@ void kvm_mmu_zap_all(struct kvm *kvm) write_unlock(&kvm->mmu_lock); } +void kvm_arch_flush_shadow_all(struct kvm *kvm) +{ + kvm_mmu_zap_all(kvm); +} + +void kvm_arch_flush_shadow_memslot(struct kvm *kvm, + struct kvm_memory_slot *slot) +{ + kvm_mmu_zap_all_fast(kvm); +} + void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen) { - WARN_ON(gen & KVM_MEMSLOT_GEN_UPDATE_IN_PROGRESS); + WARN_ON_ONCE(gen & KVM_MEMSLOT_GEN_UPDATE_IN_PROGRESS); gen &= MMIO_SPTE_GEN_MASK; @@ -6862,7 +6800,7 @@ static void mmu_destroy_caches(void) static int get_nx_huge_pages(char *buffer, const struct kernel_param *kp) { if (nx_hugepage_mitigation_hard_disabled) - return sprintf(buffer, "never\n"); + return sysfs_emit(buffer, "never\n"); return param_get_bool(buffer, kp); } diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index d39af5639ce97a6a31781003a6636edb16569ec2..decc1f1536694f31529f3d8c2c7cf67d1185a8c0 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -6,18 +6,10 @@ #include #include -#undef MMU_DEBUG - -#ifdef MMU_DEBUG -extern bool dbg; - -#define pgprintk(x...) do { if (dbg) printk(x); } while (0) -#define rmap_printk(fmt, args...) do { if (dbg) printk("%s: " fmt, __func__, ## args); } while (0) -#define MMU_WARN_ON(x) WARN_ON(x) +#ifdef CONFIG_KVM_PROVE_MMU +#define KVM_MMU_WARN_ON(x) WARN_ON_ONCE(x) #else -#define pgprintk(x...) do { } while (0) -#define rmap_printk(x...) do { } while (0) -#define MMU_WARN_ON(x) do { } while (0) +#define KVM_MMU_WARN_ON(x) BUILD_BUG_ON_INVALID(x) #endif /* Page table builder macros common to shadow (host) PTEs and guest PTEs. */ @@ -44,6 +36,16 @@ extern bool dbg; #define INVALID_PAE_ROOT 0 #define IS_VALID_PAE_ROOT(x) (!!(x)) +static inline hpa_t kvm_mmu_get_dummy_root(void) +{ + return my_zero_pfn(0) << PAGE_SHIFT; +} + +static inline bool kvm_mmu_is_dummy_root(hpa_t shadow_page) +{ + return is_zero_pfn(shadow_page >> PAGE_SHIFT); +} + typedef u64 __rcu *tdp_ptep_t; struct kvm_mmu_page { @@ -56,7 +58,12 @@ struct kvm_mmu_page { bool tdp_mmu_page; bool unsync; - u8 mmu_valid_gen; + union { + u8 mmu_valid_gen; + + /* Only accessed under slots_lock. */ + bool tdp_mmu_scheduled_root_to_zap; + }; /* * The shadow page can't be replaced by an equivalent huge page @@ -98,13 +105,7 @@ struct kvm_mmu_page { struct kvm_rmap_head parent_ptes; /* rmap pointers to parent sptes */ tdp_ptep_t ptep; }; - union { - DECLARE_BITMAP(unsync_child_bitmap, 512); - struct { - struct work_struct tdp_mmu_async_work; - void *tdp_mmu_async_data; - }; - }; + DECLARE_BITMAP(unsync_child_bitmap, 512); /* * Tracks shadow pages that, if zapped, would allow KVM to create an NX @@ -170,9 +171,6 @@ bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm, struct kvm_memory_slot *slot, u64 gfn, int min_level); -void kvm_flush_remote_tlbs_range(struct kvm *kvm, gfn_t start_gfn, - gfn_t nr_pages); - /* Flush the given page (huge or not) of guest memory. */ static inline void kvm_flush_remote_tlbs_gfn(struct kvm *kvm, gfn_t gfn, int level) { diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index 0a2ac438d6471fe05acf646707ba02ce2b62751f..c87da11f3a049b75e2dc6cae714b37e868666252 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -12,13 +12,13 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include -#include - #include "mmu.h" #include "mmu_internal.h" +#include "page_track.h" bool kvm_page_track_write_tracking_enabled(struct kvm *kvm) { @@ -28,103 +28,64 @@ bool kvm_page_track_write_tracking_enabled(struct kvm *kvm) void kvm_page_track_free_memslot(struct kvm_memory_slot *slot) { - int i; - - for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) { - kvfree(slot->arch.gfn_track[i]); - slot->arch.gfn_track[i] = NULL; - } + kvfree(slot->arch.gfn_write_track); + slot->arch.gfn_write_track = NULL; } -int kvm_page_track_create_memslot(struct kvm *kvm, - struct kvm_memory_slot *slot, - unsigned long npages) +static int __kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot, + unsigned long npages) { - int i; - - for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) { - if (i == KVM_PAGE_TRACK_WRITE && - !kvm_page_track_write_tracking_enabled(kvm)) - continue; - - slot->arch.gfn_track[i] = - __vcalloc(npages, sizeof(*slot->arch.gfn_track[i]), - GFP_KERNEL_ACCOUNT); - if (!slot->arch.gfn_track[i]) - goto track_free; - } + const size_t size = sizeof(*slot->arch.gfn_write_track); - return 0; + if (!slot->arch.gfn_write_track) + slot->arch.gfn_write_track = __vcalloc(npages, size, + GFP_KERNEL_ACCOUNT); -track_free: - kvm_page_track_free_memslot(slot); - return -ENOMEM; + return slot->arch.gfn_write_track ? 0 : -ENOMEM; } -static inline bool page_track_mode_is_valid(enum kvm_page_track_mode mode) +int kvm_page_track_create_memslot(struct kvm *kvm, + struct kvm_memory_slot *slot, + unsigned long npages) { - if (mode < 0 || mode >= KVM_PAGE_TRACK_MAX) - return false; + if (!kvm_page_track_write_tracking_enabled(kvm)) + return 0; - return true; + return __kvm_page_track_write_tracking_alloc(slot, npages); } int kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot) { - unsigned short *gfn_track; - - if (slot->arch.gfn_track[KVM_PAGE_TRACK_WRITE]) - return 0; - - gfn_track = __vcalloc(slot->npages, sizeof(*gfn_track), - GFP_KERNEL_ACCOUNT); - if (gfn_track == NULL) - return -ENOMEM; - - slot->arch.gfn_track[KVM_PAGE_TRACK_WRITE] = gfn_track; - return 0; + return __kvm_page_track_write_tracking_alloc(slot, slot->npages); } -static void update_gfn_track(struct kvm_memory_slot *slot, gfn_t gfn, - enum kvm_page_track_mode mode, short count) +static void update_gfn_write_track(struct kvm_memory_slot *slot, gfn_t gfn, + short count) { int index, val; index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); - val = slot->arch.gfn_track[mode][index]; + val = slot->arch.gfn_write_track[index]; - if (WARN_ON(val + count < 0 || val + count > USHRT_MAX)) + if (WARN_ON_ONCE(val + count < 0 || val + count > USHRT_MAX)) return; - slot->arch.gfn_track[mode][index] += count; + slot->arch.gfn_write_track[index] += count; } -/* - * add guest page to the tracking pool so that corresponding access on that - * page will be intercepted. - * - * It should be called under the protection both of mmu-lock and kvm->srcu - * or kvm->slots_lock. - * - * @kvm: the guest instance we are interested in. - * @slot: the @gfn belongs to. - * @gfn: the guest page. - * @mode: tracking mode, currently only write track is supported. - */ -void kvm_slot_page_track_add_page(struct kvm *kvm, - struct kvm_memory_slot *slot, gfn_t gfn, - enum kvm_page_track_mode mode) +void __kvm_write_track_add_gfn(struct kvm *kvm, struct kvm_memory_slot *slot, + gfn_t gfn) { + lockdep_assert_held_write(&kvm->mmu_lock); - if (WARN_ON(!page_track_mode_is_valid(mode))) - return; + lockdep_assert_once(lockdep_is_held(&kvm->slots_lock) || + srcu_read_lock_held(&kvm->srcu)); - if (WARN_ON(mode == KVM_PAGE_TRACK_WRITE && - !kvm_page_track_write_tracking_enabled(kvm))) + if (KVM_BUG_ON(!kvm_page_track_write_tracking_enabled(kvm), kvm)) return; - update_gfn_track(slot, gfn, mode, 1); + update_gfn_write_track(slot, gfn, 1); /* * new track stops large page mapping for the @@ -132,37 +93,22 @@ void kvm_slot_page_track_add_page(struct kvm *kvm, */ kvm_mmu_gfn_disallow_lpage(slot, gfn); - if (mode == KVM_PAGE_TRACK_WRITE) - if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K)) - kvm_flush_remote_tlbs(kvm); + if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K)) + kvm_flush_remote_tlbs(kvm); } -EXPORT_SYMBOL_GPL(kvm_slot_page_track_add_page); -/* - * remove the guest page from the tracking pool which stops the interception - * of corresponding access on that page. It is the opposed operation of - * kvm_slot_page_track_add_page(). - * - * It should be called under the protection both of mmu-lock and kvm->srcu - * or kvm->slots_lock. - * - * @kvm: the guest instance we are interested in. - * @slot: the @gfn belongs to. - * @gfn: the guest page. - * @mode: tracking mode, currently only write track is supported. - */ -void kvm_slot_page_track_remove_page(struct kvm *kvm, - struct kvm_memory_slot *slot, gfn_t gfn, - enum kvm_page_track_mode mode) +void __kvm_write_track_remove_gfn(struct kvm *kvm, + struct kvm_memory_slot *slot, gfn_t gfn) { - if (WARN_ON(!page_track_mode_is_valid(mode))) - return; + lockdep_assert_held_write(&kvm->mmu_lock); - if (WARN_ON(mode == KVM_PAGE_TRACK_WRITE && - !kvm_page_track_write_tracking_enabled(kvm))) + lockdep_assert_once(lockdep_is_held(&kvm->slots_lock) || + srcu_read_lock_held(&kvm->srcu)); + + if (KVM_BUG_ON(!kvm_page_track_write_tracking_enabled(kvm), kvm)) return; - update_gfn_track(slot, gfn, mode, -1); + update_gfn_write_track(slot, gfn, -1); /* * allow large page mapping for the tracked page @@ -170,31 +116,26 @@ void kvm_slot_page_track_remove_page(struct kvm *kvm, */ kvm_mmu_gfn_allow_lpage(slot, gfn); } -EXPORT_SYMBOL_GPL(kvm_slot_page_track_remove_page); /* * check if the corresponding access on the specified guest page is tracked. */ -bool kvm_slot_page_track_is_active(struct kvm *kvm, - const struct kvm_memory_slot *slot, - gfn_t gfn, enum kvm_page_track_mode mode) +bool kvm_gfn_is_write_tracked(struct kvm *kvm, + const struct kvm_memory_slot *slot, gfn_t gfn) { int index; - if (WARN_ON(!page_track_mode_is_valid(mode))) - return false; - if (!slot) return false; - if (mode == KVM_PAGE_TRACK_WRITE && - !kvm_page_track_write_tracking_enabled(kvm)) + if (!kvm_page_track_write_tracking_enabled(kvm)) return false; index = gfn_to_index(gfn, slot->base_gfn, PG_LEVEL_4K); - return !!READ_ONCE(slot->arch.gfn_track[mode][index]); + return !!READ_ONCE(slot->arch.gfn_write_track[index]); } +#ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING void kvm_page_track_cleanup(struct kvm *kvm) { struct kvm_page_track_notifier_head *head; @@ -216,17 +157,22 @@ int kvm_page_track_init(struct kvm *kvm) * register the notifier so that event interception for the tracked guest * pages can be received. */ -void -kvm_page_track_register_notifier(struct kvm *kvm, - struct kvm_page_track_notifier_node *n) +int kvm_page_track_register_notifier(struct kvm *kvm, + struct kvm_page_track_notifier_node *n) { struct kvm_page_track_notifier_head *head; + if (!kvm || kvm->mm != current->mm) + return -ESRCH; + + kvm_get_kvm(kvm); + head = &kvm->arch.track_notifier_head; write_lock(&kvm->mmu_lock); hlist_add_head_rcu(&n->node, &head->track_notifier_list); write_unlock(&kvm->mmu_lock); + return 0; } EXPORT_SYMBOL_GPL(kvm_page_track_register_notifier); @@ -234,9 +180,8 @@ EXPORT_SYMBOL_GPL(kvm_page_track_register_notifier); * stop receiving the event interception. It is the opposed operation of * kvm_page_track_register_notifier(). */ -void -kvm_page_track_unregister_notifier(struct kvm *kvm, - struct kvm_page_track_notifier_node *n) +void kvm_page_track_unregister_notifier(struct kvm *kvm, + struct kvm_page_track_notifier_node *n) { struct kvm_page_track_notifier_head *head; @@ -246,6 +191,8 @@ kvm_page_track_unregister_notifier(struct kvm *kvm, hlist_del_rcu(&n->node); write_unlock(&kvm->mmu_lock); synchronize_srcu(&head->track_srcu); + + kvm_put_kvm(kvm); } EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier); @@ -256,34 +203,30 @@ EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier); * The node should figure out if the written page is the one that node is * interested in by itself. */ -void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, - int bytes) +void __kvm_page_track_write(struct kvm *kvm, gpa_t gpa, const u8 *new, int bytes) { struct kvm_page_track_notifier_head *head; struct kvm_page_track_notifier_node *n; int idx; - head = &vcpu->kvm->arch.track_notifier_head; + head = &kvm->arch.track_notifier_head; if (hlist_empty(&head->track_notifier_list)) return; idx = srcu_read_lock(&head->track_srcu); hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, - srcu_read_lock_held(&head->track_srcu)) + srcu_read_lock_held(&head->track_srcu)) if (n->track_write) - n->track_write(vcpu, gpa, new, bytes, n); + n->track_write(gpa, new, bytes, n); srcu_read_unlock(&head->track_srcu, idx); } /* - * Notify the node that memory slot is being removed or moved so that it can - * drop write-protection for the pages in the memory slot. - * - * The node should figure out it has any write-protected pages in this slot - * by itself. + * Notify external page track nodes that a memory region is being removed from + * the VM, e.g. so that users can free any associated metadata. */ -void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot) +void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot) { struct kvm_page_track_notifier_head *head; struct kvm_page_track_notifier_node *n; @@ -296,8 +239,69 @@ void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot) idx = srcu_read_lock(&head->track_srcu); hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, - srcu_read_lock_held(&head->track_srcu)) - if (n->track_flush_slot) - n->track_flush_slot(kvm, slot, n); + srcu_read_lock_held(&head->track_srcu)) + if (n->track_remove_region) + n->track_remove_region(slot->base_gfn, slot->npages, n); srcu_read_unlock(&head->track_srcu, idx); } + +/* + * add guest page to the tracking pool so that corresponding access on that + * page will be intercepted. + * + * @kvm: the guest instance we are interested in. + * @gfn: the guest page. + */ +int kvm_write_track_add_gfn(struct kvm *kvm, gfn_t gfn) +{ + struct kvm_memory_slot *slot; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + + slot = gfn_to_memslot(kvm, gfn); + if (!slot) { + srcu_read_unlock(&kvm->srcu, idx); + return -EINVAL; + } + + write_lock(&kvm->mmu_lock); + __kvm_write_track_add_gfn(kvm, slot, gfn); + write_unlock(&kvm->mmu_lock); + + srcu_read_unlock(&kvm->srcu, idx); + + return 0; +} +EXPORT_SYMBOL_GPL(kvm_write_track_add_gfn); + +/* + * remove the guest page from the tracking pool which stops the interception + * of corresponding access on that page. + * + * @kvm: the guest instance we are interested in. + * @gfn: the guest page. + */ +int kvm_write_track_remove_gfn(struct kvm *kvm, gfn_t gfn) +{ + struct kvm_memory_slot *slot; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + + slot = gfn_to_memslot(kvm, gfn); + if (!slot) { + srcu_read_unlock(&kvm->srcu, idx); + return -EINVAL; + } + + write_lock(&kvm->mmu_lock); + __kvm_write_track_remove_gfn(kvm, slot, gfn); + write_unlock(&kvm->mmu_lock); + + srcu_read_unlock(&kvm->srcu, idx); + + return 0; +} +EXPORT_SYMBOL_GPL(kvm_write_track_remove_gfn); +#endif diff --git a/arch/x86/kvm/mmu/page_track.h b/arch/x86/kvm/mmu/page_track.h new file mode 100644 index 0000000000000000000000000000000000000000..d4d72ed999b1dbbf4b3384c127adf1258e689e48 --- /dev/null +++ b/arch/x86/kvm/mmu/page_track.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __KVM_X86_PAGE_TRACK_H +#define __KVM_X86_PAGE_TRACK_H + +#include + +#include + + +bool kvm_page_track_write_tracking_enabled(struct kvm *kvm); +int kvm_page_track_write_tracking_alloc(struct kvm_memory_slot *slot); + +void kvm_page_track_free_memslot(struct kvm_memory_slot *slot); +int kvm_page_track_create_memslot(struct kvm *kvm, + struct kvm_memory_slot *slot, + unsigned long npages); + +void __kvm_write_track_add_gfn(struct kvm *kvm, struct kvm_memory_slot *slot, + gfn_t gfn); +void __kvm_write_track_remove_gfn(struct kvm *kvm, + struct kvm_memory_slot *slot, gfn_t gfn); + +bool kvm_gfn_is_write_tracked(struct kvm *kvm, + const struct kvm_memory_slot *slot, gfn_t gfn); + +#ifdef CONFIG_KVM_EXTERNAL_WRITE_TRACKING +int kvm_page_track_init(struct kvm *kvm); +void kvm_page_track_cleanup(struct kvm *kvm); + +void __kvm_page_track_write(struct kvm *kvm, gpa_t gpa, const u8 *new, int bytes); +void kvm_page_track_delete_slot(struct kvm *kvm, struct kvm_memory_slot *slot); + +static inline bool kvm_page_track_has_external_user(struct kvm *kvm) +{ + return !hlist_empty(&kvm->arch.track_notifier_head.track_notifier_list); +} +#else +static inline int kvm_page_track_init(struct kvm *kvm) { return 0; } +static inline void kvm_page_track_cleanup(struct kvm *kvm) { } + +static inline void __kvm_page_track_write(struct kvm *kvm, gpa_t gpa, + const u8 *new, int bytes) { } +static inline void kvm_page_track_delete_slot(struct kvm *kvm, + struct kvm_memory_slot *slot) { } + +static inline bool kvm_page_track_has_external_user(struct kvm *kvm) { return false; } + +#endif /* CONFIG_KVM_EXTERNAL_WRITE_TRACKING */ + +static inline void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, + const u8 *new, int bytes) +{ + __kvm_page_track_write(vcpu->kvm, gpa, new, bytes); + + kvm_mmu_track_write(vcpu, gpa, new, bytes); +} + +#endif /* __KVM_X86_PAGE_TRACK_H */ diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 0662e0278e706c742e3468c0bbdec9823766a8da..c85255073f67231f0d25417232c99c9eeceb8f7b 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -338,7 +338,6 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, } #endif walker->max_level = walker->level; - ASSERT(!(is_long_mode(vcpu) && !is_pae(vcpu))); /* * FIXME: on Intel processors, loads of the PDPTE registers for PAE paging @@ -348,9 +347,21 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, nested_access = (have_ad ? PFERR_WRITE_MASK : 0) | PFERR_USER_MASK; pte_access = ~0; + + /* + * Queue a page fault for injection if this assertion fails, as callers + * assume that walker.fault contains sane info on a walk failure. I.e. + * avoid making the situation worse by inducing even worse badness + * between when the assertion fails and when KVM kicks the vCPU out to + * userspace (because the VM is bugged). + */ + if (KVM_BUG_ON(is_long_mode(vcpu) && !is_pae(vcpu), vcpu->kvm)) + goto error; + ++walker->level; do { + struct kvm_memory_slot *slot; unsigned long host_addr; pt_access = pte_access; @@ -381,7 +392,11 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, if (unlikely(real_gpa == INVALID_GPA)) return 0; - host_addr = kvm_vcpu_gfn_to_hva_prot(vcpu, gpa_to_gfn(real_gpa), + slot = kvm_vcpu_gfn_to_memslot(vcpu, gpa_to_gfn(real_gpa)); + if (!kvm_is_visible_memslot(slot)) + goto error; + + host_addr = gfn_to_hva_memslot_prot(slot, gpa_to_gfn(real_gpa), &walker->pte_writable[walker->level - 1]); if (unlikely(kvm_is_error_hva(host_addr))) goto error; @@ -456,9 +471,6 @@ static int FNAME(walk_addr_generic)(struct guest_walker *walker, goto retry_walk; } - pgprintk("%s: pte %llx pte_access %x pt_access %x\n", - __func__, (u64)pte, walker->pte_access, - walker->pt_access[walker->level - 1]); return 1; error: @@ -529,8 +541,6 @@ FNAME(prefetch_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, if (FNAME(prefetch_invalid_gpte)(vcpu, sp, spte, gpte)) return false; - pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte); - gfn = gpte_to_gfn(gpte); pte_access = sp->role.access & FNAME(gpte_access)(gpte); FNAME(protect_clean_gpte)(vcpu->arch.mmu, &pte_access, gpte); @@ -638,8 +648,19 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, if (FNAME(gpte_changed)(vcpu, gw, top_level)) goto out_gpte_changed; - if (WARN_ON(!VALID_PAGE(vcpu->arch.mmu->root.hpa))) + if (WARN_ON_ONCE(!VALID_PAGE(vcpu->arch.mmu->root.hpa))) + goto out_gpte_changed; + + /* + * Load a new root and retry the faulting instruction in the extremely + * unlikely scenario that the guest root gfn became visible between + * loading a dummy root and handling the resulting page fault, e.g. if + * userspace create a memslot in the interim. + */ + if (unlikely(kvm_mmu_is_dummy_root(vcpu->arch.mmu->root.hpa))) { + kvm_make_request(KVM_REQ_MMU_FREE_OBSOLETE_ROOTS, vcpu); goto out_gpte_changed; + } for_each_shadow_entry(vcpu, fault->addr, it) { gfn_t table_gfn; @@ -758,7 +779,6 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault struct guest_walker walker; int r; - pgprintk("%s: addr %lx err %x\n", __func__, fault->addr, fault->error_code); WARN_ON_ONCE(fault->is_tdp); /* @@ -773,7 +793,6 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault * The page is not mapped by the guest. Let the guest handle it. */ if (!r) { - pgprintk("%s: guest page fault\n", __func__); if (!fault->prefetch) kvm_inject_emulated_page_fault(vcpu, &walker.fault); @@ -837,7 +856,7 @@ static gpa_t FNAME(get_level1_sp_gpa)(struct kvm_mmu_page *sp) { int offset = 0; - WARN_ON(sp->role.level != PG_LEVEL_4K); + WARN_ON_ONCE(sp->role.level != PG_LEVEL_4K); if (PTTYPE == 32) offset = sp->role.quadrant << SPTE_LEVEL_BITS; diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index cf2c6426a6fc36a27c542c0f3e219db45d08aa94..4a599130e9c9948d6beb0a45cfce0cb43b05c51e 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -61,7 +61,7 @@ static u64 generation_mmio_spte_mask(u64 gen) { u64 mask; - WARN_ON(gen & ~MMIO_SPTE_GEN_MASK); + WARN_ON_ONCE(gen & ~MMIO_SPTE_GEN_MASK); mask = (gen << MMIO_SPTE_GEN_LOW_SHIFT) & MMIO_SPTE_GEN_LOW_MASK; mask |= (gen << MMIO_SPTE_GEN_HIGH_SHIFT) & MMIO_SPTE_GEN_HIGH_MASK; @@ -221,8 +221,6 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, * shadow pages and unsync'ing pages is not allowed. */ if (mmu_try_to_unsync_pages(vcpu->kvm, slot, gfn, can_unsync, prefetch)) { - pgprintk("%s: found shadow page for %llx, marking ro\n", - __func__, gfn); wrprot = true; pte_access &= ~ACC_WRITE_MASK; spte &= ~(PT_WRITABLE_MASK | shadow_mmu_writable_mask); @@ -242,7 +240,7 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, if ((spte & PT_WRITABLE_MASK) && kvm_slot_dirty_track_enabled(slot)) { /* Enforced by kvm_mmu_hugepage_adjust. */ - WARN_ON(level > PG_LEVEL_4K); + WARN_ON_ONCE(level > PG_LEVEL_4K); mark_page_dirty_in_slot(vcpu->kvm, slot, gfn); } diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 1279db2eab44c7887f09d40ab2c895bf409122eb..a129951c9a8856eb3b427c75adcaed7e312dfd78 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -3,6 +3,7 @@ #ifndef KVM_X86_MMU_SPTE_H #define KVM_X86_MMU_SPTE_H +#include "mmu.h" #include "mmu_internal.h" /* @@ -236,6 +237,18 @@ static inline struct kvm_mmu_page *sptep_to_sp(u64 *sptep) return to_shadow_page(__pa(sptep)); } +static inline struct kvm_mmu_page *root_to_sp(hpa_t root) +{ + if (kvm_mmu_is_dummy_root(root)) + return NULL; + + /* + * The "root" may be a special root, e.g. a PAE entry, treat it as a + * SPTE to ensure any non-PA bits are dropped. + */ + return spte_to_child_sp(root); +} + static inline bool is_mmio_spte(u64 spte) { return (spte & shadow_mmio_mask) == shadow_mmio_value && @@ -265,13 +278,13 @@ static inline bool sp_ad_disabled(struct kvm_mmu_page *sp) static inline bool spte_ad_enabled(u64 spte) { - MMU_WARN_ON(!is_shadow_present_pte(spte)); + KVM_MMU_WARN_ON(!is_shadow_present_pte(spte)); return (spte & SPTE_TDP_AD_MASK) != SPTE_TDP_AD_DISABLED; } static inline bool spte_ad_need_write_protect(u64 spte) { - MMU_WARN_ON(!is_shadow_present_pte(spte)); + KVM_MMU_WARN_ON(!is_shadow_present_pte(spte)); /* * This is benign for non-TDP SPTEs as SPTE_TDP_AD_ENABLED is '0', * and non-TDP SPTEs will never set these bits. Optimize for 64-bit @@ -282,13 +295,13 @@ static inline bool spte_ad_need_write_protect(u64 spte) static inline u64 spte_shadow_accessed_mask(u64 spte) { - MMU_WARN_ON(!is_shadow_present_pte(spte)); + KVM_MMU_WARN_ON(!is_shadow_present_pte(spte)); return spte_ad_enabled(spte) ? shadow_accessed_mask : 0; } static inline u64 spte_shadow_dirty_mask(u64 spte) { - MMU_WARN_ON(!is_shadow_present_pte(spte)); + KVM_MMU_WARN_ON(!is_shadow_present_pte(spte)); return spte_ad_enabled(spte) ? shadow_dirty_mask : 0; } diff --git a/arch/x86/kvm/mmu/tdp_iter.c b/arch/x86/kvm/mmu/tdp_iter.c index d2eb0d4f87108d047b539841374e416d1a6b22f3..bd30ebfb2f2c9622a26baa37e2586842a0d02006 100644 --- a/arch/x86/kvm/mmu/tdp_iter.c +++ b/arch/x86/kvm/mmu/tdp_iter.c @@ -39,13 +39,14 @@ void tdp_iter_restart(struct tdp_iter *iter) void tdp_iter_start(struct tdp_iter *iter, struct kvm_mmu_page *root, int min_level, gfn_t next_last_level_gfn) { - int root_level = root->role.level; - - WARN_ON(root_level < 1); - WARN_ON(root_level > PT64_ROOT_MAX_LEVEL); + if (WARN_ON_ONCE(!root || (root->role.level < 1) || + (root->role.level > PT64_ROOT_MAX_LEVEL))) { + iter->valid = false; + return; + } iter->next_last_level_gfn = next_last_level_gfn; - iter->root_level = root_level; + iter->root_level = root->role.level; iter->min_level = min_level; iter->pt_path[iter->root_level - 1] = (tdp_ptep_t)root->spt; iter->as_id = kvm_mmu_page_as_id(root); diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 512163d52194b9945ec19e03b90ae590810107ab..6cd4dd631a2fac815be473c62333c940a074c925 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -12,18 +12,10 @@ #include /* Initializes the TDP MMU for the VM, if enabled. */ -int kvm_mmu_init_tdp_mmu(struct kvm *kvm) +void kvm_mmu_init_tdp_mmu(struct kvm *kvm) { - struct workqueue_struct *wq; - - wq = alloc_workqueue("kvm", WQ_UNBOUND|WQ_MEM_RECLAIM|WQ_CPU_INTENSIVE, 0); - if (!wq) - return -ENOMEM; - INIT_LIST_HEAD(&kvm->arch.tdp_mmu_roots); spin_lock_init(&kvm->arch.tdp_mmu_pages_lock); - kvm->arch.tdp_mmu_zap_wq = wq; - return 1; } /* Arbitrarily returns true so that this may be used in if statements. */ @@ -46,20 +38,15 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm) * ultimately frees all roots. */ kvm_tdp_mmu_invalidate_all_roots(kvm); - - /* - * Destroying a workqueue also first flushes the workqueue, i.e. no - * need to invoke kvm_tdp_mmu_zap_invalidated_roots(). - */ - destroy_workqueue(kvm->arch.tdp_mmu_zap_wq); + kvm_tdp_mmu_zap_invalidated_roots(kvm); WARN_ON(atomic64_read(&kvm->arch.tdp_mmu_pages)); WARN_ON(!list_empty(&kvm->arch.tdp_mmu_roots)); /* * Ensure that all the outstanding RCU callbacks to free shadow pages - * can run before the VM is torn down. Work items on tdp_mmu_zap_wq - * can call kvm_tdp_mmu_put_root and create new callbacks. + * can run before the VM is torn down. Putting the last reference to + * zapped roots will create new callbacks. */ rcu_barrier(); } @@ -86,46 +73,6 @@ static void tdp_mmu_free_sp_rcu_callback(struct rcu_head *head) tdp_mmu_free_sp(sp); } -static void tdp_mmu_zap_root(struct kvm *kvm, struct kvm_mmu_page *root, - bool shared); - -static void tdp_mmu_zap_root_work(struct work_struct *work) -{ - struct kvm_mmu_page *root = container_of(work, struct kvm_mmu_page, - tdp_mmu_async_work); - struct kvm *kvm = root->tdp_mmu_async_data; - - read_lock(&kvm->mmu_lock); - - /* - * A TLB flush is not necessary as KVM performs a local TLB flush when - * allocating a new root (see kvm_mmu_load()), and when migrating vCPU - * to a different pCPU. Note, the local TLB flush on reuse also - * invalidates any paging-structure-cache entries, i.e. TLB entries for - * intermediate paging structures, that may be zapped, as such entries - * are associated with the ASID on both VMX and SVM. - */ - tdp_mmu_zap_root(kvm, root, true); - - /* - * Drop the refcount using kvm_tdp_mmu_put_root() to test its logic for - * avoiding an infinite loop. By design, the root is reachable while - * it's being asynchronously zapped, thus a different task can put its - * last reference, i.e. flowing through kvm_tdp_mmu_put_root() for an - * asynchronously zapped root is unavoidable. - */ - kvm_tdp_mmu_put_root(kvm, root, true); - - read_unlock(&kvm->mmu_lock); -} - -static void tdp_mmu_schedule_zap_root(struct kvm *kvm, struct kvm_mmu_page *root) -{ - root->tdp_mmu_async_data = kvm; - INIT_WORK(&root->tdp_mmu_async_work, tdp_mmu_zap_root_work); - queue_work(kvm->arch.tdp_mmu_zap_wq, &root->tdp_mmu_async_work); -} - void kvm_tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root, bool shared) { @@ -211,8 +158,12 @@ static struct kvm_mmu_page *tdp_mmu_next_root(struct kvm *kvm, #define for_each_valid_tdp_mmu_root_yield_safe(_kvm, _root, _as_id, _shared) \ __for_each_tdp_mmu_root_yield_safe(_kvm, _root, _as_id, _shared, true) -#define for_each_tdp_mmu_root_yield_safe(_kvm, _root, _as_id) \ - __for_each_tdp_mmu_root_yield_safe(_kvm, _root, _as_id, false, false) +#define for_each_tdp_mmu_root_yield_safe(_kvm, _root, _shared) \ + for (_root = tdp_mmu_next_root(_kvm, NULL, _shared, false); \ + _root; \ + _root = tdp_mmu_next_root(_kvm, _root, _shared, false)) \ + if (!kvm_lockdep_assert_mmu_lock_held(_kvm, _shared)) { \ + } else /* * Iterate over all TDP MMU roots. Requires that mmu_lock be held for write, @@ -292,7 +243,7 @@ hpa_t kvm_tdp_mmu_get_vcpu_root_hpa(struct kvm_vcpu *vcpu) * by a memslot update or by the destruction of the VM. Initialize the * refcount to two; one reference for the vCPU, and one reference for * the TDP MMU itself, which is held until the root is invalidated and - * is ultimately put by tdp_mmu_zap_root_work(). + * is ultimately put by kvm_tdp_mmu_zap_invalidated_roots(). */ refcount_set(&root->tdp_mmu_root_count, 2); @@ -475,9 +426,9 @@ static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, bool is_leaf = is_present && is_last_spte(new_spte, level); bool pfn_changed = spte_to_pfn(old_spte) != spte_to_pfn(new_spte); - WARN_ON(level > PT64_ROOT_MAX_LEVEL); - WARN_ON(level < PG_LEVEL_4K); - WARN_ON(gfn & (KVM_PAGES_PER_HPAGE(level) - 1)); + WARN_ON_ONCE(level > PT64_ROOT_MAX_LEVEL); + WARN_ON_ONCE(level < PG_LEVEL_4K); + WARN_ON_ONCE(gfn & (KVM_PAGES_PER_HPAGE(level) - 1)); /* * If this warning were to trigger it would indicate that there was a @@ -522,9 +473,9 @@ static void handle_changed_spte(struct kvm *kvm, int as_id, gfn_t gfn, * impact the guest since both the former and current SPTEs * are nonpresent. */ - if (WARN_ON(!is_mmio_spte(old_spte) && - !is_mmio_spte(new_spte) && - !is_removed_spte(new_spte))) + if (WARN_ON_ONCE(!is_mmio_spte(old_spte) && + !is_mmio_spte(new_spte) && + !is_removed_spte(new_spte))) pr_err("Unexpected SPTE change! Nonpresent SPTEs\n" "should not be replaced with another,\n" "different nonpresent SPTE, unless one or both\n" @@ -661,7 +612,7 @@ static u64 tdp_mmu_set_spte(struct kvm *kvm, int as_id, tdp_ptep_t sptep, * should be used. If operating under the MMU lock in write mode, the * use of the removed SPTE should not be necessary. */ - WARN_ON(is_removed_spte(old_spte) || is_removed_spte(new_spte)); + WARN_ON_ONCE(is_removed_spte(old_spte) || is_removed_spte(new_spte)); old_spte = kvm_tdp_mmu_write_spte(sptep, old_spte, new_spte, level); @@ -689,7 +640,7 @@ static inline void tdp_mmu_iter_set_spte(struct kvm *kvm, struct tdp_iter *iter, else #define tdp_mmu_for_each_pte(_iter, _mmu, _start, _end) \ - for_each_tdp_pte(_iter, to_shadow_page(_mmu->root.hpa), _start, _end) + for_each_tdp_pte(_iter, root_to_sp(_mmu->root.hpa), _start, _end) /* * Yield if the MMU lock is contended or this thread needs to return control @@ -709,7 +660,7 @@ static inline bool __must_check tdp_mmu_iter_cond_resched(struct kvm *kvm, struct tdp_iter *iter, bool flush, bool shared) { - WARN_ON(iter->yielded); + WARN_ON_ONCE(iter->yielded); /* Ensure forward progress has been made before yielding. */ if (iter->next_last_level_gfn == iter->yielded_gfn) @@ -728,7 +679,7 @@ static inline bool __must_check tdp_mmu_iter_cond_resched(struct kvm *kvm, rcu_read_lock(); - WARN_ON(iter->gfn > iter->next_last_level_gfn); + WARN_ON_ONCE(iter->gfn > iter->next_last_level_gfn); iter->yielded = true; } @@ -877,13 +828,12 @@ static bool tdp_mmu_zap_leafs(struct kvm *kvm, struct kvm_mmu_page *root, * true if a TLB flush is needed before releasing the MMU lock, i.e. if one or * more SPTEs were zapped since the MMU lock was last acquired. */ -bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, int as_id, gfn_t start, gfn_t end, - bool can_yield, bool flush) +bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, gfn_t start, gfn_t end, bool flush) { struct kvm_mmu_page *root; - for_each_tdp_mmu_root_yield_safe(kvm, root, as_id) - flush = tdp_mmu_zap_leafs(kvm, root, start, end, can_yield, flush); + for_each_tdp_mmu_root_yield_safe(kvm, root, false) + flush = tdp_mmu_zap_leafs(kvm, root, start, end, true, flush); return flush; } @@ -891,7 +841,6 @@ bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, int as_id, gfn_t start, gfn_t end, void kvm_tdp_mmu_zap_all(struct kvm *kvm) { struct kvm_mmu_page *root; - int i; /* * Zap all roots, including invalid roots, as all SPTEs must be dropped @@ -905,10 +854,8 @@ void kvm_tdp_mmu_zap_all(struct kvm *kvm) * is being destroyed or the userspace VMM has exited. In both cases, * KVM_RUN is unreachable, i.e. no vCPUs will ever service the request. */ - for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) { - for_each_tdp_mmu_root_yield_safe(kvm, root, i) - tdp_mmu_zap_root(kvm, root, false); - } + for_each_tdp_mmu_root_yield_safe(kvm, root, false) + tdp_mmu_zap_root(kvm, root, false); } /* @@ -917,18 +864,47 @@ void kvm_tdp_mmu_zap_all(struct kvm *kvm) */ void kvm_tdp_mmu_zap_invalidated_roots(struct kvm *kvm) { - flush_workqueue(kvm->arch.tdp_mmu_zap_wq); + struct kvm_mmu_page *root; + + read_lock(&kvm->mmu_lock); + + for_each_tdp_mmu_root_yield_safe(kvm, root, true) { + if (!root->tdp_mmu_scheduled_root_to_zap) + continue; + + root->tdp_mmu_scheduled_root_to_zap = false; + KVM_BUG_ON(!root->role.invalid, kvm); + + /* + * A TLB flush is not necessary as KVM performs a local TLB + * flush when allocating a new root (see kvm_mmu_load()), and + * when migrating a vCPU to a different pCPU. Note, the local + * TLB flush on reuse also invalidates paging-structure-cache + * entries, i.e. TLB entries for intermediate paging structures, + * that may be zapped, as such entries are associated with the + * ASID on both VMX and SVM. + */ + tdp_mmu_zap_root(kvm, root, true); + + /* + * The referenced needs to be put *after* zapping the root, as + * the root must be reachable by mmu_notifiers while it's being + * zapped + */ + kvm_tdp_mmu_put_root(kvm, root, true); + } + + read_unlock(&kvm->mmu_lock); } /* * Mark each TDP MMU root as invalid to prevent vCPUs from reusing a root that * is about to be zapped, e.g. in response to a memslots update. The actual - * zapping is performed asynchronously. Using a separate workqueue makes it - * easy to ensure that the destruction is performed before the "fast zap" - * completes, without keeping a separate list of invalidated roots; the list is - * effectively the list of work items in the workqueue. + * zapping is done separately so that it happens with mmu_lock with read, + * whereas invalidating roots must be done with mmu_lock held for write (unless + * the VM is being destroyed). * - * Note, the asynchronous worker is gifted the TDP MMU's reference. + * Note, kvm_tdp_mmu_zap_invalidated_roots() is gifted the TDP MMU's reference. * See kvm_tdp_mmu_get_vcpu_root_hpa(). */ void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm) @@ -953,19 +929,20 @@ void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm) /* * As above, mmu_lock isn't held when destroying the VM! There can't * be other references to @kvm, i.e. nothing else can invalidate roots - * or be consuming roots, but walking the list of roots does need to be - * guarded against roots being deleted by the asynchronous zap worker. + * or get/put references to roots. */ - rcu_read_lock(); - - list_for_each_entry_rcu(root, &kvm->arch.tdp_mmu_roots, link) { + list_for_each_entry(root, &kvm->arch.tdp_mmu_roots, link) { + /* + * Note, invalid roots can outlive a memslot update! Invalid + * roots must be *zapped* before the memslot update completes, + * but a different task can acquire a reference and keep the + * root alive after its been zapped. + */ if (!root->role.invalid) { + root->tdp_mmu_scheduled_root_to_zap = true; root->role.invalid = true; - tdp_mmu_schedule_zap_root(kvm, root); } } - - rcu_read_unlock(); } /* @@ -1146,8 +1123,13 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) bool kvm_tdp_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range, bool flush) { - return kvm_tdp_mmu_zap_leafs(kvm, range->slot->as_id, range->start, - range->end, range->may_block, flush); + struct kvm_mmu_page *root; + + __for_each_tdp_mmu_root_yield_safe(kvm, root, range->slot->as_id, false, false) + flush = tdp_mmu_zap_leafs(kvm, root, range->start, range->end, + range->may_block, flush); + + return flush; } typedef bool (*tdp_handler_t)(struct kvm *kvm, struct tdp_iter *iter, @@ -1241,7 +1223,7 @@ static bool set_spte_gfn(struct kvm *kvm, struct tdp_iter *iter, u64 new_spte; /* Huge pages aren't expected to be modified without first being zapped. */ - WARN_ON(pte_huge(range->pte) || range->start + 1 != range->end); + WARN_ON_ONCE(pte_huge(range->arg.pte) || range->start + 1 != range->end); if (iter->level != PG_LEVEL_4K || !is_shadow_present_pte(iter->old_spte)) @@ -1255,9 +1237,9 @@ static bool set_spte_gfn(struct kvm *kvm, struct tdp_iter *iter, */ tdp_mmu_iter_set_spte(kvm, iter, 0); - if (!pte_write(range->pte)) { + if (!pte_write(range->arg.pte)) { new_spte = kvm_mmu_changed_pte_notifier_make_spte(iter->old_spte, - pte_pfn(range->pte)); + pte_pfn(range->arg.pte)); tdp_mmu_iter_set_spte(kvm, iter, new_spte); } @@ -1548,8 +1530,8 @@ static bool clear_dirty_gfn_range(struct kvm *kvm, struct kvm_mmu_page *root, if (!is_shadow_present_pte(iter.old_spte)) continue; - MMU_WARN_ON(kvm_ad_enabled() && - spte_ad_need_write_protect(iter.old_spte)); + KVM_MMU_WARN_ON(kvm_ad_enabled() && + spte_ad_need_write_protect(iter.old_spte)); if (!(iter.old_spte & dbit)) continue; @@ -1600,6 +1582,8 @@ static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root, shadow_dirty_mask; struct tdp_iter iter; + lockdep_assert_held_write(&kvm->mmu_lock); + rcu_read_lock(); tdp_root_for_each_leaf_pte(iter, root, gfn + __ffs(mask), @@ -1607,8 +1591,8 @@ static void clear_dirty_pt_masked(struct kvm *kvm, struct kvm_mmu_page *root, if (!mask) break; - MMU_WARN_ON(kvm_ad_enabled() && - spte_ad_need_write_protect(iter.old_spte)); + KVM_MMU_WARN_ON(kvm_ad_enabled() && + spte_ad_need_write_protect(iter.old_spte)); if (iter.level > PG_LEVEL_4K || !(mask & (1UL << (iter.gfn - gfn)))) @@ -1646,7 +1630,6 @@ void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm, { struct kvm_mmu_page *root; - lockdep_assert_held_write(&kvm->mmu_lock); for_each_tdp_mmu_root(kvm, root, slot->as_id) clear_dirty_pt_masked(kvm, root, gfn, mask, wrprot); } diff --git a/arch/x86/kvm/mmu/tdp_mmu.h b/arch/x86/kvm/mmu/tdp_mmu.h index 0a63b1afabd3cceef4e6e2ecb8ca5b3893becb5f..733a3aef3a96eaa32964e8fc042c26fae0a0ee9c 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.h +++ b/arch/x86/kvm/mmu/tdp_mmu.h @@ -7,7 +7,7 @@ #include "spte.h" -int kvm_mmu_init_tdp_mmu(struct kvm *kvm); +void kvm_mmu_init_tdp_mmu(struct kvm *kvm); void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm); hpa_t kvm_tdp_mmu_get_vcpu_root_hpa(struct kvm_vcpu *vcpu); @@ -20,8 +20,7 @@ __must_check static inline bool kvm_tdp_mmu_get_root(struct kvm_mmu_page *root) void kvm_tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root, bool shared); -bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, int as_id, gfn_t start, - gfn_t end, bool can_yield, bool flush); +bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, gfn_t start, gfn_t end, bool flush); bool kvm_tdp_mmu_zap_sp(struct kvm *kvm, struct kvm_mmu_page *sp); void kvm_tdp_mmu_zap_all(struct kvm *kvm); void kvm_tdp_mmu_invalidate_all_roots(struct kvm *kvm); diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index bf653df86112071badbc3fea07bb2e5cad4f3ce2..edb89b51b3838155f8262431b06f89c2b8fcd6d1 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -382,9 +382,6 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc) struct kvm_x86_pmu_event_filter *filter; struct kvm *kvm = pmc->vcpu->kvm; - if (!static_call(kvm_x86_pmu_hw_event_available)(pmc)) - return false; - filter = srcu_dereference(kvm->arch.pmu_event_filter, &kvm->srcu); if (!filter) return true; @@ -398,6 +395,7 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc) static bool pmc_event_is_allowed(struct kvm_pmc *pmc) { return pmc_is_globally_enabled(pmc) && pmc_speculative_in_use(pmc) && + static_call(kvm_x86_pmu_hw_event_available)(pmc) && check_pmu_event_filter(pmc); } diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h index 56cbdb24400ae13a99356dcbf1dd81ed54dda95e..b816506783755a1ce87d511014c9903fe3e25798 100644 --- a/arch/x86/kvm/reverse_cpuid.h +++ b/arch/x86/kvm/reverse_cpuid.h @@ -43,6 +43,7 @@ enum kvm_only_cpuid_leafs { /* Intel-defined sub-features, CPUID level 0x00000007:1 (EDX) */ #define X86_FEATURE_AVX_VNNI_INT8 KVM_X86_FEATURE(CPUID_7_1_EDX, 4) #define X86_FEATURE_AVX_NE_CONVERT KVM_X86_FEATURE(CPUID_7_1_EDX, 5) +#define X86_FEATURE_AMX_COMPLEX KVM_X86_FEATURE(CPUID_7_1_EDX, 8) #define X86_FEATURE_PREFETCHITI KVM_X86_FEATURE(CPUID_7_1_EDX, 14) /* CPUID level 0x80000007 (EDX). */ diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index cfc8ab7730250598cf0f29be7bacfe5e49a82424..2092db892d7d052a6301396ca753de694c20d8a8 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -791,6 +791,7 @@ static int svm_ir_list_add(struct vcpu_svm *svm, struct amd_iommu_pi_data *pi) int ret = 0; unsigned long flags; struct amd_svm_iommu_ir *ir; + u64 entry; /** * In some cases, the existing irte is updated and re-set, @@ -824,6 +825,18 @@ static int svm_ir_list_add(struct vcpu_svm *svm, struct amd_iommu_pi_data *pi) ir->data = pi->ir_data; spin_lock_irqsave(&svm->ir_list_lock, flags); + + /* + * Update the target pCPU for IOMMU doorbells if the vCPU is running. + * If the vCPU is NOT running, i.e. is blocking or scheduled out, KVM + * will update the pCPU info when the vCPU awkened and/or scheduled in. + * See also avic_vcpu_load(). + */ + entry = READ_ONCE(*(svm->avic_physical_id_cache)); + if (entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK) + amd_iommu_update_ga(entry & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK, + true, pi->ir_data); + list_add(&ir->node, &svm->ir_list); spin_unlock_irqrestore(&svm->ir_list_lock, flags); out: @@ -986,10 +999,11 @@ static inline int avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int cpu, bool r) { int ret = 0; - unsigned long flags; struct amd_svm_iommu_ir *ir; struct vcpu_svm *svm = to_svm(vcpu); + lockdep_assert_held(&svm->ir_list_lock); + if (!kvm_arch_has_assigned_device(vcpu->kvm)) return 0; @@ -997,19 +1011,15 @@ avic_update_iommu_vcpu_affinity(struct kvm_vcpu *vcpu, int cpu, bool r) * Here, we go through the per-vcpu ir_list to update all existing * interrupt remapping table entry targeting this vcpu. */ - spin_lock_irqsave(&svm->ir_list_lock, flags); - if (list_empty(&svm->ir_list)) - goto out; + return 0; list_for_each_entry(ir, &svm->ir_list, node) { ret = amd_iommu_update_ga(cpu, r, ir->data); if (ret) - break; + return ret; } -out: - spin_unlock_irqrestore(&svm->ir_list_lock, flags); - return ret; + return 0; } void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) @@ -1017,6 +1027,7 @@ void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) u64 entry; int h_physical_id = kvm_cpu_get_apicid(cpu); struct vcpu_svm *svm = to_svm(vcpu); + unsigned long flags; lockdep_assert_preemption_disabled(); @@ -1033,6 +1044,15 @@ void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (kvm_vcpu_is_blocking(vcpu)) return; + /* + * Grab the per-vCPU interrupt remapping lock even if the VM doesn't + * _currently_ have assigned devices, as that can change. Holding + * ir_list_lock ensures that either svm_ir_list_add() will consume + * up-to-date entry information, or that this task will wait until + * svm_ir_list_add() completes to set the new target pCPU. + */ + spin_lock_irqsave(&svm->ir_list_lock, flags); + entry = READ_ONCE(*(svm->avic_physical_id_cache)); WARN_ON_ONCE(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK); @@ -1042,25 +1062,48 @@ void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) WRITE_ONCE(*(svm->avic_physical_id_cache), entry); avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, true); + + spin_unlock_irqrestore(&svm->ir_list_lock, flags); } void avic_vcpu_put(struct kvm_vcpu *vcpu) { u64 entry; struct vcpu_svm *svm = to_svm(vcpu); + unsigned long flags; lockdep_assert_preemption_disabled(); + /* + * Note, reading the Physical ID entry outside of ir_list_lock is safe + * as only the pCPU that has loaded (or is loading) the vCPU is allowed + * to modify the entry, and preemption is disabled. I.e. the vCPU + * can't be scheduled out and thus avic_vcpu_{put,load}() can't run + * recursively. + */ entry = READ_ONCE(*(svm->avic_physical_id_cache)); /* Nothing to do if IsRunning == '0' due to vCPU blocking. */ if (!(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK)) return; + /* + * Take and hold the per-vCPU interrupt remapping lock while updating + * the Physical ID entry even though the lock doesn't protect against + * multiple writers (see above). Holding ir_list_lock ensures that + * either svm_ir_list_add() will consume up-to-date entry information, + * or that this task will wait until svm_ir_list_add() completes to + * mark the vCPU as not running. + */ + spin_lock_irqsave(&svm->ir_list_lock, flags); + avic_update_iommu_vcpu_affinity(vcpu, -1, 0); entry &= ~AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK; WRITE_ONCE(*(svm->avic_physical_id_cache), entry); + + spin_unlock_irqrestore(&svm->ir_list_lock, flags); + } void avic_refresh_virtual_apic_mode(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index 96936ddf1b3c5680d881b8a997925a9afa8e4c44..dd496c9e5f91f28519ebc27ff6135dbd00ebeada 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -107,7 +107,7 @@ static void nested_svm_uninit_mmu_context(struct kvm_vcpu *vcpu) static bool nested_vmcb_needs_vls_intercept(struct vcpu_svm *svm) { - if (!svm->v_vmload_vmsave_enabled) + if (!guest_can_use(&svm->vcpu, X86_FEATURE_V_VMSAVE_VMLOAD)) return true; if (!nested_npt_enabled(svm)) @@ -552,6 +552,7 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12 bool new_vmcb12 = false; struct vmcb *vmcb01 = svm->vmcb01.ptr; struct vmcb *vmcb02 = svm->nested.vmcb02.ptr; + struct kvm_vcpu *vcpu = &svm->vcpu; nested_vmcb02_compute_g_pat(svm); @@ -577,18 +578,18 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12 vmcb_mark_dirty(vmcb02, VMCB_DT); } - kvm_set_rflags(&svm->vcpu, vmcb12->save.rflags | X86_EFLAGS_FIXED); + kvm_set_rflags(vcpu, vmcb12->save.rflags | X86_EFLAGS_FIXED); - svm_set_efer(&svm->vcpu, svm->nested.save.efer); + svm_set_efer(vcpu, svm->nested.save.efer); - svm_set_cr0(&svm->vcpu, svm->nested.save.cr0); - svm_set_cr4(&svm->vcpu, svm->nested.save.cr4); + svm_set_cr0(vcpu, svm->nested.save.cr0); + svm_set_cr4(vcpu, svm->nested.save.cr4); svm->vcpu.arch.cr2 = vmcb12->save.cr2; - kvm_rax_write(&svm->vcpu, vmcb12->save.rax); - kvm_rsp_write(&svm->vcpu, vmcb12->save.rsp); - kvm_rip_write(&svm->vcpu, vmcb12->save.rip); + kvm_rax_write(vcpu, vmcb12->save.rax); + kvm_rsp_write(vcpu, vmcb12->save.rsp); + kvm_rip_write(vcpu, vmcb12->save.rip); /* In case we don't even reach vcpu_run, the fields are not updated */ vmcb02->save.rax = vmcb12->save.rax; @@ -602,7 +603,8 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12 vmcb_mark_dirty(vmcb02, VMCB_DR); } - if (unlikely(svm->lbrv_enabled && (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) { + if (unlikely(guest_can_use(vcpu, X86_FEATURE_LBRV) && + (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) { /* * Reserved bits of DEBUGCTL are ignored. Be consistent with * svm_set_msr's definition of reserved bits. @@ -658,7 +660,8 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, * exit_int_info, exit_int_info_err, next_rip, insn_len, insn_bytes. */ - if (svm->vgif_enabled && (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK)) + if (guest_can_use(vcpu, X86_FEATURE_VGIF) && + (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK)) int_ctl_vmcb12_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK); else int_ctl_vmcb01_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK); @@ -695,10 +698,9 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, vmcb02->control.tsc_offset = vcpu->arch.tsc_offset; - if (svm->tsc_ratio_msr != kvm_caps.default_tsc_scaling_ratio) { - WARN_ON(!svm->tsc_scaling_enabled); + if (guest_can_use(vcpu, X86_FEATURE_TSCRATEMSR) && + svm->tsc_ratio_msr != kvm_caps.default_tsc_scaling_ratio) nested_svm_update_tsc_ratio_msr(vcpu); - } vmcb02->control.int_ctl = (svm->nested.ctl.int_ctl & int_ctl_vmcb12_bits) | @@ -717,7 +719,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, * what a nrips=0 CPU would do (L1 is responsible for advancing RIP * prior to injecting the event). */ - if (svm->nrips_enabled) + if (guest_can_use(vcpu, X86_FEATURE_NRIPS)) vmcb02->control.next_rip = svm->nested.ctl.next_rip; else if (boot_cpu_has(X86_FEATURE_NRIPS)) vmcb02->control.next_rip = vmcb12_rip; @@ -727,7 +729,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, svm->soft_int_injected = true; svm->soft_int_csbase = vmcb12_csbase; svm->soft_int_old_rip = vmcb12_rip; - if (svm->nrips_enabled) + if (guest_can_use(vcpu, X86_FEATURE_NRIPS)) svm->soft_int_next_rip = svm->nested.ctl.next_rip; else svm->soft_int_next_rip = vmcb12_rip; @@ -735,15 +737,21 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, vmcb02->control.virt_ext = vmcb01->control.virt_ext & LBR_CTL_ENABLE_MASK; - if (svm->lbrv_enabled) + if (guest_can_use(vcpu, X86_FEATURE_LBRV)) vmcb02->control.virt_ext |= (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK); if (!nested_vmcb_needs_vls_intercept(svm)) vmcb02->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; - pause_count12 = svm->pause_filter_enabled ? svm->nested.ctl.pause_filter_count : 0; - pause_thresh12 = svm->pause_threshold_enabled ? svm->nested.ctl.pause_filter_thresh : 0; + if (guest_can_use(vcpu, X86_FEATURE_PAUSEFILTER)) + pause_count12 = svm->nested.ctl.pause_filter_count; + else + pause_count12 = 0; + if (guest_can_use(vcpu, X86_FEATURE_PFTHRESHOLD)) + pause_thresh12 = svm->nested.ctl.pause_filter_thresh; + else + pause_thresh12 = 0; if (kvm_pause_in_guest(svm->vcpu.kvm)) { /* use guest values since host doesn't intercept PAUSE */ vmcb02->control.pause_filter_count = pause_count12; @@ -1027,7 +1035,7 @@ int nested_svm_vmexit(struct vcpu_svm *svm) if (vmcb12->control.exit_code != SVM_EXIT_ERR) nested_save_pending_event_to_vmcb12(svm, vmcb12); - if (svm->nrips_enabled) + if (guest_can_use(vcpu, X86_FEATURE_NRIPS)) vmcb12->control.next_rip = vmcb02->control.next_rip; vmcb12->control.int_ctl = svm->nested.ctl.int_ctl; @@ -1066,7 +1074,8 @@ int nested_svm_vmexit(struct vcpu_svm *svm) if (!nested_exit_on_intr(svm)) kvm_make_request(KVM_REQ_EVENT, &svm->vcpu); - if (unlikely(svm->lbrv_enabled && (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) { + if (unlikely(guest_can_use(vcpu, X86_FEATURE_LBRV) && + (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) { svm_copy_lbrs(vmcb12, vmcb02); svm_update_lbrv(vcpu); } else if (unlikely(vmcb01->control.virt_ext & LBR_CTL_ENABLE_MASK)) { @@ -1101,10 +1110,10 @@ int nested_svm_vmexit(struct vcpu_svm *svm) vmcb_mark_dirty(vmcb01, VMCB_INTERCEPTS); } - if (svm->tsc_ratio_msr != kvm_caps.default_tsc_scaling_ratio) { - WARN_ON(!svm->tsc_scaling_enabled); + if (kvm_caps.has_tsc_control && + vcpu->arch.tsc_scaling_ratio != vcpu->arch.l1_tsc_scaling_ratio) { vcpu->arch.tsc_scaling_ratio = vcpu->arch.l1_tsc_scaling_ratio; - __svm_write_tsc_multiplier(vcpu->arch.tsc_scaling_ratio); + svm_write_tsc_multiplier(vcpu); } svm->nested.ctl.nested_cr3 = 0; @@ -1537,7 +1546,7 @@ void nested_svm_update_tsc_ratio_msr(struct kvm_vcpu *vcpu) vcpu->arch.tsc_scaling_ratio = kvm_calc_nested_tsc_multiplier(vcpu->arch.l1_tsc_scaling_ratio, svm->tsc_ratio_msr); - __svm_write_tsc_multiplier(vcpu->arch.tsc_scaling_ratio); + svm_write_tsc_multiplier(vcpu); } /* Inverse operation of nested_copy_vmcb_control_to_cache(). asid is copied too. */ diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index d3aec1f2cad20f0507a02b3987b42c33c4419bef..4900c078045acc1bf437859f46e9acbe9c66b0b4 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "mmu.h" #include "x86.h" @@ -54,9 +55,14 @@ module_param_named(sev, sev_enabled, bool, 0444); /* enable/disable SEV-ES support */ static bool sev_es_enabled = true; module_param_named(sev_es, sev_es_enabled, bool, 0444); + +/* enable/disable SEV-ES DebugSwap support */ +static bool sev_es_debug_swap_enabled = true; +module_param_named(debug_swap, sev_es_debug_swap_enabled, bool, 0444); #else #define sev_enabled false #define sev_es_enabled false +#define sev_es_debug_swap_enabled false #endif /* CONFIG_KVM_AMD_SEV */ static u8 sev_enc_bit; @@ -606,6 +612,9 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm) save->xss = svm->vcpu.arch.ia32_xss; save->dr6 = svm->vcpu.arch.dr6; + if (sev_es_debug_swap_enabled) + save->sev_features |= SVM_SEV_FEAT_DEBUG_SWAP; + pr_debug("Virtual Machine Save Area (VMSA):\n"); print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, save, sizeof(*save), false); @@ -619,6 +628,11 @@ static int __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu, struct vcpu_svm *svm = to_svm(vcpu); int ret; + if (vcpu->guest_debug) { + pr_warn_once("KVM_SET_GUEST_DEBUG for SEV-ES guest is not supported"); + return -EINVAL; + } + /* Perform some pre-encryption checks against the VMSA */ ret = sev_es_sync_vmsa(svm); if (ret) @@ -1725,7 +1739,7 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm) * Note, the source is not required to have the same number of * vCPUs as the destination when migrating a vanilla SEV VM. */ - src_vcpu = kvm_get_vcpu(dst_kvm, i); + src_vcpu = kvm_get_vcpu(src_kvm, i); src_svm = to_svm(src_vcpu); /* @@ -2171,7 +2185,7 @@ void __init sev_hardware_setup(void) bool sev_es_supported = false; bool sev_supported = false; - if (!sev_enabled || !npt_enabled) + if (!sev_enabled || !npt_enabled || !nrips) goto out; /* @@ -2256,6 +2270,9 @@ void __init sev_hardware_setup(void) sev_enabled = sev_supported; sev_es_enabled = sev_es_supported; + if (!sev_es_enabled || !cpu_feature_enabled(X86_FEATURE_DEBUG_SWAP) || + !cpu_feature_enabled(X86_FEATURE_NO_NESTED_DATA_BP)) + sev_es_debug_swap_enabled = false; #endif } @@ -2881,7 +2898,10 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) svm->sev_es.ghcb_sa); break; case SVM_VMGEXIT_NMI_COMPLETE: - ret = svm_invoke_exit_handler(vcpu, SVM_EXIT_IRET); + ++vcpu->stat.nmi_window_exits; + svm->nmi_masked = false; + kvm_make_request(KVM_REQ_EVENT, vcpu); + ret = 1; break; case SVM_VMGEXIT_AP_HLT_LOOP: ret = kvm_emulate_ap_reset_hold(vcpu); @@ -2942,8 +2962,35 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in) count, in); } +static void sev_es_vcpu_after_set_cpuid(struct vcpu_svm *svm) +{ + struct kvm_vcpu *vcpu = &svm->vcpu; + + if (boot_cpu_has(X86_FEATURE_V_TSC_AUX)) { + bool v_tsc_aux = guest_cpuid_has(vcpu, X86_FEATURE_RDTSCP) || + guest_cpuid_has(vcpu, X86_FEATURE_RDPID); + + set_msr_interception(vcpu, svm->msrpm, MSR_TSC_AUX, v_tsc_aux, v_tsc_aux); + } +} + +void sev_vcpu_after_set_cpuid(struct vcpu_svm *svm) +{ + struct kvm_vcpu *vcpu = &svm->vcpu; + struct kvm_cpuid_entry2 *best; + + /* For sev guests, the memory encryption bit is not reserved in CR3. */ + best = kvm_find_cpuid_entry(vcpu, 0x8000001F); + if (best) + vcpu->arch.reserved_gpa_bits &= ~(1UL << (best->ebx & 0x3f)); + + if (sev_es_guest(svm->vcpu.kvm)) + sev_es_vcpu_after_set_cpuid(svm); +} + static void sev_es_init_vmcb(struct vcpu_svm *svm) { + struct vmcb *vmcb = svm->vmcb01.ptr; struct kvm_vcpu *vcpu = &svm->vcpu; svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ES_ENABLE; @@ -2952,9 +2999,12 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) /* * An SEV-ES guest requires a VMSA area that is a separate from the * VMCB page. Do not include the encryption mask on the VMSA physical - * address since hardware will access it using the guest key. + * address since hardware will access it using the guest key. Note, + * the VMSA will be NULL if this vCPU is the destination for intrahost + * migration, and will be copied later. */ - svm->vmcb->control.vmsa_pa = __pa(svm->sev_es.vmsa); + if (svm->sev_es.vmsa) + svm->vmcb->control.vmsa_pa = __pa(svm->sev_es.vmsa); /* Can't intercept CR register access, HV can't modify CR registers */ svm_clr_intercept(svm, INTERCEPT_CR0_READ); @@ -2972,8 +3022,23 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) svm_set_intercept(svm, TRAP_CR4_WRITE); svm_set_intercept(svm, TRAP_CR8_WRITE); - /* No support for enable_vmware_backdoor */ - clr_exception_intercept(svm, GP_VECTOR); + vmcb->control.intercepts[INTERCEPT_DR] = 0; + if (!sev_es_debug_swap_enabled) { + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); + recalc_intercepts(svm); + } else { + /* + * Disable #DB intercept iff DebugSwap is enabled. KVM doesn't + * allow debugging SEV-ES guests, and enables DebugSwap iff + * NO_NESTED_DATA_BP is supported, so there's no reason to + * intercept #DB when DebugSwap is enabled. For simplicity + * with respect to guest debug, intercept #DB for other VMs + * even if NO_NESTED_DATA_BP is supported, i.e. even if the + * guest can't DoS the CPU with infinite #DB vectoring. + */ + clr_exception_intercept(svm, DB_VECTOR); + } /* Can't intercept XSETBV, HV can't modify XCR0 directly */ svm_clr_intercept(svm, INTERCEPT_XSETBV); @@ -2985,14 +3050,6 @@ static void sev_es_init_vmcb(struct vcpu_svm *svm) set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); - - if (boot_cpu_has(X86_FEATURE_V_TSC_AUX) && - (guest_cpuid_has(&svm->vcpu, X86_FEATURE_RDTSCP) || - guest_cpuid_has(&svm->vcpu, X86_FEATURE_RDPID))) { - set_msr_interception(vcpu, svm->msrpm, MSR_TSC_AUX, 1, 1); - if (guest_cpuid_has(&svm->vcpu, X86_FEATURE_RDTSCP)) - svm_clr_intercept(svm, INTERCEPT_RDTSCP); - } } void sev_init_vmcb(struct vcpu_svm *svm) @@ -3000,6 +3057,12 @@ void sev_init_vmcb(struct vcpu_svm *svm) svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE; clr_exception_intercept(svm, UD_VECTOR); + /* + * Don't intercept #GP for SEV guests, e.g. for the VMware backdoor, as + * KVM can't decrypt guest memory to decode the faulting instruction. + */ + clr_exception_intercept(svm, GP_VECTOR); + if (sev_es_guest(svm->vcpu.kvm)) sev_es_init_vmcb(svm); } @@ -3018,20 +3081,41 @@ void sev_es_vcpu_reset(struct vcpu_svm *svm) void sev_es_prepare_switch_to_guest(struct sev_es_save_area *hostsa) { /* - * As an SEV-ES guest, hardware will restore the host state on VMEXIT, - * of which one step is to perform a VMLOAD. KVM performs the - * corresponding VMSAVE in svm_prepare_guest_switch for both - * traditional and SEV-ES guests. + * All host state for SEV-ES guests is categorized into three swap types + * based on how it is handled by hardware during a world switch: + * + * A: VMRUN: Host state saved in host save area + * VMEXIT: Host state loaded from host save area + * + * B: VMRUN: Host state _NOT_ saved in host save area + * VMEXIT: Host state loaded from host save area + * + * C: VMRUN: Host state _NOT_ saved in host save area + * VMEXIT: Host state initialized to default(reset) values + * + * Manually save type-B state, i.e. state that is loaded by VMEXIT but + * isn't saved by VMRUN, that isn't already saved by VMSAVE (performed + * by common SVM code). */ - - /* XCR0 is restored on VMEXIT, save the current host value */ hostsa->xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); - - /* PKRU is restored on VMEXIT, save the current host value */ hostsa->pkru = read_pkru(); - - /* MSR_IA32_XSS is restored on VMEXIT, save the currnet host value */ hostsa->xss = host_xss; + + /* + * If DebugSwap is enabled, debug registers are loaded but NOT saved by + * the CPU (Type-B). If DebugSwap is disabled/unsupported, the CPU both + * saves and loads debug registers (Type-A). + */ + if (sev_es_debug_swap_enabled) { + hostsa->dr0 = native_get_debugreg(0); + hostsa->dr1 = native_get_debugreg(1); + hostsa->dr2 = native_get_debugreg(2); + hostsa->dr3 = native_get_debugreg(3); + hostsa->dr0_addr_mask = amd_get_dr_addr_mask(0); + hostsa->dr1_addr_mask = amd_get_dr_addr_mask(1); + hostsa->dr2_addr_mask = amd_get_dr_addr_mask(2); + hostsa->dr3_addr_mask = amd_get_dr_addr_mask(3); + } } void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index d4bfdc607fe7f3257c9c68a4ee34c6a2c3a7e5c9..9507df93f410a631b1b06acd9937bea881e7b5c9 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -39,10 +39,9 @@ #include #include #include +#include #include -#include - #include #include "trace.h" @@ -203,7 +202,7 @@ static int nested = true; module_param(nested, int, S_IRUGO); /* enable/disable Next RIP Save */ -static int nrips = true; +int nrips = true; module_param(nrips, int, 0444); /* enable/disable Virtual VMLOAD VMSAVE */ @@ -365,6 +364,8 @@ static void svm_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) svm->vmcb->control.int_state |= SVM_INTERRUPT_SHADOW_MASK; } +static bool svm_can_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, + void *insn, int insn_len); static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, bool commit_side_effects) @@ -385,6 +386,14 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, } if (!svm->next_rip) { + /* + * FIXME: Drop this when kvm_emulate_instruction() does the + * right thing and treats "can't emulate" as outright failure + * for EMULTYPE_SKIP. + */ + if (!svm_can_emulate_instruction(vcpu, EMULTYPE_SKIP, NULL, 0)) + return 0; + if (unlikely(!commit_side_effects)) old_rflags = svm->vmcb->save.rflags; @@ -517,14 +526,21 @@ static void svm_init_osvw(struct kvm_vcpu *vcpu) vcpu->arch.osvw.status |= 1; } -static bool kvm_is_svm_supported(void) +static bool __kvm_is_svm_supported(void) { - int cpu = raw_smp_processor_id(); - const char *msg; + int cpu = smp_processor_id(); + struct cpuinfo_x86 *c = &cpu_data(cpu); + u64 vm_cr; - if (!cpu_has_svm(&msg)) { - pr_err("SVM not supported by CPU %d, %s\n", cpu, msg); + if (c->x86_vendor != X86_VENDOR_AMD && + c->x86_vendor != X86_VENDOR_HYGON) { + pr_err("CPU %d isn't AMD or Hygon\n", cpu); + return false; + } + + if (!cpu_has(c, X86_FEATURE_SVM)) { + pr_err("SVM not supported by CPU %d\n", cpu); return false; } @@ -542,25 +558,55 @@ static bool kvm_is_svm_supported(void) return true; } +static bool kvm_is_svm_supported(void) +{ + bool supported; + + migrate_disable(); + supported = __kvm_is_svm_supported(); + migrate_enable(); + + return supported; +} + static int svm_check_processor_compat(void) { - if (!kvm_is_svm_supported()) + if (!__kvm_is_svm_supported()) return -EIO; return 0; } -void __svm_write_tsc_multiplier(u64 multiplier) +static void __svm_write_tsc_multiplier(u64 multiplier) { - preempt_disable(); - if (multiplier == __this_cpu_read(current_tsc_ratio)) - goto out; + return; wrmsrl(MSR_AMD64_TSC_RATIO, multiplier); __this_cpu_write(current_tsc_ratio, multiplier); -out: - preempt_enable(); +} + +static inline void kvm_cpu_svm_disable(void) +{ + uint64_t efer; + + wrmsrl(MSR_VM_HSAVE_PA, 0); + rdmsrl(MSR_EFER, efer); + if (efer & EFER_SVME) { + /* + * Force GIF=1 prior to disabling SVM, e.g. to ensure INIT and + * NMI aren't blocked. + */ + stgi(); + wrmsrl(MSR_EFER, efer & ~EFER_SVME); + } +} + +static void svm_emergency_disable(void) +{ + kvm_rebooting = true; + + kvm_cpu_svm_disable(); } static void svm_hardware_disable(void) @@ -569,7 +615,7 @@ static void svm_hardware_disable(void) if (tsc_scaling) __svm_write_tsc_multiplier(SVM_TSC_RATIO_DEFAULT); - cpu_svm_disable(); + kvm_cpu_svm_disable(); amd_pmu_disable_virt(); } @@ -637,6 +683,21 @@ static int svm_hardware_enable(void) amd_pmu_enable_virt(); + /* + * If TSC_AUX virtualization is supported, TSC_AUX becomes a swap type + * "B" field (see sev_es_prepare_switch_to_guest()) for SEV-ES guests. + * Since Linux does not change the value of TSC_AUX once set, prime the + * TSC_AUX field now to avoid a RDMSR on every vCPU run. + */ + if (boot_cpu_has(X86_FEATURE_V_TSC_AUX)) { + struct sev_es_save_area *hostsa; + u32 msr_hi; + + hostsa = (struct sev_es_save_area *)(page_address(sd->save_area) + 0x400); + + rdmsr(MSR_TSC_AUX, hostsa->tsc_aux, msr_hi); + } + return 0; } @@ -677,6 +738,39 @@ static int svm_cpu_init(int cpu) } +static void set_dr_intercepts(struct vcpu_svm *svm) +{ + struct vmcb *vmcb = svm->vmcb01.ptr; + + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR0_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR1_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR2_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR3_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR4_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR5_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR6_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR0_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR1_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR2_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR3_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR4_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR5_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR6_WRITE); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); + vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); + + recalc_intercepts(svm); +} + +static void clr_dr_intercepts(struct vcpu_svm *svm) +{ + struct vmcb *vmcb = svm->vmcb01.ptr; + + vmcb->control.intercepts[INTERCEPT_DR] = 0; + + recalc_intercepts(svm); +} + static int direct_access_msr_slot(u32 msr) { u32 i; @@ -947,50 +1041,24 @@ static void svm_disable_lbrv(struct kvm_vcpu *vcpu) svm_copy_lbrs(svm->vmcb01.ptr, svm->vmcb); } -static int svm_get_lbr_msr(struct vcpu_svm *svm, u32 index) +static struct vmcb *svm_get_lbr_vmcb(struct vcpu_svm *svm) { /* - * If the LBR virtualization is disabled, the LBR msrs are always - * kept in the vmcb01 to avoid copying them on nested guest entries. - * - * If nested, and the LBR virtualization is enabled/disabled, the msrs - * are moved between the vmcb01 and vmcb02 as needed. + * If LBR virtualization is disabled, the LBR MSRs are always kept in + * vmcb01. If LBR virtualization is enabled and L1 is running VMs of + * its own, the MSRs are moved between vmcb01 and vmcb02 as needed. */ - struct vmcb *vmcb = - (svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) ? - svm->vmcb : svm->vmcb01.ptr; - - switch (index) { - case MSR_IA32_DEBUGCTLMSR: - return vmcb->save.dbgctl; - case MSR_IA32_LASTBRANCHFROMIP: - return vmcb->save.br_from; - case MSR_IA32_LASTBRANCHTOIP: - return vmcb->save.br_to; - case MSR_IA32_LASTINTFROMIP: - return vmcb->save.last_excp_from; - case MSR_IA32_LASTINTTOIP: - return vmcb->save.last_excp_to; - default: - KVM_BUG(false, svm->vcpu.kvm, - "%s: Unknown MSR 0x%x", __func__, index); - return 0; - } + return svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK ? svm->vmcb : + svm->vmcb01.ptr; } void svm_update_lbrv(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - - bool enable_lbrv = svm_get_lbr_msr(svm, MSR_IA32_DEBUGCTLMSR) & - DEBUGCTLMSR_LBR; - - bool current_enable_lbrv = !!(svm->vmcb->control.virt_ext & - LBR_CTL_ENABLE_MASK); - - if (unlikely(is_guest_mode(vcpu) && svm->lbrv_enabled)) - if (unlikely(svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK)) - enable_lbrv = true; + bool current_enable_lbrv = svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK; + bool enable_lbrv = (svm_get_lbr_vmcb(svm)->save.dbgctl & DEBUGCTLMSR_LBR) || + (is_guest_mode(vcpu) && guest_can_use(vcpu, X86_FEATURE_LBRV) && + (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK)); if (enable_lbrv == current_enable_lbrv) return; @@ -1101,21 +1169,23 @@ static u64 svm_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu) return svm->tsc_ratio_msr; } -static void svm_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) +static void svm_write_tsc_offset(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb01.ptr->control.tsc_offset = vcpu->arch.l1_tsc_offset; - svm->vmcb->control.tsc_offset = offset; + svm->vmcb->control.tsc_offset = vcpu->arch.tsc_offset; vmcb_mark_dirty(svm->vmcb, VMCB_INTERCEPTS); } -static void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 multiplier) +void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu) { - __svm_write_tsc_multiplier(multiplier); + preempt_disable(); + if (to_svm(vcpu)->guest_state_loaded) + __svm_write_tsc_multiplier(vcpu->arch.tsc_scaling_ratio); + preempt_enable(); } - /* Evaluate instruction intercepts that depend on guest CPUID features. */ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu, struct vcpu_svm *svm) @@ -1156,8 +1226,6 @@ static inline void init_vmcb_after_set_cpuid(struct kvm_vcpu *vcpu) set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SYSENTER_EIP, 0, 0); set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SYSENTER_ESP, 0, 0); - - svm->v_vmload_vmsave_enabled = false; } else { /* * If hardware supports Virtual VMLOAD VMSAVE then enable it @@ -1201,10 +1269,9 @@ static void init_vmcb(struct kvm_vcpu *vcpu) * Guest access to VMware backdoor ports could legitimately * trigger #GP because of TSS I/O permission bitmap. * We intercept those #GP and allow access to them anyway - * as VMware does. Don't intercept #GP for SEV guests as KVM can't - * decrypt guest memory to decode the faulting instruction. + * as VMware does. */ - if (enable_vmware_backdoor && !sev_guest(vcpu->kvm)) + if (enable_vmware_backdoor) set_exception_intercept(svm, GP_VECTOR); svm_set_intercept(svm, INTERCEPT_INTR); @@ -1480,7 +1547,14 @@ static void svm_prepare_switch_to_guest(struct kvm_vcpu *vcpu) if (tsc_scaling) __svm_write_tsc_multiplier(vcpu->arch.tsc_scaling_ratio); - if (likely(tsc_aux_uret_slot >= 0)) + /* + * TSC_AUX is always virtualized for SEV-ES guests when the feature is + * available. The user return MSR support is not required in this case + * because TSC_AUX is restored on #VMEXIT from the host save area + * (which has been initialized in svm_hardware_enable()). + */ + if (likely(tsc_aux_uret_slot >= 0) && + (!boot_cpu_has(X86_FEATURE_V_TSC_AUX) || !sev_es_guest(vcpu->kvm))) kvm_set_user_return_msr(tsc_aux_uret_slot, svm->tsc_aux, -1ull); svm->guest_state_loaded = true; @@ -1949,7 +2023,7 @@ static void svm_sync_dirty_debug_regs(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - if (vcpu->arch.guest_state_protected) + if (WARN_ON_ONCE(sev_es_guest(vcpu->kvm))) return; get_debugreg(vcpu->arch.db[0], 0); @@ -2510,12 +2584,13 @@ static int iret_interception(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); + WARN_ON_ONCE(sev_es_guest(vcpu->kvm)); + ++vcpu->stat.nmi_window_exits; svm->awaiting_iret_completion = true; svm_clr_iret_intercept(svm); - if (!sev_es_guest(vcpu->kvm)) - svm->nmi_iret_rip = kvm_rip_read(vcpu); + svm->nmi_iret_rip = kvm_rip_read(vcpu); kvm_make_request(KVM_REQ_EVENT, vcpu); return 1; @@ -2680,6 +2755,13 @@ static int dr_interception(struct kvm_vcpu *vcpu) unsigned long val; int err = 0; + /* + * SEV-ES intercepts DR7 only to disable guest debugging and the guest issues a VMGEXIT + * for DR7 write only. KVM cannot change DR7 (always swapped as type 'A') so return early. + */ + if (sev_es_guest(vcpu->kvm)) + return 1; + if (vcpu->guest_debug == 0) { /* * No more DR vmexits; force a reload of the debug registers @@ -2764,7 +2846,8 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) switch (msr_info->index) { case MSR_AMD64_TSC_RATIO: - if (!msr_info->host_initiated && !svm->tsc_scaling_enabled) + if (!msr_info->host_initiated && + !guest_can_use(vcpu, X86_FEATURE_TSCRATEMSR)) return 1; msr_info->data = svm->tsc_ratio_msr; break; @@ -2802,11 +2885,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = svm->tsc_aux; break; case MSR_IA32_DEBUGCTLMSR: + msr_info->data = svm_get_lbr_vmcb(svm)->save.dbgctl; + break; case MSR_IA32_LASTBRANCHFROMIP: + msr_info->data = svm_get_lbr_vmcb(svm)->save.br_from; + break; case MSR_IA32_LASTBRANCHTOIP: + msr_info->data = svm_get_lbr_vmcb(svm)->save.br_to; + break; case MSR_IA32_LASTINTFROMIP: + msr_info->data = svm_get_lbr_vmcb(svm)->save.last_excp_from; + break; case MSR_IA32_LASTINTTOIP: - msr_info->data = svm_get_lbr_msr(svm, msr_info->index); + msr_info->data = svm_get_lbr_vmcb(svm)->save.last_excp_to; break; case MSR_VM_HSAVE_PA: msr_info->data = svm->nested.hsave_msr; @@ -2906,7 +2997,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) switch (ecx) { case MSR_AMD64_TSC_RATIO: - if (!svm->tsc_scaling_enabled) { + if (!guest_can_use(vcpu, X86_FEATURE_TSCRATEMSR)) { if (!msr->host_initiated) return 1; @@ -2928,7 +3019,8 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) svm->tsc_ratio_msr = data; - if (svm->tsc_scaling_enabled && is_guest_mode(vcpu)) + if (guest_can_use(vcpu, X86_FEATURE_TSCRATEMSR) && + is_guest_mode(vcpu)) nested_svm_update_tsc_ratio_msr(vcpu); break; @@ -3016,6 +3108,16 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) svm->sysenter_esp_hi = guest_cpuid_is_intel(vcpu) ? (data >> 32) : 0; break; case MSR_TSC_AUX: + /* + * TSC_AUX is always virtualized for SEV-ES guests when the + * feature is available. The user return MSR support is not + * required in this case because TSC_AUX is restored on #VMEXIT + * from the host save area (which has been initialized in + * svm_hardware_enable()). + */ + if (boot_cpu_has(X86_FEATURE_V_TSC_AUX) && sev_es_guest(vcpu->kvm)) + break; + /* * TSC_AUX is usually changed only during boot and never read * directly. Intercept TSC_AUX instead of exposing it to the @@ -3037,13 +3139,8 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) if (data & DEBUGCTL_RESERVED_BITS) return 1; - if (svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK) - svm->vmcb->save.dbgctl = data; - else - svm->vmcb01.ptr->save.dbgctl = data; - + svm_get_lbr_vmcb(svm)->save.dbgctl = data; svm_update_lbrv(vcpu); - break; case MSR_VM_HSAVE_PA: /* @@ -3769,6 +3866,19 @@ static void svm_enable_nmi_window(struct kvm_vcpu *vcpu) if (svm_get_nmi_mask(vcpu) && !svm->awaiting_iret_completion) return; /* IRET will cause a vm exit */ + /* + * SEV-ES guests are responsible for signaling when a vCPU is ready to + * receive a new NMI, as SEV-ES guests can't be single-stepped, i.e. + * KVM can't intercept and single-step IRET to detect when NMIs are + * unblocked (architecturally speaking). See SVM_VMGEXIT_NMI_COMPLETE. + * + * Note, GIF is guaranteed to be '1' for SEV-ES guests as hardware + * ignores SEV-ES guest writes to EFER.SVME *and* CLGI/STGI are not + * supported NAEs in the GHCB protocol. + */ + if (sev_es_guest(vcpu->kvm)) + return; + if (!gif_set(svm)) { if (vgif) svm_set_intercept(svm, INTERCEPT_STGI); @@ -3918,12 +4028,11 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) svm->soft_int_injected = false; /* - * If we've made progress since setting HF_IRET_MASK, we've + * If we've made progress since setting awaiting_iret_completion, we've * executed an IRET and can allow NMI injection. */ if (svm->awaiting_iret_completion && - (sev_es_guest(vcpu->kvm) || - kvm_rip_read(vcpu) != svm->nmi_iret_rip)) { + kvm_rip_read(vcpu) != svm->nmi_iret_rip) { svm->awaiting_iret_completion = false; svm->nmi_masked = false; kvm_make_request(KVM_REQ_EVENT, vcpu); @@ -4207,30 +4316,38 @@ static bool svm_has_emulated_msr(struct kvm *kvm, u32 index) static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - struct kvm_cpuid_entry2 *best; - vcpu->arch.xsaves_enabled = guest_cpuid_has(vcpu, X86_FEATURE_XSAVE) && - boot_cpu_has(X86_FEATURE_XSAVE) && - boot_cpu_has(X86_FEATURE_XSAVES); - - /* Update nrips enabled cache */ - svm->nrips_enabled = kvm_cpu_cap_has(X86_FEATURE_NRIPS) && - guest_cpuid_has(vcpu, X86_FEATURE_NRIPS); - - svm->tsc_scaling_enabled = tsc_scaling && guest_cpuid_has(vcpu, X86_FEATURE_TSCRATEMSR); - svm->lbrv_enabled = lbrv && guest_cpuid_has(vcpu, X86_FEATURE_LBRV); - - svm->v_vmload_vmsave_enabled = vls && guest_cpuid_has(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD); - - svm->pause_filter_enabled = kvm_cpu_cap_has(X86_FEATURE_PAUSEFILTER) && - guest_cpuid_has(vcpu, X86_FEATURE_PAUSEFILTER); + /* + * SVM doesn't provide a way to disable just XSAVES in the guest, KVM + * can only disable all variants of by disallowing CR4.OSXSAVE from + * being set. As a result, if the host has XSAVE and XSAVES, and the + * guest has XSAVE enabled, the guest can execute XSAVES without + * faulting. Treat XSAVES as enabled in this case regardless of + * whether it's advertised to the guest so that KVM context switches + * XSS on VM-Enter/VM-Exit. Failure to do so would effectively give + * the guest read/write access to the host's XSS. + */ + if (boot_cpu_has(X86_FEATURE_XSAVE) && + boot_cpu_has(X86_FEATURE_XSAVES) && + guest_cpuid_has(vcpu, X86_FEATURE_XSAVE)) + kvm_governed_feature_set(vcpu, X86_FEATURE_XSAVES); - svm->pause_threshold_enabled = kvm_cpu_cap_has(X86_FEATURE_PFTHRESHOLD) && - guest_cpuid_has(vcpu, X86_FEATURE_PFTHRESHOLD); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_NRIPS); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_TSCRATEMSR); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_LBRV); - svm->vgif_enabled = vgif && guest_cpuid_has(vcpu, X86_FEATURE_VGIF); + /* + * Intercept VMLOAD if the vCPU mode is Intel in order to emulate that + * VMLOAD drops bits 63:32 of SYSENTER (ignoring the fact that exposing + * SVM on Intel is bonkers and extremely unlikely to work). + */ + if (!guest_cpuid_is_intel(vcpu)) + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_V_VMSAVE_VMLOAD); - svm->vnmi_enabled = vnmi && guest_cpuid_has(vcpu, X86_FEATURE_VNMI); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_PAUSEFILTER); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_PFTHRESHOLD); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_VGIF); + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_VNMI); svm_recalc_instruction_intercepts(vcpu, svm); @@ -4242,12 +4359,8 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) set_msr_interception(vcpu, svm->msrpm, MSR_IA32_FLUSH_CMD, 0, !!guest_cpuid_has(vcpu, X86_FEATURE_FLUSH_L1D)); - /* For sev guests, the memory encryption bit is not reserved in CR3. */ - if (sev_guest(vcpu->kvm)) { - best = kvm_find_cpuid_entry(vcpu, 0x8000001F); - if (best) - vcpu->arch.reserved_gpa_bits &= ~(1UL << (best->ebx & 0x3f)); - } + if (sev_guest(vcpu->kvm)) + sev_vcpu_after_set_cpuid(svm); init_vmcb_after_set_cpuid(vcpu); } @@ -4651,16 +4764,25 @@ static bool svm_can_emulate_instruction(struct kvm_vcpu *vcpu, int emul_type, * and cannot be decrypted by KVM, i.e. KVM would read cyphertext and * decode garbage. * - * Inject #UD if KVM reached this point without an instruction buffer. - * In practice, this path should never be hit by a well-behaved guest, - * e.g. KVM doesn't intercept #UD or #GP for SEV guests, but this path - * is still theoretically reachable, e.g. via unaccelerated fault-like - * AVIC access, and needs to be handled by KVM to avoid putting the - * guest into an infinite loop. Injecting #UD is somewhat arbitrary, - * but its the least awful option given lack of insight into the guest. + * If KVM is NOT trying to simply skip an instruction, inject #UD if + * KVM reached this point without an instruction buffer. In practice, + * this path should never be hit by a well-behaved guest, e.g. KVM + * doesn't intercept #UD or #GP for SEV guests, but this path is still + * theoretically reachable, e.g. via unaccelerated fault-like AVIC + * access, and needs to be handled by KVM to avoid putting the guest + * into an infinite loop. Injecting #UD is somewhat arbitrary, but + * its the least awful option given lack of insight into the guest. + * + * If KVM is trying to skip an instruction, simply resume the guest. + * If a #NPF occurs while the guest is vectoring an INT3/INTO, then KVM + * will attempt to re-inject the INT3/INTO and skip the instruction. + * In that scenario, retrying the INT3/INTO and hoping the guest will + * make forward progress is the only option that has a chance of + * success (and in practice it will work the vast majority of the time). */ if (unlikely(!insn)) { - kvm_queue_exception(vcpu, UD_VECTOR); + if (!(emul_type & EMULTYPE_SKIP)) + kvm_queue_exception(vcpu, UD_VECTOR); return false; } @@ -5112,9 +5234,11 @@ static __init int svm_hardware_setup(void) svm_adjust_mmio_mask(); + nrips = nrips && boot_cpu_has(X86_FEATURE_NRIPS); + /* * Note, SEV setup consumes npt_enabled and enable_mmio_caching (which - * may be modified by svm_adjust_mmio_mask()). + * may be modified by svm_adjust_mmio_mask()), as well as nrips. */ sev_hardware_setup(); @@ -5126,11 +5250,6 @@ static __init int svm_hardware_setup(void) goto err; } - if (nrips) { - if (!boot_cpu_has(X86_FEATURE_NRIPS)) - nrips = false; - } - enable_apicv = avic = avic && avic_hardware_setup(); if (!enable_apicv) { @@ -5213,6 +5332,13 @@ static struct kvm_x86_init_ops svm_init_ops __initdata = { .pmu_ops = &amd_pmu_ops, }; +static void __svm_exit(void) +{ + kvm_x86_vendor_exit(); + + cpu_emergency_unregister_virt_callback(svm_emergency_disable); +} + static int __init svm_init(void) { int r; @@ -5226,6 +5352,8 @@ static int __init svm_init(void) if (r) return r; + cpu_emergency_register_virt_callback(svm_emergency_disable); + /* * Common KVM initialization _must_ come last, after this, /dev/kvm is * exposed to userspace! @@ -5238,14 +5366,14 @@ static int __init svm_init(void) return 0; err_kvm_init: - kvm_x86_vendor_exit(); + __svm_exit(); return r; } static void __exit svm_exit(void) { kvm_exit(); - kvm_x86_vendor_exit(); + __svm_exit(); } module_init(svm_init) diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 8239c8de45acfd3c6117acd8dbe5c14d2b9039d7..be67ab7fdd104e33f1ecc960e94d23e9385acf73 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -22,6 +22,7 @@ #include #include +#include "cpuid.h" #include "kvm_cache_regs.h" #define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT) @@ -33,6 +34,7 @@ #define MSRPM_OFFSETS 32 extern u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly; extern bool npt_enabled; +extern int nrips; extern int vgif; extern bool intercept_smi; extern bool x2avic_enabled; @@ -260,16 +262,6 @@ struct vcpu_svm { unsigned long soft_int_next_rip; bool soft_int_injected; - /* optional nested SVM features that are enabled for this guest */ - bool nrips_enabled : 1; - bool tsc_scaling_enabled : 1; - bool v_vmload_vmsave_enabled : 1; - bool lbrv_enabled : 1; - bool pause_filter_enabled : 1; - bool pause_threshold_enabled : 1; - bool vgif_enabled : 1; - bool vnmi_enabled : 1; - u32 ldr_reg; u32 dfr_reg; struct page *avic_backing_page; @@ -406,48 +398,6 @@ static inline bool vmcb12_is_intercept(struct vmcb_ctrl_area_cached *control, u3 return test_bit(bit, (unsigned long *)&control->intercepts); } -static inline void set_dr_intercepts(struct vcpu_svm *svm) -{ - struct vmcb *vmcb = svm->vmcb01.ptr; - - if (!sev_es_guest(svm->vcpu.kvm)) { - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR0_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR1_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR2_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR3_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR4_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR5_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR6_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR0_WRITE); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR1_WRITE); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR2_WRITE); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR3_WRITE); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR4_WRITE); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR5_WRITE); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR6_WRITE); - } - - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); - - recalc_intercepts(svm); -} - -static inline void clr_dr_intercepts(struct vcpu_svm *svm) -{ - struct vmcb *vmcb = svm->vmcb01.ptr; - - vmcb->control.intercepts[INTERCEPT_DR] = 0; - - /* DR7 access must remain intercepted for an SEV-ES guest */ - if (sev_es_guest(svm->vcpu.kvm)) { - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_READ); - vmcb_set_intercept(&vmcb->control, INTERCEPT_DR7_WRITE); - } - - recalc_intercepts(svm); -} - static inline void set_exception_intercept(struct vcpu_svm *svm, u32 bit) { struct vmcb *vmcb = svm->vmcb01.ptr; @@ -493,7 +443,8 @@ static inline bool svm_is_intercept(struct vcpu_svm *svm, int bit) static inline bool nested_vgif_enabled(struct vcpu_svm *svm) { - return svm->vgif_enabled && (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK); + return guest_can_use(&svm->vcpu, X86_FEATURE_VGIF) && + (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK); } static inline struct vmcb *get_vgif_vmcb(struct vcpu_svm *svm) @@ -544,7 +495,7 @@ static inline bool nested_npt_enabled(struct vcpu_svm *svm) static inline bool nested_vnmi_enabled(struct vcpu_svm *svm) { - return svm->vnmi_enabled && + return guest_can_use(&svm->vcpu, X86_FEATURE_VNMI) && (svm->nested.ctl.int_ctl & V_NMI_ENABLE_MASK); } @@ -660,7 +611,7 @@ int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr, bool has_error_code, u32 error_code); int nested_svm_exit_special(struct vcpu_svm *svm); void nested_svm_update_tsc_ratio_msr(struct kvm_vcpu *vcpu); -void __svm_write_tsc_multiplier(u64 multiplier); +void svm_write_tsc_multiplier(struct kvm_vcpu *vcpu); void nested_copy_vmcb_control_to_cache(struct vcpu_svm *svm, struct vmcb_control_area *control); void nested_copy_vmcb_save_to_cache(struct vcpu_svm *svm, @@ -733,6 +684,7 @@ void __init sev_hardware_setup(void); void sev_hardware_unsetup(void); int sev_cpu_init(struct svm_cpu_data *sd); void sev_init_vmcb(struct vcpu_svm *svm); +void sev_vcpu_after_set_cpuid(struct vcpu_svm *svm); void sev_free_vcpu(struct kvm_vcpu *vcpu); int sev_handle_vmgexit(struct kvm_vcpu *vcpu); int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in); diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index d0abee35d7ba48fd134d4c52c262e1e9471eb4ae..41a4533f9989748c9226255e6aa7c043ced40ecc 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -252,7 +252,7 @@ static inline bool cpu_has_vmx_pml(void) static inline bool cpu_has_vmx_xsaves(void) { return vmcs_config.cpu_based_2nd_exec_ctrl & - SECONDARY_EXEC_XSAVES; + SECONDARY_EXEC_ENABLE_XSAVES; } static inline bool cpu_has_vmx_waitpkg(void) diff --git a/arch/x86/kvm/vmx/hyperv.c b/arch/x86/kvm/vmx/hyperv.c index 79450e1ed7cfb21710badb9185b97e551d3ffccf..313b8bb5b8a7cb0befdb65cafcb9752c966dd81a 100644 --- a/arch/x86/kvm/vmx/hyperv.c +++ b/arch/x86/kvm/vmx/hyperv.c @@ -78,7 +78,7 @@ SECONDARY_EXEC_DESC | \ SECONDARY_EXEC_ENABLE_RDTSCP | \ SECONDARY_EXEC_ENABLE_INVPCID | \ - SECONDARY_EXEC_XSAVES | \ + SECONDARY_EXEC_ENABLE_XSAVES | \ SECONDARY_EXEC_RDSEED_EXITING | \ SECONDARY_EXEC_RDRAND_EXITING | \ SECONDARY_EXEC_TSC_SCALING | \ diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index 516391cc0d64fb9689d17fea0d04903c13abe66a..c5ec0ef51ff78fa3baae19092d12057c07476394 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -2307,7 +2307,7 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_ENABLE_RDTSCP | - SECONDARY_EXEC_XSAVES | + SECONDARY_EXEC_ENABLE_XSAVES | SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE | SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | SECONDARY_EXEC_APIC_REGISTER_VIRT | @@ -6331,7 +6331,7 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu, * If if it were, XSS would have to be checked against * the XSS exit bitmap in vmcs12. */ - return nested_cpu_has2(vmcs12, SECONDARY_EXEC_XSAVES); + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENABLE_XSAVES); case EXIT_REASON_UMWAIT: case EXIT_REASON_TPAUSE: return nested_cpu_has2(vmcs12, @@ -6426,7 +6426,7 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu, vmx = to_vmx(vcpu); vmcs12 = get_vmcs12(vcpu); - if (nested_vmx_allowed(vcpu) && + if (guest_can_use(vcpu, X86_FEATURE_VMX) && (vmx->nested.vmxon || vmx->nested.smm.vmxon)) { kvm_state.hdr.vmx.vmxon_pa = vmx->nested.vmxon_ptr; kvm_state.hdr.vmx.vmcs12_pa = vmx->nested.current_vmptr; @@ -6567,7 +6567,7 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu, if (kvm_state->flags & ~KVM_STATE_NESTED_EVMCS) return -EINVAL; } else { - if (!nested_vmx_allowed(vcpu)) + if (!guest_can_use(vcpu, X86_FEATURE_VMX)) return -EINVAL; if (!page_address_valid(vcpu, kvm_state->hdr.vmx.vmxon_pa)) @@ -6601,7 +6601,8 @@ static int vmx_set_nested_state(struct kvm_vcpu *vcpu, return -EINVAL; if ((kvm_state->flags & KVM_STATE_NESTED_EVMCS) && - (!nested_vmx_allowed(vcpu) || !vmx->nested.enlightened_vmcs_enabled)) + (!guest_can_use(vcpu, X86_FEATURE_VMX) || + !vmx->nested.enlightened_vmcs_enabled)) return -EINVAL; vmx_leave_nested(vcpu); @@ -6874,7 +6875,7 @@ static void nested_vmx_setup_secondary_ctls(u32 ept_caps, SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_ENABLE_VMFUNC | SECONDARY_EXEC_RDSEED_EXITING | - SECONDARY_EXEC_XSAVES | + SECONDARY_EXEC_ENABLE_XSAVES | SECONDARY_EXEC_TSC_SCALING | SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE; diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h index 96952263b029bfed932ec13190c545843b6acff0..b4b9d51438c6b3e263ce80722ae69eadc26f1fb9 100644 --- a/arch/x86/kvm/vmx/nested.h +++ b/arch/x86/kvm/vmx/nested.h @@ -168,7 +168,7 @@ static inline int nested_cpu_has_ept(struct vmcs12 *vmcs12) static inline bool nested_cpu_has_xsaves(struct vmcs12 *vmcs12) { - return nested_cpu_has2(vmcs12, SECONDARY_EXEC_XSAVES); + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENABLE_XSAVES); } static inline bool nested_cpu_has_pml(struct vmcs12 *vmcs12) diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 80c769c58a876530674e823434bcbf627d19d3c6..f2efa0bf7ae8d4ce11670f3a05a6a50d6247d5bb 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -22,23 +22,51 @@ #define MSR_PMC_FULL_WIDTH_BIT (MSR_IA32_PMC0 - MSR_IA32_PERFCTR0) +enum intel_pmu_architectural_events { + /* + * The order of the architectural events matters as support for each + * event is enumerated via CPUID using the index of the event. + */ + INTEL_ARCH_CPU_CYCLES, + INTEL_ARCH_INSTRUCTIONS_RETIRED, + INTEL_ARCH_REFERENCE_CYCLES, + INTEL_ARCH_LLC_REFERENCES, + INTEL_ARCH_LLC_MISSES, + INTEL_ARCH_BRANCHES_RETIRED, + INTEL_ARCH_BRANCHES_MISPREDICTED, + + NR_REAL_INTEL_ARCH_EVENTS, + + /* + * Pseudo-architectural event used to implement IA32_FIXED_CTR2, a.k.a. + * TSC reference cycles. The architectural reference cycles event may + * or may not actually use the TSC as the reference, e.g. might use the + * core crystal clock or the bus clock (yeah, "architectural"). + */ + PSEUDO_ARCH_REFERENCE_CYCLES = NR_REAL_INTEL_ARCH_EVENTS, + NR_INTEL_ARCH_EVENTS, +}; + static struct { u8 eventsel; u8 unit_mask; } const intel_arch_events[] = { - [0] = { 0x3c, 0x00 }, - [1] = { 0xc0, 0x00 }, - [2] = { 0x3c, 0x01 }, - [3] = { 0x2e, 0x4f }, - [4] = { 0x2e, 0x41 }, - [5] = { 0xc4, 0x00 }, - [6] = { 0xc5, 0x00 }, - /* The above index must match CPUID 0x0A.EBX bit vector */ - [7] = { 0x00, 0x03 }, + [INTEL_ARCH_CPU_CYCLES] = { 0x3c, 0x00 }, + [INTEL_ARCH_INSTRUCTIONS_RETIRED] = { 0xc0, 0x00 }, + [INTEL_ARCH_REFERENCE_CYCLES] = { 0x3c, 0x01 }, + [INTEL_ARCH_LLC_REFERENCES] = { 0x2e, 0x4f }, + [INTEL_ARCH_LLC_MISSES] = { 0x2e, 0x41 }, + [INTEL_ARCH_BRANCHES_RETIRED] = { 0xc4, 0x00 }, + [INTEL_ARCH_BRANCHES_MISPREDICTED] = { 0xc5, 0x00 }, + [PSEUDO_ARCH_REFERENCE_CYCLES] = { 0x00, 0x03 }, }; /* mapping between fixed pmc index and intel_arch_events array */ -static int fixed_pmc_events[] = {1, 0, 7}; +static int fixed_pmc_events[] = { + [0] = INTEL_ARCH_INSTRUCTIONS_RETIRED, + [1] = INTEL_ARCH_CPU_CYCLES, + [2] = PSEUDO_ARCH_REFERENCE_CYCLES, +}; static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data) { @@ -80,16 +108,18 @@ static bool intel_hw_event_available(struct kvm_pmc *pmc) u8 unit_mask = (pmc->eventsel & ARCH_PERFMON_EVENTSEL_UMASK) >> 8; int i; - for (i = 0; i < ARRAY_SIZE(intel_arch_events); i++) { + BUILD_BUG_ON(ARRAY_SIZE(intel_arch_events) != NR_INTEL_ARCH_EVENTS); + + /* + * Disallow events reported as unavailable in guest CPUID. Note, this + * doesn't apply to pseudo-architectural events. + */ + for (i = 0; i < NR_REAL_INTEL_ARCH_EVENTS; i++) { if (intel_arch_events[i].eventsel != event_select || intel_arch_events[i].unit_mask != unit_mask) continue; - /* disable event that reported as not present by cpuid */ - if ((i < 7) && !(pmu->available_event_types & (1 << i))) - return false; - - break; + return pmu->available_event_types & BIT(i); } return true; @@ -438,16 +468,17 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) static void setup_fixed_pmc_eventsel(struct kvm_pmu *pmu) { - size_t size = ARRAY_SIZE(fixed_pmc_events); - struct kvm_pmc *pmc; - u32 event; int i; + BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_events) != KVM_PMC_MAX_FIXED); + for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { - pmc = &pmu->fixed_counters[i]; - event = fixed_pmc_events[array_index_nospec(i, size)]; + int index = array_index_nospec(i, KVM_PMC_MAX_FIXED); + struct kvm_pmc *pmc = &pmu->fixed_counters[index]; + u32 event = fixed_pmc_events[index]; + pmc->eventsel = (intel_arch_events[event].unit_mask << 8) | - intel_arch_events[event].eventsel; + intel_arch_events[event].eventsel; } } @@ -508,10 +539,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) if (pmu->version == 1) { pmu->nr_arch_fixed_counters = 0; } else { - pmu->nr_arch_fixed_counters = - min3(ARRAY_SIZE(fixed_pmc_events), - (size_t) edx.split.num_counters_fixed, - (size_t)kvm_pmu_cap.num_counters_fixed); + pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed, + kvm_pmu_cap.num_counters_fixed); edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, kvm_pmu_cap.bit_width_fixed); pmu->counter_bitmask[KVM_PMC_FIXED] = diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b483a8baaacf32056de25f7e471c0078fee6722d..72e3943f36935c1ed6ae13ba2f1e6f20a6264de8 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -41,13 +41,12 @@ #include #include #include -#include +#include #include #include #include #include #include -#include #include #include "capabilities.h" @@ -237,9 +236,6 @@ static const struct { #define L1D_CACHE_ORDER 4 static void *vmx_l1d_flush_pages; -/* Control for disabling CPU Fill buffer clear */ -static bool __read_mostly vmx_fb_clear_ctrl_available; - static int vmx_setup_l1d_flush(enum vmx_l1d_flush_state l1tf) { struct page *page; @@ -255,14 +251,9 @@ static int vmx_setup_l1d_flush(enum vmx_l1d_flush_state l1tf) return 0; } - if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) { - u64 msr; - - rdmsrl(MSR_IA32_ARCH_CAPABILITIES, msr); - if (msr & ARCH_CAP_SKIP_VMENTRY_L1DFLUSH) { - l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_NOT_REQUIRED; - return 0; - } + if (host_arch_capabilities & ARCH_CAP_SKIP_VMENTRY_L1DFLUSH) { + l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_NOT_REQUIRED; + return 0; } /* If set to auto use the default l1tf mitigation method */ @@ -366,22 +357,9 @@ static int vmentry_l1d_flush_set(const char *s, const struct kernel_param *kp) static int vmentry_l1d_flush_get(char *s, const struct kernel_param *kp) { if (WARN_ON_ONCE(l1tf_vmx_mitigation >= ARRAY_SIZE(vmentry_l1d_param))) - return sprintf(s, "???\n"); + return sysfs_emit(s, "???\n"); - return sprintf(s, "%s\n", vmentry_l1d_param[l1tf_vmx_mitigation].option); -} - -static void vmx_setup_fb_clear_ctrl(void) -{ - u64 msr; - - if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES) && - !boot_cpu_has_bug(X86_BUG_MDS) && - !boot_cpu_has_bug(X86_BUG_TAA)) { - rdmsrl(MSR_IA32_ARCH_CAPABILITIES, msr); - if (msr & ARCH_CAP_FB_CLEAR_CTRL) - vmx_fb_clear_ctrl_available = true; - } + return sysfs_emit(s, "%s\n", vmentry_l1d_param[l1tf_vmx_mitigation].option); } static __always_inline void vmx_disable_fb_clear(struct vcpu_vmx *vmx) @@ -409,7 +387,9 @@ static __always_inline void vmx_enable_fb_clear(struct vcpu_vmx *vmx) static void vmx_update_fb_clear_dis(struct kvm_vcpu *vcpu, struct vcpu_vmx *vmx) { - vmx->disable_fb_clear = vmx_fb_clear_ctrl_available; + vmx->disable_fb_clear = (host_arch_capabilities & ARCH_CAP_FB_CLEAR_CTRL) && + !boot_cpu_has_bug(X86_BUG_MDS) && + !boot_cpu_has_bug(X86_BUG_TAA); /* * If guest will not execute VERW, there is no need to set FB_CLEAR_DIS @@ -754,17 +734,51 @@ static int vmx_set_guest_uret_msr(struct vcpu_vmx *vmx, return ret; } -#ifdef CONFIG_KEXEC_CORE -static void crash_vmclear_local_loaded_vmcss(void) +/* + * Disable VMX and clear CR4.VMXE (even if VMXOFF faults) + * + * Note, VMXOFF causes a #UD if the CPU is !post-VMXON, but it's impossible to + * atomically track post-VMXON state, e.g. this may be called in NMI context. + * Eat all faults as all other faults on VMXOFF faults are mode related, i.e. + * faults are guaranteed to be due to the !post-VMXON check unless the CPU is + * magically in RM, VM86, compat mode, or at CPL>0. + */ +static int kvm_cpu_vmxoff(void) +{ + asm_volatile_goto("1: vmxoff\n\t" + _ASM_EXTABLE(1b, %l[fault]) + ::: "cc", "memory" : fault); + + cr4_clear_bits(X86_CR4_VMXE); + return 0; + +fault: + cr4_clear_bits(X86_CR4_VMXE); + return -EIO; +} + +static void vmx_emergency_disable(void) { int cpu = raw_smp_processor_id(); struct loaded_vmcs *v; + kvm_rebooting = true; + + /* + * Note, CR4.VMXE can be _cleared_ in NMI context, but it can only be + * set in task context. If this races with VMX is disabled by an NMI, + * VMCLEAR and VMXOFF may #UD, but KVM will eat those faults due to + * kvm_rebooting set. + */ + if (!(__read_cr4() & X86_CR4_VMXE)) + return; + list_for_each_entry(v, &per_cpu(loaded_vmcss_on_cpu, cpu), loaded_vmcss_on_cpu_link) vmcs_clear(v->vmcs); + + kvm_cpu_vmxoff(); } -#endif /* CONFIG_KEXEC_CORE */ static void __loaded_vmcs_clear(void *arg) { @@ -1899,25 +1913,14 @@ u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu) return kvm_caps.default_tsc_scaling_ratio; } -static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) +static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu) { - vmcs_write64(TSC_OFFSET, offset); + vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset); } -static void vmx_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 multiplier) +static void vmx_write_tsc_multiplier(struct kvm_vcpu *vcpu) { - vmcs_write64(TSC_MULTIPLIER, multiplier); -} - -/* - * nested_vmx_allowed() checks whether a guest should be allowed to use VMX - * instructions and MSRs (i.e., nested VMX). Nested VMX is disabled for - * all guests if the "nested" module option is off, and can also be disabled - * for a single guest by disabling its VMX cpuid bit. - */ -bool nested_vmx_allowed(struct kvm_vcpu *vcpu) -{ - return nested && guest_cpuid_has(vcpu, X86_FEATURE_VMX); + vmcs_write64(TSC_MULTIPLIER, vcpu->arch.tsc_scaling_ratio); } /* @@ -2047,7 +2050,7 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) [msr_info->index - MSR_IA32_SGXLEPUBKEYHASH0]; break; case KVM_FIRST_EMULATED_VMX_MSR ... KVM_LAST_EMULATED_VMX_MSR: - if (!nested_vmx_allowed(vcpu)) + if (!guest_can_use(vcpu, X86_FEATURE_VMX)) return 1; if (vmx_get_vmx_msr(&vmx->nested.msrs, msr_info->index, &msr_info->data)) @@ -2355,7 +2358,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case KVM_FIRST_EMULATED_VMX_MSR ... KVM_LAST_EMULATED_VMX_MSR: if (!msr_info->host_initiated) return 1; /* they are read-only */ - if (!nested_vmx_allowed(vcpu)) + if (!guest_can_use(vcpu, X86_FEATURE_VMX)) return 1; return vmx_set_vmx_msr(vcpu, msr_index, data); case MSR_IA32_RTIT_CTL: @@ -2729,11 +2732,11 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, return 0; } -static bool kvm_is_vmx_supported(void) +static bool __kvm_is_vmx_supported(void) { - int cpu = raw_smp_processor_id(); + int cpu = smp_processor_id(); - if (!cpu_has_vmx()) { + if (!(cpuid_ecx(1) & feature_bit(VMX))) { pr_err("VMX not supported by CPU %d\n", cpu); return false; } @@ -2747,13 +2750,24 @@ static bool kvm_is_vmx_supported(void) return true; } +static bool kvm_is_vmx_supported(void) +{ + bool supported; + + migrate_disable(); + supported = __kvm_is_vmx_supported(); + migrate_enable(); + + return supported; +} + static int vmx_check_processor_compat(void) { int cpu = raw_smp_processor_id(); struct vmcs_config vmcs_conf; struct vmx_capability vmx_cap; - if (!kvm_is_vmx_supported()) + if (!__kvm_is_vmx_supported()) return -EIO; if (setup_vmcs_config(&vmcs_conf, &vmx_cap) < 0) { @@ -2833,7 +2847,7 @@ static void vmx_hardware_disable(void) { vmclear_local_loaded_vmcss(); - if (cpu_vmxoff()) + if (kvm_cpu_vmxoff()) kvm_spurious_fault(); hv_reset_evmcs(); @@ -3071,13 +3085,6 @@ static void enter_rmode(struct kvm_vcpu *vcpu) vmx->rmode.vm86_active = 1; - /* - * Very old userspace does not call KVM_SET_TSS_ADDR before entering - * vcpu. Warn the user that an update is overdue. - */ - if (!kvm_vmx->tss_addr) - pr_warn_once("KVM_SET_TSS_ADDR needs to be called before running vCPU\n"); - vmx_segment_cache_clear(vmx); vmcs_writel(GUEST_TR_BASE, kvm_vmx->tss_addr); @@ -3350,7 +3357,7 @@ void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) vmx->emulation_required = vmx_emulation_required(vcpu); } -static int vmx_get_max_tdp_level(void) +static int vmx_get_max_ept_level(void) { if (cpu_has_vmx_ept_5levels()) return 5; @@ -4553,16 +4560,19 @@ vmx_adjust_secondary_exec_control(struct vcpu_vmx *vmx, u32 *exec_control, * based on a single guest CPUID bit, with a dedicated feature bit. This also * verifies that the control is actually supported by KVM and hardware. */ -#define vmx_adjust_sec_exec_control(vmx, exec_control, name, feat_name, ctrl_name, exiting) \ -({ \ - bool __enabled; \ - \ - if (cpu_has_vmx_##name()) { \ - __enabled = guest_cpuid_has(&(vmx)->vcpu, \ - X86_FEATURE_##feat_name); \ - vmx_adjust_secondary_exec_control(vmx, exec_control, \ - SECONDARY_EXEC_##ctrl_name, __enabled, exiting); \ - } \ +#define vmx_adjust_sec_exec_control(vmx, exec_control, name, feat_name, ctrl_name, exiting) \ +({ \ + struct kvm_vcpu *__vcpu = &(vmx)->vcpu; \ + bool __enabled; \ + \ + if (cpu_has_vmx_##name()) { \ + if (kvm_is_governed_feature(X86_FEATURE_##feat_name)) \ + __enabled = guest_can_use(__vcpu, X86_FEATURE_##feat_name); \ + else \ + __enabled = guest_cpuid_has(__vcpu, X86_FEATURE_##feat_name); \ + vmx_adjust_secondary_exec_control(vmx, exec_control, SECONDARY_EXEC_##ctrl_name,\ + __enabled, exiting); \ + } \ }) /* More macro magic for ENABLE_/opt-in versus _EXITING/opt-out controls. */ @@ -4622,19 +4632,7 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx) if (!enable_pml || !atomic_read(&vcpu->kvm->nr_memslots_dirty_logging)) exec_control &= ~SECONDARY_EXEC_ENABLE_PML; - if (cpu_has_vmx_xsaves()) { - /* Exposing XSAVES only when XSAVE is exposed */ - bool xsaves_enabled = - boot_cpu_has(X86_FEATURE_XSAVE) && - guest_cpuid_has(vcpu, X86_FEATURE_XSAVE) && - guest_cpuid_has(vcpu, X86_FEATURE_XSAVES); - - vcpu->arch.xsaves_enabled = xsaves_enabled; - - vmx_adjust_secondary_exec_control(vmx, &exec_control, - SECONDARY_EXEC_XSAVES, - xsaves_enabled, false); - } + vmx_adjust_sec_exec_feature(vmx, &exec_control, xsaves, XSAVES); /* * RDPID is also gated by ENABLE_RDTSCP, turn on the control if either @@ -4653,6 +4651,7 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx) SECONDARY_EXEC_ENABLE_RDTSCP, rdpid_or_rdtscp_enabled, false); } + vmx_adjust_sec_exec_feature(vmx, &exec_control, invpcid, INVPCID); vmx_adjust_sec_exec_exiting(vmx, &exec_control, rdrand, RDRAND); @@ -6796,8 +6795,10 @@ static void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu) vmcs_write64(APIC_ACCESS_ADDR, pfn_to_hpa(pfn)); read_unlock(&vcpu->kvm->mmu_lock); - vmx_flush_tlb_current(vcpu); - + /* + * No need for a manual TLB flush at this point, KVM has already done a + * flush if there were SPTEs pointing at the previous page. + */ out: /* * Do not pin apic access page in memory, the MMU notifier @@ -7243,13 +7244,20 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, flags); vcpu->arch.cr2 = native_read_cr2(); + vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET; + + vmx->idt_vectoring_info = 0; vmx_enable_fb_clear(vmx); - if (unlikely(vmx->fail)) + if (unlikely(vmx->fail)) { vmx->exit_reason.full = 0xdead; - else - vmx->exit_reason.full = vmcs_read32(VM_EXIT_REASON); + goto out; + } + + vmx->exit_reason.full = vmcs_read32(VM_EXIT_REASON); + if (likely(!vmx->exit_reason.failed_vmentry)) + vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD); if ((u16)vmx->exit_reason.basic == EXIT_REASON_EXCEPTION_NMI && is_nmi(vmx_get_intr_info(vcpu))) { @@ -7258,6 +7266,7 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, kvm_after_interrupt(vcpu); } +out: guest_state_exit_irqoff(); } @@ -7379,8 +7388,6 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu) loadsegment(es, __USER_DS); #endif - vcpu->arch.regs_avail &= ~VMX_REGS_LAZY_LOAD_SET; - pt_guest_exit(vmx); kvm_load_host_xsave_state(vcpu); @@ -7397,17 +7404,12 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu) vmx->nested.nested_run_pending = 0; } - vmx->idt_vectoring_info = 0; - if (unlikely(vmx->fail)) return EXIT_FASTPATH_NONE; if (unlikely((u16)vmx->exit_reason.basic == EXIT_REASON_MCE_DURING_VMENTRY)) kvm_machine_check(); - if (likely(!vmx->exit_reason.failed_vmentry)) - vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD); - trace_kvm_exit(vcpu, KVM_ISA_VMX); if (unlikely(vmx->exit_reason.failed_vmentry)) @@ -7751,8 +7753,16 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); - /* xsaves_enabled is recomputed in vmx_compute_secondary_exec_control(). */ - vcpu->arch.xsaves_enabled = false; + /* + * XSAVES is effectively enabled if and only if XSAVE is also exposed + * to the guest. XSAVES depends on CR4.OSXSAVE, and CR4.OSXSAVE can be + * set if and only if XSAVE is supported. + */ + if (boot_cpu_has(X86_FEATURE_XSAVE) && + guest_cpuid_has(vcpu, X86_FEATURE_XSAVE)) + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_XSAVES); + + kvm_governed_feature_check_and_set(vcpu, X86_FEATURE_VMX); vmx_setup_uret_msrs(vmx); @@ -7760,7 +7770,7 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) vmcs_set_secondary_exec_control(vmx, vmx_secondary_exec_control(vmx)); - if (nested_vmx_allowed(vcpu)) + if (guest_can_use(vcpu, X86_FEATURE_VMX)) vmx->msr_ia32_feature_control_valid_bits |= FEAT_CTL_VMX_ENABLED_INSIDE_SMX | FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX; @@ -7769,7 +7779,7 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) ~(FEAT_CTL_VMX_ENABLED_INSIDE_SMX | FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX); - if (nested_vmx_allowed(vcpu)) + if (guest_can_use(vcpu, X86_FEATURE_VMX)) nested_vmx_cr_fixed1_bits_update(vcpu); if (boot_cpu_has(X86_FEATURE_INTEL_PT) && @@ -8526,7 +8536,7 @@ static __init int hardware_setup(void) */ vmx_setup_me_spte_mask(); - kvm_configure_mmu(enable_ept, 0, vmx_get_max_tdp_level(), + kvm_configure_mmu(enable_ept, 0, vmx_get_max_ept_level(), ept_caps_to_lpage_level(vmx_capability.ept)); /* @@ -8622,10 +8632,8 @@ static void __vmx_exit(void) { allow_smaller_maxphyaddr = false; -#ifdef CONFIG_KEXEC_CORE - RCU_INIT_POINTER(crash_vmclear_loaded_vmcss, NULL); - synchronize_rcu(); -#endif + cpu_emergency_unregister_virt_callback(vmx_emergency_disable); + vmx_cleanup_l1d_flush(); } @@ -8666,18 +8674,14 @@ static int __init vmx_init(void) if (r) goto err_l1d_flush; - vmx_setup_fb_clear_ctrl(); - for_each_possible_cpu(cpu) { INIT_LIST_HEAD(&per_cpu(loaded_vmcss_on_cpu, cpu)); pi_init_cpu(cpu); } -#ifdef CONFIG_KEXEC_CORE - rcu_assign_pointer(crash_vmclear_loaded_vmcss, - crash_vmclear_local_loaded_vmcss); -#endif + cpu_emergency_register_virt_callback(vmx_emergency_disable); + vmx_check_vmcs12_offsets(); /* diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 32384ba3849949c2b04f195b7a37c47c7fcc7b43..c2130d2c8e24bb5ff3a529a4bde67f875376adda 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -374,7 +374,6 @@ struct kvm_vmx { u64 *pid_table; }; -bool nested_vmx_allowed(struct kvm_vcpu *vcpu); void vmx_vcpu_load_vmcs(struct kvm_vcpu *vcpu, int cpu, struct loaded_vmcs *buddy); int allocate_vpid(void); @@ -562,7 +561,7 @@ static inline u8 vmx_get_rvi(void) SECONDARY_EXEC_APIC_REGISTER_VIRT | \ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | \ SECONDARY_EXEC_SHADOW_VMCS | \ - SECONDARY_EXEC_XSAVES | \ + SECONDARY_EXEC_ENABLE_XSAVES | \ SECONDARY_EXEC_RDSEED_EXITING | \ SECONDARY_EXEC_RDRAND_EXITING | \ SECONDARY_EXEC_ENABLE_PML | \ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c381770bcbf13509379d7218c794b5c49628844b..9f18b06bbda66bf411bb492fa4ab4c5fd9e90080 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -25,6 +25,7 @@ #include "tss.h" #include "kvm_cache_regs.h" #include "kvm_emulate.h" +#include "mmu/page_track.h" #include "x86.h" #include "cpuid.h" #include "pmu.h" @@ -237,6 +238,9 @@ EXPORT_SYMBOL_GPL(enable_apicv); u64 __read_mostly host_xss; EXPORT_SYMBOL_GPL(host_xss); +u64 __read_mostly host_arch_capabilities; +EXPORT_SYMBOL_GPL(host_arch_capabilities); + const struct _kvm_stats_desc kvm_vm_stats_desc[] = { KVM_GENERIC_VM_STATS(), STATS_DESC_COUNTER(VM, mmu_shadow_zapped), @@ -1021,7 +1025,7 @@ void kvm_load_guest_xsave_state(struct kvm_vcpu *vcpu) if (vcpu->arch.xcr0 != host_xcr0) xsetbv(XCR_XFEATURE_ENABLED_MASK, vcpu->arch.xcr0); - if (vcpu->arch.xsaves_enabled && + if (guest_can_use(vcpu, X86_FEATURE_XSAVES) && vcpu->arch.ia32_xss != host_xss) wrmsrl(MSR_IA32_XSS, vcpu->arch.ia32_xss); } @@ -1052,7 +1056,7 @@ void kvm_load_host_xsave_state(struct kvm_vcpu *vcpu) if (vcpu->arch.xcr0 != host_xcr0) xsetbv(XCR_XFEATURE_ENABLED_MASK, host_xcr0); - if (vcpu->arch.xsaves_enabled && + if (guest_can_use(vcpu, X86_FEATURE_XSAVES) && vcpu->arch.ia32_xss != host_xss) wrmsrl(MSR_IA32_XSS, host_xss); } @@ -1620,12 +1624,7 @@ static bool kvm_is_immutable_feature_msr(u32 msr) static u64 kvm_get_arch_capabilities(void) { - u64 data = 0; - - if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) { - rdmsrl(MSR_IA32_ARCH_CAPABILITIES, data); - data &= KVM_SUPPORTED_ARCH_CAP; - } + u64 data = host_arch_capabilities & KVM_SUPPORTED_ARCH_CAP; /* * If nx_huge_pages is enabled, KVM's shadow paging will ensure that @@ -2631,7 +2630,7 @@ static void kvm_vcpu_write_tsc_offset(struct kvm_vcpu *vcpu, u64 l1_offset) else vcpu->arch.tsc_offset = l1_offset; - static_call(kvm_x86_write_tsc_offset)(vcpu, vcpu->arch.tsc_offset); + static_call(kvm_x86_write_tsc_offset)(vcpu); } static void kvm_vcpu_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 l1_multiplier) @@ -2647,8 +2646,7 @@ static void kvm_vcpu_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 l1_multipli vcpu->arch.tsc_scaling_ratio = l1_multiplier; if (kvm_caps.has_tsc_control) - static_call(kvm_x86_write_tsc_multiplier)( - vcpu, vcpu->arch.tsc_scaling_ratio); + static_call(kvm_x86_write_tsc_multiplier)(vcpu); } static inline bool kvm_check_tsc_unstable(void) @@ -4665,7 +4663,6 @@ static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr) return 0; default: return -ENXIO; - break; } } @@ -6532,7 +6529,7 @@ static void kvm_free_msr_filter(struct kvm_x86_msr_filter *msr_filter) static int kvm_add_msr_filter(struct kvm_x86_msr_filter *msr_filter, struct kvm_msr_filter_range *user_range) { - unsigned long *bitmap = NULL; + unsigned long *bitmap; size_t bitmap_size; if (!user_range->nmsrs) @@ -8245,11 +8242,6 @@ static bool emulator_get_cpuid(struct x86_emulate_ctxt *ctxt, return kvm_cpuid(emul_to_vcpu(ctxt), eax, ebx, ecx, edx, exact_only); } -static bool emulator_guest_has_long_mode(struct x86_emulate_ctxt *ctxt) -{ - return guest_cpuid_has(emul_to_vcpu(ctxt), X86_FEATURE_LM); -} - static bool emulator_guest_has_movbe(struct x86_emulate_ctxt *ctxt) { return guest_cpuid_has(emul_to_vcpu(ctxt), X86_FEATURE_MOVBE); @@ -8351,7 +8343,6 @@ static const struct x86_emulate_ops emulate_ops = { .fix_hypercall = emulator_fix_hypercall, .intercept = emulator_intercept, .get_cpuid = emulator_get_cpuid, - .guest_has_long_mode = emulator_guest_has_long_mode, .guest_has_movbe = emulator_guest_has_movbe, .guest_has_fxsr = emulator_guest_has_fxsr, .guest_has_rdpid = emulator_guest_has_rdpid, @@ -9172,7 +9163,7 @@ static int kvmclock_cpu_down_prep(unsigned int cpu) static void tsc_khz_changed(void *data) { struct cpufreq_freqs *freq = data; - unsigned long khz = 0; + unsigned long khz; WARN_ON_ONCE(boot_cpu_has(X86_FEATURE_CONSTANT_TSC)); @@ -9512,6 +9503,9 @@ static int __kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) kvm_init_pmu_capability(ops->pmu_ops); + if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) + rdmsrl(MSR_IA32_ARCH_CAPABILITIES, host_arch_capabilities); + r = ops->hardware_setup(); if (r != 0) goto out_mmu_exit; @@ -11111,12 +11105,17 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) r = -EINTR; goto out; } + /* - * It should be impossible for the hypervisor timer to be in - * use before KVM has ever run the vCPU. + * Don't bother switching APIC timer emulation from the + * hypervisor timer to the software timer, the only way for the + * APIC timer to be active is if userspace stuffed vCPU state, + * i.e. put the vCPU into a nonsensical state. Only an INIT + * will transition the vCPU out of UNINITIALIZED (without more + * state stuffing from userspace), which will reset the local + * APIC and thus cancel the timer or drop the IRQ (if the timer + * already expired). */ - WARN_ON_ONCE(kvm_lapic_hv_timer_in_use(vcpu)); - kvm_vcpu_srcu_read_unlock(vcpu); kvm_vcpu_block(vcpu); kvm_vcpu_srcu_read_lock(vcpu); @@ -11798,15 +11797,22 @@ static int sync_regs(struct kvm_vcpu *vcpu) __set_regs(vcpu, &vcpu->run->s.regs.regs); vcpu->run->kvm_dirty_regs &= ~KVM_SYNC_X86_REGS; } + if (vcpu->run->kvm_dirty_regs & KVM_SYNC_X86_SREGS) { - if (__set_sregs(vcpu, &vcpu->run->s.regs.sregs)) + struct kvm_sregs sregs = vcpu->run->s.regs.sregs; + + if (__set_sregs(vcpu, &sregs)) return -EINVAL; + vcpu->run->kvm_dirty_regs &= ~KVM_SYNC_X86_SREGS; } + if (vcpu->run->kvm_dirty_regs & KVM_SYNC_X86_EVENTS) { - if (kvm_vcpu_ioctl_x86_set_vcpu_events( - vcpu, &vcpu->run->s.regs.events)) + struct kvm_vcpu_events events = vcpu->run->s.regs.events; + + if (kvm_vcpu_ioctl_x86_set_vcpu_events(vcpu, &events)) return -EINVAL; + vcpu->run->kvm_dirty_regs &= ~KVM_SYNC_X86_EVENTS; } @@ -12302,9 +12308,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) if (ret) goto out; - ret = kvm_mmu_init_vm(kvm); - if (ret) - goto out_page_track; + kvm_mmu_init_vm(kvm); ret = static_call(kvm_x86_vm_init)(kvm); if (ret) @@ -12349,7 +12353,6 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) out_uninit_mmu: kvm_mmu_uninit_vm(kvm); -out_page_track: kvm_page_track_cleanup(kvm); out: return ret; @@ -12627,6 +12630,13 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, struct kvm_memory_slot *new, enum kvm_mr_change change) { + /* + * KVM doesn't support moving memslots when there are external page + * trackers attached to the VM, i.e. if KVMGT is in use. + */ + if (change == KVM_MR_MOVE && kvm_page_track_has_external_user(kvm)) + return -EINVAL; + if (change == KVM_MR_CREATE || change == KVM_MR_MOVE) { if ((new->base_gfn + new->npages - 1) > kvm_mmu_max_gfn()) return -EINVAL; @@ -12772,7 +12782,7 @@ static void kvm_mmu_slot_apply_flags(struct kvm *kvm, * See is_writable_pte() for more details (the case involving * access-tracked SPTEs is particularly relevant). */ - kvm_arch_flush_remote_tlbs_memslot(kvm, new); + kvm_flush_remote_tlbs_memslot(kvm, new); } } @@ -12781,6 +12791,9 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, const struct kvm_memory_slot *new, enum kvm_mr_change change) { + if (change == KVM_MR_DELETE) + kvm_page_track_delete_slot(kvm, old); + if (!kvm->arch.n_requested_mmu_pages && (change == KVM_MR_CREATE || change == KVM_MR_DELETE)) { unsigned long nr_mmu_pages; @@ -12797,17 +12810,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm, kvm_arch_free_memslot(kvm, old); } -void kvm_arch_flush_shadow_all(struct kvm *kvm) -{ - kvm_mmu_zap_all(kvm); -} - -void kvm_arch_flush_shadow_memslot(struct kvm *kvm, - struct kvm_memory_slot *slot) -{ - kvm_page_track_flush_slot(kvm, slot); -} - static inline bool kvm_guest_apic_has_interrupt(struct kvm_vcpu *vcpu) { return (is_guest_mode(vcpu) && diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 82e3dafc5453facc8eeaba39a3bde8fd90081ab3..1e7be1f6ab299d78a76e76385db159dee679220b 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -323,6 +323,7 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu); extern u64 host_xcr0; extern u64 host_xss; +extern u64 host_arch_capabilities; extern struct kvm_caps kvm_caps; diff --git a/arch/x86/lib/memcpy_64.S b/arch/x86/lib/memcpy_64.S index 8f95fb267caa7c91d066002838198106ef052199..76697df8dfd5bf912fae0c4983f269434151a834 100644 --- a/arch/x86/lib/memcpy_64.S +++ b/arch/x86/lib/memcpy_64.S @@ -40,7 +40,7 @@ SYM_TYPED_FUNC_START(__memcpy) SYM_FUNC_END(__memcpy) EXPORT_SYMBOL(__memcpy) -SYM_FUNC_ALIAS(memcpy, __memcpy) +SYM_FUNC_ALIAS_MEMFUNC(memcpy, __memcpy) EXPORT_SYMBOL(memcpy) SYM_FUNC_START_LOCAL(memcpy_orig) diff --git a/arch/x86/lib/memmove_64.S b/arch/x86/lib/memmove_64.S index 0559b206fb1109c459d4a74b50dd6226f69a8153..ccdf3a597045e4c1a8df2146d7a4afdf0376bc3a 100644 --- a/arch/x86/lib/memmove_64.S +++ b/arch/x86/lib/memmove_64.S @@ -212,5 +212,5 @@ SYM_FUNC_START(__memmove) SYM_FUNC_END(__memmove) EXPORT_SYMBOL(__memmove) -SYM_FUNC_ALIAS(memmove, __memmove) +SYM_FUNC_ALIAS_MEMFUNC(memmove, __memmove) EXPORT_SYMBOL(memmove) diff --git a/arch/x86/lib/memset_64.S b/arch/x86/lib/memset_64.S index 7c59a704c4584bf7ef3e6a50f2021c31e6f15029..3d818b849ec64b865e857a2cc0f3d99a1df9d1f1 100644 --- a/arch/x86/lib/memset_64.S +++ b/arch/x86/lib/memset_64.S @@ -40,7 +40,7 @@ SYM_FUNC_START(__memset) SYM_FUNC_END(__memset) EXPORT_SYMBOL(__memset) -SYM_FUNC_ALIAS(memset, __memset) +SYM_FUNC_ALIAS_MEMFUNC(memset, __memset) EXPORT_SYMBOL(memset) SYM_FUNC_START_LOCAL(memset_orig) diff --git a/arch/x86/lib/putuser.S b/arch/x86/lib/putuser.S index 1451e0c4ae22af2ac0721596755be825d51e32fd..235bbda6fc8230fed1b8f240b569e8951121ad19 100644 --- a/arch/x86/lib/putuser.S +++ b/arch/x86/lib/putuser.S @@ -56,7 +56,6 @@ SYM_FUNC_END(__put_user_1) EXPORT_SYMBOL(__put_user_1) SYM_FUNC_START(__put_user_nocheck_1) - ENDBR ASM_STAC 2: movb %al,(%_ASM_CX) xor %ecx,%ecx @@ -76,7 +75,6 @@ SYM_FUNC_END(__put_user_2) EXPORT_SYMBOL(__put_user_2) SYM_FUNC_START(__put_user_nocheck_2) - ENDBR ASM_STAC 4: movw %ax,(%_ASM_CX) xor %ecx,%ecx @@ -96,7 +94,6 @@ SYM_FUNC_END(__put_user_4) EXPORT_SYMBOL(__put_user_4) SYM_FUNC_START(__put_user_nocheck_4) - ENDBR ASM_STAC 6: movl %eax,(%_ASM_CX) xor %ecx,%ecx @@ -119,7 +116,6 @@ SYM_FUNC_END(__put_user_8) EXPORT_SYMBOL(__put_user_8) SYM_FUNC_START(__put_user_nocheck_8) - ENDBR ASM_STAC 9: mov %_ASM_AX,(%_ASM_CX) #ifdef CONFIG_X86_32 diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index e06a199423c0fedd79cb9a4cc4173655ecf3956c..b2cc7b4552a16630164b6af8e574eee448933ca5 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c @@ -140,3 +140,15 @@ void __init efi_runtime_update_mappings(void) } } } + +void arch_efi_call_virt_setup(void) +{ + efi_fpu_begin(); + firmware_restrict_branch_speculation_start(); +} + +void arch_efi_call_virt_teardown(void) +{ + firmware_restrict_branch_speculation_end(); + efi_fpu_end(); +} diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index 77f7ac3668cb4ac0c97d01a7dce648e01be15fbb..91d31ac422d6cde7ab5434bcd02176cbcd6b07ff 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -474,19 +474,34 @@ void __init efi_dump_pagetable(void) * can not change under us. * It should be ensured that there are no concurrent calls to this function. */ -void efi_enter_mm(void) +static void efi_enter_mm(void) { efi_prev_mm = current->active_mm; current->active_mm = &efi_mm; switch_mm(efi_prev_mm, &efi_mm, NULL); } -void efi_leave_mm(void) +static void efi_leave_mm(void) { current->active_mm = efi_prev_mm; switch_mm(&efi_mm, efi_prev_mm, NULL); } +void arch_efi_call_virt_setup(void) +{ + efi_sync_low_kernel_mappings(); + efi_fpu_begin(); + firmware_restrict_branch_speculation_start(); + efi_enter_mm(); +} + +void arch_efi_call_virt_teardown(void) +{ + efi_leave_mm(); + firmware_restrict_branch_speculation_end(); + efi_fpu_end(); +} + static DEFINE_SPINLOCK(efi_runtime_lock); /* diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile index c2a29be35c01bb306efbd01daabb618636f6d880..08aa0f25f12a0fb92b7f9002740c50a1c0f2ad9d 100644 --- a/arch/x86/purgatory/Makefile +++ b/arch/x86/purgatory/Makefile @@ -19,6 +19,10 @@ CFLAGS_sha256.o := -D__DISABLE_EXPORTS -D__NO_FORTIFY # optimization flags. KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS)) +# When LTO is enabled, llvm emits many text sections, which is not supported +# by kexec. Remove -flto=* flags. +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) + # When linking purgatory.ro with -r unresolved symbols are not checked, # also link a purgatory.chk binary without -r to check for unresolved symbols. PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile index ee89f6bb9242636900dab80c31bdfc371896ee05..8bc72a51b257e13d26e57dc6ee1fbe1169bff175 100644 --- a/arch/x86/um/Makefile +++ b/arch/x86/um/Makefile @@ -13,15 +13,16 @@ obj-y = bugs_$(BITS).o delay.o fault.o ldt.o \ ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \ stub_$(BITS).o stub_segv.o \ sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \ - mem_$(BITS).o subarch.o os-$(OS)/ + mem_$(BITS).o subarch.o os-Linux/ ifeq ($(CONFIG_X86_32),y) -obj-y += checksum_32.o syscalls_32.o +obj-y += syscalls_32.o obj-$(CONFIG_ELF_CORE) += elfcore.o subarch-y = ../lib/string_32.o ../lib/atomic64_32.o ../lib/atomic64_cx8_32.o subarch-y += ../lib/cmpxchg8b_emu.o ../lib/atomic64_386_32.o +subarch-y += ../lib/checksum_32.o subarch-y += ../kernel/sys_ia32.o else diff --git a/arch/x86/um/asm/mm_context.h b/arch/x86/um/asm/mm_context.h index 4a73d63e4760e84d3d4d522445986fc15bcec148..dc32dc023c2f8fe474b0412cb587c50dc4d85111 100644 --- a/arch/x86/um/asm/mm_context.h +++ b/arch/x86/um/asm/mm_context.h @@ -11,8 +11,6 @@ #include #include -extern void ldt_host_info(void); - #define LDT_PAGES_MAX \ ((LDT_ENTRIES * LDT_ENTRY_SIZE)/PAGE_SIZE) #define LDT_ENTRIES_PER_PAGE \ diff --git a/arch/x86/um/checksum_32.S b/arch/x86/um/checksum_32.S deleted file mode 100644 index aed782ab77213fa5b2092b75ce5675039a368085..0000000000000000000000000000000000000000 --- a/arch/x86/um/checksum_32.S +++ /dev/null @@ -1,214 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * INET An implementation of the TCP/IP protocol suite for the LINUX - * operating system. INET is implemented using the BSD Socket - * interface as the means of communication with the user level. - * - * IP/TCP/UDP checksumming routines - * - * Authors: Jorge Cwik, - * Arnt Gulbrandsen, - * Tom May, - * Pentium Pro/II routines: - * Alexander Kjeldaas - * Finn Arne Gangstad - * Lots of code moved from tcp.c and ip.c; see those files - * for more names. - * - * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception - * handling. - * Andi Kleen, add zeroing on error - * converted to pure assembler - */ - -#include -#include -#include - -/* - * computes a partial checksum, e.g. for TCP/UDP fragments - */ - -/* -unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) - */ - -.text -.align 4 -.globl csum_partial - -#ifndef CONFIG_X86_USE_PPRO_CHECKSUM - - /* - * Experiments with Ethernet and SLIP connections show that buff - * is aligned on either a 2-byte or 4-byte boundary. We get at - * least a twofold speedup on 486 and Pentium if it is 4-byte aligned. - * Fortunately, it is easy to convert 2-byte alignment to 4-byte - * alignment for the unrolled loop. - */ -csum_partial: - pushl %esi - pushl %ebx - movl 20(%esp),%eax # Function arg: unsigned int sum - movl 16(%esp),%ecx # Function arg: int len - movl 12(%esp),%esi # Function arg: unsigned char *buff - testl $2, %esi # Check alignment. - jz 2f # Jump if alignment is ok. - subl $2, %ecx # Alignment uses up two bytes. - jae 1f # Jump if we had at least two bytes. - addl $2, %ecx # ecx was < 2. Deal with it. - jmp 4f -1: movw (%esi), %bx - addl $2, %esi - addw %bx, %ax - adcl $0, %eax -2: - movl %ecx, %edx - shrl $5, %ecx - jz 2f - testl %esi, %esi -1: movl (%esi), %ebx - adcl %ebx, %eax - movl 4(%esi), %ebx - adcl %ebx, %eax - movl 8(%esi), %ebx - adcl %ebx, %eax - movl 12(%esi), %ebx - adcl %ebx, %eax - movl 16(%esi), %ebx - adcl %ebx, %eax - movl 20(%esi), %ebx - adcl %ebx, %eax - movl 24(%esi), %ebx - adcl %ebx, %eax - movl 28(%esi), %ebx - adcl %ebx, %eax - lea 32(%esi), %esi - dec %ecx - jne 1b - adcl $0, %eax -2: movl %edx, %ecx - andl $0x1c, %edx - je 4f - shrl $2, %edx # This clears CF -3: adcl (%esi), %eax - lea 4(%esi), %esi - dec %edx - jne 3b - adcl $0, %eax -4: andl $3, %ecx - jz 7f - cmpl $2, %ecx - jb 5f - movw (%esi),%cx - leal 2(%esi),%esi - je 6f - shll $16,%ecx -5: movb (%esi),%cl -6: addl %ecx,%eax - adcl $0, %eax -7: - popl %ebx - popl %esi - RET - -#else - -/* Version for PentiumII/PPro */ - -csum_partial: - pushl %esi - pushl %ebx - movl 20(%esp),%eax # Function arg: unsigned int sum - movl 16(%esp),%ecx # Function arg: int len - movl 12(%esp),%esi # Function arg: const unsigned char *buf - - testl $2, %esi - jnz 30f -10: - movl %ecx, %edx - movl %ecx, %ebx - andl $0x7c, %ebx - shrl $7, %ecx - addl %ebx,%esi - shrl $2, %ebx - negl %ebx - lea 45f(%ebx,%ebx,2), %ebx - testl %esi, %esi - jmp *%ebx - - # Handle 2-byte-aligned regions -20: addw (%esi), %ax - lea 2(%esi), %esi - adcl $0, %eax - jmp 10b - -30: subl $2, %ecx - ja 20b - je 32f - movzbl (%esi),%ebx # csumming 1 byte, 2-aligned - addl %ebx, %eax - adcl $0, %eax - jmp 80f -32: - addw (%esi), %ax # csumming 2 bytes, 2-aligned - adcl $0, %eax - jmp 80f - -40: - addl -128(%esi), %eax - adcl -124(%esi), %eax - adcl -120(%esi), %eax - adcl -116(%esi), %eax - adcl -112(%esi), %eax - adcl -108(%esi), %eax - adcl -104(%esi), %eax - adcl -100(%esi), %eax - adcl -96(%esi), %eax - adcl -92(%esi), %eax - adcl -88(%esi), %eax - adcl -84(%esi), %eax - adcl -80(%esi), %eax - adcl -76(%esi), %eax - adcl -72(%esi), %eax - adcl -68(%esi), %eax - adcl -64(%esi), %eax - adcl -60(%esi), %eax - adcl -56(%esi), %eax - adcl -52(%esi), %eax - adcl -48(%esi), %eax - adcl -44(%esi), %eax - adcl -40(%esi), %eax - adcl -36(%esi), %eax - adcl -32(%esi), %eax - adcl -28(%esi), %eax - adcl -24(%esi), %eax - adcl -20(%esi), %eax - adcl -16(%esi), %eax - adcl -12(%esi), %eax - adcl -8(%esi), %eax - adcl -4(%esi), %eax -45: - lea 128(%esi), %esi - adcl $0, %eax - dec %ecx - jge 40b - movl %edx, %ecx -50: andl $3, %ecx - jz 80f - - # Handle the last 1-3 bytes without jumping - notl %ecx # 1->2, 2->1, 3->0, higher bits are masked - movl $0xffffff,%ebx # by the shll and shrl instructions - shll $3,%ecx - shrl %cl,%ebx - andl -128(%esi),%ebx # esi is 4-aligned so should be ok - addl %ebx,%eax - adcl $0,%eax -80: - popl %ebx - popl %esi - RET - -#endif - EXPORT_SYMBOL(csum_partial) diff --git a/arch/x86/xen/efi.c b/arch/x86/xen/efi.c index 863d0d6b3edc47e06346c378356b412ba2358707..7250d0e0e1a98cf8490e4b83e0ac13d44979772a 100644 --- a/arch/x86/xen/efi.c +++ b/arch/x86/xen/efi.c @@ -138,7 +138,7 @@ void __init xen_efi_init(struct boot_params *boot_params) if (efi_systab_xen == NULL) return; - strncpy((char *)&boot_params->efi_info.efi_loader_signature, "Xen", + strscpy((char *)&boot_params->efi_info.efi_loader_signature, "Xen", sizeof(boot_params->efi_info.efi_loader_signature)); boot_params->efi_info.efi_systab = (__u32)__pa(efi_systab_xen); boot_params->efi_info.efi_systab_hi = (__u32)(__pa(efi_systab_xen) >> 32); diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index b8db2148c07d525a543ecdd1ed4be5da236fc81c..0337392a3121412f8afe2946f48ff03646f780fa 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -32,7 +32,7 @@ EXPORT_SYMBOL_GPL(hypercall_page); * &HYPERVISOR_shared_info->vcpu_info[cpu]. See xen_hvm_init_shared_info * and xen_vcpu_setup for details. By default it points to share_info->vcpu_info * but during boot it is switched to point to xen_vcpu_info. - * The pointer is used in __xen_evtchn_do_upcall to acknowledge pending events. + * The pointer is used in xen_evtchn_do_upcall to acknowledge pending events. */ DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu); DEFINE_PER_CPU(struct vcpu_info, xen_vcpu_info); diff --git a/arch/x86/xen/enlighten_hvm.c b/arch/x86/xen/enlighten_hvm.c index 9a192f51f1b0216dd4e0f8cb101d49f58326fbeb..3f8c34707c50014d9364f0430728446e35059d8f 100644 --- a/arch/x86/xen/enlighten_hvm.c +++ b/arch/x86/xen/enlighten_hvm.c @@ -136,7 +136,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_xen_hvm_callback) inc_irq_stat(irq_hv_callback_count); - xen_hvm_evtchn_do_upcall(); + xen_evtchn_do_upcall(); set_irq_regs(old_regs); } diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index 49352fad7d1de671094c98630426be11cd12e8d3..bbbfdd495ebd3ac590fd152e0504c6bedae611de 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -101,6 +101,17 @@ struct tls_descs { struct desc_struct desc[3]; }; +DEFINE_PER_CPU(enum xen_lazy_mode, xen_lazy_mode) = XEN_LAZY_NONE; +DEFINE_PER_CPU(unsigned int, xen_lazy_nesting); + +enum xen_lazy_mode xen_get_lazy_mode(void) +{ + if (in_interrupt()) + return XEN_LAZY_NONE; + + return this_cpu_read(xen_lazy_mode); +} + /* * Updating the 3 TLS descriptors in the GDT on every task switch is * surprisingly expensive so we avoid updating them if they haven't @@ -362,10 +373,25 @@ static noinstr unsigned long xen_get_debugreg(int reg) return HYPERVISOR_get_debugreg(reg); } +static void xen_start_context_switch(struct task_struct *prev) +{ + BUG_ON(preemptible()); + + if (this_cpu_read(xen_lazy_mode) == XEN_LAZY_MMU) { + arch_leave_lazy_mmu_mode(); + set_ti_thread_flag(task_thread_info(prev), TIF_LAZY_MMU_UPDATES); + } + enter_lazy(XEN_LAZY_CPU); +} + static void xen_end_context_switch(struct task_struct *next) { + BUG_ON(preemptible()); + xen_mc_flush(); - paravirt_end_context_switch(next); + leave_lazy(XEN_LAZY_CPU); + if (test_and_clear_ti_thread_flag(task_thread_info(next), TIF_LAZY_MMU_UPDATES)) + arch_enter_lazy_mmu_mode(); } static unsigned long xen_store_tr(void) @@ -472,7 +498,7 @@ static void xen_set_ldt(const void *addr, unsigned entries) MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); - xen_mc_issue(PARAVIRT_LAZY_CPU); + xen_mc_issue(XEN_LAZY_CPU); } static void xen_load_gdt(const struct desc_ptr *dtr) @@ -568,7 +594,7 @@ static void xen_load_tls(struct thread_struct *t, unsigned int cpu) * exception between the new %fs descriptor being loaded and * %fs being effectively cleared at __switch_to(). */ - if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_CPU) + if (xen_get_lazy_mode() == XEN_LAZY_CPU) loadsegment(fs, 0); xen_mc_batch(); @@ -577,7 +603,7 @@ static void xen_load_tls(struct thread_struct *t, unsigned int cpu) load_TLS_descriptor(t, cpu, 1); load_TLS_descriptor(t, cpu, 2); - xen_mc_issue(PARAVIRT_LAZY_CPU); + xen_mc_issue(XEN_LAZY_CPU); } static void xen_load_gs_index(unsigned int idx) @@ -909,7 +935,7 @@ static void xen_load_sp0(unsigned long sp0) mcs = xen_mc_entry(0); MULTI_stack_switch(mcs.mc, __KERNEL_DS, sp0); - xen_mc_issue(PARAVIRT_LAZY_CPU); + xen_mc_issue(XEN_LAZY_CPU); this_cpu_write(cpu_tss_rw.x86_tss.sp0, sp0); } @@ -973,7 +999,7 @@ static void xen_write_cr0(unsigned long cr0) MULTI_fpu_taskswitch(mcs.mc, (cr0 & X86_CR0_TS) != 0); - xen_mc_issue(PARAVIRT_LAZY_CPU); + xen_mc_issue(XEN_LAZY_CPU); } static void xen_write_cr4(unsigned long cr4) @@ -1156,7 +1182,7 @@ static const typeof(pv_ops) xen_cpu_ops __initconst = { #endif .io_delay = xen_io_delay, - .start_context_switch = paravirt_start_context_switch, + .start_context_switch = xen_start_context_switch, .end_context_switch = xen_end_context_switch, }, }; diff --git a/arch/x86/xen/mmu_pv.c b/arch/x86/xen/mmu_pv.c index 1652c39e3dfbd309278f887233c026736a96b9a2..b6830554ff6905633fc543fb39e1b5b4a024bb07 100644 --- a/arch/x86/xen/mmu_pv.c +++ b/arch/x86/xen/mmu_pv.c @@ -236,7 +236,7 @@ static void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val) u.val = pmd_val_ma(val); xen_extend_mmu_update(&u); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); preempt_enable(); } @@ -270,7 +270,7 @@ static bool xen_batched_set_pte(pte_t *ptep, pte_t pteval) { struct mmu_update u; - if (paravirt_get_lazy_mode() != PARAVIRT_LAZY_MMU) + if (xen_get_lazy_mode() != XEN_LAZY_MMU) return false; xen_mc_batch(); @@ -279,7 +279,7 @@ static bool xen_batched_set_pte(pte_t *ptep, pte_t pteval) u.val = pte_val_ma(pteval); xen_extend_mmu_update(&u); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); return true; } @@ -325,7 +325,7 @@ void xen_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, u.val = pte_val_ma(pte); xen_extend_mmu_update(&u); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); } /* Assume pteval_t is equivalent to all the other *val_t types. */ @@ -419,7 +419,7 @@ static void xen_set_pud_hyper(pud_t *ptr, pud_t val) u.val = pud_val_ma(val); xen_extend_mmu_update(&u); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); preempt_enable(); } @@ -499,7 +499,7 @@ static void __init xen_set_p4d_hyper(p4d_t *ptr, p4d_t val) __xen_set_p4d_hyper(ptr, val); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); preempt_enable(); } @@ -531,7 +531,7 @@ static void xen_set_p4d(p4d_t *ptr, p4d_t val) if (user_ptr) __xen_set_p4d_hyper((p4d_t *)user_ptr, val); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); } #if CONFIG_PGTABLE_LEVELS >= 5 @@ -1245,7 +1245,7 @@ static noinline void xen_flush_tlb(void) op->cmd = MMUEXT_TLB_FLUSH_LOCAL; MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); preempt_enable(); } @@ -1265,7 +1265,7 @@ static void xen_flush_tlb_one_user(unsigned long addr) op->arg1.linear_addr = addr & PAGE_MASK; MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); preempt_enable(); } @@ -1302,7 +1302,7 @@ static void xen_flush_tlb_multi(const struct cpumask *cpus, MULTI_mmuext_op(mcs.mc, &args->op, 1, NULL, DOMID_SELF); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); } static unsigned long xen_read_cr3(void) @@ -1361,7 +1361,7 @@ static void xen_write_cr3(unsigned long cr3) else __xen_write_cr3(false, 0); - xen_mc_issue(PARAVIRT_LAZY_CPU); /* interrupts restored */ + xen_mc_issue(XEN_LAZY_CPU); /* interrupts restored */ } /* @@ -1396,7 +1396,7 @@ static void __init xen_write_cr3_init(unsigned long cr3) __xen_write_cr3(true, cr3); - xen_mc_issue(PARAVIRT_LAZY_CPU); /* interrupts restored */ + xen_mc_issue(XEN_LAZY_CPU); /* interrupts restored */ } static int xen_pgd_alloc(struct mm_struct *mm) @@ -1557,7 +1557,7 @@ static inline void xen_alloc_ptpage(struct mm_struct *mm, unsigned long pfn, if (level == PT_PTE && USE_SPLIT_PTE_PTLOCKS && !pinned) __pin_pagetable_pfn(MMUEXT_PIN_L1_TABLE, pfn); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); } } @@ -1587,7 +1587,7 @@ static inline void xen_release_ptpage(unsigned long pfn, unsigned level) __set_pfn_prot(pfn, PAGE_KERNEL); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); ClearPagePinned(page); } @@ -1804,7 +1804,7 @@ void __init xen_setup_kernel_pagetable(pgd_t *pgd, unsigned long max_pfn) */ xen_mc_batch(); __xen_write_cr3(true, __pa(init_top_pgt)); - xen_mc_issue(PARAVIRT_LAZY_CPU); + xen_mc_issue(XEN_LAZY_CPU); /* We can't that easily rip out L3 and L2, as the Xen pagetables are * set out this way: [L4], [L1], [L2], [L3], [L1], [L1] ... for @@ -2083,6 +2083,23 @@ static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot) #endif } +static void xen_enter_lazy_mmu(void) +{ + enter_lazy(XEN_LAZY_MMU); +} + +static void xen_flush_lazy_mmu(void) +{ + preempt_disable(); + + if (xen_get_lazy_mode() == XEN_LAZY_MMU) { + arch_leave_lazy_mmu_mode(); + arch_enter_lazy_mmu_mode(); + } + + preempt_enable(); +} + static void __init xen_post_allocator_init(void) { pv_ops.mmu.set_pte = xen_set_pte; @@ -2107,7 +2124,7 @@ static void xen_leave_lazy_mmu(void) { preempt_disable(); xen_mc_flush(); - paravirt_leave_lazy_mmu(); + leave_lazy(XEN_LAZY_MMU); preempt_enable(); } @@ -2166,9 +2183,9 @@ static const typeof(pv_ops) xen_mmu_ops __initconst = { .exit_mmap = xen_exit_mmap, .lazy_mode = { - .enter = paravirt_enter_lazy_mmu, + .enter = xen_enter_lazy_mmu, .leave = xen_leave_lazy_mmu, - .flush = paravirt_flush_lazy_mmu, + .flush = xen_flush_lazy_mmu, }, .set_fixmap = xen_set_fixmap, @@ -2385,7 +2402,7 @@ static noinline void xen_flush_tlb_all(void) op->cmd = MMUEXT_TLB_FLUSH_ALL; MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); - xen_mc_issue(PARAVIRT_LAZY_MMU); + xen_mc_issue(XEN_LAZY_MMU); preempt_enable(); } diff --git a/arch/x86/xen/multicalls.h b/arch/x86/xen/multicalls.h index 1c51b2c87f30a12dfb8d022c54de1f88046234ef..c3867b585e0d8d56419c5a6641f5c2eeb3a30a16 100644 --- a/arch/x86/xen/multicalls.h +++ b/arch/x86/xen/multicalls.h @@ -26,7 +26,7 @@ static inline void xen_mc_batch(void) /* need to disable interrupts until this entry is complete */ local_irq_save(flags); - trace_xen_mc_batch(paravirt_get_lazy_mode()); + trace_xen_mc_batch(xen_get_lazy_mode()); __this_cpu_write(xen_mc_irq_flags, flags); } @@ -44,7 +44,7 @@ static inline void xen_mc_issue(unsigned mode) { trace_xen_mc_issue(mode); - if ((paravirt_get_lazy_mode() & mode) == 0) + if ((xen_get_lazy_mode() & mode) == 0) xen_mc_flush(); /* restore flags saved in xen_mc_batch */ diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index a5488cc40f587b1b496e4c290fdd76b04589686e..7d792077e5fdc778d530135a7b32c2f8287af038 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -71,6 +71,9 @@ config ARCH_HAS_ILOG2_U32 config ARCH_HAS_ILOG2_U64 def_bool n +config ARCH_MTD_XIP + def_bool y + config NO_IOPORT_MAP def_bool n diff --git a/arch/xtensa/boot/Makefile b/arch/xtensa/boot/Makefile index a65b7a9ebff281bd510139f93ae43b42837607ee..d8b0fadf429a97fc57b7ac06b76eee6e846018a0 100644 --- a/arch/xtensa/boot/Makefile +++ b/arch/xtensa/boot/Makefile @@ -9,8 +9,7 @@ # KBUILD_CFLAGS used when building rest of boot (takes effect recursively) -KBUILD_CFLAGS += -fno-builtin -Iarch/$(ARCH)/boot/include -HOSTFLAGS += -Iarch/$(ARCH)/boot/include +KBUILD_CFLAGS += -fno-builtin subdir-y := lib targets += vmlinux.bin vmlinux.bin.gz diff --git a/arch/xtensa/boot/lib/zmem.c b/arch/xtensa/boot/lib/zmem.c index e3ecd743c5153d3437e501280fa36b35177fb8a4..b89189355122a6a10ac1f39bdbd9e689d33cfe89 100644 --- a/arch/xtensa/boot/lib/zmem.c +++ b/arch/xtensa/boot/lib/zmem.c @@ -4,13 +4,14 @@ /* bits taken from ppc */ extern void *avail_ram, *end_avail; +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp); -void exit (void) +static void exit(void) { for (;;); } -void *zalloc(unsigned size) +static void *zalloc(unsigned int size) { void *p = avail_ram; diff --git a/arch/xtensa/include/asm/core.h b/arch/xtensa/include/asm/core.h index 0e1bb6f019d6b09293f6e16e7abb6297436bb0c9..6f02f6f21890fd5a71ba291d799e12c0d1deac63 100644 --- a/arch/xtensa/include/asm/core.h +++ b/arch/xtensa/include/asm/core.h @@ -6,6 +6,10 @@ #include +#ifndef XCHAL_HAVE_DIV32 +#define XCHAL_HAVE_DIV32 0 +#endif + #ifndef XCHAL_HAVE_EXCLUSIVE #define XCHAL_HAVE_EXCLUSIVE 0 #endif @@ -52,4 +56,13 @@ #define XTENSA_STACK_ALIGNMENT 16 #endif +#ifndef XCHAL_HW_MIN_VERSION +#if defined(XCHAL_HW_MIN_VERSION_MAJOR) && defined(XCHAL_HW_MIN_VERSION_MINOR) +#define XCHAL_HW_MIN_VERSION (XCHAL_HW_MIN_VERSION_MAJOR * 100 + \ + XCHAL_HW_MIN_VERSION_MINOR) +#else +#define XCHAL_HW_MIN_VERSION 0 +#endif +#endif + #endif diff --git a/arch/xtensa/include/asm/hw_breakpoint.h b/arch/xtensa/include/asm/hw_breakpoint.h index 9f119c1ca0b5d8dac50613b3fa27f5a1cb303451..9ec86f440a48e8e0ba97a876fdae6c19d7983801 100644 --- a/arch/xtensa/include/asm/hw_breakpoint.h +++ b/arch/xtensa/include/asm/hw_breakpoint.h @@ -48,6 +48,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp); void hw_breakpoint_pmu_read(struct perf_event *bp); int check_hw_breakpoint(struct pt_regs *regs); void clear_ptrace_hw_breakpoint(struct task_struct *tsk); +void restore_dbreak(void); #else diff --git a/arch/xtensa/include/asm/mtd-xip.h b/arch/xtensa/include/asm/mtd-xip.h new file mode 100644 index 0000000000000000000000000000000000000000..514325155cf82deb229529306d15ecace2dedb57 --- /dev/null +++ b/arch/xtensa/include/asm/mtd-xip.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_MTD_XIP_H +#define _ASM_MTD_XIP_H + +#include + +#define xip_irqpending() (xtensa_get_sr(interrupt) & xtensa_get_sr(intenable)) +#define xip_currtime() (xtensa_get_sr(ccount)) +#define xip_elapsed_since(x) ((xtensa_get_sr(ccount) - (x)) / 1000) /* should work up to 1GHz */ +#define xip_cpu_idle() do { asm volatile ("waiti 0"); } while (0) + +#endif /* _ASM_MTD_XIP_H */ + diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h index a6d09fe0483110e46aaa8c48333d52677e2c86c0..d008a153a2b9f7a9782b5874643f2d5a3741e995 100644 --- a/arch/xtensa/include/asm/processor.h +++ b/arch/xtensa/include/asm/processor.h @@ -14,6 +14,8 @@ #include #include + +#include #include #include #include @@ -217,6 +219,9 @@ struct mm_struct; extern unsigned long __get_wchan(struct task_struct *p); +void init_arch(bp_tag_t *bp_start); +void do_notify_resume(struct pt_regs *regs); + #define KSTK_EIP(tsk) (task_pt_regs(tsk)->pc) #define KSTK_ESP(tsk) (task_pt_regs(tsk)->areg[1]) diff --git a/arch/xtensa/include/asm/ptrace.h b/arch/xtensa/include/asm/ptrace.h index 308f209a4740790762dd6b2cbd9973bdf9259496..a270467556dc84df2c6ceb0786acee3669a059f8 100644 --- a/arch/xtensa/include/asm/ptrace.h +++ b/arch/xtensa/include/asm/ptrace.h @@ -106,6 +106,9 @@ static inline unsigned long regs_return_value(struct pt_regs *regs) return regs->areg[2]; } +int do_syscall_trace_enter(struct pt_regs *regs); +void do_syscall_trace_leave(struct pt_regs *regs); + #else /* __ASSEMBLY__ */ # include diff --git a/arch/xtensa/include/asm/sections.h b/arch/xtensa/include/asm/sections.h index 3bc6b9afa99391ddde4d2c5ccf06b468b02a7eed..e5da6d7092bead27075a2b2ac9558c7e715b538d 100644 --- a/arch/xtensa/include/asm/sections.h +++ b/arch/xtensa/include/asm/sections.h @@ -34,6 +34,10 @@ extern char _SecondaryResetVector_text_start[]; extern char _SecondaryResetVector_text_end[]; #endif #ifdef CONFIG_XIP_KERNEL +#ifdef CONFIG_VECTORS_ADDR +extern char _xip_text_start[]; +extern char _xip_text_end[]; +#endif extern char _xip_start[]; extern char _xip_end[]; #endif diff --git a/arch/xtensa/include/asm/smp.h b/arch/xtensa/include/asm/smp.h index 5dc5bf8cdd77bdf51c1d915a9a662028d7493544..e446e6fc45579484cd549b7664681c7a17abb6c8 100644 --- a/arch/xtensa/include/asm/smp.h +++ b/arch/xtensa/include/asm/smp.h @@ -23,6 +23,7 @@ struct cpumask; void arch_send_call_function_ipi_mask(const struct cpumask *mask); void arch_send_call_function_single_ipi(int cpu); +void secondary_start_kernel(void); void smp_init_cpus(void); void secondary_init_irq(void); void ipi_init(void); diff --git a/arch/xtensa/include/asm/tlb.h b/arch/xtensa/include/asm/tlb.h index 50889935138ad2170b1e4c57aaccbf9551fdf997..8c3ceb4270180bd2d747d7952754b3d5dab947b3 100644 --- a/arch/xtensa/include/asm/tlb.h +++ b/arch/xtensa/include/asm/tlb.h @@ -18,4 +18,6 @@ #define __pte_free_tlb(tlb, pte, address) pte_free((tlb)->mm, pte) +void check_tlb_sanity(void); + #endif /* _XTENSA_TLB_H */ diff --git a/arch/xtensa/kernel/hw_breakpoint.c b/arch/xtensa/kernel/hw_breakpoint.c index 285fb2942b06593a585e154f32fa67d920afc295..1eeecd58eb0c822ab4d066aff411a31a211d8c96 100644 --- a/arch/xtensa/kernel/hw_breakpoint.c +++ b/arch/xtensa/kernel/hw_breakpoint.c @@ -13,6 +13,7 @@ #include #include #include +#include /* Breakpoint currently in use for each IBREAKA. */ static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[XCHAL_NUM_IBREAK]); diff --git a/arch/xtensa/kernel/irq.c b/arch/xtensa/kernel/irq.c index 42f106004400ca0b9047610052b79aad9717ca6c..b1e410f6b5ab8e043759b66693f86eae90b668fd 100644 --- a/arch/xtensa/kernel/irq.c +++ b/arch/xtensa/kernel/irq.c @@ -28,6 +28,7 @@ #include #include #include +#include DECLARE_PER_CPU(unsigned long, nmi_count); diff --git a/arch/xtensa/kernel/perf_event.c b/arch/xtensa/kernel/perf_event.c index a0d05c8598d0fe0e488bbceb781b5a3e363b39b7..183618090d05bfd8d340e1bd4d8d23cfe9217966 100644 --- a/arch/xtensa/kernel/perf_event.c +++ b/arch/xtensa/kernel/perf_event.c @@ -13,17 +13,26 @@ #include #include +#include #include #include +#define XTENSA_HWVERSION_RG_2015_0 260000 + +#if XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RG_2015_0 +#define XTENSA_PMU_ERI_BASE 0x00101000 +#else +#define XTENSA_PMU_ERI_BASE 0x00001000 +#endif + /* Global control/status for all perf counters */ -#define XTENSA_PMU_PMG 0x1000 +#define XTENSA_PMU_PMG XTENSA_PMU_ERI_BASE /* Perf counter values */ -#define XTENSA_PMU_PM(i) (0x1080 + (i) * 4) +#define XTENSA_PMU_PM(i) (XTENSA_PMU_ERI_BASE + 0x80 + (i) * 4) /* Perf counter control registers */ -#define XTENSA_PMU_PMCTRL(i) (0x1100 + (i) * 4) +#define XTENSA_PMU_PMCTRL(i) (XTENSA_PMU_ERI_BASE + 0x100 + (i) * 4) /* Perf counter status registers */ -#define XTENSA_PMU_PMSTAT(i) (0x1180 + (i) * 4) +#define XTENSA_PMU_PMSTAT(i) (XTENSA_PMU_ERI_BASE + 0x180 + (i) * 4) #define XTENSA_PMU_PMG_PMEN 0x1 diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index f29477162edefdff2bee2eeb3017f1b8e5947f55..9056cd1a8302cdcd8ca5b4ac3d4a8a3ac7ffdaef 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -541,7 +541,6 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } -void do_syscall_trace_leave(struct pt_regs *regs); int do_syscall_trace_enter(struct pt_regs *regs) { if (regs->syscall == NO_SYSCALL) diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c index aba3ff4e60d85740a64a116884391ffda8906bbc..52d6e4870a04c99fcdbdda6ce423c097a39b64eb 100644 --- a/arch/xtensa/kernel/setup.c +++ b/arch/xtensa/kernel/setup.c @@ -311,6 +311,9 @@ void __init setup_arch(char **cmdline_p) mem_reserve(__pa(_stext), __pa(_end)); #ifdef CONFIG_XIP_KERNEL +#ifdef CONFIG_VECTORS_ADDR + mem_reserve(__pa(_xip_text_start), __pa(_xip_text_end)); +#endif mem_reserve(__pa(_xip_start), __pa(_xip_end)); #endif diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index 5c01d7e70d90da4a0f495273180005cf235f4a7c..81f0b106cfc19144433fdaaf593f75d61a657383 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include extern struct task_struct *coproc_owners[]; diff --git a/arch/xtensa/kernel/smp.c b/arch/xtensa/kernel/smp.c index 07dd6baf18cf8d1b47510e012f3d2dc0ad9b1dfc..94a23f1007264af2f0f313786b3b92a4ddab641f 100644 --- a/arch/xtensa/kernel/smp.c +++ b/arch/xtensa/kernel/smp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/xtensa/kernel/stacktrace.c b/arch/xtensa/kernel/stacktrace.c index f643ea5e36dab50fd21fad55706118785247837a..831ffb648bda7e160408fd97530eddee760c27fc 100644 --- a/arch/xtensa/kernel/stacktrace.c +++ b/arch/xtensa/kernel/stacktrace.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 427c125a137aae95b8635d860c609c60fa500883..38092d21acf8e4b285e649cbea6b3bc6920bfa7c 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c @@ -23,6 +23,7 @@ * for more details. */ +#include #include #include #include diff --git a/arch/xtensa/kernel/vmlinux.lds.S b/arch/xtensa/kernel/vmlinux.lds.S index c14fd96f459d65b76bf10552c7202d05e0cf5805..f47e9bbbd29117cbfdbcbee1e1dc86c175ff34b1 100644 --- a/arch/xtensa/kernel/vmlinux.lds.S +++ b/arch/xtensa/kernel/vmlinux.lds.S @@ -118,6 +118,7 @@ SECTIONS SECTION_VECTOR2 (.DoubleExceptionVector.text, DOUBLEEXC_VECTOR_VADDR) *(.exception.text) + *(.xiptext) #endif IRQENTRY_TEXT @@ -201,6 +202,9 @@ SECTIONS .DebugInterruptVector.text); RELOCATE_ENTRY(_exception_text, .exception.text); +#ifdef CONFIG_XIP_KERNEL + RELOCATE_ENTRY(_xip_text, .xiptext); +#endif #endif #ifdef CONFIG_XIP_KERNEL RELOCATE_ENTRY(_xip_data, .data); @@ -319,7 +323,12 @@ SECTIONS LAST) #undef LAST #define LAST .exception.text - + SECTION_VECTOR4 (_xip_text, + .xiptext, + , + LAST) +#undef LAST +#define LAST .xiptext #endif . = (LOADADDR(LAST) + SIZEOF(LAST) + 3) & ~ 3; diff --git a/arch/xtensa/lib/umulsidi3.S b/arch/xtensa/lib/umulsidi3.S index 8c7a94a0c5d07937eeea538c5882e0854b79d3ca..5da501b57813646ef2cfa59b96bd9cd05fe0c52d 100644 --- a/arch/xtensa/lib/umulsidi3.S +++ b/arch/xtensa/lib/umulsidi3.S @@ -3,7 +3,9 @@ #include #include -#if !XCHAL_HAVE_MUL16 && !XCHAL_HAVE_MUL32 && !XCHAL_HAVE_MAC16 +#if XCHAL_HAVE_MUL16 || XCHAL_HAVE_MUL32 || XCHAL_HAVE_MAC16 +#define XCHAL_NO_MUL 0 +#else #define XCHAL_NO_MUL 1 #endif diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c index d1eb8d6c5b826702b87e289b5737310cb9695995..16e11b6f6f78ed1f2fbc33e460a607128aa7788a 100644 --- a/arch/xtensa/mm/fault.c +++ b/arch/xtensa/mm/fault.c @@ -20,6 +20,7 @@ #include #include #include +#include void bad_page_fault(struct pt_regs*, unsigned long, int); diff --git a/arch/xtensa/mm/tlb.c b/arch/xtensa/mm/tlb.c index 0a11fc5f185b7f17380f648a0d2d2672915de4bb..4f974b74883caeb7960d8f825f0759ca3f40d16e 100644 --- a/arch/xtensa/mm/tlb.c +++ b/arch/xtensa/mm/tlb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index 85c82cd42188aff4181210953ccded5729e136cf..e89f27f2bb18dabc13a7de5f3719cff77a2752c0 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -201,7 +201,7 @@ static int tuntap_write(struct iss_net_private *lp, struct sk_buff **skb) return simc_write(lp->tp.info.tuntap.fd, (*skb)->data, (*skb)->len); } -unsigned short tuntap_protocol(struct sk_buff *skb) +static unsigned short tuntap_protocol(struct sk_buff *skb) { return eth_type_trans(skb, skb->dev); } @@ -441,7 +441,7 @@ static int iss_net_change_mtu(struct net_device *dev, int new_mtu) return -EINVAL; } -void iss_net_user_timer_expire(struct timer_list *unused) +static void iss_net_user_timer_expire(struct timer_list *unused) { } diff --git a/block/blk-map.c b/block/blk-map.c index 44d74a30ddac042ec29c2b6880724c3156fd49d8..8584babf3ea0ca2590f30383b9594231266e9437 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -315,12 +315,11 @@ static int bio_map_user_iov(struct request *rq, struct iov_iter *iter, n = bytes; if (!bio_add_hw_page(rq->q, bio, page, n, offs, - max_sectors, &same_page)) { - if (same_page) - bio_release_page(bio, page); + max_sectors, &same_page)) break; - } + if (same_page) + bio_release_page(bio, page); bytes -= n; offs = 0; } diff --git a/block/blk-mq.c b/block/blk-mq.c index ec922c6bccbe20737d21e03a4d4964c8dab1d66f..1fafd54dce3cb9e9f51fb3a4cb0cb6e907a1c066 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -4405,11 +4405,8 @@ static int blk_mq_realloc_tag_set_tags(struct blk_mq_tag_set *set, struct blk_mq_tags **new_tags; int i; - if (set->nr_hw_queues >= new_nr_hw_queues) { - for (i = new_nr_hw_queues; i < set->nr_hw_queues; i++) - __blk_mq_free_map_and_rqs(set, i); + if (set->nr_hw_queues >= new_nr_hw_queues) goto done; - } new_tags = kcalloc_node(new_nr_hw_queues, sizeof(struct blk_mq_tags *), GFP_KERNEL, set->numa_node); @@ -4719,7 +4716,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, { struct request_queue *q; LIST_HEAD(head); - int prev_nr_hw_queues; + int prev_nr_hw_queues = set->nr_hw_queues; + int i; lockdep_assert_held(&set->tag_list_lock); @@ -4746,7 +4744,6 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, blk_mq_sysfs_unregister_hctxs(q); } - prev_nr_hw_queues = set->nr_hw_queues; if (blk_mq_realloc_tag_set_tags(set, nr_hw_queues) < 0) goto reregister; @@ -4781,6 +4778,10 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, list_for_each_entry(q, &set->tag_list, tag_set_list) blk_mq_unfreeze_queue(q); + + /* Free the excess tags when nr_hw_queues shrink. */ + for (i = set->nr_hw_queues; i < prev_nr_hw_queues; i++) + __blk_mq_free_map_and_rqs(set, i); } void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues) diff --git a/block/blk-rq-qos.c b/block/blk-rq-qos.c index 167be74df4eeca66c175a1e77f2a274cc6602ba8..dd7310c94713c954aac5d511147f6feb2d27663e 100644 --- a/block/blk-rq-qos.c +++ b/block/blk-rq-qos.c @@ -270,7 +270,7 @@ void rq_qos_wait(struct rq_wait *rqw, void *private_data, finish_wait(&rqw->wait, &data.wq); /* - * We raced with wbt_wake_function() getting a token, + * We raced with rq_qos_wake_function() getting a token, * which means we now have two. Put our local token * and wake anyone else potentially waiting for one. */ diff --git a/block/blk-throttle.c b/block/blk-throttle.c index 7397ff199d6695e40f4e181f91982d4ec8870252..38a881cf97d0b4d4f8cd6b321ead22d055c46aa7 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -697,11 +697,41 @@ static bool throtl_slice_used(struct throtl_grp *tg, bool rw) return true; } +static unsigned int calculate_io_allowed(u32 iops_limit, + unsigned long jiffy_elapsed) +{ + unsigned int io_allowed; + u64 tmp; + + /* + * jiffy_elapsed should not be a big value as minimum iops can be + * 1 then at max jiffy elapsed should be equivalent of 1 second as we + * will allow dispatch after 1 second and after that slice should + * have been trimmed. + */ + + tmp = (u64)iops_limit * jiffy_elapsed; + do_div(tmp, HZ); + + if (tmp > UINT_MAX) + io_allowed = UINT_MAX; + else + io_allowed = tmp; + + return io_allowed; +} + +static u64 calculate_bytes_allowed(u64 bps_limit, unsigned long jiffy_elapsed) +{ + return mul_u64_u64_div_u64(bps_limit, (u64)jiffy_elapsed, (u64)HZ); +} + /* Trim the used slices and adjust slice start accordingly */ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw) { - unsigned long nr_slices, time_elapsed, io_trim; - u64 bytes_trim, tmp; + unsigned long time_elapsed; + long long bytes_trim; + int io_trim; BUG_ON(time_before(tg->slice_end[rw], tg->slice_start[rw])); @@ -723,67 +753,38 @@ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw) throtl_set_slice_end(tg, rw, jiffies + tg->td->throtl_slice); - time_elapsed = jiffies - tg->slice_start[rw]; - - nr_slices = time_elapsed / tg->td->throtl_slice; - - if (!nr_slices) + time_elapsed = rounddown(jiffies - tg->slice_start[rw], + tg->td->throtl_slice); + if (!time_elapsed) return; - tmp = tg_bps_limit(tg, rw) * tg->td->throtl_slice * nr_slices; - do_div(tmp, HZ); - bytes_trim = tmp; - io_trim = (tg_iops_limit(tg, rw) * tg->td->throtl_slice * nr_slices) / - HZ; - - if (!bytes_trim && !io_trim) + bytes_trim = calculate_bytes_allowed(tg_bps_limit(tg, rw), + time_elapsed) + + tg->carryover_bytes[rw]; + io_trim = calculate_io_allowed(tg_iops_limit(tg, rw), time_elapsed) + + tg->carryover_ios[rw]; + if (bytes_trim <= 0 && io_trim <= 0) return; - if (tg->bytes_disp[rw] >= bytes_trim) + tg->carryover_bytes[rw] = 0; + if ((long long)tg->bytes_disp[rw] >= bytes_trim) tg->bytes_disp[rw] -= bytes_trim; else tg->bytes_disp[rw] = 0; - if (tg->io_disp[rw] >= io_trim) + tg->carryover_ios[rw] = 0; + if ((int)tg->io_disp[rw] >= io_trim) tg->io_disp[rw] -= io_trim; else tg->io_disp[rw] = 0; - tg->slice_start[rw] += nr_slices * tg->td->throtl_slice; + tg->slice_start[rw] += time_elapsed; throtl_log(&tg->service_queue, - "[%c] trim slice nr=%lu bytes=%llu io=%lu start=%lu end=%lu jiffies=%lu", - rw == READ ? 'R' : 'W', nr_slices, bytes_trim, io_trim, - tg->slice_start[rw], tg->slice_end[rw], jiffies); -} - -static unsigned int calculate_io_allowed(u32 iops_limit, - unsigned long jiffy_elapsed) -{ - unsigned int io_allowed; - u64 tmp; - - /* - * jiffy_elapsed should not be a big value as minimum iops can be - * 1 then at max jiffy elapsed should be equivalent of 1 second as we - * will allow dispatch after 1 second and after that slice should - * have been trimmed. - */ - - tmp = (u64)iops_limit * jiffy_elapsed; - do_div(tmp, HZ); - - if (tmp > UINT_MAX) - io_allowed = UINT_MAX; - else - io_allowed = tmp; - - return io_allowed; -} - -static u64 calculate_bytes_allowed(u64 bps_limit, unsigned long jiffy_elapsed) -{ - return mul_u64_u64_div_u64(bps_limit, (u64)jiffy_elapsed, (u64)HZ); + "[%c] trim slice nr=%lu bytes=%lld io=%d start=%lu end=%lu jiffies=%lu", + rw == READ ? 'R' : 'W', time_elapsed / tg->td->throtl_slice, + bytes_trim, io_trim, tg->slice_start[rw], tg->slice_end[rw], + jiffies); } static void __tg_update_carryover(struct throtl_grp *tg, bool rw) @@ -816,7 +817,7 @@ static void tg_update_carryover(struct throtl_grp *tg) __tg_update_carryover(tg, WRITE); /* see comments in struct throtl_grp for meaning of these fields. */ - throtl_log(&tg->service_queue, "%s: %llu %llu %u %u\n", __func__, + throtl_log(&tg->service_queue, "%s: %lld %lld %d %d\n", __func__, tg->carryover_bytes[READ], tg->carryover_bytes[WRITE], tg->carryover_ios[READ], tg->carryover_ios[WRITE]); } @@ -825,7 +826,7 @@ static unsigned long tg_within_iops_limit(struct throtl_grp *tg, struct bio *bio u32 iops_limit) { bool rw = bio_data_dir(bio); - unsigned int io_allowed; + int io_allowed; unsigned long jiffy_elapsed, jiffy_wait, jiffy_elapsed_rnd; if (iops_limit == UINT_MAX) { @@ -838,9 +839,8 @@ static unsigned long tg_within_iops_limit(struct throtl_grp *tg, struct bio *bio jiffy_elapsed_rnd = roundup(jiffy_elapsed + 1, tg->td->throtl_slice); io_allowed = calculate_io_allowed(iops_limit, jiffy_elapsed_rnd) + tg->carryover_ios[rw]; - if (tg->io_disp[rw] + 1 <= io_allowed) { + if (io_allowed > 0 && tg->io_disp[rw] + 1 <= io_allowed) return 0; - } /* Calc approx time to dispatch */ jiffy_wait = jiffy_elapsed_rnd - jiffy_elapsed; @@ -851,7 +851,8 @@ static unsigned long tg_within_bps_limit(struct throtl_grp *tg, struct bio *bio, u64 bps_limit) { bool rw = bio_data_dir(bio); - u64 bytes_allowed, extra_bytes; + long long bytes_allowed; + u64 extra_bytes; unsigned long jiffy_elapsed, jiffy_wait, jiffy_elapsed_rnd; unsigned int bio_size = throtl_bio_data_size(bio); @@ -869,9 +870,8 @@ static unsigned long tg_within_bps_limit(struct throtl_grp *tg, struct bio *bio, jiffy_elapsed_rnd = roundup(jiffy_elapsed_rnd, tg->td->throtl_slice); bytes_allowed = calculate_bytes_allowed(bps_limit, jiffy_elapsed_rnd) + tg->carryover_bytes[rw]; - if (tg->bytes_disp[rw] + bio_size <= bytes_allowed) { + if (bytes_allowed > 0 && tg->bytes_disp[rw] + bio_size <= bytes_allowed) return 0; - } /* Calc approx time to dispatch */ extra_bytes = tg->bytes_disp[rw] + bio_size - bytes_allowed; diff --git a/block/blk-throttle.h b/block/blk-throttle.h index d1ccbfe9f797882c05256a5780fa151093174c44..bffbc9cfc8ab6bac79ad4d3cff04622ce1f852d8 100644 --- a/block/blk-throttle.h +++ b/block/blk-throttle.h @@ -127,8 +127,8 @@ struct throtl_grp { * bytes/ios are waited already in previous configuration, and they will * be used to calculate wait time under new configuration. */ - uint64_t carryover_bytes[2]; - unsigned int carryover_ios[2]; + long long carryover_bytes[2]; + int carryover_ios[2]; unsigned long last_check_time; diff --git a/block/disk-events.c b/block/disk-events.c index 422db8292d0997972cc4c62516a7e312aae421b5..13c3372c465a395d32035a6cf32a93f2a886a6a7 100644 --- a/block/disk-events.c +++ b/block/disk-events.c @@ -290,7 +290,6 @@ EXPORT_SYMBOL(disk_check_media_change); /** * disk_force_media_change - force a media change event * @disk: the disk which will raise the event - * @events: the events to raise * * Should be called when the media changes for @disk. Generates a uevent * and attempts to free all dentries and inodes and invalidates all block diff --git a/block/fops.c b/block/fops.c index a24a624d3bf71a900e0661bcba040bf3f65e5cc5..acff3d5d22d461af733814b8ce841324e5c87c7c 100644 --- a/block/fops.c +++ b/block/fops.c @@ -671,10 +671,6 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from) iov_iter_truncate(from, size); } - ret = file_remove_privs(file); - if (ret) - return ret; - ret = file_update_time(file); if (ret) return ret; diff --git a/block/ioctl.c b/block/ioctl.c index 648670ddb164a06eeeebe429de59e5de1a969ec6..d5f5cd61efd7fc738644432d220268f59d5f38ab 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -20,6 +20,8 @@ static int blkpg_do_ioctl(struct block_device *bdev, struct blkpg_partition p; long long start, length; + if (disk->flags & GENHD_FL_NO_PART) + return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (copy_from_user(&p, upart, sizeof(struct blkpg_partition))) diff --git a/crypto/sm2.c b/crypto/sm2.c index 285b3cb7c0bc781b4c8be08b1c2c32a931ad0c3c..5ab120d74c592b77dd53c9a44a9c68ab9138f5dd 100644 --- a/crypto/sm2.c +++ b/crypto/sm2.c @@ -278,10 +278,14 @@ int sm2_compute_z_digest(struct shash_desc *desc, if (!ec) return -ENOMEM; - err = __sm2_set_pub_key(ec, key, keylen); + err = sm2_ec_ctx_init(ec); if (err) goto out_free_ec; + err = __sm2_set_pub_key(ec, key, keylen); + if (err) + goto out_deinit_ec; + bits_len = SM2_DEFAULT_USERID_LEN * 8; entl[0] = bits_len >> 8; entl[1] = bits_len & 0xff; diff --git a/drivers/Kconfig b/drivers/Kconfig index 496ca02ee18f4d8b09d32deab3c8fc81842d9da4..efb66e25fa2dd2304f1caf6d503aa04fa33d9c4a 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -15,6 +15,8 @@ source "drivers/base/Kconfig" source "drivers/bus/Kconfig" +source "drivers/cache/Kconfig" + source "drivers/connector/Kconfig" source "drivers/firmware/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 0957f63ecb425d4ee79933dfaa525af3d209eefd..1bec7819a837ab86c0169ca4b6a06b2d37b5483c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -11,6 +11,7 @@ ifdef building_out_of_srctree MAKEFLAGS += --include-dir=$(srctree) endif +obj-y += cache/ obj-y += irqchip/ obj-y += bus/ @@ -45,7 +46,7 @@ obj-$(CONFIG_DMADEVICES) += dma/ # SOC specific infrastructure drivers. obj-y += soc/ -obj-$(CONFIG_PM_GENERIC_DOMAINS) += genpd/ +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pmdomain/ obj-y += virtio/ obj-$(CONFIG_VDPA) += vdpa/ diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index ba79f397c9e8ed92ba5240dac46737be75f00c18..467a60235370c58faa09901624a62b5a85ac32fe 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -327,7 +327,7 @@ static int ivpu_wait_for_ready(struct ivpu_device *vdev) } if (!ret) - ivpu_info(vdev, "VPU ready message received successfully\n"); + ivpu_dbg(vdev, PM, "VPU ready message received successfully\n"); else ivpu_hw_diagnose_failure(vdev); @@ -634,6 +634,7 @@ static void ivpu_dev_fini(struct ivpu_device *vdev) static struct pci_device_id ivpu_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_MTL) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_ARL) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) }, { } }; diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 9e8c075fe9ef15c447a6705ae5845d9a7792bd2f..03b3d6532fb626306e106afa2715dd02f6800ed0 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -23,6 +23,7 @@ #define DRIVER_DATE "20230117" #define PCI_DEVICE_ID_MTL 0x7d1d +#define PCI_DEVICE_ID_ARL 0xad1d #define PCI_DEVICE_ID_LNL 0x643e #define IVPU_HW_37XX 37 @@ -165,6 +166,7 @@ static inline int ivpu_hw_gen(struct ivpu_device *vdev) { switch (ivpu_device_id(vdev)) { case PCI_DEVICE_ID_MTL: + case PCI_DEVICE_ID_ARL: return IVPU_HW_37XX; case PCI_DEVICE_ID_LNL: return IVPU_HW_40XX; diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index 9827ea4d7b83b46483a26fd2d9a0e622bc4fa9cf..0191cf8e5964a9d8532425ee80274f276a924a72 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -220,7 +220,8 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev) if (ret) return ret; - fw->mem = ivpu_bo_alloc_internal(vdev, fw->runtime_addr, fw->runtime_size, DRM_IVPU_BO_WC); + fw->mem = ivpu_bo_alloc_internal(vdev, fw->runtime_addr, fw->runtime_size, + DRM_IVPU_BO_CACHED | DRM_IVPU_BO_NOSNOOP); if (!fw->mem) { ivpu_err(vdev, "Failed to allocate firmware runtime memory\n"); return -ENOMEM; @@ -330,7 +331,7 @@ int ivpu_fw_load(struct ivpu_device *vdev) memset(start, 0, size); } - wmb(); /* Flush WC buffers after writing fw->mem */ + clflush_cache_range(fw->mem->kvaddr, fw->mem->base.size); return 0; } @@ -432,6 +433,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params if (!ivpu_fw_is_cold_boot(vdev)) { boot_params->save_restore_ret_address = 0; vdev->pm->is_warmboot = true; + clflush_cache_range(vdev->fw->mem->kvaddr, SZ_4K); return; } @@ -493,7 +495,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params boot_params->punit_telemetry_sram_size = ivpu_hw_reg_telemetry_size_get(vdev); boot_params->vpu_telemetry_enable = ivpu_hw_reg_telemetry_enable_get(vdev); - wmb(); /* Flush WC buffers after writing bootparams */ + clflush_cache_range(vdev->fw->mem->kvaddr, SZ_4K); ivpu_fw_boot_params_print(vdev, boot_params); } diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h index 6b0ceda5f2537491bda6314f40b573a0f0a6a27f..f4130586ff1b210602f90915db8133a68b4ad35f 100644 --- a/drivers/accel/ivpu/ivpu_gem.h +++ b/drivers/accel/ivpu/ivpu_gem.h @@ -8,6 +8,8 @@ #include #include +#define DRM_IVPU_BO_NOSNOOP 0x10000000 + struct dma_buf; struct ivpu_bo_ops; struct ivpu_file_priv; @@ -83,6 +85,9 @@ static inline u32 ivpu_bo_cache_mode(struct ivpu_bo *bo) static inline bool ivpu_bo_is_snooped(struct ivpu_bo *bo) { + if (bo->flags & DRM_IVPU_BO_NOSNOOP) + return false; + return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED; } diff --git a/drivers/accel/ivpu/ivpu_hw_40xx.c b/drivers/accel/ivpu/ivpu_hw_40xx.c index 34626d66fa10284263435dda3c62866ce6853d55..8bdb59a45da6b38793d32352e159eebc42647ee3 100644 --- a/drivers/accel/ivpu/ivpu_hw_40xx.c +++ b/drivers/accel/ivpu/ivpu_hw_40xx.c @@ -57,8 +57,7 @@ #define ICB_0_1_IRQ_MASK ((((u64)ICB_1_IRQ_MASK) << 32) | ICB_0_IRQ_MASK) -#define BUTTRESS_IRQ_MASK ((REG_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, FREQ_CHANGE)) | \ - (REG_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, ATS_ERR)) | \ +#define BUTTRESS_IRQ_MASK ((REG_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, ATS_ERR)) | \ (REG_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, CFI0_ERR)) | \ (REG_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, CFI1_ERR)) | \ (REG_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, IMR0_ERR)) | \ @@ -196,6 +195,14 @@ static int ivpu_pll_wait_for_status_ready(struct ivpu_device *vdev) return REGB_POLL_FLD(VPU_40XX_BUTTRESS_VPU_STATUS, READY, 1, PLL_TIMEOUT_US); } +static int ivpu_wait_for_clock_own_resource_ack(struct ivpu_device *vdev) +{ + if (ivpu_is_simics(vdev)) + return 0; + + return REGB_POLL_FLD(VPU_40XX_BUTTRESS_VPU_STATUS, CLOCK_RESOURCE_OWN_ACK, 1, TIMEOUT_US); +} + static void ivpu_pll_init_frequency_ratios(struct ivpu_device *vdev) { struct ivpu_hw_info *hw = vdev->hw; @@ -556,6 +563,12 @@ static int ivpu_boot_pwr_domain_enable(struct ivpu_device *vdev) { int ret; + ret = ivpu_wait_for_clock_own_resource_ack(vdev); + if (ret) { + ivpu_err(vdev, "Timed out waiting for clock own resource ACK\n"); + return ret; + } + ivpu_boot_pwr_island_trickle_drive(vdev, true); ivpu_boot_pwr_island_drive(vdev, true); @@ -1046,8 +1059,6 @@ static irqreturn_t ivpu_hw_40xx_irqb_handler(struct ivpu_device *vdev, int irq) if (status == 0) return IRQ_NONE; - REGB_WR32(VPU_40XX_BUTTRESS_INTERRUPT_STAT, status); - if (REG_TEST_FLD(VPU_40XX_BUTTRESS_INTERRUPT_STAT, FREQ_CHANGE, status)) ivpu_dbg(vdev, IRQ, "FREQ_CHANGE"); @@ -1092,6 +1103,9 @@ static irqreturn_t ivpu_hw_40xx_irqb_handler(struct ivpu_device *vdev, int irq) schedule_recovery = true; } + /* This must be done after interrupts are cleared at the source. */ + REGB_WR32(VPU_40XX_BUTTRESS_INTERRUPT_STAT, status); + if (schedule_recovery) ivpu_pm_schedule_recovery(vdev); @@ -1103,9 +1117,14 @@ static irqreturn_t ivpu_hw_40xx_irq_handler(int irq, void *ptr) struct ivpu_device *vdev = ptr; irqreturn_t ret = IRQ_NONE; + REGB_WR32(VPU_40XX_BUTTRESS_GLOBAL_INT_MASK, 0x1); + ret |= ivpu_hw_40xx_irqv_handler(vdev, irq); ret |= ivpu_hw_40xx_irqb_handler(vdev, irq); + /* Re-enable global interrupts to re-trigger MSI for pending interrupts */ + REGB_WR32(VPU_40XX_BUTTRESS_GLOBAL_INT_MASK, 0x0); + if (ret & IRQ_WAKE_THREAD) return IRQ_WAKE_THREAD; diff --git a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h index 5139cfe88532b9afd3e34c1ac13e63aeb547581f..ff4a5d4f58215f6a4cf9851d499c9bfeb0b38e0f 100644 --- a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h +++ b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h @@ -70,6 +70,8 @@ #define VPU_40XX_BUTTRESS_VPU_STATUS_READY_MASK BIT_MASK(0) #define VPU_40XX_BUTTRESS_VPU_STATUS_IDLE_MASK BIT_MASK(1) #define VPU_40XX_BUTTRESS_VPU_STATUS_DUP_IDLE_MASK BIT_MASK(2) +#define VPU_40XX_BUTTRESS_VPU_STATUS_CLOCK_RESOURCE_OWN_ACK_MASK BIT_MASK(6) +#define VPU_40XX_BUTTRESS_VPU_STATUS_POWER_RESOURCE_OWN_ACK_MASK BIT_MASK(7) #define VPU_40XX_BUTTRESS_VPU_STATUS_PERF_CLK_MASK BIT_MASK(11) #define VPU_40XX_BUTTRESS_VPU_STATUS_DISABLE_CLK_RELINQUISH_MASK BIT_MASK(12) diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index fa0af59e39ab66f0a0c20d8e4bfc4892c2ff7426..295c0d7b50398ede66f44b01e5aa31ab9f151ddf 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -209,10 +209,10 @@ int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, struct ivpu_ipc_rx_msg *rx_msg; int wait_ret, ret = 0; - wait_ret = wait_event_interruptible_timeout(cons->rx_msg_wq, - (IS_KTHREAD() && kthread_should_stop()) || - !list_empty(&cons->rx_msg_list), - msecs_to_jiffies(timeout_ms)); + wait_ret = wait_event_timeout(cons->rx_msg_wq, + (IS_KTHREAD() && kthread_should_stop()) || + !list_empty(&cons->rx_msg_list), + msecs_to_jiffies(timeout_ms)); if (IS_KTHREAD() && kthread_should_stop()) return -EINTR; @@ -220,9 +220,6 @@ int ivpu_ipc_receive(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, if (wait_ret == 0) return -ETIMEDOUT; - if (wait_ret < 0) - return -ERESTARTSYS; - spin_lock_irq(&cons->rx_msg_lock); rx_msg = list_first_entry_or_null(&cons->rx_msg_list, struct ivpu_ipc_rx_msg, link); if (!rx_msg) { diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c index 831bfd2b2d39d4e33c03d1c335c814d728bf50cd..bdddef2c59eecff8e04487a753d5c479439d67f9 100644 --- a/drivers/accel/ivpu/ivpu_jsm_msg.c +++ b/drivers/accel/ivpu/ivpu_jsm_msg.c @@ -118,8 +118,7 @@ int ivpu_jsm_dyndbg_control(struct ivpu_device *vdev, char *command, size_t size struct vpu_jsm_msg resp; int ret; - if (!strncpy(req.payload.dyndbg_control.dyndbg_cmd, command, VPU_DYNDBG_CMD_MAX_LEN - 1)) - return -ENOMEM; + strscpy(req.payload.dyndbg_control.dyndbg_cmd, command, VPU_DYNDBG_CMD_MAX_LEN); ret = ivpu_ipc_send_receive(vdev, &req, VPU_JSM_MSG_DYNDBG_CONTROL_RSP, &resp, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm); diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 948e31f7ce6e70918c9034989cb7d6dbddf4a70b..b411948594ff89ad2a9904a8681b02476a4bd13d 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -2057,7 +2057,9 @@ static int acpi_video_bus_add(struct acpi_device *device) !auto_detect) acpi_video_bus_register_backlight(video); - acpi_video_bus_add_notify_handler(video); + error = acpi_video_bus_add_notify_handler(video); + if (error) + goto err_del; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); @@ -2067,10 +2069,11 @@ static int acpi_video_bus_add(struct acpi_device *device) return 0; err_remove: + acpi_video_bus_remove_notify_handler(video); +err_del: mutex_lock(&video_list_lock); list_del(&video->entry); mutex_unlock(&video_list_lock); - acpi_video_bus_remove_notify_handler(video); acpi_video_bus_unregister_backlight(video); err_put_video: acpi_video_bus_put_devices(video); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index f0e6738ae3c94146a3878f833f0406f3fedf44a0..f96bf32cd3687599573b7d2c60a1fe64b4340452 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -855,7 +855,7 @@ static size_t sizeof_idt(struct acpi_nfit_interleave *idt) { if (idt->header.length < sizeof(*idt)) return 0; - return sizeof(*idt) + sizeof(u32) * (idt->line_count - 1); + return sizeof(*idt) + sizeof(u32) * idt->line_count; } static bool add_idt(struct acpi_nfit_desc *acpi_desc, diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index dc615ef6550a12b58592de70fddbd4a9f8f6304d..3a34a8c425fe4a673119d5ac7d9fef87ed145f38 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1217,8 +1217,7 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr) strscpy(state->desc, lpi->desc, CPUIDLE_DESC_LEN); state->exit_latency = lpi->wake_latency; state->target_residency = lpi->min_residency; - if (lpi->arch_flags) - state->flags |= CPUIDLE_FLAG_TIMER_STOP; + state->flags |= arch_get_idle_state_flags(lpi->arch_flags); if (i != 0 && lpi->entry_method == ACPI_CSTATE_FFH) state->flags |= CPUIDLE_FLAG_RCU_IDLE; state->enter = acpi_idle_lpi_enter; diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c index 1a8591e9a9bf1a215b058ee227e6b1ec8fe8c4d5..994091bd52de4027877638c66a02d41e100b4748 100644 --- a/drivers/acpi/processor_pdc.c +++ b/drivers/acpi/processor_pdc.c @@ -19,6 +19,7 @@ static void acpi_set_pdc_bits(u32 *buf) { buf[0] = ACPI_PDC_REVISION_ID; buf[1] = 1; + buf[2] = 0; /* Twiddle arch-specific bits needed for _PDC */ arch_acpi_set_proc_cap_bits(&buf[2]); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 531a9e3df7178e55960f606d78ec1a4a04a038ef..691d4b7686ee7eb40d03ba6b81263a2dc51c8838 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1584,7 +1584,7 @@ static const struct iommu_ops *acpi_iommu_configure_id(struct device *dev, * If we have reason to believe the IOMMU driver missed the initial * iommu_probe_device() call for dev, replay it to get things in order. */ - if (!err && dev->bus && !device_iommu_mapped(dev)) + if (!err && dev->bus) err = iommu_probe_device(dev); /* Ignore all other errors apart from EPROBE_DEFER */ diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 419590f41ed5f172c21b3510ad3c4e4a8bdd6323..312730f8272eeca4edc6d6695469b25497e57e15 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -492,26 +492,22 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp) } static int thermal_get_trend(struct thermal_zone_device *thermal, - int trip_index, enum thermal_trend *trend) + const struct thermal_trip *trip, + enum thermal_trend *trend) { struct acpi_thermal *tz = thermal_zone_device_priv(thermal); struct acpi_thermal_trip *acpi_trip; - int t, i; + int t; - if (!tz || trip_index < 0) + if (!tz || !trip) return -EINVAL; - if (tz->trips.critical.valid) - trip_index--; - - if (tz->trips.hot.valid) - trip_index--; - - if (trip_index < 0) + acpi_trip = trip->priv; + if (!acpi_trip || !acpi_trip->valid) return -EINVAL; - acpi_trip = &tz->trips.passive.trip; - if (acpi_trip->valid && !trip_index--) { + switch (trip->type) { + case THERMAL_TRIP_PASSIVE: t = tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature) + tz->trips.passive.tc2 * (tz->temperature - @@ -524,19 +520,18 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, *trend = THERMAL_TREND_STABLE; return 0; - } - - t = acpi_thermal_temp(tz, tz->temperature); - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - acpi_trip = &tz->trips.active[i].trip; - if (acpi_trip->valid && !trip_index--) { - if (t > acpi_thermal_temp(tz, acpi_trip->temperature)) { - *trend = THERMAL_TREND_RAISING; - return 0; - } + case THERMAL_TRIP_ACTIVE: + t = acpi_thermal_temp(tz, tz->temperature); + if (t <= trip->temperature) break; - } + + *trend = THERMAL_TREND_RAISING; + + return 0; + + default: + break; } return -EINVAL; diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index addba109406bef11df1c3e39e65318f1157335e7..08745e7db8201fcfd95bb63767b529e2ce337a60 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -421,6 +421,8 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x34d3), board_ahci_low_power }, /* Ice Lake LP AHCI */ { PCI_VDEVICE(INTEL, 0x02d3), board_ahci_low_power }, /* Comet Lake PCH-U AHCI */ { PCI_VDEVICE(INTEL, 0x02d7), board_ahci_low_power }, /* Comet Lake PCH RAID */ + /* Elkhart Lake IDs 0x4b60 & 0x4b62 https://sata-io.org/product/8803 not tested yet */ + { PCI_VDEVICE(INTEL, 0x4b63), board_ahci_low_power }, /* Elkhart Lake AHCI */ /* JMicron 360/1/3/5/6, match class to avoid IDE function */ { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, @@ -807,7 +809,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; struct ahci_host_priv *hpriv = ap->host->private_data; @@ -1881,6 +1883,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) else dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n"); + if (!(hpriv->cap & HOST_CAP_PART)) + host->flags |= ATA_HOST_NO_PART; + + if (!(hpriv->cap & HOST_CAP_SSC)) + host->flags |= ATA_HOST_NO_SSC; + + if (!(hpriv->cap2 & HOST_CAP2_SDS)) + host->flags |= ATA_HOST_NO_DEVSLP; + if (pi.flags & ATA_FLAG_EM) ahci_reset_em(host); diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c index c2b6be083af434d2ea2a539bae101b9b7259f9cf..64f7f7d6ba84e07c2f2db2fbbdfb3d315f821ec2 100644 --- a/drivers/ata/ahci_ceva.c +++ b/drivers/ata/ahci_ceva.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include "ahci.h" diff --git a/drivers/ata/ahci_dwc.c b/drivers/ata/ahci_dwc.c index 9604a2f6ed489e4ad5d9bde8ede8032446daf262..ed263de3fd7067dfcb75c694de75692ff1ab79a5 100644 --- a/drivers/ata/ahci_dwc.c +++ b/drivers/ata/ahci_dwc.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/ata/ahci_mtk.c b/drivers/ata/ahci_mtk.c index 5083fb6c49277324a7e38fd44f158067c64747df..adc851cd55789c9b99ce6461ecb1af78c817e346 100644 --- a/drivers/ata/ahci_mtk.c +++ b/drivers/ata/ahci_mtk.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index 76450151858236df0b3c3465e28d2109ec6691fb..f3187351e8a6c2daef3438dc3008ecac92692b48 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include "ahci.h" diff --git a/drivers/ata/ahci_octeon.c b/drivers/ata/ahci_octeon.c index e89807fa928e4bd1d158554c02e947263a67389f..9accf8923891ab93a2c1c01dbfccea7f0282e146 100644 --- a/drivers/ata/ahci_octeon.c +++ b/drivers/ata/ahci_octeon.c @@ -31,13 +31,11 @@ static int ahci_octeon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - struct resource *res; void __iomem *base; u64 cfg; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c index 3d01b118c9a1a2f8f1770525b8560f007deefc20..b1a4e57578e203f1b8db661782e49b8f1e71fba8 100644 --- a/drivers/ata/ahci_qoriq.c +++ b/drivers/ata/ahci_qoriq.c @@ -12,9 +12,7 @@ #include #include #include -#include #include -#include #include #include #include "ahci.h" @@ -90,7 +88,7 @@ MODULE_DEVICE_TABLE(acpi, ahci_qoriq_acpi_match); static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); void __iomem *port_mmio = ahci_port_base(link->ap); u32 px_cmd, px_is, px_val; struct ata_port *ap = link->ap; diff --git a/drivers/ata/ahci_seattle.c b/drivers/ata/ahci_seattle.c index 2c32d58c6ae7543bafb327c99254ceb461b61004..59f97aa7ac75c29a1c1973e9b4d93c806c354ea9 100644 --- a/drivers/ata/ahci_seattle.c +++ b/drivers/ata/ahci_seattle.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -132,8 +131,7 @@ static const struct ata_port_info *ahci_seattle_get_port_info( if (!plat_data) return &ahci_port_info; - plat_data->sgpio_ctrl = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 1)); + plat_data->sgpio_ctrl = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(plat_data->sgpio_ctrl)) return &ahci_port_info; diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c index 04531fa95e404c41ac5313f77231d603b01e7b50..58b2683954ddf765e377bf124407d71d14caa4f6 100644 --- a/drivers/ata/ahci_sunxi.c +++ b/drivers/ata/ahci_sunxi.c @@ -13,8 +13,8 @@ #include #include #include +#include #include -#include #include #include #include "ahci.h" diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 21c20793e517780ec69c1e65d6df06a9b7315fd0..8703c2a4658bb9b777459629e06545f1f369cb9d 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -530,8 +530,7 @@ static int tegra_ahci_probe(struct platform_device *pdev) tegra->pdev = pdev; tegra->soc = of_device_get_match_data(&pdev->dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - tegra->sata_regs = devm_ioremap_resource(&pdev->dev, res); + tegra->sata_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(tegra->sata_regs)) return PTR_ERR(tegra->sata_regs); diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index eb773f2e28fcbbc6eeccbc9b0eff6197e2ef6a87..ccef5e63bdf9d916bda97eb96b1957eda0635bf5 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -110,9 +110,8 @@ static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) * @timeout : timeout for achieving the value. */ static int xgene_ahci_poll_reg_val(struct ata_port *ap, - void __iomem *reg, unsigned - int val, unsigned long interval, - unsigned long timeout) + void __iomem *reg, unsigned int val, + unsigned int interval, unsigned int timeout) { unsigned long deadline; unsigned int tmp; @@ -350,7 +349,7 @@ static void xgene_ahci_set_phy_cfg(struct xgene_ahci_context *ctx, int channel) static int xgene_ahci_do_hardreset(struct ata_link *link, unsigned long deadline, bool *online) { - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); struct ata_port *ap = link->ap; struct ahci_host_priv *hpriv = ap->host->private_data; struct xgene_ahci_context *ctx = hpriv->plat_data; @@ -755,20 +754,17 @@ static int xgene_ahci_probe(struct platform_device *pdev) ctx->dev = dev; /* Retrieve the IP core resource */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - ctx->csr_core = devm_ioremap_resource(dev, res); + ctx->csr_core = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(ctx->csr_core)) return PTR_ERR(ctx->csr_core); /* Retrieve the IP diagnostic resource */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - ctx->csr_diag = devm_ioremap_resource(dev, res); + ctx->csr_diag = devm_platform_ioremap_resource(pdev, 2); if (IS_ERR(ctx->csr_diag)) return PTR_ERR(ctx->csr_diag); /* Retrieve the IP AXI resource */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 3); - ctx->csr_axi = devm_ioremap_resource(dev, res); + ctx->csr_axi = devm_platform_ioremap_resource(pdev, 3); if (IS_ERR(ctx->csr_axi)) return PTR_ERR(ctx->csr_axi); diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 06aec35f88f2c61cf01b0b08d994585a73a4aa87..f1263364fa97fa5f37f878f5a16c376290fb9e18 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1256,6 +1256,26 @@ static ssize_t ahci_activity_show(struct ata_device *dev, char *buf) return sprintf(buf, "%d\n", emp->blink_policy); } +static void ahci_port_clear_pending_irq(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 tmp; + + /* clear SError */ + tmp = readl(port_mmio + PORT_SCR_ERR); + dev_dbg(ap->host->dev, "PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* clear port IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + dev_dbg(ap->host->dev, "PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << ap->port_no, hpriv->mmio + HOST_IRQ_STAT); +} + static void ahci_port_init(struct device *dev, struct ata_port *ap, int port_no, void __iomem *mmio, void __iomem *port_mmio) @@ -1270,18 +1290,7 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap, if (rc) dev_warn(dev, "%s (%d)\n", emsg, rc); - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - dev_dbg(dev, "PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); - - /* clear port IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - dev_dbg(dev, "PORT_IRQ_STAT 0x%x\n", tmp); - if (tmp) - writel(tmp, port_mmio + PORT_IRQ_STAT); - - writel(1 << port_no, mmio + HOST_IRQ_STAT); + ahci_port_clear_pending_irq(ap); /* mark esata ports */ tmp = readl(port_mmio + PORT_CMD); @@ -1403,7 +1412,7 @@ EXPORT_SYMBOL_GPL(ahci_kick_engine); static int ahci_exec_polled_cmd(struct ata_port *ap, int pmp, struct ata_taskfile *tf, int is_cmd, u16 flags, - unsigned long timeout_msec) + unsigned int timeout_msec) { const u32 cmd_fis_len = 5; /* five dwords */ struct ahci_port_priv *pp = ap->private_data; @@ -1448,7 +1457,8 @@ int ahci_do_softreset(struct ata_link *link, unsigned int *class, struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp = ap->private_data; const char *reason = NULL; - unsigned long now, msecs; + unsigned long now; + unsigned int msecs; struct ata_taskfile tf; bool fbs_disabled = false; int rc; @@ -1587,7 +1597,7 @@ static int ahci_pmp_retry_softreset(struct ata_link *link, unsigned int *class, int ahci_do_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline, bool *online) { - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; struct ahci_host_priv *hpriv = ap->host->private_data; @@ -1602,6 +1612,8 @@ int ahci_do_hardreset(struct ata_link *link, unsigned int *class, tf.status = ATA_BUSY; ata_tf_to_fis(&tf, 0, 0, d2h_fis); + ahci_port_clear_pending_irq(ap); + rc = sata_link_hardreset(link, timing, deadline, online, ahci_check_ready); diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 9a8d43f54adcf36ea5b9d9636f509641aec38d56..581704e61f286bf7f013637d7c8fccd54f14f726 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include "ahci.h" diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 04db0f2c683a72d6779aafa8ced3dea777917fd5..d8cc1e27a125f0fbcadcd31fa146afc8868e2b6e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1586,13 +1586,11 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev, } } - if (ap->ops->error_handler) - ata_eh_release(ap); + ata_eh_release(ap); rc = wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout)); - if (ap->ops->error_handler) - ata_eh_acquire(ap); + ata_eh_acquire(ap); ata_sff_flush_pio_task(ap); @@ -1607,10 +1605,7 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev, if (qc->flags & ATA_QCFLAG_ACTIVE) { qc->err_mask |= AC_ERR_TIMEOUT; - if (ap->ops->error_handler) - ata_port_freeze(ap); - else - ata_qc_complete(qc); + ata_port_freeze(ap); ata_dev_warn(dev, "qc timeout after %u msecs (cmd 0x%x)\n", timeout, command); @@ -1977,6 +1972,96 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, return rc; } +/** + * ata_dev_power_set_standby - Set a device power mode to standby + * @dev: target device + * + * Issue a STANDBY IMMEDIATE command to set a device power mode to standby. + * For an HDD device, this spins down the disks. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +void ata_dev_power_set_standby(struct ata_device *dev) +{ + unsigned long ap_flags = dev->link->ap->flags; + struct ata_taskfile tf; + unsigned int err_mask; + + /* Issue STANDBY IMMEDIATE command only if supported by the device */ + if (dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) + return; + + /* + * Some odd clown BIOSes issue spindown on power off (ACPI S4 or S5) + * causing some drives to spin up and down again. For these, do nothing + * if we are being called on shutdown. + */ + if ((ap_flags & ATA_FLAG_NO_POWEROFF_SPINDOWN) && + system_state == SYSTEM_POWER_OFF) + return; + + if ((ap_flags & ATA_FLAG_NO_HIBERNATE_SPINDOWN) && + system_entering_hibernation()) + return; + + ata_tf_init(dev, &tf); + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol = ATA_PROT_NODATA; + tf.command = ATA_CMD_STANDBYNOW1; + + ata_dev_notice(dev, "Entering standby power mode\n"); + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + if (err_mask) + ata_dev_err(dev, "STANDBY IMMEDIATE failed (err_mask=0x%x)\n", + err_mask); +} + +/** + * ata_dev_power_set_active - Set a device power mode to active + * @dev: target device + * + * Issue a VERIFY command to enter to ensure that the device is in the + * active power mode. For a spun-down HDD (standby or idle power mode), + * the VERIFY command will complete after the disk spins up. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +void ata_dev_power_set_active(struct ata_device *dev) +{ + struct ata_taskfile tf; + unsigned int err_mask; + + /* + * Issue READ VERIFY SECTORS command for 1 sector at lba=0 only + * if supported by the device. + */ + if (dev->class != ATA_DEV_ATA && dev->class != ATA_DEV_ZAC) + return; + + ata_tf_init(dev, &tf); + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol = ATA_PROT_NODATA; + tf.command = ATA_CMD_VERIFY; + tf.nsect = 1; + if (dev->flags & ATA_DFLAG_LBA) { + tf.flags |= ATA_TFLAG_LBA; + tf.device |= ATA_LBA; + } else { + /* CHS */ + tf.lbal = 0x1; /* sect */ + } + + ata_dev_notice(dev, "Entering active power mode\n"); + + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); + if (err_mask) + ata_dev_err(dev, "VERIFY failed (err_mask=0x%x)\n", + err_mask); +} + /** * ata_read_log_page - read a specific log page * @dev: target device @@ -2534,7 +2619,7 @@ static int ata_dev_config_lba(struct ata_device *dev) { const u16 *id = dev->id; const char *lba_desc; - char ncq_desc[24]; + char ncq_desc[32]; int ret; dev->flags |= ATA_DFLAG_LBA; @@ -3062,144 +3147,6 @@ int ata_cable_sata(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_cable_sata); -/** - * ata_bus_probe - Reset and probe ATA bus - * @ap: Bus to probe - * - * Master ATA bus probing function. Initiates a hardware-dependent - * bus reset, then attempts to identify any devices found on - * the bus. - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * Zero on success, negative errno otherwise. - */ - -int ata_bus_probe(struct ata_port *ap) -{ - unsigned int classes[ATA_MAX_DEVICES]; - int tries[ATA_MAX_DEVICES]; - int rc; - struct ata_device *dev; - - ata_for_each_dev(dev, &ap->link, ALL) - tries[dev->devno] = ATA_PROBE_MAX_TRIES; - - retry: - ata_for_each_dev(dev, &ap->link, ALL) { - /* If we issue an SRST then an ATA drive (not ATAPI) - * may change configuration and be in PIO0 timing. If - * we do a hard reset (or are coming from power on) - * this is true for ATA or ATAPI. Until we've set a - * suitable controller mode we should not touch the - * bus as we may be talking too fast. - */ - dev->pio_mode = XFER_PIO_0; - dev->dma_mode = 0xff; - - /* If the controller has a pio mode setup function - * then use it to set the chipset to rights. Don't - * touch the DMA setup as that will be dealt with when - * configuring devices. - */ - if (ap->ops->set_piomode) - ap->ops->set_piomode(ap, dev); - } - - /* reset and determine device classes */ - ap->ops->phy_reset(ap); - - ata_for_each_dev(dev, &ap->link, ALL) { - if (dev->class != ATA_DEV_UNKNOWN) - classes[dev->devno] = dev->class; - else - classes[dev->devno] = ATA_DEV_NONE; - - dev->class = ATA_DEV_UNKNOWN; - } - - /* read IDENTIFY page and configure devices. We have to do the identify - specific sequence bass-ackwards so that PDIAG- is released by - the slave device */ - - ata_for_each_dev(dev, &ap->link, ALL_REVERSE) { - if (tries[dev->devno]) - dev->class = classes[dev->devno]; - - if (!ata_dev_enabled(dev)) - continue; - - rc = ata_dev_read_id(dev, &dev->class, ATA_READID_POSTRESET, - dev->id); - if (rc) - goto fail; - } - - /* Now ask for the cable type as PDIAG- should have been released */ - if (ap->ops->cable_detect) - ap->cbl = ap->ops->cable_detect(ap); - - /* We may have SATA bridge glue hiding here irrespective of - * the reported cable types and sensed types. When SATA - * drives indicate we have a bridge, we don't know which end - * of the link the bridge is which is a problem. - */ - ata_for_each_dev(dev, &ap->link, ENABLED) - if (ata_id_is_sata(dev->id)) - ap->cbl = ATA_CBL_SATA; - - /* After the identify sequence we can now set up the devices. We do - this in the normal order so that the user doesn't get confused */ - - ata_for_each_dev(dev, &ap->link, ENABLED) { - ap->link.eh_context.i.flags |= ATA_EHI_PRINTINFO; - rc = ata_dev_configure(dev); - ap->link.eh_context.i.flags &= ~ATA_EHI_PRINTINFO; - if (rc) - goto fail; - } - - /* configure transfer mode */ - rc = ata_set_mode(&ap->link, &dev); - if (rc) - goto fail; - - ata_for_each_dev(dev, &ap->link, ENABLED) - return 0; - - return -ENODEV; - - fail: - tries[dev->devno]--; - - switch (rc) { - case -EINVAL: - /* eeek, something went very wrong, give up */ - tries[dev->devno] = 0; - break; - - case -ENODEV: - /* give it just one more chance */ - tries[dev->devno] = min(tries[dev->devno], 1); - fallthrough; - case -EIO: - if (tries[dev->devno] == 1) { - /* This is the last chance, better to slow - * down than lose it. - */ - sata_down_spd_limit(&ap->link, 0); - ata_down_xfermask_limit(dev, ATA_DNXFER_PIO); - } - } - - if (!tries[dev->devno]) - ata_dev_disable(dev); - - goto retry; -} - /** * sata_print_link_status - Print SATA link status * @link: SATA link to printk link status about @@ -3782,7 +3729,7 @@ int ata_std_prereset(struct ata_link *link, unsigned long deadline) { struct ata_port *ap = link->ap; struct ata_eh_context *ehc = &link->eh_context; - const unsigned long *timing = sata_ehc_deb_timing(ehc); + const unsigned int *timing = sata_ehc_deb_timing(ehc); int rc; /* if we're about to do hardreset, nothing more to do */ @@ -3824,7 +3771,7 @@ EXPORT_SYMBOL_GPL(ata_std_prereset); int sata_std_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); bool online; int rc; @@ -4213,10 +4160,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { ATA_HORKAGE_ZERO_AFTER_TRIM }, { "Micron_M500_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM }, - { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | - ATA_HORKAGE_ZERO_AFTER_TRIM }, { "Micron_M5[15]0_*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM }, + { "Micron_1100_*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM }, { "Crucial_CT*M550*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM }, { "Crucial_CT*MX100*", "MU01", ATA_HORKAGE_NO_NCQ_TRIM | @@ -4874,126 +4823,100 @@ static void ata_verify_xfer(struct ata_queued_cmd *qc) void ata_qc_complete(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + struct ata_device *dev = qc->dev; + struct ata_eh_info *ehi = &dev->link->eh_info; /* Trigger the LED (if available) */ ledtrig_disk_activity(!!(qc->tf.flags & ATA_TFLAG_WRITE)); - /* XXX: New EH and old EH use different mechanisms to - * synchronize EH with regular execution path. - * - * In new EH, a qc owned by EH is marked with ATA_QCFLAG_EH. - * Normal execution path is responsible for not accessing a - * qc owned by EH. libata core enforces the rule by returning NULL - * from ata_qc_from_tag() for qcs owned by EH. + /* + * In order to synchronize EH with the regular execution path, a qc that + * is owned by EH is marked with ATA_QCFLAG_EH. * - * Old EH depends on ata_qc_complete() nullifying completion - * requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does - * not synchronize with interrupt handler. Only PIO task is - * taken care of. + * The normal execution path is responsible for not accessing a qc owned + * by EH. libata core enforces the rule by returning NULL from + * ata_qc_from_tag() for qcs owned by EH. */ - if (ap->ops->error_handler) { - struct ata_device *dev = qc->dev; - struct ata_eh_info *ehi = &dev->link->eh_info; - - if (unlikely(qc->err_mask)) - qc->flags |= ATA_QCFLAG_EH; + if (unlikely(qc->err_mask)) + qc->flags |= ATA_QCFLAG_EH; - /* - * Finish internal commands without any further processing - * and always with the result TF filled. - */ - if (unlikely(ata_tag_internal(qc->tag))) { - fill_result_tf(qc); - trace_ata_qc_complete_internal(qc); - __ata_qc_complete(qc); - return; - } + /* + * Finish internal commands without any further processing and always + * with the result TF filled. + */ + if (unlikely(ata_tag_internal(qc->tag))) { + fill_result_tf(qc); + trace_ata_qc_complete_internal(qc); + __ata_qc_complete(qc); + return; + } - /* - * Non-internal qc has failed. Fill the result TF and - * summon EH. - */ - if (unlikely(qc->flags & ATA_QCFLAG_EH)) { - fill_result_tf(qc); - trace_ata_qc_complete_failed(qc); - ata_qc_schedule_eh(qc); - return; - } + /* Non-internal qc has failed. Fill the result TF and summon EH. */ + if (unlikely(qc->flags & ATA_QCFLAG_EH)) { + fill_result_tf(qc); + trace_ata_qc_complete_failed(qc); + ata_qc_schedule_eh(qc); + return; + } - WARN_ON_ONCE(ata_port_is_frozen(ap)); + WARN_ON_ONCE(ata_port_is_frozen(ap)); - /* read result TF if requested */ - if (qc->flags & ATA_QCFLAG_RESULT_TF) - fill_result_tf(qc); + /* read result TF if requested */ + if (qc->flags & ATA_QCFLAG_RESULT_TF) + fill_result_tf(qc); - trace_ata_qc_complete_done(qc); + trace_ata_qc_complete_done(qc); + /* + * For CDL commands that completed without an error, check if we have + * sense data (ATA_SENSE is set). If we do, then the command may have + * been aborted by the device due to a limit timeout using the policy + * 0xD. For these commands, invoke EH to get the command sense data. + */ + if (qc->flags & ATA_QCFLAG_HAS_CDL && + qc->result_tf.status & ATA_SENSE) { /* - * For CDL commands that completed without an error, check if - * we have sense data (ATA_SENSE is set). If we do, then the - * command may have been aborted by the device due to a limit - * timeout using the policy 0xD. For these commands, invoke EH - * to get the command sense data. + * Tell SCSI EH to not overwrite scmd->result even if this + * command is finished with result SAM_STAT_GOOD. */ - if (qc->result_tf.status & ATA_SENSE && - ((ata_is_ncq(qc->tf.protocol) && - dev->flags & ATA_DFLAG_CDL_ENABLED) || - (!ata_is_ncq(qc->tf.protocol) && - ata_id_sense_reporting_enabled(dev->id)))) { - /* - * Tell SCSI EH to not overwrite scmd->result even if - * this command is finished with result SAM_STAT_GOOD. - */ - qc->scsicmd->flags |= SCMD_FORCE_EH_SUCCESS; - qc->flags |= ATA_QCFLAG_EH_SUCCESS_CMD; - ehi->dev_action[dev->devno] |= ATA_EH_GET_SUCCESS_SENSE; - - /* - * set pending so that ata_qc_schedule_eh() does not - * trigger fast drain, and freeze the port. - */ - ap->pflags |= ATA_PFLAG_EH_PENDING; - ata_qc_schedule_eh(qc); - return; - } + qc->scsicmd->flags |= SCMD_FORCE_EH_SUCCESS; + qc->flags |= ATA_QCFLAG_EH_SUCCESS_CMD; + ehi->dev_action[dev->devno] |= ATA_EH_GET_SUCCESS_SENSE; - /* Some commands need post-processing after successful - * completion. + /* + * set pending so that ata_qc_schedule_eh() does not trigger + * fast drain, and freeze the port. */ - switch (qc->tf.command) { - case ATA_CMD_SET_FEATURES: - if (qc->tf.feature != SETFEATURES_WC_ON && - qc->tf.feature != SETFEATURES_WC_OFF && - qc->tf.feature != SETFEATURES_RA_ON && - qc->tf.feature != SETFEATURES_RA_OFF) - break; - fallthrough; - case ATA_CMD_INIT_DEV_PARAMS: /* CHS translation changed */ - case ATA_CMD_SET_MULTI: /* multi_count changed */ - /* revalidate device */ - ehi->dev_action[dev->devno] |= ATA_EH_REVALIDATE; - ata_port_schedule_eh(ap); - break; + ap->pflags |= ATA_PFLAG_EH_PENDING; + ata_qc_schedule_eh(qc); + return; + } - case ATA_CMD_SLEEP: - dev->flags |= ATA_DFLAG_SLEEPING; + /* Some commands need post-processing after successful completion. */ + switch (qc->tf.command) { + case ATA_CMD_SET_FEATURES: + if (qc->tf.feature != SETFEATURES_WC_ON && + qc->tf.feature != SETFEATURES_WC_OFF && + qc->tf.feature != SETFEATURES_RA_ON && + qc->tf.feature != SETFEATURES_RA_OFF) break; - } - - if (unlikely(dev->flags & ATA_DFLAG_DUBIOUS_XFER)) - ata_verify_xfer(qc); + fallthrough; + case ATA_CMD_INIT_DEV_PARAMS: /* CHS translation changed */ + case ATA_CMD_SET_MULTI: /* multi_count changed */ + /* revalidate device */ + ehi->dev_action[dev->devno] |= ATA_EH_REVALIDATE; + ata_port_schedule_eh(ap); + break; - __ata_qc_complete(qc); - } else { - if (qc->flags & ATA_QCFLAG_EH_SCHEDULED) - return; + case ATA_CMD_SLEEP: + dev->flags |= ATA_DFLAG_SLEEPING; + break; + } - /* read result TF if failed or requested */ - if (qc->err_mask || qc->flags & ATA_QCFLAG_RESULT_TF) - fill_result_tf(qc); + if (unlikely(dev->flags & ATA_DFLAG_DUBIOUS_XFER)) + ata_verify_xfer(qc); - __ata_qc_complete(qc); - } + __ata_qc_complete(qc); } EXPORT_SYMBOL_GPL(ata_qc_complete); @@ -5039,11 +4962,8 @@ void ata_qc_issue(struct ata_queued_cmd *qc) struct ata_link *link = qc->dev->link; u8 prot = qc->tf.protocol; - /* Make sure only one non-NCQ command is outstanding. The - * check is skipped for old EH because it reuses active qc to - * request ATAPI sense. - */ - WARN_ON_ONCE(ap->ops->error_handler && ata_tag_valid(link->active_tag)); + /* Make sure only one non-NCQ command is outstanding. */ + WARN_ON_ONCE(ata_tag_valid(link->active_tag)); if (ata_is_ncq(prot)) { WARN_ON_ONCE(link->sactive & (1 << qc->hw_tag)); @@ -5207,17 +5127,19 @@ static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, struct ata_link *link; unsigned long flags; - /* Previous resume operation might still be in - * progress. Wait for PM_PENDING to clear. + spin_lock_irqsave(ap->lock, flags); + + /* + * A previous PM operation might still be in progress. Wait for + * ATA_PFLAG_PM_PENDING to clear. */ if (ap->pflags & ATA_PFLAG_PM_PENDING) { + spin_unlock_irqrestore(ap->lock, flags); ata_port_wait_eh(ap); - WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); + spin_lock_irqsave(ap->lock, flags); } - /* request PM ops to EH */ - spin_lock_irqsave(ap->lock, flags); - + /* Request PM operation to EH */ ap->pm_mesg = mesg; ap->pflags |= ATA_PFLAG_PM_PENDING; ata_for_each_link(link, ap, HOST_FIRST) { @@ -5229,10 +5151,8 @@ static void ata_port_request_pm(struct ata_port *ap, pm_message_t mesg, spin_unlock_irqrestore(ap->lock, flags); - if (!async) { + if (!async) ata_port_wait_eh(ap); - WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); - } } /* @@ -5248,11 +5168,27 @@ static const unsigned int ata_port_suspend_ehi = ATA_EHI_QUIET static void ata_port_suspend(struct ata_port *ap, pm_message_t mesg) { + /* + * We are about to suspend the port, so we do not care about + * scsi_rescan_device() calls scheduled by previous resume operations. + * The next resume will schedule the rescan again. So cancel any rescan + * that is not done yet. + */ + cancel_delayed_work_sync(&ap->scsi_rescan_task); + ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, false); } static void ata_port_suspend_async(struct ata_port *ap, pm_message_t mesg) { + /* + * We are about to suspend the port, so we do not care about + * scsi_rescan_device() calls scheduled by previous resume operations. + * The next resume will schedule the rescan again. So cancel any rescan + * that is not done yet. + */ + cancel_delayed_work_sync(&ap->scsi_rescan_task); + ata_port_request_pm(ap, mesg, 0, ata_port_suspend_ehi, true); } @@ -5399,7 +5335,7 @@ EXPORT_SYMBOL_GPL(ata_host_resume); #endif const struct device_type ata_port_type = { - .name = "ata_port", + .name = ATA_PORT_TYPE_NAME, #ifdef CONFIG_PM .pm = &ata_port_pm_ops, #endif @@ -5896,7 +5832,7 @@ void ata_host_init(struct ata_host *host, struct device *dev, } EXPORT_SYMBOL_GPL(ata_host_init); -void __ata_port_probe(struct ata_port *ap) +void ata_port_probe(struct ata_port *ap) { struct ata_eh_info *ehi = &ap->link.eh_info; unsigned long flags; @@ -5914,20 +5850,7 @@ void __ata_port_probe(struct ata_port *ap) spin_unlock_irqrestore(ap->lock, flags); } - -int ata_port_probe(struct ata_port *ap) -{ - int rc = 0; - - if (ap->ops->error_handler) { - __ata_port_probe(ap); - ata_port_wait_eh(ap); - } else { - rc = ata_bus_probe(ap); - } - return rc; -} - +EXPORT_SYMBOL_GPL(ata_port_probe); static void async_port_probe(void *data, async_cookie_t cookie) { @@ -5943,7 +5866,8 @@ static void async_port_probe(void *data, async_cookie_t cookie) if (!(ap->host->flags & ATA_HOST_PARALLEL_SCAN) && ap->port_no != 0) async_synchronize_cookie(cookie); - (void)ata_port_probe(ap); + ata_port_probe(ap); + ata_port_wait_eh(ap); /* in order to keep device order, we need to synchronize at this point */ async_synchronize_cookie(cookie); @@ -6130,14 +6054,30 @@ static void ata_port_detach(struct ata_port *ap) struct ata_link *link; struct ata_device *dev; - if (!ap->ops->error_handler) - goto skip_eh; + /* Wait for any ongoing EH */ + ata_port_wait_eh(ap); - /* tell EH we're leaving & flush EH */ + mutex_lock(&ap->scsi_scan_mutex); spin_lock_irqsave(ap->lock, flags); + + /* Remove scsi devices */ + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(dev, link, ALL) { + if (dev->sdev) { + spin_unlock_irqrestore(ap->lock, flags); + scsi_remove_device(dev->sdev); + spin_lock_irqsave(ap->lock, flags); + dev->sdev = NULL; + } + } + } + + /* Tell EH to disable all devices */ ap->pflags |= ATA_PFLAG_UNLOADING; ata_port_schedule_eh(ap); + spin_unlock_irqrestore(ap->lock, flags); + mutex_unlock(&ap->scsi_scan_mutex); /* wait till EH commits suicide */ ata_port_wait_eh(ap); @@ -6148,7 +6088,6 @@ static void ata_port_detach(struct ata_port *ap) cancel_delayed_work_sync(&ap->hotplug_task); cancel_delayed_work_sync(&ap->scsi_rescan_task); - skip_eh: /* clean up zpodd on port removal */ ata_for_each_link(link, ap, HOST_FIRST) { ata_for_each_dev(dev, link, ALL) { @@ -6684,7 +6623,7 @@ EXPORT_SYMBOL_GPL(ata_msleep); * The final register value. */ u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, u32 val, - unsigned long interval, unsigned long timeout) + unsigned int interval, unsigned int timeout) { unsigned long deadline; u32 tmp; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 35e03679b0bfe77e26587865e0830477759b0994..5686353e442cf41fb27174112617ae51f97fdfee 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -78,12 +78,12 @@ enum { * are mostly for error handling, hotplug and those outlier devices that * take an exceptionally long time to recover from reset. */ -static const unsigned long ata_eh_reset_timeouts[] = { +static const unsigned int ata_eh_reset_timeouts[] = { 10000, /* most drives spin up by 10sec */ 10000, /* > 99% working drives spin up before 20sec */ 35000, /* give > 30 secs of idleness for outlier devices */ 5000, /* and sweet one last chance */ - ULONG_MAX, /* > 1 min has elapsed, give up */ + UINT_MAX, /* > 1 min has elapsed, give up */ }; static const unsigned int ata_eh_identify_timeouts[] = { @@ -147,6 +147,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { .timeouts = ata_eh_other_timeouts, }, { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT), .timeouts = ata_eh_flush_timeouts }, + { .commands = CMDS(ATA_CMD_VERIFY), + .timeouts = ata_eh_reset_timeouts }, }; #undef CMDS @@ -498,7 +500,19 @@ static void ata_eh_unload(struct ata_port *ap) struct ata_device *dev; unsigned long flags; - /* Restore SControl IPM and SPD for the next driver and + /* + * Unless we are restarting, transition all enabled devices to + * standby power mode. + */ + if (system_state != SYSTEM_RESTART) { + ata_for_each_link(link, ap, PMP_FIRST) { + ata_for_each_dev(dev, link, ENABLED) + ata_dev_power_set_standby(dev); + } + } + + /* + * Restore SControl IPM and SPD for the next driver and * disable attached devices. */ ata_for_each_link(link, ap, PMP_FIRST) { @@ -571,13 +585,10 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, /* make sure sff pio task is not running */ ata_sff_flush_pio_task(ap); - if (!ap->ops->error_handler) - return; - /* synchronize with host lock and sort out timeouts */ /* - * For new EH, all qcs are finished in one of three ways - + * For EH, all qcs are finished in one of three ways - * normal completion, error completion, and SCSI timeout. * Both completions can race against SCSI timeout. When normal * completion wins, the qc never reaches EH. When error @@ -659,100 +670,99 @@ EXPORT_SYMBOL(ata_scsi_cmd_error_handler); void ata_scsi_port_error_handler(struct Scsi_Host *host, struct ata_port *ap) { unsigned long flags; + struct ata_link *link; - /* invoke error handler */ - if (ap->ops->error_handler) { - struct ata_link *link; - - /* acquire EH ownership */ - ata_eh_acquire(ap); + /* acquire EH ownership */ + ata_eh_acquire(ap); repeat: - /* kill fast drain timer */ - del_timer_sync(&ap->fastdrain_timer); + /* kill fast drain timer */ + del_timer_sync(&ap->fastdrain_timer); - /* process port resume request */ - ata_eh_handle_port_resume(ap); + /* process port resume request */ + ata_eh_handle_port_resume(ap); - /* fetch & clear EH info */ - spin_lock_irqsave(ap->lock, flags); + /* fetch & clear EH info */ + spin_lock_irqsave(ap->lock, flags); - ata_for_each_link(link, ap, HOST_FIRST) { - struct ata_eh_context *ehc = &link->eh_context; - struct ata_device *dev; + ata_for_each_link(link, ap, HOST_FIRST) { + struct ata_eh_context *ehc = &link->eh_context; + struct ata_device *dev; - memset(&link->eh_context, 0, sizeof(link->eh_context)); - link->eh_context.i = link->eh_info; - memset(&link->eh_info, 0, sizeof(link->eh_info)); + memset(&link->eh_context, 0, sizeof(link->eh_context)); + link->eh_context.i = link->eh_info; + memset(&link->eh_info, 0, sizeof(link->eh_info)); - ata_for_each_dev(dev, link, ENABLED) { - int devno = dev->devno; + ata_for_each_dev(dev, link, ENABLED) { + int devno = dev->devno; - ehc->saved_xfer_mode[devno] = dev->xfer_mode; - if (ata_ncq_enabled(dev)) - ehc->saved_ncq_enabled |= 1 << devno; - } + ehc->saved_xfer_mode[devno] = dev->xfer_mode; + if (ata_ncq_enabled(dev)) + ehc->saved_ncq_enabled |= 1 << devno; + + /* If we are resuming, wake up the device */ + if (ap->pflags & ATA_PFLAG_RESUMING) + ehc->i.dev_action[devno] |= ATA_EH_SET_ACTIVE; } + } - ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; - ap->pflags &= ~ATA_PFLAG_EH_PENDING; - ap->excl_link = NULL; /* don't maintain exclusion over EH */ + ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; + ap->pflags &= ~ATA_PFLAG_EH_PENDING; + ap->excl_link = NULL; /* don't maintain exclusion over EH */ - spin_unlock_irqrestore(ap->lock, flags); + spin_unlock_irqrestore(ap->lock, flags); - /* invoke EH, skip if unloading or suspended */ - if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) - ap->ops->error_handler(ap); - else { - /* if unloading, commence suicide */ - if ((ap->pflags & ATA_PFLAG_UNLOADING) && - !(ap->pflags & ATA_PFLAG_UNLOADED)) - ata_eh_unload(ap); - ata_eh_finish(ap); - } + /* invoke EH, skip if unloading or suspended */ + if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) + ap->ops->error_handler(ap); + else { + /* if unloading, commence suicide */ + if ((ap->pflags & ATA_PFLAG_UNLOADING) && + !(ap->pflags & ATA_PFLAG_UNLOADED)) + ata_eh_unload(ap); + ata_eh_finish(ap); + } - /* process port suspend request */ - ata_eh_handle_port_suspend(ap); + /* process port suspend request */ + ata_eh_handle_port_suspend(ap); - /* Exception might have happened after ->error_handler - * recovered the port but before this point. Repeat - * EH in such case. - */ - spin_lock_irqsave(ap->lock, flags); + /* + * Exception might have happened after ->error_handler recovered the + * port but before this point. Repeat EH in such case. + */ + spin_lock_irqsave(ap->lock, flags); - if (ap->pflags & ATA_PFLAG_EH_PENDING) { - if (--ap->eh_tries) { - spin_unlock_irqrestore(ap->lock, flags); - goto repeat; - } - ata_port_err(ap, - "EH pending after %d tries, giving up\n", - ATA_EH_MAX_TRIES); - ap->pflags &= ~ATA_PFLAG_EH_PENDING; + if (ap->pflags & ATA_PFLAG_EH_PENDING) { + if (--ap->eh_tries) { + spin_unlock_irqrestore(ap->lock, flags); + goto repeat; } + ata_port_err(ap, + "EH pending after %d tries, giving up\n", + ATA_EH_MAX_TRIES); + ap->pflags &= ~ATA_PFLAG_EH_PENDING; + } - /* this run is complete, make sure EH info is clear */ - ata_for_each_link(link, ap, HOST_FIRST) - memset(&link->eh_info, 0, sizeof(link->eh_info)); + /* this run is complete, make sure EH info is clear */ + ata_for_each_link(link, ap, HOST_FIRST) + memset(&link->eh_info, 0, sizeof(link->eh_info)); - /* end eh (clear host_eh_scheduled) while holding - * ap->lock such that if exception occurs after this - * point but before EH completion, SCSI midlayer will - * re-initiate EH. - */ - ap->ops->end_eh(ap); + /* + * end eh (clear host_eh_scheduled) while holding ap->lock such that if + * exception occurs after this point but before EH completion, SCSI + * midlayer will re-initiate EH. + */ + ap->ops->end_eh(ap); - spin_unlock_irqrestore(ap->lock, flags); - ata_eh_release(ap); - } else { - WARN_ON(ata_qc_from_tag(ap, ap->link.active_tag) == NULL); - ap->ops->eng_timeout(ap); - } + spin_unlock_irqrestore(ap->lock, flags); + ata_eh_release(ap); scsi_eh_flush_done_q(&ap->eh_done_q); /* clean up */ spin_lock_irqsave(ap->lock, flags); + ap->pflags &= ~ATA_PFLAG_RESUMING; + if (ap->pflags & ATA_PFLAG_LOADING) ap->pflags &= ~ATA_PFLAG_LOADING; else if ((ap->pflags & ATA_PFLAG_SCSI_HOTPLUG) && @@ -912,8 +922,6 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - WARN_ON(!ap->ops->error_handler); - qc->flags |= ATA_QCFLAG_EH; ata_eh_set_pending(ap, 1); @@ -934,8 +942,6 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) */ void ata_std_sched_eh(struct ata_port *ap) { - WARN_ON(!ap->ops->error_handler); - if (ap->pflags & ATA_PFLAG_INITIALIZING) return; @@ -989,8 +995,6 @@ static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link) struct ata_queued_cmd *qc; int tag, nr_aborted = 0; - WARN_ON(!ap->ops->error_handler); - /* we're gonna abort all commands, no need for fast drain */ ata_eh_set_pending(ap, 0); @@ -1065,8 +1069,6 @@ EXPORT_SYMBOL_GPL(ata_port_abort); */ static void __ata_port_freeze(struct ata_port *ap) { - WARN_ON(!ap->ops->error_handler); - if (ap->ops->freeze) ap->ops->freeze(ap); @@ -1091,8 +1093,6 @@ static void __ata_port_freeze(struct ata_port *ap) */ int ata_port_freeze(struct ata_port *ap) { - WARN_ON(!ap->ops->error_handler); - __ata_port_freeze(ap); return ata_port_abort(ap); @@ -1112,9 +1112,6 @@ void ata_eh_freeze_port(struct ata_port *ap) { unsigned long flags; - if (!ap->ops->error_handler) - return; - spin_lock_irqsave(ap->lock, flags); __ata_port_freeze(ap); spin_unlock_irqrestore(ap->lock, flags); @@ -1134,9 +1131,6 @@ void ata_eh_thaw_port(struct ata_port *ap) { unsigned long flags; - if (!ap->ops->error_handler) - return; - spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~ATA_PFLAG_FROZEN; @@ -1244,6 +1238,13 @@ void ata_eh_detach_dev(struct ata_device *dev) struct ata_eh_context *ehc = &link->eh_context; unsigned long flags; + /* + * If the device is still enabled, transition it to standby power mode + * (i.e. spin down HDDs). + */ + if (ata_dev_enabled(dev)) + ata_dev_power_set_standby(dev); + ata_dev_disable(dev); spin_lock_irqsave(ap->lock, flags); @@ -2331,7 +2332,7 @@ static void ata_eh_link_report(struct ata_link *link) struct ata_eh_context *ehc = &link->eh_context; struct ata_queued_cmd *qc; const char *frozen, *desc; - char tries_buf[6] = ""; + char tries_buf[16] = ""; int tag, nr_failed = 0; if (ehc->i.flags & ATA_EHI_QUIET) @@ -2575,7 +2576,7 @@ int ata_eh_reset(struct ata_link *link, int classify, /* * Prepare to reset */ - while (ata_eh_reset_timeouts[max_tries] != ULONG_MAX) + while (ata_eh_reset_timeouts[max_tries] != UINT_MAX) max_tries++; if (link->flags & ATA_LFLAG_RST_ONCE) max_tries = 1; @@ -2822,23 +2823,13 @@ int ata_eh_reset(struct ata_link *link, int classify, } } - /* - * Some controllers can't be frozen very well and may set spurious - * error conditions during reset. Clear accumulated error - * information and re-thaw the port if frozen. As reset is the - * final recovery action and we cross check link onlineness against - * device classification later, no hotplug event is lost by this. - */ + /* clear cached SError */ spin_lock_irqsave(link->ap->lock, flags); - memset(&link->eh_info, 0, sizeof(link->eh_info)); + link->eh_info.serror = 0; if (slave) - memset(&slave->eh_info, 0, sizeof(link->eh_info)); - ap->pflags &= ~ATA_PFLAG_EH_PENDING; + slave->eh_info.serror = 0; spin_unlock_irqrestore(link->ap->lock, flags); - if (ata_port_is_frozen(ap)) - ata_eh_thaw_port(ap); - /* * Make sure onlineness and classification result correspond. * Hotplug could have happened during reset and some @@ -3052,6 +3043,15 @@ static int ata_eh_revalidate_and_attach(struct ata_link *link, if (ehc->i.flags & ATA_EHI_DID_RESET) readid_flags |= ATA_READID_POSTRESET; + /* + * When resuming, before executing any command, make sure to + * transition the device to the active power mode. + */ + if ((action & ATA_EH_SET_ACTIVE) && ata_dev_enabled(dev)) { + ata_dev_power_set_active(dev); + ata_eh_done(link, dev, ATA_EH_SET_ACTIVE); + } + if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) { WARN_ON(dev->class == ATA_DEV_PMP); @@ -4025,6 +4025,7 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) unsigned long flags; int rc = 0; struct ata_device *dev; + struct ata_link *link; /* are we suspending? */ spin_lock_irqsave(ap->lock, flags); @@ -4037,6 +4038,12 @@ static void ata_eh_handle_port_suspend(struct ata_port *ap) WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); + /* Set all devices attached to the port in standby mode */ + ata_for_each_link(link, ap, HOST_FIRST) { + ata_for_each_dev(dev, link, ENABLED) + ata_dev_power_set_standby(dev); + } + /* * If we have a ZPODD attached, check its zero * power ready status before the port is frozen. @@ -4119,6 +4126,7 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) /* update the flags */ spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED); + ap->pflags |= ATA_PFLAG_RESUMING; spin_unlock_irqrestore(ap->lock, flags); } #endif /* CONFIG_PM */ diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index 85e279a12f62c495f257e8ef25129c7b3e953ad5..a701e1538482f09eb5960a606edad01f34920401 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -19,11 +19,11 @@ #include "libata-transport.h" /* debounce timing parameters in msecs { interval, duration, timeout } */ -const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 }; +const unsigned int sata_deb_timing_normal[] = { 5, 100, 2000 }; EXPORT_SYMBOL_GPL(sata_deb_timing_normal); -const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 }; +const unsigned int sata_deb_timing_hotplug[] = { 25, 500, 2000 }; EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); -const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 }; +const unsigned int sata_deb_timing_long[] = { 100, 2000, 5000 }; EXPORT_SYMBOL_GPL(sata_deb_timing_long); /** @@ -232,11 +232,11 @@ EXPORT_SYMBOL_GPL(ata_tf_from_fis); * RETURNS: * 0 on success, -errno on failure. */ -int sata_link_debounce(struct ata_link *link, const unsigned long *params, +int sata_link_debounce(struct ata_link *link, const unsigned int *params, unsigned long deadline) { - unsigned long interval = params[0]; - unsigned long duration = params[1]; + unsigned int interval = params[0]; + unsigned int duration = params[1]; unsigned long last_jiffies, t; u32 last, cur; int rc; @@ -295,7 +295,7 @@ EXPORT_SYMBOL_GPL(sata_link_debounce); * RETURNS: * 0 on success, -errno on failure. */ -int sata_link_resume(struct ata_link *link, const unsigned long *params, +int sata_link_resume(struct ata_link *link, const unsigned int *params, unsigned long deadline) { int tries = ATA_LINK_RESUME_TRIES; @@ -396,10 +396,23 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, case ATA_LPM_MED_POWER_WITH_DIPM: case ATA_LPM_MIN_POWER_WITH_PARTIAL: case ATA_LPM_MIN_POWER: - if (ata_link_nr_enabled(link) > 0) - /* no restrictions on LPM transitions */ + if (ata_link_nr_enabled(link) > 0) { + /* assume no restrictions on LPM transitions */ scontrol &= ~(0x7 << 8); - else { + + /* + * If the controller does not support partial, slumber, + * or devsleep, then disallow these transitions. + */ + if (link->ap->host->flags & ATA_HOST_NO_PART) + scontrol |= (0x1 << 8); + + if (link->ap->host->flags & ATA_HOST_NO_SSC) + scontrol |= (0x2 << 8); + + if (link->ap->host->flags & ATA_HOST_NO_DEVSLP) + scontrol |= (0x4 << 8); + } else { /* empty port, power off */ scontrol &= ~0xf; scontrol |= (0x1 << 2); @@ -528,7 +541,7 @@ EXPORT_SYMBOL_GPL(sata_set_spd); * RETURNS: * 0 on success, -errno otherwise. */ -int sata_link_hardreset(struct ata_link *link, const unsigned long *timing, +int sata_link_hardreset(struct ata_link *link, const unsigned int *timing, unsigned long deadline, bool *online, int (*check_ready)(struct ata_link *)) { @@ -1139,92 +1152,12 @@ struct ata_port *ata_sas_port_alloc(struct ata_host *host, ap->flags |= port_info->flags; ap->ops = port_info->port_ops; ap->cbl = ATA_CBL_SATA; + ap->print_id = atomic_inc_return(&ata_print_id); return ap; } EXPORT_SYMBOL_GPL(ata_sas_port_alloc); -/** - * ata_sas_port_start - Set port up for dma. - * @ap: Port to initialize - * - * Called just after data structures for each port are - * initialized. - * - * May be used as the port_start() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -int ata_sas_port_start(struct ata_port *ap) -{ - /* - * the port is marked as frozen at allocation time, but if we don't - * have new eh, we won't thaw it - */ - if (!ap->ops->error_handler) - ap->pflags &= ~ATA_PFLAG_FROZEN; - return 0; -} -EXPORT_SYMBOL_GPL(ata_sas_port_start); - -/** - * ata_sas_port_stop - Undo ata_sas_port_start() - * @ap: Port to shut down - * - * May be used as the port_stop() entry in ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ - -void ata_sas_port_stop(struct ata_port *ap) -{ -} -EXPORT_SYMBOL_GPL(ata_sas_port_stop); - -/** - * ata_sas_async_probe - simply schedule probing and return - * @ap: Port to probe - * - * For batch scheduling of probe for sas attached ata devices, assumes - * the port has already been through ata_sas_port_init() - */ -void ata_sas_async_probe(struct ata_port *ap) -{ - __ata_port_probe(ap); -} -EXPORT_SYMBOL_GPL(ata_sas_async_probe); - -int ata_sas_sync_probe(struct ata_port *ap) -{ - return ata_port_probe(ap); -} -EXPORT_SYMBOL_GPL(ata_sas_sync_probe); - - -/** - * ata_sas_port_init - Initialize a SATA device - * @ap: SATA port to initialize - * - * LOCKING: - * PCI/etc. bus probe sem. - * - * RETURNS: - * Zero on success, non-zero on error. - */ - -int ata_sas_port_init(struct ata_port *ap) -{ - int rc = ap->ops->port_start(ap); - - if (rc) - return rc; - ap->print_id = atomic_inc_return(&ata_print_id); - return 0; -} -EXPORT_SYMBOL_GPL(ata_sas_port_init); - int ata_sas_tport_add(struct device *parent, struct ata_port *ap) { return ata_tport_add(parent, ap); @@ -1237,20 +1170,6 @@ void ata_sas_tport_delete(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_tport_delete); -/** - * ata_sas_port_destroy - Destroy a SATA port allocated by ata_sas_port_alloc - * @ap: SATA port to destroy - * - */ - -void ata_sas_port_destroy(struct ata_port *ap) -{ - if (ap->ops->port_stop) - ap->ops->port_stop(ap); - kfree(ap); -} -EXPORT_SYMBOL_GPL(ata_sas_port_destroy); - /** * ata_sas_slave_configure - Default slave_config routine for libata devices * @sdev: SCSI device to configure diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index c6ece32de8e31b69c71178ad89d02b9e90386940..a371b497035e4099f96d623d178f94c832a2f288 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -135,11 +135,11 @@ static ssize_t ata_scsi_park_store(struct device *device, struct scsi_device *sdev = to_scsi_device(device); struct ata_port *ap; struct ata_device *dev; - long int input; + int input; unsigned long flags; int rc; - rc = kstrtol(buf, 10, &input); + rc = kstrtoint(buf, 10, &input); if (rc) return rc; if (input < -2) @@ -709,47 +709,6 @@ static void ata_qc_set_pc_nbytes(struct ata_queued_cmd *qc) qc->nbytes = scsi_bufflen(scmd) + qc->extrabytes; } -/** - * ata_dump_status - user friendly display of error info - * @ap: the port in question - * @tf: ptr to filled out taskfile - * - * Decode and dump the ATA error/status registers for the user so - * that they have some idea what really happened at the non - * make-believe layer. - * - * LOCKING: - * inherited from caller - */ -static void ata_dump_status(struct ata_port *ap, struct ata_taskfile *tf) -{ - u8 stat = tf->status, err = tf->error; - - if (stat & ATA_BUSY) { - ata_port_warn(ap, "status=0x%02x {Busy} ", stat); - } else { - ata_port_warn(ap, "status=0x%02x { %s%s%s%s%s%s%s} ", stat, - stat & ATA_DRDY ? "DriveReady " : "", - stat & ATA_DF ? "DeviceFault " : "", - stat & ATA_DSC ? "SeekComplete " : "", - stat & ATA_DRQ ? "DataRequest " : "", - stat & ATA_CORR ? "CorrectedError " : "", - stat & ATA_SENSE ? "Sense " : "", - stat & ATA_ERR ? "Error " : ""); - if (err) - ata_port_warn(ap, "error=0x%02x {%s%s%s%s%s%s", err, - err & ATA_ABORTED ? - "DriveStatusError " : "", - err & ATA_ICRC ? - (err & ATA_ABORTED ? - "BadCRC " : "Sector ") : "", - err & ATA_UNC ? "UncorrectableError " : "", - err & ATA_IDNF ? "SectorIdNotFound " : "", - err & ATA_TRK0NF ? "TrackZeroNotFound " : "", - err & ATA_AMNF ? "AddrMarkNotFound " : ""); - } -} - /** * ata_to_sense_error - convert ATA error to SCSI error * @id: ATA device number @@ -758,7 +717,6 @@ static void ata_dump_status(struct ata_port *ap, struct ata_taskfile *tf) * @sk: the sense key we'll fill out * @asc: the additional sense code we'll fill out * @ascq: the additional sense code qualifier we'll fill out - * @verbose: be verbose * * Converts an ATA error into a SCSI error. Fill out pointers to * SK, ASC, and ASCQ bytes for later use in fixed or descriptor @@ -768,7 +726,7 @@ static void ata_dump_status(struct ata_port *ap, struct ata_taskfile *tf) * spin_lock_irqsave(host lock) */ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, - u8 *asc, u8 *ascq, int verbose) + u8 *asc, u8 *ascq) { int i; @@ -847,7 +805,7 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, *sk = sense_table[i][1]; *asc = sense_table[i][2]; *ascq = sense_table[i][3]; - goto translate_done; + return; } } } @@ -862,7 +820,7 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, *sk = stat_table[i][1]; *asc = stat_table[i][2]; *ascq = stat_table[i][3]; - goto translate_done; + return; } } @@ -873,12 +831,6 @@ static void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, *sk = ABORTED_COMMAND; *asc = 0x00; *ascq = 0x00; - - translate_done: - if (verbose) - pr_err("ata%u: translated ATA stat/err 0x%02x/%02x to SCSI SK/ASC/ASCQ 0x%x/%02x/%02x\n", - id, drv_stat, drv_err, *sk, *asc, *ascq); - return; } /* @@ -904,7 +856,6 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) struct ata_taskfile *tf = &qc->result_tf; unsigned char *sb = cmd->sense_buffer; unsigned char *desc = sb + 8; - int verbose = qc->ap->ops->error_handler == NULL; u8 sense_key, asc, ascq; memset(sb, 0, SCSI_SENSE_BUFFERSIZE); @@ -916,7 +867,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) if (qc->err_mask || tf->status & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->status, tf->error, - &sense_key, &asc, &ascq, verbose); + &sense_key, &asc, &ascq); ata_scsi_set_sense(qc->dev, cmd, sense_key, asc, ascq); } else { /* @@ -999,7 +950,6 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) struct scsi_cmnd *cmd = qc->scsicmd; struct ata_taskfile *tf = &qc->result_tf; unsigned char *sb = cmd->sense_buffer; - int verbose = qc->ap->ops->error_handler == NULL; u64 block; u8 sense_key, asc, ascq; @@ -1017,7 +967,7 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) if (qc->err_mask || tf->status & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->print_id, tf->status, tf->error, - &sense_key, &asc, &ascq, verbose); + &sense_key, &asc, &ascq); ata_scsi_set_sense(dev, cmd, sense_key, asc, ascq); } else { /* Could not decode error */ @@ -1100,14 +1050,13 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev) } } else { sdev->sector_size = ata_id_logical_sector_size(dev->id); + /* - * Stop the drive on suspend but do not issue START STOP UNIT - * on resume as this is not necessary and may fail: the device - * will be woken up by ata_port_pm_resume() with a port reset - * and device revalidation. + * Ask the sd driver to issue START STOP UNIT on runtime suspend + * and resume only. For system level suspend/resume, devices + * power state is handled directly by libata EH. */ - sdev->manage_start_stop = 1; - sdev->no_start_on_resume = 1; + sdev->manage_runtime_start_stop = true; } /* @@ -1139,6 +1088,42 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev) return 0; } +/** + * ata_scsi_slave_alloc - Early setup of SCSI device + * @sdev: SCSI device to examine + * + * This is called from scsi_alloc_sdev() when the scsi device + * associated with an ATA device is scanned on a port. + * + * LOCKING: + * Defined by SCSI layer. We don't really care. + */ + +int ata_scsi_slave_alloc(struct scsi_device *sdev) +{ + struct ata_port *ap = ata_shost_to_port(sdev->host); + struct device_link *link; + + ata_scsi_sdev_config(sdev); + + /* + * Create a link from the ata_port device to the scsi device to ensure + * that PM does suspend/resume in the correct order: the scsi device is + * consumer (child) and the ata port the supplier (parent). + */ + link = device_link_add(&sdev->sdev_gendev, &ap->tdev, + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); + if (!link) { + ata_port_err(ap, "Failed to create link to scsi device %s\n", + dev_name(&sdev->sdev_gendev)); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ata_scsi_slave_alloc); + /** * ata_scsi_slave_config - Set SCSI device attributes * @sdev: SCSI device to examine @@ -1155,14 +1140,11 @@ int ata_scsi_slave_config(struct scsi_device *sdev) { struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev = __ata_scsi_find_dev(ap, sdev); - int rc = 0; - - ata_scsi_sdev_config(sdev); if (dev) - rc = ata_scsi_dev_config(sdev, dev); + return ata_scsi_dev_config(sdev, dev); - return rc; + return 0; } EXPORT_SYMBOL_GPL(ata_scsi_slave_config); @@ -1186,8 +1168,7 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev) unsigned long flags; struct ata_device *dev; - if (!ap->ops->error_handler) - return; + device_link_remove(&sdev->sdev_gendev, &ap->tdev); spin_lock_irqsave(ap->lock, flags); dev = __ata_scsi_find_dev(ap, sdev); @@ -1248,7 +1229,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) } if (cdb[4] & 0x1) { - tf->nsect = 1; /* 1 sector, lba=0 */ + tf->nsect = 1; /* 1 sector, lba=0 */ if (qc->dev->flags & ATA_DFLAG_LBA) { tf->flags |= ATA_TFLAG_LBA; @@ -1264,7 +1245,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) tf->lbah = 0x0; /* cyl high */ } - tf->command = ATA_CMD_VERIFY; /* READ VERIFY */ + tf->command = ATA_CMD_VERIFY; /* READ VERIFY */ } else { /* Some odd clown BIOSen issue spindown on power off (ACPI S4 * or S5) causing some drives to spin up and down again. @@ -1274,7 +1255,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) goto skip; if ((qc->ap->flags & ATA_FLAG_NO_HIBERNATE_SPINDOWN) && - system_entering_hibernation()) + system_entering_hibernation()) goto skip; /* Issue ATA STANDBY IMMEDIATE command */ @@ -1675,7 +1656,6 @@ static void ata_qc_done(struct ata_queued_cmd *qc) static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; struct scsi_cmnd *cmd = qc->scsicmd; u8 *cdb = cmd->cmnd; int need_sense = (qc->err_mask != 0) && @@ -1699,9 +1679,6 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) /* Keep the SCSI ML and status byte, clear host byte. */ cmd->result &= 0x0000ffff; - if (need_sense && !ap->ops->error_handler) - ata_dump_status(ap, &qc->result_tf); - ata_qc_done(qc); } @@ -1892,6 +1869,9 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) hdr[2] = 0x7; /* claim SPC-5 version compatibility */ } + if (args->dev->flags & ATA_DFLAG_CDL) + hdr[2] = 0xd; /* claim SPC-6 version compatibility */ + memcpy(rbuf, hdr, sizeof(hdr)); memcpy(&rbuf[8], "ATA ", 8); ata_id_string(args->id, &rbuf[16], ATA_ID_PROD, 16); @@ -2608,71 +2588,6 @@ static unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf) return 0; } -static void atapi_sense_complete(struct ata_queued_cmd *qc) -{ - if (qc->err_mask && ((qc->err_mask & AC_ERR_DEV) == 0)) { - /* FIXME: not quite right; we don't want the - * translation of taskfile registers into - * a sense descriptors, since that's only - * correct for ATA, not ATAPI - */ - ata_gen_passthru_sense(qc); - } - - ata_qc_done(qc); -} - -/* is it pointless to prefer PIO for "safety reasons"? */ -static inline int ata_pio_use_silly(struct ata_port *ap) -{ - return (ap->flags & ATA_FLAG_PIO_DMA); -} - -static void atapi_request_sense(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scsi_cmnd *cmd = qc->scsicmd; - - memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); - -#ifdef CONFIG_ATA_SFF - if (ap->ops->sff_tf_read) - ap->ops->sff_tf_read(ap, &qc->tf); -#endif - - /* fill these in, for the case where they are -not- overwritten */ - cmd->sense_buffer[0] = 0x70; - cmd->sense_buffer[2] = qc->tf.error >> 4; - - ata_qc_reinit(qc); - - /* setup sg table and init transfer direction */ - sg_init_one(&qc->sgent, cmd->sense_buffer, SCSI_SENSE_BUFFERSIZE); - ata_sg_init(qc, &qc->sgent, 1); - qc->dma_dir = DMA_FROM_DEVICE; - - memset(&qc->cdb, 0, qc->dev->cdb_len); - qc->cdb[0] = REQUEST_SENSE; - qc->cdb[4] = SCSI_SENSE_BUFFERSIZE; - - qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - qc->tf.command = ATA_CMD_PACKET; - - if (ata_pio_use_silly(ap)) { - qc->tf.protocol = ATAPI_PROT_DMA; - qc->tf.feature |= ATAPI_PKT_DMA; - } else { - qc->tf.protocol = ATAPI_PROT_PIO; - qc->tf.lbam = SCSI_SENSE_BUFFERSIZE; - qc->tf.lbah = 0; - } - qc->nbytes = SCSI_SENSE_BUFFERSIZE; - - qc->complete_fn = atapi_sense_complete; - - ata_qc_issue(qc); -} - /* * ATAPI devices typically report zero for their SCSI version, and sometimes * deviate from the spec WRT response data format. If SCSI version is @@ -2698,9 +2613,8 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) struct scsi_cmnd *cmd = qc->scsicmd; unsigned int err_mask = qc->err_mask; - /* handle completion from new EH */ - if (unlikely(qc->ap->ops->error_handler && - (err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID))) { + /* handle completion from EH */ + if (unlikely(err_mask || qc->flags & ATA_QCFLAG_SENSE_VALID)) { if (!(qc->flags & ATA_QCFLAG_SENSE_VALID)) { /* FIXME: not quite right; we don't want the @@ -2732,23 +2646,10 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) return; } - /* successful completion or old EH failure path */ - if (unlikely(err_mask & AC_ERR_DEV)) { - cmd->result = SAM_STAT_CHECK_CONDITION; - atapi_request_sense(qc); - return; - } else if (unlikely(err_mask)) { - /* FIXME: not quite right; we don't want the - * translation of taskfile registers into - * a sense descriptors, since that's only - * correct for ATA, not ATAPI - */ - ata_gen_passthru_sense(qc); - } else { - if (cmd->cmnd[0] == INQUIRY && (cmd->cmnd[1] & 0x03) == 0) - atapi_fixup_inquiry(cmd); - cmd->result = SAM_STAT_GOOD; - } + /* successful completion path */ + if (cmd->cmnd[0] == INQUIRY && (cmd->cmnd[1] & 0x03) == 0) + atapi_fixup_inquiry(cmd); + cmd->result = SAM_STAT_GOOD; ata_qc_done(qc); } @@ -4448,7 +4349,7 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd) break; case MAINTENANCE_IN: - if (scsicmd[1] == MI_REPORT_SUPPORTED_OPERATION_CODES) + if ((scsicmd[1] & 0x1f) == MI_REPORT_SUPPORTED_OPERATION_CODES) ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in); else ata_scsi_set_invalid_field(dev, cmd, 1, 0xff); @@ -4797,9 +4698,6 @@ int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, unsigned long flags; int devno, rc = 0; - if (!ap->ops->error_handler) - return -EOPNOTSUPP; - if (lun != SCAN_WILD_CARD && lun) return -EINVAL; @@ -4861,7 +4759,7 @@ void ata_scsi_dev_rescan(struct work_struct *work) struct ata_link *link; struct ata_device *dev; unsigned long flags; - bool delay_rescan = false; + int ret = 0; mutex_lock(&ap->scsi_scan_mutex); spin_lock_irqsave(ap->lock, flags); @@ -4870,37 +4768,34 @@ void ata_scsi_dev_rescan(struct work_struct *work) ata_for_each_dev(dev, link, ENABLED) { struct scsi_device *sdev = dev->sdev; + /* + * If the port was suspended before this was scheduled, + * bail out. + */ + if (ap->pflags & ATA_PFLAG_SUSPENDED) + goto unlock; + if (!sdev) continue; if (scsi_device_get(sdev)) continue; - /* - * If the rescan work was scheduled because of a resume - * event, the port is already fully resumed, but the - * SCSI device may not yet be fully resumed. In such - * case, executing scsi_rescan_device() may cause a - * deadlock with the PM code on device_lock(). Prevent - * this by giving up and retrying rescan after a short - * delay. - */ - delay_rescan = sdev->sdev_gendev.power.is_suspended; - if (delay_rescan) { - scsi_device_put(sdev); - break; - } - spin_unlock_irqrestore(ap->lock, flags); - scsi_rescan_device(&(sdev->sdev_gendev)); + ret = scsi_rescan_device(sdev); scsi_device_put(sdev); spin_lock_irqsave(ap->lock, flags); + + if (ret) + goto unlock; } } +unlock: spin_unlock_irqrestore(ap->lock, flags); mutex_unlock(&ap->scsi_scan_mutex); - if (delay_rescan) + /* Reschedule with a delay if scsi_rescan_device() returned an error */ + if (ret) schedule_delayed_work(&ap->scsi_rescan_task, msecs_to_jiffies(5)); } diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 9d28badfe41d667af5ecbdc9274bd912ba4f6612..8fcc622fcb3d49c67dd7c2c6efcf40c47a020f9f 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -883,31 +883,21 @@ static void ata_hsm_qc_complete(struct ata_queued_cmd *qc, int in_wq) { struct ata_port *ap = qc->ap; - if (ap->ops->error_handler) { - if (in_wq) { - /* EH might have kicked in while host lock is - * released. - */ - qc = ata_qc_from_tag(ap, qc->tag); - if (qc) { - if (likely(!(qc->err_mask & AC_ERR_HSM))) { - ata_sff_irq_on(ap); - ata_qc_complete(qc); - } else - ata_port_freeze(ap); - } - } else { - if (likely(!(qc->err_mask & AC_ERR_HSM))) + if (in_wq) { + /* EH might have kicked in while host lock is released. */ + qc = ata_qc_from_tag(ap, qc->tag); + if (qc) { + if (likely(!(qc->err_mask & AC_ERR_HSM))) { + ata_sff_irq_on(ap); ata_qc_complete(qc); - else + } else ata_port_freeze(ap); } } else { - if (in_wq) { - ata_sff_irq_on(ap); - ata_qc_complete(qc); - } else + if (likely(!(qc->err_mask & AC_ERR_HSM))) ata_qc_complete(qc); + else + ata_port_freeze(ap); } } @@ -1971,7 +1961,7 @@ int sata_sff_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { struct ata_eh_context *ehc = &link->eh_context; - const unsigned long *timing = sata_ehc_deb_timing(ehc); + const unsigned int *timing = sata_ehc_deb_timing(ehc); bool online; int rc; diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index e4fb9d1b9b39803a624ce8bf0185193a84d61d00..3e49a877500e124ec36fedb95ae122ca793afec6 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -266,6 +266,10 @@ void ata_tport_delete(struct ata_port *ap) put_device(dev); } +static const struct device_type ata_port_sas_type = { + .name = ATA_PORT_TYPE_NAME, +}; + /** ata_tport_add - initialize a transport ATA port structure * * @parent: parent device @@ -283,7 +287,10 @@ int ata_tport_add(struct device *parent, struct device *dev = &ap->tdev; device_initialize(dev); - dev->type = &ata_port_type; + if (ap->flags & ATA_FLAG_SAS_HOST) + dev->type = &ata_port_sas_type; + else + dev->type = &ata_port_type; dev->parent = parent; ata_host_get(ap->host); diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index cf993885d2b259b1fc992065b89bed9f37e87b69..05ac80da8ebc7d11153310ac892c25258c03993a 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -30,6 +30,8 @@ enum { ATA_DNXFER_QUIET = (1 << 31), }; +#define ATA_PORT_TYPE_NAME "ata_port" + extern atomic_t ata_print_id; extern int atapi_passthru16; extern int libata_fua; @@ -60,6 +62,8 @@ extern int ata_dev_reread_id(struct ata_device *dev, unsigned int readid_flags); extern int ata_dev_revalidate(struct ata_device *dev, unsigned int new_class, unsigned int readid_flags); extern int ata_dev_configure(struct ata_device *dev); +extern void ata_dev_power_set_standby(struct ata_device *dev); +extern void ata_dev_power_set_active(struct ata_device *dev); extern int sata_down_spd_limit(struct ata_link *link, u32 spd_limit); extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel); extern unsigned int ata_dev_set_feature(struct ata_device *dev, @@ -78,8 +82,6 @@ extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct ata_port *ata_port_alloc(struct ata_host *host); extern const char *sata_spd_string(unsigned int spd); -extern int ata_port_probe(struct ata_port *ap); -extern void __ata_port_probe(struct ata_port *ap); extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, u8 page, void *buf, unsigned int sectors); @@ -124,7 +126,6 @@ extern void ata_scsi_media_change_notify(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); extern void ata_scsi_dev_rescan(struct work_struct *work); -extern int ata_bus_probe(struct ata_port *ap); extern int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, unsigned int id, u64 lun); void ata_scsi_sdev_config(struct scsi_device *sdev); diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c index 314eaa1679540751efb7bc27a346584aec4d0ef6..d0c6924d25b67fbd2f3976fb1a928741a74bed87 100644 --- a/drivers/ata/pata_arasan_cf.c +++ b/drivers/ata/pata_arasan_cf.c @@ -917,15 +917,13 @@ static int arasan_cf_probe(struct platform_device *pdev) return ret; } -static int arasan_cf_remove(struct platform_device *pdev) +static void arasan_cf_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct arasan_cf_dev *acdev = host->ports[0]->private_data; ata_host_detach(host); cf_exit(acdev); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -966,7 +964,7 @@ MODULE_DEVICE_TABLE(of, arasan_cf_id_table); static struct platform_driver arasan_cf_driver = { .probe = arasan_cf_probe, - .remove = arasan_cf_remove, + .remove_new = arasan_cf_remove, .driver = { .name = DRIVER_NAME, .pm = &arasan_cf_pm_ops, diff --git a/drivers/ata/pata_buddha.c b/drivers/ata/pata_buddha.c index 49bc619b83e255a34e14f41dd53deb3c61cf818d..c36ee991d5e5fdcccb110c3530cda821811fa5c5 100644 --- a/drivers/ata/pata_buddha.c +++ b/drivers/ata/pata_buddha.c @@ -27,7 +27,6 @@ #include #include -#include #include #define DRV_NAME "pata_buddha" diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c index c6e043e05d43b768f32edef39252fc3f7f20644d..c84a20892f1b0ce914937e9ddb6e2dd382b77cb2 100644 --- a/drivers/ata/pata_ep93xx.c +++ b/drivers/ata/pata_ep93xx.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -910,6 +911,12 @@ static struct ata_port_operations ep93xx_pata_port_ops = { .port_start = ep93xx_pata_port_start, }; +static const struct soc_device_attribute ep93xx_soc_table[] = { + { .revision = "E1", .data = (void *)ATA_UDMA3 }, + { .revision = "E2", .data = (void *)ATA_UDMA4 }, + { /* sentinel */ } +}; + static int ep93xx_pata_probe(struct platform_device *pdev) { struct ep93xx_pata_data *drv_data; @@ -939,7 +946,7 @@ static int ep93xx_pata_probe(struct platform_device *pdev) drv_data = devm_kzalloc(&pdev->dev, sizeof(*drv_data), GFP_KERNEL); if (!drv_data) { - err = -ENXIO; + err = -ENOMEM; goto err_rel_gpio; } @@ -952,7 +959,7 @@ static int ep93xx_pata_probe(struct platform_device *pdev) /* allocate host */ host = ata_host_alloc(&pdev->dev, 1); if (!host) { - err = -ENXIO; + err = -ENOMEM; goto err_rel_dma; } @@ -976,12 +983,11 @@ static int ep93xx_pata_probe(struct platform_device *pdev) * so this driver supports only UDMA modes. */ if (drv_data->dma_rx_channel && drv_data->dma_tx_channel) { - int chip_rev = ep93xx_chip_revision(); + const struct soc_device_attribute *match; - if (chip_rev == EP93XX_CHIP_REV_E1) - ap->udma_mask = ATA_UDMA3; - else if (chip_rev == EP93XX_CHIP_REV_E2) - ap->udma_mask = ATA_UDMA4; + match = soc_device_match(ep93xx_soc_table); + if (match) + ap->udma_mask = (unsigned int) match->data; else ap->udma_mask = ATA_UDMA2; } @@ -1004,7 +1010,7 @@ static int ep93xx_pata_probe(struct platform_device *pdev) return err; } -static int ep93xx_pata_remove(struct platform_device *pdev) +static void ep93xx_pata_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct ep93xx_pata_data *drv_data = host->private_data; @@ -1013,7 +1019,6 @@ static int ep93xx_pata_remove(struct platform_device *pdev) ep93xx_pata_release_dma(drv_data); ep93xx_pata_clear_regs(drv_data->ide_base); ep93xx_ide_release_gpio(pdev); - return 0; } static struct platform_driver ep93xx_pata_platform_driver = { @@ -1021,7 +1026,7 @@ static struct platform_driver ep93xx_pata_platform_driver = { .name = DRV_NAME, }, .probe = ep93xx_pata_probe, - .remove = ep93xx_pata_remove, + .remove_new = ep93xx_pata_remove, }; module_platform_driver(ep93xx_pata_platform_driver); diff --git a/drivers/ata/pata_falcon.c b/drivers/ata/pata_falcon.c index 996516e64f13504e38cb372e6b87b721418832c5..0c2ae430f5aae009c111a4bc63b78bd0e73595ee 100644 --- a/drivers/ata/pata_falcon.c +++ b/drivers/ata/pata_falcon.c @@ -28,11 +28,15 @@ #include #include #include -#include #define DRV_NAME "pata_falcon" #define DRV_VERSION "0.1.0" +static int pata_falcon_swap_mask; + +module_param_named(data_swab, pata_falcon_swap_mask, int, 0444); +MODULE_PARM_DESC(data_swab, "Data byte swap enable/disable bitmap (0x1==drive1, 0x2==drive2, 0x4==drive3, 0x8==drive4, default==0)"); + static const struct scsi_host_template pata_falcon_sht = { ATA_PIO_SHT(DRV_NAME), }; @@ -50,7 +54,7 @@ static unsigned int pata_falcon_data_xfer(struct ata_queued_cmd *qc, if (dev->class == ATA_DEV_ATA && cmd && !blk_rq_is_passthrough(scsi_cmd_to_rq(cmd))) - swap = 0; + swap = (uintptr_t)ap->private_data & BIT(dev->devno); /* Transfer multiple of 2 bytes */ if (rw == READ) { @@ -123,8 +127,9 @@ static int __init pata_falcon_init_one(struct platform_device *pdev) struct resource *base_res, *ctl_res, *irq_res; struct ata_host *host; struct ata_port *ap; - void __iomem *base; - int irq = 0; + void __iomem *base, *ctl_base; + int mask_shift = 0; /* Q40 & Falcon default */ + int irq = 0, io_offset = 1, reg_shift = 2; /* Falcon defaults */ dev_info(&pdev->dev, "Atari Falcon and Q40/Q60 PATA controller\n"); @@ -165,26 +170,38 @@ static int __init pata_falcon_init_one(struct platform_device *pdev) ap->pio_mask = ATA_PIO4; ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY; - base = (void __iomem *)base_mem_res->start; /* N.B. this assumes data_addr will be used for word-sized I/O only */ - ap->ioaddr.data_addr = base + 0 + 0 * 4; - ap->ioaddr.error_addr = base + 1 + 1 * 4; - ap->ioaddr.feature_addr = base + 1 + 1 * 4; - ap->ioaddr.nsect_addr = base + 1 + 2 * 4; - ap->ioaddr.lbal_addr = base + 1 + 3 * 4; - ap->ioaddr.lbam_addr = base + 1 + 4 * 4; - ap->ioaddr.lbah_addr = base + 1 + 5 * 4; - ap->ioaddr.device_addr = base + 1 + 6 * 4; - ap->ioaddr.status_addr = base + 1 + 7 * 4; - ap->ioaddr.command_addr = base + 1 + 7 * 4; - - base = (void __iomem *)ctl_mem_res->start; - ap->ioaddr.altstatus_addr = base + 1; - ap->ioaddr.ctl_addr = base + 1; - - ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", - (unsigned long)base_mem_res->start, - (unsigned long)ctl_mem_res->start); + ap->ioaddr.data_addr = (void __iomem *)base_mem_res->start; + + if (base_res) { /* only Q40 has IO resources */ + io_offset = 0x10000; + reg_shift = 0; + base = (void __iomem *)base_res->start; + ctl_base = (void __iomem *)ctl_res->start; + } else { + base = (void __iomem *)base_mem_res->start; + ctl_base = (void __iomem *)ctl_mem_res->start; + } + + ap->ioaddr.error_addr = base + io_offset + (1 << reg_shift); + ap->ioaddr.feature_addr = base + io_offset + (1 << reg_shift); + ap->ioaddr.nsect_addr = base + io_offset + (2 << reg_shift); + ap->ioaddr.lbal_addr = base + io_offset + (3 << reg_shift); + ap->ioaddr.lbam_addr = base + io_offset + (4 << reg_shift); + ap->ioaddr.lbah_addr = base + io_offset + (5 << reg_shift); + ap->ioaddr.device_addr = base + io_offset + (6 << reg_shift); + ap->ioaddr.status_addr = base + io_offset + (7 << reg_shift); + ap->ioaddr.command_addr = base + io_offset + (7 << reg_shift); + + ap->ioaddr.altstatus_addr = ctl_base + io_offset; + ap->ioaddr.ctl_addr = ctl_base + io_offset; + + ata_port_desc(ap, "cmd %px ctl %px data %px", + base, ctl_base, ap->ioaddr.data_addr); + + if (pdev->id > 0) + mask_shift = 2; + ap->private_data = (void *)(uintptr_t)(pata_falcon_swap_mask >> mask_shift); irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq_res && irq_res->start > 0) { diff --git a/drivers/ata/pata_ftide010.c b/drivers/ata/pata_ftide010.c index 6f6734c09b1115c6176779d8526008c3b183420a..4d6ef90ccc774b5c9f0b0edb8d455e65f1251244 100644 --- a/drivers/ata/pata_ftide010.c +++ b/drivers/ata/pata_ftide010.c @@ -14,8 +14,7 @@ #include #include #include -#include -#include +#include #include #include "sata_gemini.h" @@ -470,11 +469,7 @@ static int pata_ftide010_probe(struct platform_device *pdev) if (irq < 0) return irq; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - ftide->base = devm_ioremap_resource(dev, res); + ftide->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(ftide->base)) return PTR_ERR(ftide->base); @@ -541,15 +536,13 @@ static int pata_ftide010_probe(struct platform_device *pdev) return ret; } -static int pata_ftide010_remove(struct platform_device *pdev) +static void pata_ftide010_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct ftide010 *ftide = host->private_data; ata_host_detach(ftide->host); clk_disable_unprepare(ftide->pclk); - - return 0; } static const struct of_device_id pata_ftide010_of_match[] = { @@ -563,10 +556,11 @@ static struct platform_driver pata_ftide010_driver = { .of_match_table = pata_ftide010_of_match, }, .probe = pata_ftide010_probe, - .remove = pata_ftide010_remove, + .remove_new = pata_ftide010_remove, }; module_platform_driver(pata_ftide010_driver); +MODULE_DESCRIPTION("low level driver for Faraday Technology FTIDE010"); MODULE_AUTHOR("Linus Walleij "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/ata/pata_gayle.c b/drivers/ata/pata_gayle.c index e5aa07f92106547e202ee94cd598f9c780ca01a3..3bdbe2b65a2b49f14777fd5fdd21b04f111ba056 100644 --- a/drivers/ata/pata_gayle.c +++ b/drivers/ata/pata_gayle.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #define DRV_NAME "pata_gayle" diff --git a/drivers/ata/pata_imx.c b/drivers/ata/pata_imx.c index 4013f28679a9b548cc6c96b21a7a8e38da31acae..d0aa8fc929b4508b5d6d3f6a9e93f87e5e9fe72d 100644 --- a/drivers/ata/pata_imx.c +++ b/drivers/ata/pata_imx.c @@ -141,21 +141,15 @@ static int pata_imx_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->clk = devm_clk_get(&pdev->dev, NULL); + priv->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "Failed to get clock\n"); + dev_err(&pdev->dev, "Failed to get and enable clock\n"); return PTR_ERR(priv->clk); } - ret = clk_prepare_enable(priv->clk); - if (ret) - return ret; - host = ata_host_alloc(&pdev->dev, 1); - if (!host) { - ret = -ENOMEM; - goto err; - } + if (!host) + return -ENOMEM; host->private_data = priv; ap = host->ports[0]; @@ -164,12 +158,9 @@ static int pata_imx_probe(struct platform_device *pdev) ap->pio_mask = ATA_PIO4; ap->flags |= ATA_FLAG_SLAVE_POSS; - io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->host_regs = devm_ioremap_resource(&pdev->dev, io_res); - if (IS_ERR(priv->host_regs)) { - ret = PTR_ERR(priv->host_regs); - goto err; - } + priv->host_regs = devm_platform_get_and_ioremap_resource(pdev, 0, &io_res); + if (IS_ERR(priv->host_regs)) + return PTR_ERR(priv->host_regs); ap->ioaddr.cmd_addr = priv->host_regs + PATA_IMX_DRIVE_DATA; ap->ioaddr.ctl_addr = priv->host_regs + PATA_IMX_DRIVE_CONTROL; @@ -195,16 +186,12 @@ static int pata_imx_probe(struct platform_device *pdev) &pata_imx_sht); if (ret) - goto err; + return ret; return 0; -err: - clk_disable_unprepare(priv->clk); - - return ret; } -static int pata_imx_remove(struct platform_device *pdev) +static void pata_imx_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct pata_imx_priv *priv = host->private_data; @@ -212,10 +199,6 @@ static int pata_imx_remove(struct platform_device *pdev) ata_host_detach(host); __raw_writel(0, priv->host_regs + PATA_IMX_ATA_INT_EN); - - clk_disable_unprepare(priv->clk); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -266,7 +249,7 @@ MODULE_DEVICE_TABLE(of, imx_pata_dt_ids); static struct platform_driver pata_imx_driver = { .probe = pata_imx_probe, - .remove = pata_imx_remove, + .remove_new = pata_imx_remove, .driver = { .name = DRV_NAME, .of_match_table = imx_pata_dt_ids, diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index b1daa4d3fcd97818bb6b26180008d515cef9ea00..246bb4f8f1f7f6fa451f9ddd1e2adb381cc962d4 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -242,12 +242,6 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) int ret; int irq; - cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctl = platform_get_resource(pdev, IORESOURCE_MEM, 1); - - if (!cmd || !ctl) - return -EINVAL; - ixpp = devm_kzalloc(dev, sizeof(*ixpp), GFP_KERNEL); if (!ixpp) return -ENOMEM; @@ -271,18 +265,18 @@ static int ixp4xx_pata_probe(struct platform_device *pdev) if (ret) return ret; - ixpp->cmd = devm_ioremap_resource(dev, cmd); - ixpp->ctl = devm_ioremap_resource(dev, ctl); - if (IS_ERR(ixpp->cmd) || IS_ERR(ixpp->ctl)) - return -ENOMEM; + ixpp->cmd = devm_platform_get_and_ioremap_resource(pdev, 0, &cmd); + if (IS_ERR(ixpp->cmd)) + return PTR_ERR(ixpp->cmd); + + ixpp->ctl = devm_platform_get_and_ioremap_resource(pdev, 1, &ctl); + if (IS_ERR(ixpp->ctl)) + return PTR_ERR(ixpp->ctl); irq = platform_get_irq(pdev, 0); - if (irq > 0) - irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); - else if (irq < 0) + if (irq < 0) return irq; - else - return -EINVAL; + irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); /* Just one port to set up */ ixp4xx_setup_port(ixpp->host->ports[0], ixpp, cmd->start, ctl->start); diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 66c9dea4ea6eeff1af6e35d3495e65696b832a84..6c317a461a1f63178ce9491fb7f4f233f21ff47a 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -19,9 +19,10 @@ #include #include #include +#include #include #include -#include +#include #include #include @@ -800,8 +801,7 @@ static int mpc52xx_ata_probe(struct platform_device *op) return rv; } -static int -mpc52xx_ata_remove(struct platform_device *op) +static void mpc52xx_ata_remove(struct platform_device *op) { struct ata_host *host = platform_get_drvdata(op); struct mpc52xx_ata_priv *priv = host->private_data; @@ -815,8 +815,6 @@ mpc52xx_ata_remove(struct platform_device *op) irq_dispose_mapping(task_irq); bcom_ata_release(priv->dmatsk); irq_dispose_mapping(priv->ata_irq); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -857,7 +855,7 @@ static const struct of_device_id mpc52xx_ata_of_match[] = { static struct platform_driver mpc52xx_ata_of_platform_driver = { .probe = mpc52xx_ata_probe, - .remove = mpc52xx_ata_remove, + .remove_new = mpc52xx_ata_remove, #ifdef CONFIG_PM_SLEEP .suspend = mpc52xx_ata_suspend, .resume = mpc52xx_ata_resume, diff --git a/drivers/ata/pata_parport/comm.c b/drivers/ata/pata_parport/comm.c index 4839becbbd5646f11c187611d0554a726bad515c..94b8d352102e85c0e70a1cedf8c55a15abb2b9cd 100644 --- a/drivers/ata/pata_parport/comm.c +++ b/drivers/ata/pata_parport/comm.c @@ -37,7 +37,7 @@ static int comm_read_regr(struct pi_adapter *pi, int cont, int regr) { int l, h, r; - r = regr + cont_map[cont]; + r = regr + cont_map[cont]; switch (pi->mode) { case 0: @@ -90,7 +90,6 @@ static void comm_connect(struct pi_adapter *pi) } static void comm_disconnect(struct pi_adapter *pi) - { w2(0); w2(0); w2(0); w2(4); w0(pi->saved_r0); @@ -172,12 +171,12 @@ static void comm_write_block(struct pi_adapter *pi, char *buf, int count) w4l(swab16(((u16 *)buf)[2 * k]) | swab16(((u16 *)buf)[2 * k + 1]) << 16); break; - } + } } static void comm_log_adapter(struct pi_adapter *pi) - -{ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" }; +{ + char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" }; dev_info(&pi->dev, "DataStor Commuter at 0x%x, mode %d (%s), delay %d\n", diff --git a/drivers/ata/pata_pxa.c b/drivers/ata/pata_pxa.c index ea402e02c46e25d16dfdb1d36d0d3028369507b4..5275c6464f57fcec2710620e68198c64df519377 100644 --- a/drivers/ata/pata_pxa.c +++ b/drivers/ata/pata_pxa.c @@ -295,7 +295,7 @@ static int pxa_ata_probe(struct platform_device *pdev) return ret; } -static int pxa_ata_remove(struct platform_device *pdev) +static void pxa_ata_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct pata_pxa_data *data = host->ports[0]->private_data; @@ -303,13 +303,11 @@ static int pxa_ata_remove(struct platform_device *pdev) dma_release_channel(data->dma_chan); ata_host_detach(host); - - return 0; } static struct platform_driver pxa_ata_driver = { .probe = pxa_ata_probe, - .remove = pxa_ata_remove, + .remove_new = pxa_ata_remove, .driver = { .name = DRV_NAME, }, diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index 3974d294a341b1da85c85f4daece3ce7513b1ebb..0fa253ad7c93e682f60c410d75b3ad9124364492 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -155,18 +155,16 @@ static int rb532_pata_driver_probe(struct platform_device *pdev) return 0; } -static int rb532_pata_driver_remove(struct platform_device *pdev) +static void rb532_pata_driver_remove(struct platform_device *pdev) { struct ata_host *ah = platform_get_drvdata(pdev); ata_host_detach(ah); - - return 0; } static struct platform_driver rb532_pata_platform_driver = { .probe = rb532_pata_driver_probe, - .remove = rb532_pata_driver_remove, + .remove_new = rb532_pata_driver_remove, .driver = { .name = DRV_NAME, }, diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 3b62ea482f1ab176019ab31ef57e11efd8d1d5af..93882e976ede456d5168d844a3dd1005301ea3dd 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -180,8 +180,7 @@ static void sl82c105_bmdma_start(struct ata_queued_cmd *qc) * document. * * This function is also called to turn off DMA when a timeout occurs - * during DMA operation. In both cases we need to reset the engine, - * so no actual eng_timeout handler is required. + * during DMA operation. In both cases we need to reset the engine. * * We assume bmdma_stop is always called if bmdma_start as called. If * not then we may need to wrap qc_issue. diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index fabdd1e380f94cef1f468ef9dd0d98e0b0510f8f..52f5168e4db542ad6626da8dd6ba00cc8833eada 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -18,9 +18,8 @@ #include #include #include -#include +#include #include -#include #include #include #include @@ -1211,7 +1210,7 @@ static int sata_dwc_probe(struct platform_device *ofdev) return err; } -static int sata_dwc_remove(struct platform_device *ofdev) +static void sata_dwc_remove(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct ata_host *host = dev_get_drvdata(dev); @@ -1227,7 +1226,6 @@ static int sata_dwc_remove(struct platform_device *ofdev) #endif dev_dbg(dev, "done\n"); - return 0; } static const struct of_device_id sata_dwc_match[] = { @@ -1242,7 +1240,7 @@ static struct platform_driver sata_dwc_driver = { .of_match_table = sata_dwc_match, }, .probe = sata_dwc_probe, - .remove = sata_dwc_remove, + .remove_new = sata_dwc_remove, }; module_platform_driver(sata_dwc_driver); diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index ccd99b9aa9ff70cf131d9825926c8ef06d587dbd..01aa05f4c3f5cd1c43cd03643da4a04d8facce20 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -12,6 +12,9 @@ #include #include +#include +#include +#include #include #include @@ -19,9 +22,6 @@ #include #include #include -#include -#include -#include static unsigned int intr_coalescing_count; module_param(intr_coalescing_count, int, S_IRUGO); @@ -1526,7 +1526,7 @@ static int sata_fsl_probe(struct platform_device *ofdev) return retval; } -static int sata_fsl_remove(struct platform_device *ofdev) +static void sata_fsl_remove(struct platform_device *ofdev) { struct ata_host *host = platform_get_drvdata(ofdev); struct sata_fsl_host_priv *host_priv = host->private_data; @@ -1535,8 +1535,6 @@ static int sata_fsl_remove(struct platform_device *ofdev) device_remove_file(&ofdev->dev, &host_priv->rx_watermark); ata_host_detach(host); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1591,7 +1589,7 @@ static struct platform_driver fsl_sata_driver = { .of_match_table = fsl_sata_match, }, .probe = sata_fsl_probe, - .remove = sata_fsl_remove, + .remove_new = sata_fsl_remove, #ifdef CONFIG_PM_SLEEP .suspend = sata_fsl_suspend, .resume = sata_fsl_resume, diff --git a/drivers/ata/sata_gemini.c b/drivers/ata/sata_gemini.c index c42cc9bbbc4ed46188a8a80b7f2837cbd732d5b8..400b22ee99c33affba7b25ae46de0a2014bfd71f 100644 --- a/drivers/ata/sata_gemini.c +++ b/drivers/ata/sata_gemini.c @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -400,7 +399,7 @@ static int gemini_sata_probe(struct platform_device *pdev) return ret; } -static int gemini_sata_remove(struct platform_device *pdev) +static void gemini_sata_remove(struct platform_device *pdev) { struct sata_gemini *sg = platform_get_drvdata(pdev); @@ -409,8 +408,6 @@ static int gemini_sata_remove(struct platform_device *pdev) clk_unprepare(sg->sata0_pclk); } sg_singleton = NULL; - - return 0; } static const struct of_device_id gemini_sata_of_match[] = { @@ -424,10 +421,11 @@ static struct platform_driver gemini_sata_driver = { .of_match_table = gemini_sata_of_match, }, .probe = gemini_sata_probe, - .remove = gemini_sata_remove, + .remove_new = gemini_sata_remove, }; module_platform_driver(gemini_sata_driver); +MODULE_DESCRIPTION("low level driver for Cortina Systems Gemini SATA bridge"); MODULE_AUTHOR("Linus Walleij "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c index d6b324d03e5974b7fde0bdaf9b027df3083d5868..63ef7bb073ce03362b9290d32d07fe5392385c19 100644 --- a/drivers/ata/sata_highbank.c +++ b/drivers/ata/sata_highbank.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -385,7 +385,7 @@ static int highbank_initialize_phys(struct device *dev, void __iomem *addr) static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { - static const unsigned long timing[] = { 5, 100, 500}; + static const unsigned int timing[] = { 5, 100, 500}; struct ata_port *ap = link->ap; struct ahci_port_priv *pp = ap->private_data; struct ahci_host_priv *hpriv = ap->host->private_data; diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 2c8c78ed86c199799969994b34bbb603d9b3a235..db9c255dc9f2ecc60630e5eeb820b974648bc613 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -619,7 +619,7 @@ static int inic_hardreset(struct ata_link *link, unsigned int *class, struct ata_port *ap = link->ap; void __iomem *port_base = inic_port_base(ap); void __iomem *idma_ctl = port_base + PORT_IDMA_CTL; - const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); int rc; /* hammer it into sane state */ diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index d404e631d1527431bb53472f0f67cda7c9dbc1e1..45e48d653c60b5556557e4e4d12666a32b7563aa 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -1255,8 +1255,8 @@ static void mv_dump_mem(struct device *dev, void __iomem *start, unsigned bytes) for (b = 0; b < bytes; ) { for (w = 0, o = 0; b < bytes && w < 4; w++) { - o += snprintf(linebuf + o, sizeof(linebuf) - o, - "%08x ", readl(start + b)); + o += scnprintf(linebuf + o, sizeof(linebuf) - o, + "%08x ", readl(start + b)); b += sizeof(u32); } dev_dbg(dev, "%s: %p: %s\n", @@ -3633,7 +3633,7 @@ static int mv_hardreset(struct ata_link *link, unsigned int *class, /* Workaround for errata FEr SATA#10 (part 2) */ do { - const unsigned long *timing = + const unsigned int *timing = sata_ehc_deb_timing(&link->eh_context); rc = sata_link_hardreset(link, timing, deadline + extra, @@ -4210,7 +4210,7 @@ static int mv_platform_probe(struct platform_device *pdev) * A platform bus SATA device has been unplugged. Perform the needed * cleanup. Also called on module unload for any active devices. */ -static int mv_platform_remove(struct platform_device *pdev) +static void mv_platform_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct mv_host_priv *hpriv = host->private_data; @@ -4228,7 +4228,6 @@ static int mv_platform_remove(struct platform_device *pdev) } phy_power_off(hpriv->port_phys[port]); } - return 0; } #ifdef CONFIG_PM_SLEEP @@ -4284,7 +4283,7 @@ MODULE_DEVICE_TABLE(of, mv_sata_dt_ids); static struct platform_driver mv_platform_driver = { .probe = mv_platform_probe, - .remove = mv_platform_remove, + .remove_new = mv_platform_remove, .suspend = mv_platform_suspend, .resume = mv_platform_resume, .driver = { diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index abf5651c87ab45c77499c886f26aa5808a2eb011..0a0cee755bde736a743e78d9f58c4c54ff754486 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -1529,7 +1529,7 @@ static int nv_hardreset(struct ata_link *link, unsigned int *class, sata_link_hardreset(link, sata_deb_timing_hotplug, deadline, NULL, NULL); else { - const unsigned long *timing = sata_ehc_deb_timing(ehc); + const unsigned int *timing = sata_ehc_deb_timing(ehc); int rc; if (!(ehc->i.flags & ATA_EHI_QUIET)) diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index 34790f15c1b81fb5913cb033c4e6c929665befad..c1469d0768801943d6a27679b06286a33c124aab 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -861,15 +861,11 @@ static int sata_rcar_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct ata_host *host; struct sata_rcar_priv *priv; - struct resource *mem; - int irq; - int ret = 0; + int irq, ret; irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - if (!irq) - return -EINVAL; priv = devm_kzalloc(dev, sizeof(struct sata_rcar_priv), GFP_KERNEL); if (!priv) @@ -890,8 +886,7 @@ static int sata_rcar_probe(struct platform_device *pdev) host->private_data = priv; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, mem); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto err_pm_put; @@ -914,7 +909,7 @@ static int sata_rcar_probe(struct platform_device *pdev) return ret; } -static int sata_rcar_remove(struct platform_device *pdev) +static void sata_rcar_remove(struct platform_device *pdev) { struct ata_host *host = platform_get_drvdata(pdev); struct sata_rcar_priv *priv = host->private_data; @@ -930,8 +925,6 @@ static int sata_rcar_remove(struct platform_device *pdev) pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1016,7 +1009,7 @@ static const struct dev_pm_ops sata_rcar_pm_ops = { static struct platform_driver sata_rcar_driver = { .probe = sata_rcar_probe, - .remove = sata_rcar_remove, + .remove_new = sata_rcar_remove, .driver = { .name = DRV_NAME, .of_match_table = sata_rcar_match, diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e72a0257990d884db06238a147f46c7695bd93b6..142e70bfc4982c13af4752e60b34f759449a6511 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -597,7 +597,7 @@ static int sil24_init_port(struct ata_port *ap) static int sil24_exec_polled_cmd(struct ata_port *ap, int pmp, const struct ata_taskfile *tf, int is_cmd, u32 ctrl, - unsigned long timeout_msec) + unsigned int timeout_msec) { void __iomem *port = sil24_port_base(ap); struct sil24_port_priv *pp = ap->private_data; @@ -651,7 +651,7 @@ static int sil24_softreset(struct ata_link *link, unsigned int *class, { struct ata_port *ap = link->ap; int pmp = sata_srst_pmp(link); - unsigned long timeout_msec = 0; + unsigned int timeout_msec = 0; struct ata_taskfile tf; const char *reason; int rc; diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index ccc0160726372cab28670eceaf3f4a01ec81fd07..b51d7a9d0d90ce0a6c72fe841aad222708c046e1 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -232,7 +232,6 @@ static const struct scsi_host_template pdc_sata_sht = { .dma_boundary = ATA_DMA_BOUNDARY, }; -/* TODO: inherit from base port_ops after converting to new EH */ static struct ata_port_operations pdc_20621_ops = { .inherits = &ata_sff_port_ops, diff --git a/drivers/base/core.c b/drivers/base/core.c index b7d7f410c2561ca26da0efd5808e2273028c0759..4d8b315c48a15a9dd9db023f67e214855b86623d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3537,6 +3537,8 @@ int device_add(struct device *dev) /* subsystems can specify simple device enumeration */ else if (dev->bus && dev->bus->dev_name) error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); + else + error = -EINVAL; if (error) goto name_error; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index db716ffd083ee5de5e793d004365c75cb511f113..3db88bbcae0fc57adc5023cb3648ee774da64d83 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -453,7 +453,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, if (!rbnode) return -ENOMEM; regcache_rbtree_set_register(map, rbnode, - reg - rbnode->base_reg, value); + (reg - rbnode->base_reg) / map->reg_stride, + value); regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode); rbtree_ctx->cached_rbnode = rbnode; } diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 79ab532aabafbaf2089922c4b387e1dcc0a7c72a..6bc86106c7b2ab5d59b6ab66e452976d235a7d97 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -1557,7 +1557,7 @@ static int _drbd_send_page(struct drbd_peer_device *peer_device, struct page *pa do { int sent; - bvec_set_page(&bvec, page, offset, len); + bvec_set_page(&bvec, page, len, offset); iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bvec, 1, len); sent = sock_sendmsg(socket, &msg); diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index df1cd0f718b81c0d5d7dd68afc4b67c6b05e2c50..800f131222fc8f9e8ecf9c8355f2a787a80cc5fc 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -1436,8 +1436,9 @@ static int nbd_start_device_ioctl(struct nbd_device *nbd) static void nbd_clear_sock_ioctl(struct nbd_device *nbd) { - blk_mark_disk_dead(nbd->disk); nbd_clear_sock(nbd); + disk_force_media_change(nbd->disk); + nbd_bdev_reset(nbd); if (test_and_clear_bit(NBD_RT_HAS_CONFIG_REF, &nbd->config->runtime_flags)) nbd_config_put(nbd); diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 864013019d6b6508d8750c7f19c10a11b710e9ca..968090935eb23f9418c742e89688ec0277f84e02 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -1643,9 +1643,12 @@ static int null_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob) struct nullb_queue *nq = hctx->driver_data; LIST_HEAD(list); int nr = 0; + struct request *rq; spin_lock(&nq->poll_lock); list_splice_init(&nq->poll_list, &list); + list_for_each_entry(rq, &list, queuelist) + blk_mq_set_request_complete(rq); spin_unlock(&nq->poll_lock); while (!list_empty(&list)) { @@ -1671,16 +1674,21 @@ static enum blk_eh_timer_return null_timeout_rq(struct request *rq) struct blk_mq_hw_ctx *hctx = rq->mq_hctx; struct nullb_cmd *cmd = blk_mq_rq_to_pdu(rq); - pr_info("rq %p timed out\n", rq); - if (hctx->type == HCTX_TYPE_POLL) { struct nullb_queue *nq = hctx->driver_data; spin_lock(&nq->poll_lock); + /* The request may have completed meanwhile. */ + if (blk_mq_request_completed(rq)) { + spin_unlock(&nq->poll_lock); + return BLK_EH_DONE; + } list_del_init(&rq->queuelist); spin_unlock(&nq->poll_lock); } + pr_info("rq %p timed out\n", rq); + /* * If the device is marked as blocking (i.e. memory backed or zoned * device), the submission path may be blocked waiting for resources diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 2328cc05be36e99eb0f9083b0e138cf8bd52086a..a999b698b131f7763916c3bd0de5c87478fd0df4 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -632,9 +632,8 @@ void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...) static void rbd_dev_remove_parent(struct rbd_device *rbd_dev); static int rbd_dev_refresh(struct rbd_device *rbd_dev); -static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev); -static int rbd_dev_header_info(struct rbd_device *rbd_dev); -static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev); +static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev, + struct rbd_image_header *header); static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u64 snap_id); static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, @@ -995,15 +994,24 @@ static void rbd_init_layout(struct rbd_device *rbd_dev) RCU_INIT_POINTER(rbd_dev->layout.pool_ns, NULL); } +static void rbd_image_header_cleanup(struct rbd_image_header *header) +{ + kfree(header->object_prefix); + ceph_put_snap_context(header->snapc); + kfree(header->snap_sizes); + kfree(header->snap_names); + + memset(header, 0, sizeof(*header)); +} + /* * Fill an rbd image header with information from the given format 1 * on-disk header. */ -static int rbd_header_from_disk(struct rbd_device *rbd_dev, - struct rbd_image_header_ondisk *ondisk) +static int rbd_header_from_disk(struct rbd_image_header *header, + struct rbd_image_header_ondisk *ondisk, + bool first_time) { - struct rbd_image_header *header = &rbd_dev->header; - bool first_time = header->object_prefix == NULL; struct ceph_snap_context *snapc; char *object_prefix = NULL; char *snap_names = NULL; @@ -1070,11 +1078,6 @@ static int rbd_header_from_disk(struct rbd_device *rbd_dev, if (first_time) { header->object_prefix = object_prefix; header->obj_order = ondisk->options.order; - rbd_init_layout(rbd_dev); - } else { - ceph_put_snap_context(header->snapc); - kfree(header->snap_names); - kfree(header->snap_sizes); } /* The remaining fields always get updated (when we refresh) */ @@ -4859,7 +4862,9 @@ static int rbd_obj_read_sync(struct rbd_device *rbd_dev, * return, the rbd_dev->header field will contain up-to-date * information about the image. */ -static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev) +static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev, + struct rbd_image_header *header, + bool first_time) { struct rbd_image_header_ondisk *ondisk = NULL; u32 snap_count = 0; @@ -4907,7 +4912,7 @@ static int rbd_dev_v1_header_info(struct rbd_device *rbd_dev) snap_count = le32_to_cpu(ondisk->snap_count); } while (snap_count != want_count); - ret = rbd_header_from_disk(rbd_dev, ondisk); + ret = rbd_header_from_disk(header, ondisk, first_time); out: kfree(ondisk); @@ -4931,39 +4936,6 @@ static void rbd_dev_update_size(struct rbd_device *rbd_dev) } } -static int rbd_dev_refresh(struct rbd_device *rbd_dev) -{ - u64 mapping_size; - int ret; - - down_write(&rbd_dev->header_rwsem); - mapping_size = rbd_dev->mapping.size; - - ret = rbd_dev_header_info(rbd_dev); - if (ret) - goto out; - - /* - * If there is a parent, see if it has disappeared due to the - * mapped image getting flattened. - */ - if (rbd_dev->parent) { - ret = rbd_dev_v2_parent_info(rbd_dev); - if (ret) - goto out; - } - - rbd_assert(!rbd_is_snap(rbd_dev)); - rbd_dev->mapping.size = rbd_dev->header.image_size; - -out: - up_write(&rbd_dev->header_rwsem); - if (!ret && mapping_size != rbd_dev->mapping.size) - rbd_dev_update_size(rbd_dev); - - return ret; -} - static const struct blk_mq_ops rbd_mq_ops = { .queue_rq = rbd_queue_rq, }; @@ -5503,17 +5475,12 @@ static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id, return 0; } -static int rbd_dev_v2_image_size(struct rbd_device *rbd_dev) -{ - return _rbd_dev_v2_snap_size(rbd_dev, CEPH_NOSNAP, - &rbd_dev->header.obj_order, - &rbd_dev->header.image_size); -} - -static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) +static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev, + char **pobject_prefix) { size_t size; void *reply_buf; + char *object_prefix; int ret; void *p; @@ -5531,16 +5498,16 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) goto out; p = reply_buf; - rbd_dev->header.object_prefix = ceph_extract_encoded_string(&p, - p + ret, NULL, GFP_NOIO); + object_prefix = ceph_extract_encoded_string(&p, p + ret, NULL, + GFP_NOIO); + if (IS_ERR(object_prefix)) { + ret = PTR_ERR(object_prefix); + goto out; + } ret = 0; - if (IS_ERR(rbd_dev->header.object_prefix)) { - ret = PTR_ERR(rbd_dev->header.object_prefix); - rbd_dev->header.object_prefix = NULL; - } else { - dout(" object_prefix = %s\n", rbd_dev->header.object_prefix); - } + *pobject_prefix = object_prefix; + dout(" object_prefix = %s\n", object_prefix); out: kfree(reply_buf); @@ -5591,13 +5558,6 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, return 0; } -static int rbd_dev_v2_features(struct rbd_device *rbd_dev) -{ - return _rbd_dev_v2_snap_features(rbd_dev, CEPH_NOSNAP, - rbd_is_ro(rbd_dev), - &rbd_dev->header.features); -} - /* * These are generic image flags, but since they are used only for * object map, store them in rbd_dev->object_map_flags. @@ -5634,6 +5594,14 @@ struct parent_image_info { u64 overlap; }; +static void rbd_parent_info_cleanup(struct parent_image_info *pii) +{ + kfree(pii->pool_ns); + kfree(pii->image_id); + + memset(pii, 0, sizeof(*pii)); +} + /* * The caller is responsible for @pii. */ @@ -5703,6 +5671,9 @@ static int __get_parent_info(struct rbd_device *rbd_dev, if (pii->has_overlap) ceph_decode_64_safe(&p, end, pii->overlap, e_inval); + dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n", + __func__, pii->pool_id, pii->pool_ns, pii->image_id, pii->snap_id, + pii->has_overlap, pii->overlap); return 0; e_inval: @@ -5741,14 +5712,17 @@ static int __get_parent_info_legacy(struct rbd_device *rbd_dev, pii->has_overlap = true; ceph_decode_64_safe(&p, end, pii->overlap, e_inval); + dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n", + __func__, pii->pool_id, pii->pool_ns, pii->image_id, pii->snap_id, + pii->has_overlap, pii->overlap); return 0; e_inval: return -EINVAL; } -static int get_parent_info(struct rbd_device *rbd_dev, - struct parent_image_info *pii) +static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev, + struct parent_image_info *pii) { struct page *req_page, *reply_page; void *p; @@ -5776,7 +5750,7 @@ static int get_parent_info(struct rbd_device *rbd_dev, return ret; } -static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) +static int rbd_dev_setup_parent(struct rbd_device *rbd_dev) { struct rbd_spec *parent_spec; struct parent_image_info pii = { 0 }; @@ -5786,37 +5760,12 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) if (!parent_spec) return -ENOMEM; - ret = get_parent_info(rbd_dev, &pii); + ret = rbd_dev_v2_parent_info(rbd_dev, &pii); if (ret) goto out_err; - dout("%s pool_id %llu pool_ns %s image_id %s snap_id %llu has_overlap %d overlap %llu\n", - __func__, pii.pool_id, pii.pool_ns, pii.image_id, pii.snap_id, - pii.has_overlap, pii.overlap); - - if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) { - /* - * Either the parent never existed, or we have - * record of it but the image got flattened so it no - * longer has a parent. When the parent of a - * layered image disappears we immediately set the - * overlap to 0. The effect of this is that all new - * requests will be treated as if the image had no - * parent. - * - * If !pii.has_overlap, the parent image spec is not - * applicable. It's there to avoid duplication in each - * snapshot record. - */ - if (rbd_dev->parent_overlap) { - rbd_dev->parent_overlap = 0; - rbd_dev_parent_put(rbd_dev); - pr_info("%s: clone image has been flattened\n", - rbd_dev->disk->disk_name); - } - + if (pii.pool_id == CEPH_NOPOOL || !pii.has_overlap) goto out; /* No parent? No problem. */ - } /* The ceph file layout needs to fit pool id in 32 bits */ @@ -5828,58 +5777,46 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) } /* - * The parent won't change (except when the clone is - * flattened, already handled that). So we only need to - * record the parent spec we have not already done so. + * The parent won't change except when the clone is flattened, + * so we only need to record the parent image spec once. */ - if (!rbd_dev->parent_spec) { - parent_spec->pool_id = pii.pool_id; - if (pii.pool_ns && *pii.pool_ns) { - parent_spec->pool_ns = pii.pool_ns; - pii.pool_ns = NULL; - } - parent_spec->image_id = pii.image_id; - pii.image_id = NULL; - parent_spec->snap_id = pii.snap_id; - - rbd_dev->parent_spec = parent_spec; - parent_spec = NULL; /* rbd_dev now owns this */ + parent_spec->pool_id = pii.pool_id; + if (pii.pool_ns && *pii.pool_ns) { + parent_spec->pool_ns = pii.pool_ns; + pii.pool_ns = NULL; } + parent_spec->image_id = pii.image_id; + pii.image_id = NULL; + parent_spec->snap_id = pii.snap_id; + + rbd_assert(!rbd_dev->parent_spec); + rbd_dev->parent_spec = parent_spec; + parent_spec = NULL; /* rbd_dev now owns this */ /* - * We always update the parent overlap. If it's zero we issue - * a warning, as we will proceed as if there was no parent. + * Record the parent overlap. If it's zero, issue a warning as + * we will proceed as if there is no parent. */ - if (!pii.overlap) { - if (parent_spec) { - /* refresh, careful to warn just once */ - if (rbd_dev->parent_overlap) - rbd_warn(rbd_dev, - "clone now standalone (overlap became 0)"); - } else { - /* initial probe */ - rbd_warn(rbd_dev, "clone is standalone (overlap 0)"); - } - } + if (!pii.overlap) + rbd_warn(rbd_dev, "clone is standalone (overlap 0)"); rbd_dev->parent_overlap = pii.overlap; out: ret = 0; out_err: - kfree(pii.pool_ns); - kfree(pii.image_id); + rbd_parent_info_cleanup(&pii); rbd_spec_put(parent_spec); return ret; } -static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev) +static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev, + u64 *stripe_unit, u64 *stripe_count) { struct { __le64 stripe_unit; __le64 stripe_count; } __attribute__ ((packed)) striping_info_buf = { 0 }; size_t size = sizeof (striping_info_buf); - void *p; int ret; ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, @@ -5891,27 +5828,33 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev) if (ret < size) return -ERANGE; - p = &striping_info_buf; - rbd_dev->header.stripe_unit = ceph_decode_64(&p); - rbd_dev->header.stripe_count = ceph_decode_64(&p); + *stripe_unit = le64_to_cpu(striping_info_buf.stripe_unit); + *stripe_count = le64_to_cpu(striping_info_buf.stripe_count); + dout(" stripe_unit = %llu stripe_count = %llu\n", *stripe_unit, + *stripe_count); + return 0; } -static int rbd_dev_v2_data_pool(struct rbd_device *rbd_dev) +static int rbd_dev_v2_data_pool(struct rbd_device *rbd_dev, s64 *data_pool_id) { - __le64 data_pool_id; + __le64 data_pool_buf; int ret; ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid, &rbd_dev->header_oloc, "get_data_pool", - NULL, 0, &data_pool_id, sizeof(data_pool_id)); + NULL, 0, &data_pool_buf, + sizeof(data_pool_buf)); + dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret); if (ret < 0) return ret; - if (ret < sizeof(data_pool_id)) + if (ret < sizeof(data_pool_buf)) return -EBADMSG; - rbd_dev->header.data_pool_id = le64_to_cpu(data_pool_id); - WARN_ON(rbd_dev->header.data_pool_id == CEPH_NOPOOL); + *data_pool_id = le64_to_cpu(data_pool_buf); + dout(" data_pool_id = %lld\n", *data_pool_id); + WARN_ON(*data_pool_id == CEPH_NOPOOL); + return 0; } @@ -6103,7 +6046,8 @@ static int rbd_spec_fill_names(struct rbd_device *rbd_dev) return ret; } -static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev) +static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, + struct ceph_snap_context **psnapc) { size_t size; int ret; @@ -6164,9 +6108,7 @@ static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev) for (i = 0; i < snap_count; i++) snapc->snaps[i] = ceph_decode_64(&p); - ceph_put_snap_context(rbd_dev->header.snapc); - rbd_dev->header.snapc = snapc; - + *psnapc = snapc; dout(" snap context seq = %llu, snap_count = %u\n", (unsigned long long)seq, (unsigned int)snap_count); out: @@ -6215,38 +6157,42 @@ static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, return snap_name; } -static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev) +static int rbd_dev_v2_header_info(struct rbd_device *rbd_dev, + struct rbd_image_header *header, + bool first_time) { - bool first_time = rbd_dev->header.object_prefix == NULL; int ret; - ret = rbd_dev_v2_image_size(rbd_dev); + ret = _rbd_dev_v2_snap_size(rbd_dev, CEPH_NOSNAP, + first_time ? &header->obj_order : NULL, + &header->image_size); if (ret) return ret; if (first_time) { - ret = rbd_dev_v2_header_onetime(rbd_dev); + ret = rbd_dev_v2_header_onetime(rbd_dev, header); if (ret) return ret; } - ret = rbd_dev_v2_snap_context(rbd_dev); - if (ret && first_time) { - kfree(rbd_dev->header.object_prefix); - rbd_dev->header.object_prefix = NULL; - } + ret = rbd_dev_v2_snap_context(rbd_dev, &header->snapc); + if (ret) + return ret; - return ret; + return 0; } -static int rbd_dev_header_info(struct rbd_device *rbd_dev) +static int rbd_dev_header_info(struct rbd_device *rbd_dev, + struct rbd_image_header *header, + bool first_time) { rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); + rbd_assert(!header->object_prefix && !header->snapc); if (rbd_dev->image_format == 1) - return rbd_dev_v1_header_info(rbd_dev); + return rbd_dev_v1_header_info(rbd_dev, header, first_time); - return rbd_dev_v2_header_info(rbd_dev); + return rbd_dev_v2_header_info(rbd_dev, header, first_time); } /* @@ -6734,60 +6680,49 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) */ static void rbd_dev_unprobe(struct rbd_device *rbd_dev) { - struct rbd_image_header *header; - rbd_dev_parent_put(rbd_dev); rbd_object_map_free(rbd_dev); rbd_dev_mapping_clear(rbd_dev); /* Free dynamic fields from the header, then zero it out */ - header = &rbd_dev->header; - ceph_put_snap_context(header->snapc); - kfree(header->snap_sizes); - kfree(header->snap_names); - kfree(header->object_prefix); - memset(header, 0, sizeof (*header)); + rbd_image_header_cleanup(&rbd_dev->header); } -static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev) +static int rbd_dev_v2_header_onetime(struct rbd_device *rbd_dev, + struct rbd_image_header *header) { int ret; - ret = rbd_dev_v2_object_prefix(rbd_dev); + ret = rbd_dev_v2_object_prefix(rbd_dev, &header->object_prefix); if (ret) - goto out_err; + return ret; /* * Get the and check features for the image. Currently the * features are assumed to never change. */ - ret = rbd_dev_v2_features(rbd_dev); + ret = _rbd_dev_v2_snap_features(rbd_dev, CEPH_NOSNAP, + rbd_is_ro(rbd_dev), &header->features); if (ret) - goto out_err; + return ret; /* If the image supports fancy striping, get its parameters */ - if (rbd_dev->header.features & RBD_FEATURE_STRIPINGV2) { - ret = rbd_dev_v2_striping_info(rbd_dev); - if (ret < 0) - goto out_err; + if (header->features & RBD_FEATURE_STRIPINGV2) { + ret = rbd_dev_v2_striping_info(rbd_dev, &header->stripe_unit, + &header->stripe_count); + if (ret) + return ret; } - if (rbd_dev->header.features & RBD_FEATURE_DATA_POOL) { - ret = rbd_dev_v2_data_pool(rbd_dev); + if (header->features & RBD_FEATURE_DATA_POOL) { + ret = rbd_dev_v2_data_pool(rbd_dev, &header->data_pool_id); if (ret) - goto out_err; + return ret; } - rbd_init_layout(rbd_dev); return 0; - -out_err: - rbd_dev->header.features = 0; - kfree(rbd_dev->header.object_prefix); - rbd_dev->header.object_prefix = NULL; - return ret; } /* @@ -6982,13 +6917,15 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) if (!depth) down_write(&rbd_dev->header_rwsem); - ret = rbd_dev_header_info(rbd_dev); + ret = rbd_dev_header_info(rbd_dev, &rbd_dev->header, true); if (ret) { if (ret == -ENOENT && !need_watch) rbd_print_dne(rbd_dev, false); goto err_out_probe; } + rbd_init_layout(rbd_dev); + /* * If this image is the one being mapped, we have pool name and * id, image name and id, and snap name - need to fill snap id. @@ -7017,7 +6954,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) } if (rbd_dev->header.features & RBD_FEATURE_LAYERING) { - ret = rbd_dev_v2_parent_info(rbd_dev); + ret = rbd_dev_setup_parent(rbd_dev); if (ret) goto err_out_probe; } @@ -7043,6 +6980,107 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) return ret; } +static void rbd_dev_update_header(struct rbd_device *rbd_dev, + struct rbd_image_header *header) +{ + rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); + rbd_assert(rbd_dev->header.object_prefix); /* !first_time */ + + if (rbd_dev->header.image_size != header->image_size) { + rbd_dev->header.image_size = header->image_size; + + if (!rbd_is_snap(rbd_dev)) { + rbd_dev->mapping.size = header->image_size; + rbd_dev_update_size(rbd_dev); + } + } + + ceph_put_snap_context(rbd_dev->header.snapc); + rbd_dev->header.snapc = header->snapc; + header->snapc = NULL; + + if (rbd_dev->image_format == 1) { + kfree(rbd_dev->header.snap_names); + rbd_dev->header.snap_names = header->snap_names; + header->snap_names = NULL; + + kfree(rbd_dev->header.snap_sizes); + rbd_dev->header.snap_sizes = header->snap_sizes; + header->snap_sizes = NULL; + } +} + +static void rbd_dev_update_parent(struct rbd_device *rbd_dev, + struct parent_image_info *pii) +{ + if (pii->pool_id == CEPH_NOPOOL || !pii->has_overlap) { + /* + * Either the parent never existed, or we have + * record of it but the image got flattened so it no + * longer has a parent. When the parent of a + * layered image disappears we immediately set the + * overlap to 0. The effect of this is that all new + * requests will be treated as if the image had no + * parent. + * + * If !pii.has_overlap, the parent image spec is not + * applicable. It's there to avoid duplication in each + * snapshot record. + */ + if (rbd_dev->parent_overlap) { + rbd_dev->parent_overlap = 0; + rbd_dev_parent_put(rbd_dev); + pr_info("%s: clone has been flattened\n", + rbd_dev->disk->disk_name); + } + } else { + rbd_assert(rbd_dev->parent_spec); + + /* + * Update the parent overlap. If it became zero, issue + * a warning as we will proceed as if there is no parent. + */ + if (!pii->overlap && rbd_dev->parent_overlap) + rbd_warn(rbd_dev, + "clone has become standalone (overlap 0)"); + rbd_dev->parent_overlap = pii->overlap; + } +} + +static int rbd_dev_refresh(struct rbd_device *rbd_dev) +{ + struct rbd_image_header header = { 0 }; + struct parent_image_info pii = { 0 }; + int ret; + + dout("%s rbd_dev %p\n", __func__, rbd_dev); + + ret = rbd_dev_header_info(rbd_dev, &header, false); + if (ret) + goto out; + + /* + * If there is a parent, see if it has disappeared due to the + * mapped image getting flattened. + */ + if (rbd_dev->parent) { + ret = rbd_dev_v2_parent_info(rbd_dev, &pii); + if (ret) + goto out; + } + + down_write(&rbd_dev->header_rwsem); + rbd_dev_update_header(rbd_dev, &header); + if (rbd_dev->parent) + rbd_dev_update_parent(rbd_dev, &pii); + up_write(&rbd_dev->header_rwsem); + +out: + rbd_parent_info_cleanup(&pii); + rbd_image_header_cleanup(&header); + return ret; +} + static ssize_t do_rbd_add(const char *buf, size_t count) { struct rbd_device *rbd_dev = NULL; @@ -7199,7 +7237,6 @@ static void rbd_dev_remove_parent(struct rbd_device *rbd_dev) static ssize_t do_rbd_remove(const char *buf, size_t count) { struct rbd_device *rbd_dev = NULL; - struct list_head *tmp; int dev_id; char opt_buf[6]; bool force = false; @@ -7226,8 +7263,7 @@ static ssize_t do_rbd_remove(const char *buf, size_t count) ret = -ENOENT; spin_lock(&rbd_dev_list_lock); - list_for_each(tmp, &rbd_dev_list) { - rbd_dev = list_entry(tmp, struct rbd_device, node); + list_for_each_entry(rbd_dev, &rbd_dev_list, node) { if (rbd_dev->dev_id == dev_id) { ret = 0; break; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 82597ab4f747bb6fd0796bf1ab2a781962677ac4..499f4809fcdf3d507dfe33399483762569c8c725 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -4419,6 +4419,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_QCA_ROME) { data->setup_on_usb = btusb_setup_qca; + hdev->shutdown = btusb_shutdown_qca; hdev->set_bdaddr = btusb_set_bdaddr_ath3012; hdev->cmd_timeout = btusb_qca_cmd_timeout; set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index eb4e7bee1e20cf6e76dfcc706a324b82111943a8..d57bc066dce6b4c5bd97f5a662928b2b590f7221 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -38,6 +38,7 @@ enum sysc_soc { SOC_2420, SOC_2430, SOC_3430, + SOC_AM35, SOC_3630, SOC_4430, SOC_4460, @@ -1097,6 +1098,11 @@ static int sysc_enable_module(struct device *dev) if (ddata->cfg.quirks & (SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_SIDLE_ACT)) { best_mode = SYSC_IDLE_NO; + + /* Clear WAKEUP */ + if (regbits->enwkup_shift >= 0 && + ddata->cfg.sysc_val & BIT(regbits->enwkup_shift)) + reg &= ~BIT(regbits->enwkup_shift); } else { best_mode = fls(ddata->cfg.sidlemodes) - 1; if (best_mode > SYSC_IDLE_MASK) { @@ -1224,6 +1230,13 @@ static int sysc_disable_module(struct device *dev) } } + if (ddata->cfg.quirks & SYSC_QUIRK_SWSUP_SIDLE_ACT) { + /* Set WAKEUP */ + if (regbits->enwkup_shift >= 0 && + ddata->cfg.sysc_val & BIT(regbits->enwkup_shift)) + reg |= BIT(regbits->enwkup_shift); + } + reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); reg |= best_mode << regbits->sidle_shift; if (regbits->autoidle_shift >= 0 && @@ -1518,16 +1531,16 @@ struct sysc_revision_quirk { static const struct sysc_revision_quirk sysc_revision_quirks[] = { /* These drivers need to be fixed to not use pm_runtime_irq_safe() */ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), /* Uarts on omap4 and later */ SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff, - SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_LEGACY_IDLE), + SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), /* Quirks that need to be set based on the module address */ SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -ENODEV, 0x50000800, 0xffffffff, @@ -1862,7 +1875,7 @@ static void sysc_pre_reset_quirk_dss(struct sysc *ddata) dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n", __func__, val, irq_mask); - if (sysc_soc->soc == SOC_3430) { + if (sysc_soc->soc == SOC_3430 || sysc_soc->soc == SOC_AM35) { /* Clear DSS_SDI_CONTROL */ sysc_write(ddata, 0x44, 0); @@ -2150,8 +2163,7 @@ static int sysc_reset(struct sysc *ddata) } if (ddata->cfg.srst_udelay) - usleep_range(ddata->cfg.srst_udelay, - ddata->cfg.srst_udelay * 2); + fsleep(ddata->cfg.srst_udelay); if (ddata->post_reset_quirk) ddata->post_reset_quirk(ddata); @@ -3025,6 +3037,7 @@ static void ti_sysc_idle(struct work_struct *work) static const struct soc_device_attribute sysc_soc_match[] = { SOC_FLAG("OMAP242*", SOC_2420), SOC_FLAG("OMAP243*", SOC_2430), + SOC_FLAG("AM35*", SOC_AM35), SOC_FLAG("OMAP3[45]*", SOC_3430), SOC_FLAG("OMAP3[67]*", SOC_3630), SOC_FLAG("OMAP443*", SOC_4430), @@ -3229,7 +3242,7 @@ static int sysc_check_active_timer(struct sysc *ddata) * can be dropped if we stop supporting old beagleboard revisions * A to B4 at some point. */ - if (sysc_soc->soc == SOC_3430) + if (sysc_soc->soc == SOC_3430 || sysc_soc->soc == SOC_AM35) error = -ENXIO; else error = -EBUSY; diff --git a/drivers/cache/Kconfig b/drivers/cache/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..a57677f908f3ba3c134fb97e7c1eecd7159d8f0c --- /dev/null +++ b/drivers/cache/Kconfig @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "Cache Drivers" + +config AX45MP_L2_CACHE + bool "Andes Technology AX45MP L2 Cache controller" + depends on RISCV_DMA_NONCOHERENT + select RISCV_NONSTANDARD_CACHE_OPS + help + Support for the L2 cache controller on Andes Technology AX45MP platforms. + +endmenu diff --git a/drivers/cache/Makefile b/drivers/cache/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2012e7fb978dc5cc645a67f14f5af5373f04819c --- /dev/null +++ b/drivers/cache/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_AX45MP_L2_CACHE) += ax45mp_cache.o diff --git a/drivers/cache/ax45mp_cache.c b/drivers/cache/ax45mp_cache.c new file mode 100644 index 0000000000000000000000000000000000000000..57186c58dc849c15db2f9c25ad8c816398f29986 --- /dev/null +++ b/drivers/cache/ax45mp_cache.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * non-coherent cache functions for Andes AX45MP + * + * Copyright (C) 2023 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include + +#include + +/* L2 cache registers */ +#define AX45MP_L2C_REG_CTL_OFFSET 0x8 + +#define AX45MP_L2C_REG_C0_CMD_OFFSET 0x40 +#define AX45MP_L2C_REG_C0_ACC_OFFSET 0x48 +#define AX45MP_L2C_REG_STATUS_OFFSET 0x80 + +/* D-cache operation */ +#define AX45MP_CCTL_L1D_VA_INVAL 0 /* Invalidate an L1 cache entry */ +#define AX45MP_CCTL_L1D_VA_WB 1 /* Write-back an L1 cache entry */ + +/* L2 CCTL status */ +#define AX45MP_CCTL_L2_STATUS_IDLE 0 + +/* L2 CCTL status cores mask */ +#define AX45MP_CCTL_L2_STATUS_C0_MASK 0xf + +/* L2 cache operation */ +#define AX45MP_CCTL_L2_PA_INVAL 0x8 /* Invalidate an L2 cache entry */ +#define AX45MP_CCTL_L2_PA_WB 0x9 /* Write-back an L2 cache entry */ + +#define AX45MP_L2C_REG_PER_CORE_OFFSET 0x10 +#define AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET 4 + +#define AX45MP_L2C_REG_CN_CMD_OFFSET(n) \ + (AX45MP_L2C_REG_C0_CMD_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET)) +#define AX45MP_L2C_REG_CN_ACC_OFFSET(n) \ + (AX45MP_L2C_REG_C0_ACC_OFFSET + ((n) * AX45MP_L2C_REG_PER_CORE_OFFSET)) +#define AX45MP_CCTL_L2_STATUS_CN_MASK(n) \ + (AX45MP_CCTL_L2_STATUS_C0_MASK << ((n) * AX45MP_CCTL_L2_STATUS_PER_CORE_OFFSET)) + +#define AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM 0x80b +#define AX45MP_CCTL_REG_UCCTLCOMMAND_NUM 0x80c + +#define AX45MP_CACHE_LINE_SIZE 64 + +struct ax45mp_priv { + void __iomem *l2c_base; + u32 ax45mp_cache_line_size; +}; + +static struct ax45mp_priv ax45mp_priv; + +/* L2 Cache operations */ +static inline uint32_t ax45mp_cpu_l2c_get_cctl_status(void) +{ + return readl(ax45mp_priv.l2c_base + AX45MP_L2C_REG_STATUS_OFFSET); +} + +static void ax45mp_cpu_cache_operation(unsigned long start, unsigned long end, + unsigned int l1_op, unsigned int l2_op) +{ + unsigned long line_size = ax45mp_priv.ax45mp_cache_line_size; + void __iomem *base = ax45mp_priv.l2c_base; + int mhartid = smp_processor_id(); + unsigned long pa; + + while (end > start) { + csr_write(AX45MP_CCTL_REG_UCCTLBEGINADDR_NUM, start); + csr_write(AX45MP_CCTL_REG_UCCTLCOMMAND_NUM, l1_op); + + pa = virt_to_phys((void *)start); + writel(pa, base + AX45MP_L2C_REG_CN_ACC_OFFSET(mhartid)); + writel(l2_op, base + AX45MP_L2C_REG_CN_CMD_OFFSET(mhartid)); + while ((ax45mp_cpu_l2c_get_cctl_status() & + AX45MP_CCTL_L2_STATUS_CN_MASK(mhartid)) != + AX45MP_CCTL_L2_STATUS_IDLE) + ; + + start += line_size; + } +} + +/* Write-back L1 and L2 cache entry */ +static inline void ax45mp_cpu_dcache_wb_range(unsigned long start, unsigned long end) +{ + ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_WB, + AX45MP_CCTL_L2_PA_WB); +} + +/* Invalidate the L1 and L2 cache entry */ +static inline void ax45mp_cpu_dcache_inval_range(unsigned long start, unsigned long end) +{ + ax45mp_cpu_cache_operation(start, end, AX45MP_CCTL_L1D_VA_INVAL, + AX45MP_CCTL_L2_PA_INVAL); +} + +static void ax45mp_dma_cache_inv(phys_addr_t paddr, size_t size) +{ + unsigned long start = (unsigned long)phys_to_virt(paddr); + unsigned long end = start + size; + unsigned long line_size; + unsigned long flags; + + if (unlikely(start == end)) + return; + + line_size = ax45mp_priv.ax45mp_cache_line_size; + + start = start & (~(line_size - 1)); + end = ((end + line_size - 1) & (~(line_size - 1))); + + local_irq_save(flags); + + ax45mp_cpu_dcache_inval_range(start, end); + + local_irq_restore(flags); +} + +static void ax45mp_dma_cache_wback(phys_addr_t paddr, size_t size) +{ + unsigned long start = (unsigned long)phys_to_virt(paddr); + unsigned long end = start + size; + unsigned long line_size; + unsigned long flags; + + line_size = ax45mp_priv.ax45mp_cache_line_size; + start = start & (~(line_size - 1)); + local_irq_save(flags); + ax45mp_cpu_dcache_wb_range(start, end); + local_irq_restore(flags); +} + +static void ax45mp_dma_cache_wback_inv(phys_addr_t paddr, size_t size) +{ + ax45mp_dma_cache_wback(paddr, size); + ax45mp_dma_cache_inv(paddr, size); +} + +static int ax45mp_get_l2_line_size(struct device_node *np) +{ + int ret; + + ret = of_property_read_u32(np, "cache-line-size", &ax45mp_priv.ax45mp_cache_line_size); + if (ret) { + pr_err("Failed to get cache-line-size, defaulting to 64 bytes\n"); + return ret; + } + + if (ax45mp_priv.ax45mp_cache_line_size != AX45MP_CACHE_LINE_SIZE) { + pr_err("Expected cache-line-size to be 64 bytes (found:%u)\n", + ax45mp_priv.ax45mp_cache_line_size); + return -EINVAL; + } + + return 0; +} + +static const struct riscv_nonstd_cache_ops ax45mp_cmo_ops __initdata = { + .wback = &ax45mp_dma_cache_wback, + .inv = &ax45mp_dma_cache_inv, + .wback_inv = &ax45mp_dma_cache_wback_inv, +}; + +static const struct of_device_id ax45mp_cache_ids[] = { + { .compatible = "andestech,ax45mp-cache" }, + { /* sentinel */ } +}; + +static int __init ax45mp_cache_init(void) +{ + struct device_node *np; + struct resource res; + int ret; + + np = of_find_matching_node(NULL, ax45mp_cache_ids); + if (!of_device_is_available(np)) + return -ENODEV; + + ret = of_address_to_resource(np, 0, &res); + if (ret) + return ret; + + /* + * If IOCP is present on the Andes AX45MP core riscv_cbom_block_size + * will be 0 for sure, so we can definitely rely on it. If + * riscv_cbom_block_size = 0 we don't need to handle CMO using SW any + * more so we just return success here and only if its being set we + * continue further in the probe path. + */ + if (!riscv_cbom_block_size) + return 0; + + ax45mp_priv.l2c_base = ioremap(res.start, resource_size(&res)); + if (!ax45mp_priv.l2c_base) + return -ENOMEM; + + ret = ax45mp_get_l2_line_size(np); + if (ret) { + iounmap(ax45mp_priv.l2c_base); + return ret; + } + + riscv_noncoherent_register_cache_ops(&ax45mp_cmo_ops); + + return 0; +} +early_initcall(ax45mp_cache_init); diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 514f9f287a781dd910de9da4034a6ea74529bc11..c6f181702b9a7bddebf90dbd56d6c7dd259a4df2 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -394,8 +394,6 @@ find_quicksilver(struct device *dev, void *data) static int __init parisc_agp_init(void) { - extern struct sba_device *sba_list; - int err = -1; struct parisc_device *sba = NULL, *lba = NULL; struct lba_device *lbadev = NULL; diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 23f6f2eda84c080308d3e2fa0422edec34b33839..42b1062e33cd5d81ec7cc3ca9f2baf694d8365da 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -33,7 +33,7 @@ const struct class tpm_class = { .shutdown_pre = tpm_class_shutdown, }; const struct class tpmrm_class = { - .name = "tmprm", + .name = "tpmrm", }; dev_t tpm_devt; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 9eb1a185901235f3c2fbddec4e9813d3bc254648..ea085b14ab7c9f80268ced28a2d23bc06bfe0fd1 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -463,28 +463,6 @@ static bool crb_req_canceled(struct tpm_chip *chip, u8 status) return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE; } -static int crb_check_flags(struct tpm_chip *chip) -{ - u32 val; - int ret; - - ret = crb_request_locality(chip, 0); - if (ret) - return ret; - - ret = tpm2_get_tpm_pt(chip, TPM2_PT_MANUFACTURER, &val, NULL); - if (ret) - goto release; - - if (val == 0x414D4400U /* AMD */) - chip->flags |= TPM_CHIP_FLAG_HWRNG_DISABLED; - -release: - crb_relinquish_locality(chip, 0); - - return ret; -} - static const struct tpm_class_ops tpm_crb = { .flags = TPM_OPS_AUTO_STARTUP, .status = crb_status, @@ -797,12 +775,13 @@ static int crb_acpi_add(struct acpi_device *device) FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", buf->header.length, ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON); - return -EINVAL; + rc = -EINVAL; + goto out; } crb_pluton = ACPI_ADD_PTR(struct tpm2_crb_pluton, buf, sizeof(*buf)); rc = crb_map_pluton(dev, priv, buf, crb_pluton); if (rc) - return rc; + goto out; } priv->sm = sm; @@ -826,9 +805,14 @@ static int crb_acpi_add(struct acpi_device *device) if (rc) goto out; - rc = crb_check_flags(chip); - if (rc) - goto out; +#ifdef CONFIG_X86 + /* A quirk for https://www.amd.com/en/support/kb/faq/pa-410 */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + dev_info(dev, "Disabling hwrng\n"); + chip->flags |= TPM_CHIP_FLAG_HWRNG_DISABLED; + } +#endif /* CONFIG_X86 */ rc = tpm_chip_register(chip); diff --git a/drivers/clk/clk-si521xx.c b/drivers/clk/clk-si521xx.c index 4eaf1b53f06bd78950f584d7a991e6253f671cc8..ef4ba467e747bc791fbf231ecff494b61be92b01 100644 --- a/drivers/clk/clk-si521xx.c +++ b/drivers/clk/clk-si521xx.c @@ -96,7 +96,7 @@ static int si521xx_regmap_i2c_write(void *context, unsigned int reg, unsigned int val) { struct i2c_client *i2c = context; - const u8 data[3] = { reg, 1, val }; + const u8 data[2] = { reg, val }; const int count = ARRAY_SIZE(data); int ret; @@ -146,7 +146,7 @@ static int si521xx_regmap_i2c_read(void *context, unsigned int reg, static const struct regmap_config si521xx_regmap_config = { .reg_bits = 8, .val_bits = 8, - .cache_type = REGCACHE_NONE, + .cache_type = REGCACHE_FLAT, .max_register = SI521XX_REG_DA, .rd_table = &si521xx_readable_table, .wr_table = &si521xx_writeable_table, @@ -281,9 +281,10 @@ static int si521xx_probe(struct i2c_client *client) { const u16 chip_info = (u16)(uintptr_t)device_get_match_data(&client->dev); const struct clk_parent_data clk_parent_data = { .index = 0 }; - struct si521xx *si; + const u8 data[3] = { SI521XX_REG_BC, 1, 1 }; unsigned char name[6] = "DIFF0"; struct clk_init_data init = {}; + struct si521xx *si; int i, ret; if (!chip_info) @@ -308,7 +309,7 @@ static int si521xx_probe(struct i2c_client *client) "Failed to allocate register map\n"); /* Always read back 1 Byte via I2C */ - ret = regmap_write(si->regmap, SI521XX_REG_BC, 1); + ret = i2c_master_send(client, data, ARRAY_SIZE(data)); if (ret < 0) return ret; diff --git a/drivers/clk/clk-versaclock3.c b/drivers/clk/clk-versaclock3.c index 7ab2447bd2031a7e7ede11fb4bd2b564e4c1ff1e..3d7de355f8f682266739a0b8837777ef0f02f571 100644 --- a/drivers/clk/clk-versaclock3.c +++ b/drivers/clk/clk-versaclock3.c @@ -118,21 +118,21 @@ enum vc3_div { VC3_DIV5, }; -enum vc3_clk_mux { - VC3_DIFF2_MUX, - VC3_DIFF1_MUX, - VC3_SE3_MUX, - VC3_SE2_MUX, - VC3_SE1_MUX, -}; - enum vc3_clk { - VC3_DIFF2, - VC3_DIFF1, - VC3_SE3, - VC3_SE2, - VC3_SE1, VC3_REF, + VC3_SE1, + VC3_SE2, + VC3_SE3, + VC3_DIFF1, + VC3_DIFF2, +}; + +enum vc3_clk_mux { + VC3_SE1_MUX = VC3_SE1 - 1, + VC3_SE2_MUX = VC3_SE2 - 1, + VC3_SE3_MUX = VC3_SE3 - 1, + VC3_DIFF1_MUX = VC3_DIFF1 - 1, + VC3_DIFF2_MUX = VC3_DIFF2 - 1, }; struct vc3_clk_data { @@ -401,11 +401,10 @@ static long vc3_pll_round_rate(struct clk_hw *hw, unsigned long rate, /* Determine best fractional part, which is 16 bit wide */ div_frc = rate % *parent_rate; div_frc *= BIT(16) - 1; - do_div(div_frc, *parent_rate); - vc3->div_frc = (u32)div_frc; + vc3->div_frc = min_t(u64, div64_ul(div_frc, *parent_rate), U16_MAX); rate = (*parent_rate * - (vc3->div_int * VC3_2_POW_16 + div_frc) / VC3_2_POW_16); + (vc3->div_int * VC3_2_POW_16 + vc3->div_frc) / VC3_2_POW_16); } else { rate = *parent_rate * vc3->div_int; } @@ -897,33 +896,33 @@ static struct vc3_hw_data clk_div[] = { }; static struct vc3_hw_data clk_mux[] = { - [VC3_DIFF2_MUX] = { + [VC3_SE1_MUX] = { .data = &(struct vc3_clk_data) { - .offs = VC3_DIFF2_CTRL_REG, - .bitmsk = VC3_DIFF2_CTRL_REG_DIFF2_CLK_SEL + .offs = VC3_SE1_DIV4_CTRL, + .bitmsk = VC3_SE1_DIV4_CTRL_SE1_CLK_SEL }, .hw.init = &(struct clk_init_data){ - .name = "diff2_mux", + .name = "se1_mux", .ops = &vc3_clk_mux_ops, .parent_hws = (const struct clk_hw *[]) { - &clk_div[VC3_DIV1].hw, - &clk_div[VC3_DIV3].hw + &clk_div[VC3_DIV5].hw, + &clk_div[VC3_DIV4].hw }, .num_parents = 2, .flags = CLK_SET_RATE_PARENT } }, - [VC3_DIFF1_MUX] = { + [VC3_SE2_MUX] = { .data = &(struct vc3_clk_data) { - .offs = VC3_DIFF1_CTRL_REG, - .bitmsk = VC3_DIFF1_CTRL_REG_DIFF1_CLK_SEL + .offs = VC3_SE2_CTRL_REG0, + .bitmsk = VC3_SE2_CTRL_REG0_SE2_CLK_SEL }, .hw.init = &(struct clk_init_data){ - .name = "diff1_mux", + .name = "se2_mux", .ops = &vc3_clk_mux_ops, .parent_hws = (const struct clk_hw *[]) { - &clk_div[VC3_DIV1].hw, - &clk_div[VC3_DIV3].hw + &clk_div[VC3_DIV5].hw, + &clk_div[VC3_DIV4].hw }, .num_parents = 2, .flags = CLK_SET_RATE_PARENT @@ -945,33 +944,33 @@ static struct vc3_hw_data clk_mux[] = { .flags = CLK_SET_RATE_PARENT } }, - [VC3_SE2_MUX] = { + [VC3_DIFF1_MUX] = { .data = &(struct vc3_clk_data) { - .offs = VC3_SE2_CTRL_REG0, - .bitmsk = VC3_SE2_CTRL_REG0_SE2_CLK_SEL + .offs = VC3_DIFF1_CTRL_REG, + .bitmsk = VC3_DIFF1_CTRL_REG_DIFF1_CLK_SEL }, .hw.init = &(struct clk_init_data){ - .name = "se2_mux", + .name = "diff1_mux", .ops = &vc3_clk_mux_ops, .parent_hws = (const struct clk_hw *[]) { - &clk_div[VC3_DIV5].hw, - &clk_div[VC3_DIV4].hw + &clk_div[VC3_DIV1].hw, + &clk_div[VC3_DIV3].hw }, .num_parents = 2, .flags = CLK_SET_RATE_PARENT } }, - [VC3_SE1_MUX] = { + [VC3_DIFF2_MUX] = { .data = &(struct vc3_clk_data) { - .offs = VC3_SE1_DIV4_CTRL, - .bitmsk = VC3_SE1_DIV4_CTRL_SE1_CLK_SEL + .offs = VC3_DIFF2_CTRL_REG, + .bitmsk = VC3_DIFF2_CTRL_REG_DIFF2_CLK_SEL }, .hw.init = &(struct clk_init_data){ - .name = "se1_mux", + .name = "diff2_mux", .ops = &vc3_clk_mux_ops, .parent_hws = (const struct clk_hw *[]) { - &clk_div[VC3_DIV5].hw, - &clk_div[VC3_DIV4].hw + &clk_div[VC3_DIV1].hw, + &clk_div[VC3_DIV3].hw }, .num_parents = 2, .flags = CLK_SET_RATE_PARENT @@ -1110,7 +1109,7 @@ static int vc3_probe(struct i2c_client *client) name, 0, CLK_SET_RATE_PARENT, 1, 1); else clk_out[i] = devm_clk_hw_register_fixed_factor_parent_hw(dev, - name, &clk_mux[i].hw, CLK_SET_RATE_PARENT, 1, 1); + name, &clk_mux[i - 1].hw, CLK_SET_RATE_PARENT, 1, 1); if (IS_ERR(clk_out[i])) return PTR_ERR(clk_out[i]); diff --git a/drivers/clk/sprd/ums512-clk.c b/drivers/clk/sprd/ums512-clk.c index 8f4441dd572b2156cf568e62e84bfe7b7002b8ee..9384ecc6c7413867c432623377091857946acad5 100644 --- a/drivers/clk/sprd/ums512-clk.c +++ b/drivers/clk/sprd/ums512-clk.c @@ -800,7 +800,7 @@ static SPRD_MUX_CLK_DATA(uart1_clk, "uart1-clk", uart_parents, 0x250, 0, 3, UMS512_MUX_FLAG); static const struct clk_parent_data thm_parents[] = { - { .fw_name = "ext-32m" }, + { .fw_name = "ext-32k" }, { .hw = &clk_250k.hw }, }; static SPRD_MUX_CLK_DATA(thm0_clk, "thm0-clk", thm_parents, diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index a9f3fb448de62070f8897421e2be174b25a4593a..7bfba0afd77831b112c9383eabbd7f1e55bceeae 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -159,7 +159,7 @@ static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw, err = tegra_bpmp_clk_transfer(clk->bpmp, &msg); if (err < 0) - return err; + return 0; return response.rate; } diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index c4d671a5a13d9209e3ceec11e9a4294e669cc7c4..0ba0dc4ecf06201a0532d3af71194841a5167c64 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -461,13 +461,6 @@ config VF_PIT_TIMER help Support for Periodic Interrupt Timer on Freescale Vybrid Family SoCs. -config OXNAS_RPS_TIMER - bool "Oxford Semiconductor OXNAS RPS Timers driver" if COMPILE_TEST - select TIMER_OF - select CLKSRC_MMIO - help - This enables support for the Oxford Semiconductor OXNAS RPS timers. - config SYS_SUPPORTS_SH_CMT bool diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 5d93c9e3fc558c86d868340ea9018edb561a5c8f..368c3461dab8146a6f89a111677c413b974e4608 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_MTK_TIMER) += timer-mediatek.o obj-$(CONFIG_MTK_CPUX_TIMER) += timer-mediatek-cpux.o obj-$(CONFIG_CLKSRC_PISTACHIO) += timer-pistachio.o obj-$(CONFIG_CLKSRC_TI_32K) += timer-ti-32k.o -obj-$(CONFIG_OXNAS_RPS_TIMER) += timer-oxnas-rps.o obj-$(CONFIG_OWL_TIMER) += timer-owl.o obj-$(CONFIG_MILBEAUT_TIMER) += timer-milbeaut.o obj-$(CONFIG_SPRD_TIMER) += timer-sprd.o diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index e733a2a1927a402f70e14585a2f98a76e6767cb7..7dd2c615bce231322ad25519e10d4a46de987f1e 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -792,6 +792,13 @@ static __always_inline void set_next_event_mem(const int access, unsigned long e u64 cnt; ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk); + + /* Timer must be disabled before programming CVAL */ + if (ctrl & ARCH_TIMER_CTRL_ENABLE) { + ctrl &= ~ARCH_TIMER_CTRL_ENABLE; + arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk); + } + ctrl |= ARCH_TIMER_CTRL_ENABLE; ctrl &= ~ARCH_TIMER_CTRL_IT_MASK; diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index e56307a81f4dab0b955154d41eaf336a156a6918..8ff7cd4e20bb1132af3bdc0c0ef79f038294f941 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -390,7 +390,7 @@ static __always_inline u64 read_hv_clock_msr(void) static union { struct ms_hyperv_tsc_page page; u8 reserved[PAGE_SIZE]; -} tsc_pg __aligned(PAGE_SIZE); +} tsc_pg __bss_decrypted __aligned(PAGE_SIZE); static struct ms_hyperv_tsc_page *tsc_page = &tsc_pg.page; static unsigned long tsc_pfn; diff --git a/drivers/clocksource/timer-loongson1-pwm.c b/drivers/clocksource/timer-loongson1-pwm.c index 6335fee03017b507b65482d0a310ff52e29c258a..244d6683550847cb1d12c8cf7e49dc17c07fa11d 100644 --- a/drivers/clocksource/timer-loongson1-pwm.c +++ b/drivers/clocksource/timer-loongson1-pwm.c @@ -28,7 +28,7 @@ #define CNTR_WIDTH 24 -DEFINE_RAW_SPINLOCK(ls1x_timer_lock); +static DEFINE_RAW_SPINLOCK(ls1x_timer_lock); struct ls1x_clocksource { void __iomem *reg_base; diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c deleted file mode 100644 index d514b44e67dd1ee8977f21f15fa51743fb5664dd..0000000000000000000000000000000000000000 --- a/drivers/clocksource/timer-oxnas-rps.c +++ /dev/null @@ -1,288 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * drivers/clocksource/timer-oxnas-rps.c - * - * Copyright (C) 2009 Oxford Semiconductor Ltd - * Copyright (C) 2013 Ma Haijun - * Copyright (C) 2016 Neil Armstrong - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* TIMER1 used as tick - * TIMER2 used as clocksource - */ - -/* Registers definitions */ - -#define TIMER_LOAD_REG 0x0 -#define TIMER_CURR_REG 0x4 -#define TIMER_CTRL_REG 0x8 -#define TIMER_CLRINT_REG 0xC - -#define TIMER_BITS 24 - -#define TIMER_MAX_VAL (BIT(TIMER_BITS) - 1) - -#define TIMER_PERIODIC BIT(6) -#define TIMER_ENABLE BIT(7) - -#define TIMER_DIV1 (0) -#define TIMER_DIV16 (1 << 2) -#define TIMER_DIV256 (2 << 2) - -#define TIMER1_REG_OFFSET 0 -#define TIMER2_REG_OFFSET 0x20 - -/* Clockevent & Clocksource data */ - -struct oxnas_rps_timer { - struct clock_event_device clkevent; - void __iomem *clksrc_base; - void __iomem *clkevt_base; - unsigned long timer_period; - unsigned int timer_prescaler; - struct clk *clk; - int irq; -}; - -static irqreturn_t oxnas_rps_timer_irq(int irq, void *dev_id) -{ - struct oxnas_rps_timer *rps = dev_id; - - writel_relaxed(0, rps->clkevt_base + TIMER_CLRINT_REG); - - rps->clkevent.event_handler(&rps->clkevent); - - return IRQ_HANDLED; -} - -static void oxnas_rps_timer_config(struct oxnas_rps_timer *rps, - unsigned long period, - unsigned int periodic) -{ - uint32_t cfg = rps->timer_prescaler; - - if (period) - cfg |= TIMER_ENABLE; - - if (periodic) - cfg |= TIMER_PERIODIC; - - writel_relaxed(period, rps->clkevt_base + TIMER_LOAD_REG); - writel_relaxed(cfg, rps->clkevt_base + TIMER_CTRL_REG); -} - -static int oxnas_rps_timer_shutdown(struct clock_event_device *evt) -{ - struct oxnas_rps_timer *rps = - container_of(evt, struct oxnas_rps_timer, clkevent); - - oxnas_rps_timer_config(rps, 0, 0); - - return 0; -} - -static int oxnas_rps_timer_set_periodic(struct clock_event_device *evt) -{ - struct oxnas_rps_timer *rps = - container_of(evt, struct oxnas_rps_timer, clkevent); - - oxnas_rps_timer_config(rps, rps->timer_period, 1); - - return 0; -} - -static int oxnas_rps_timer_set_oneshot(struct clock_event_device *evt) -{ - struct oxnas_rps_timer *rps = - container_of(evt, struct oxnas_rps_timer, clkevent); - - oxnas_rps_timer_config(rps, rps->timer_period, 0); - - return 0; -} - -static int oxnas_rps_timer_next_event(unsigned long delta, - struct clock_event_device *evt) -{ - struct oxnas_rps_timer *rps = - container_of(evt, struct oxnas_rps_timer, clkevent); - - oxnas_rps_timer_config(rps, delta, 0); - - return 0; -} - -static int __init oxnas_rps_clockevent_init(struct oxnas_rps_timer *rps) -{ - ulong clk_rate = clk_get_rate(rps->clk); - ulong timer_rate; - - /* Start with prescaler 1 */ - rps->timer_prescaler = TIMER_DIV1; - rps->timer_period = DIV_ROUND_UP(clk_rate, HZ); - timer_rate = clk_rate; - - if (rps->timer_period > TIMER_MAX_VAL) { - rps->timer_prescaler = TIMER_DIV16; - timer_rate = clk_rate / 16; - rps->timer_period = DIV_ROUND_UP(timer_rate, HZ); - } - if (rps->timer_period > TIMER_MAX_VAL) { - rps->timer_prescaler = TIMER_DIV256; - timer_rate = clk_rate / 256; - rps->timer_period = DIV_ROUND_UP(timer_rate, HZ); - } - - rps->clkevent.name = "oxnas-rps"; - rps->clkevent.features = CLOCK_EVT_FEAT_PERIODIC | - CLOCK_EVT_FEAT_ONESHOT | - CLOCK_EVT_FEAT_DYNIRQ; - rps->clkevent.tick_resume = oxnas_rps_timer_shutdown; - rps->clkevent.set_state_shutdown = oxnas_rps_timer_shutdown; - rps->clkevent.set_state_periodic = oxnas_rps_timer_set_periodic; - rps->clkevent.set_state_oneshot = oxnas_rps_timer_set_oneshot; - rps->clkevent.set_next_event = oxnas_rps_timer_next_event; - rps->clkevent.rating = 200; - rps->clkevent.cpumask = cpu_possible_mask; - rps->clkevent.irq = rps->irq; - clockevents_config_and_register(&rps->clkevent, - timer_rate, - 1, - TIMER_MAX_VAL); - - pr_info("Registered clock event rate %luHz prescaler %x period %lu\n", - clk_rate, - rps->timer_prescaler, - rps->timer_period); - - return 0; -} - -/* Clocksource */ - -static void __iomem *timer_sched_base; - -static u64 notrace oxnas_rps_read_sched_clock(void) -{ - return ~readl_relaxed(timer_sched_base); -} - -static int __init oxnas_rps_clocksource_init(struct oxnas_rps_timer *rps) -{ - ulong clk_rate = clk_get_rate(rps->clk); - int ret; - - /* use prescale 16 */ - clk_rate = clk_rate / 16; - - writel_relaxed(TIMER_MAX_VAL, rps->clksrc_base + TIMER_LOAD_REG); - writel_relaxed(TIMER_PERIODIC | TIMER_ENABLE | TIMER_DIV16, - rps->clksrc_base + TIMER_CTRL_REG); - - timer_sched_base = rps->clksrc_base + TIMER_CURR_REG; - sched_clock_register(oxnas_rps_read_sched_clock, - TIMER_BITS, clk_rate); - ret = clocksource_mmio_init(timer_sched_base, - "oxnas_rps_clocksource_timer", - clk_rate, 250, TIMER_BITS, - clocksource_mmio_readl_down); - if (WARN_ON(ret)) { - pr_err("can't register clocksource\n"); - return ret; - } - - pr_info("Registered clocksource rate %luHz\n", clk_rate); - - return 0; -} - -static int __init oxnas_rps_timer_init(struct device_node *np) -{ - struct oxnas_rps_timer *rps; - void __iomem *base; - int ret; - - rps = kzalloc(sizeof(*rps), GFP_KERNEL); - if (!rps) - return -ENOMEM; - - rps->clk = of_clk_get(np, 0); - if (IS_ERR(rps->clk)) { - ret = PTR_ERR(rps->clk); - goto err_alloc; - } - - ret = clk_prepare_enable(rps->clk); - if (ret) - goto err_clk; - - base = of_iomap(np, 0); - if (!base) { - ret = -ENXIO; - goto err_clk_prepare; - } - - rps->irq = irq_of_parse_and_map(np, 0); - if (!rps->irq) { - ret = -EINVAL; - goto err_iomap; - } - - rps->clkevt_base = base + TIMER1_REG_OFFSET; - rps->clksrc_base = base + TIMER2_REG_OFFSET; - - /* Disable timers */ - writel_relaxed(0, rps->clkevt_base + TIMER_CTRL_REG); - writel_relaxed(0, rps->clksrc_base + TIMER_CTRL_REG); - writel_relaxed(0, rps->clkevt_base + TIMER_LOAD_REG); - writel_relaxed(0, rps->clksrc_base + TIMER_LOAD_REG); - writel_relaxed(0, rps->clkevt_base + TIMER_CLRINT_REG); - writel_relaxed(0, rps->clksrc_base + TIMER_CLRINT_REG); - - ret = request_irq(rps->irq, oxnas_rps_timer_irq, - IRQF_TIMER | IRQF_IRQPOLL, - "rps-timer", rps); - if (ret) - goto err_iomap; - - ret = oxnas_rps_clocksource_init(rps); - if (ret) - goto err_irqreq; - - ret = oxnas_rps_clockevent_init(rps); - if (ret) - goto err_irqreq; - - return 0; - -err_irqreq: - free_irq(rps->irq, rps); -err_iomap: - iounmap(base); -err_clk_prepare: - clk_disable_unprepare(rps->clk); -err_clk: - clk_put(rps->clk); -err_alloc: - kfree(rps); - - return ret; -} - -TIMER_OF_DECLARE(ox810se_rps, - "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init); -TIMER_OF_DECLARE(ox820_rps, - "oxsemi,ox820-rps-timer", oxnas_rps_timer_init); diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c index 7d5fa90699061e093ff6795293e8f0993234e94f..69fee3540d375fe730f356e2ec5ae0c137d0d7e4 100644 --- a/drivers/clocksource/timer-sun5i.c +++ b/drivers/clocksource/timer-sun5i.c @@ -16,9 +16,7 @@ #include #include #include -#include -#include -#include +#include #define TIMER_IRQ_EN_REG 0x00 #define TIMER_IRQ_EN(val) BIT(val) @@ -40,26 +38,16 @@ struct sun5i_timer { struct clk *clk; struct notifier_block clk_rate_cb; u32 ticks_per_jiffy; -}; - -#define to_sun5i_timer(x) \ - container_of(x, struct sun5i_timer, clk_rate_cb) - -struct sun5i_timer_clksrc { - struct sun5i_timer timer; struct clocksource clksrc; -}; - -#define to_sun5i_timer_clksrc(x) \ - container_of(x, struct sun5i_timer_clksrc, clksrc) - -struct sun5i_timer_clkevt { - struct sun5i_timer timer; struct clock_event_device clkevt; }; -#define to_sun5i_timer_clkevt(x) \ - container_of(x, struct sun5i_timer_clkevt, clkevt) +#define nb_to_sun5i_timer(x) \ + container_of(x, struct sun5i_timer, clk_rate_cb) +#define clksrc_to_sun5i_timer(x) \ + container_of(x, struct sun5i_timer, clksrc) +#define clkevt_to_sun5i_timer(x) \ + container_of(x, struct sun5i_timer, clkevt) /* * When we disable a timer, we need to wait at least for 2 cycles of @@ -67,30 +55,30 @@ struct sun5i_timer_clkevt { * that is already setup and runs at the same frequency than the other * timers, and we never will be disabled. */ -static void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce) +static void sun5i_clkevt_sync(struct sun5i_timer *ce) { - u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1)); + u32 old = readl(ce->base + TIMER_CNTVAL_LO_REG(1)); - while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) + while ((old - readl(ce->base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS) cpu_relax(); } -static void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer) +static void sun5i_clkevt_time_stop(struct sun5i_timer *ce, u8 timer) { - u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer)); - writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer)); + u32 val = readl(ce->base + TIMER_CTL_REG(timer)); + writel(val & ~TIMER_CTL_ENABLE, ce->base + TIMER_CTL_REG(timer)); sun5i_clkevt_sync(ce); } -static void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay) +static void sun5i_clkevt_time_setup(struct sun5i_timer *ce, u8 timer, u32 delay) { - writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer)); + writel(delay, ce->base + TIMER_INTVAL_LO_REG(timer)); } -static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic) +static void sun5i_clkevt_time_start(struct sun5i_timer *ce, u8 timer, bool periodic) { - u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer)); + u32 val = readl(ce->base + TIMER_CTL_REG(timer)); if (periodic) val &= ~TIMER_CTL_ONESHOT; @@ -98,12 +86,12 @@ static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, boo val |= TIMER_CTL_ONESHOT; writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, - ce->timer.base + TIMER_CTL_REG(timer)); + ce->base + TIMER_CTL_REG(timer)); } static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) { - struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); + struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); sun5i_clkevt_time_stop(ce, 0); return 0; @@ -111,7 +99,7 @@ static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt) static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt) { - struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); + struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); sun5i_clkevt_time_stop(ce, 0); sun5i_clkevt_time_start(ce, 0, false); @@ -120,10 +108,10 @@ static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt) static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt) { - struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); + struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); sun5i_clkevt_time_stop(ce, 0); - sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy); + sun5i_clkevt_time_setup(ce, 0, ce->ticks_per_jiffy); sun5i_clkevt_time_start(ce, 0, true); return 0; } @@ -131,7 +119,7 @@ static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt) static int sun5i_clkevt_next_event(unsigned long evt, struct clock_event_device *clkevt) { - struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt); + struct sun5i_timer *ce = clkevt_to_sun5i_timer(clkevt); sun5i_clkevt_time_stop(ce, 0); sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS); @@ -142,9 +130,9 @@ static int sun5i_clkevt_next_event(unsigned long evt, static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) { - struct sun5i_timer_clkevt *ce = dev_id; + struct sun5i_timer *ce = dev_id; - writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG); + writel(0x1, ce->base + TIMER_IRQ_ST_REG); ce->clkevt.event_handler(&ce->clkevt); return IRQ_HANDLED; @@ -152,17 +140,16 @@ static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id) static u64 sun5i_clksrc_read(struct clocksource *clksrc) { - struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc); + struct sun5i_timer *cs = clksrc_to_sun5i_timer(clksrc); - return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1)); + return ~readl(cs->base + TIMER_CNTVAL_LO_REG(1)); } -static int sun5i_rate_cb_clksrc(struct notifier_block *nb, - unsigned long event, void *data) +static int sun5i_rate_cb(struct notifier_block *nb, + unsigned long event, void *data) { struct clk_notifier_data *ndata = data; - struct sun5i_timer *timer = to_sun5i_timer(nb); - struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer); + struct sun5i_timer *cs = nb_to_sun5i_timer(nb); switch (event) { case PRE_RATE_CHANGE: @@ -171,6 +158,8 @@ static int sun5i_rate_cb_clksrc(struct notifier_block *nb, case POST_RATE_CHANGE: clocksource_register_hz(&cs->clksrc, ndata->new_rate); + clockevents_update_freq(&cs->clkevt, ndata->new_rate); + cs->ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); break; default: @@ -180,47 +169,18 @@ static int sun5i_rate_cb_clksrc(struct notifier_block *nb, return NOTIFY_DONE; } -static int __init sun5i_setup_clocksource(struct device_node *node, - void __iomem *base, - struct clk *clk, int irq) +static int sun5i_setup_clocksource(struct platform_device *pdev, + unsigned long rate) { - struct sun5i_timer_clksrc *cs; - unsigned long rate; + struct sun5i_timer *cs = platform_get_drvdata(pdev); + void __iomem *base = cs->base; int ret; - cs = kzalloc(sizeof(*cs), GFP_KERNEL); - if (!cs) - return -ENOMEM; - - ret = clk_prepare_enable(clk); - if (ret) { - pr_err("Couldn't enable parent clock\n"); - goto err_free; - } - - rate = clk_get_rate(clk); - if (!rate) { - pr_err("Couldn't get parent clock rate\n"); - ret = -EINVAL; - goto err_disable_clk; - } - - cs->timer.base = base; - cs->timer.clk = clk; - cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc; - cs->timer.clk_rate_cb.next = NULL; - - ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb); - if (ret) { - pr_err("Unable to register clock notifier.\n"); - goto err_disable_clk; - } - writel(~0, base + TIMER_INTVAL_LO_REG(1)); writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD, base + TIMER_CTL_REG(1)); - cs->clksrc.name = node->name; + cs->clksrc.name = pdev->dev.of_node->name; cs->clksrc.rating = 340; cs->clksrc.read = sun5i_clksrc_read; cs->clksrc.mask = CLOCKSOURCE_MASK(32); @@ -228,74 +188,23 @@ static int __init sun5i_setup_clocksource(struct device_node *node, ret = clocksource_register_hz(&cs->clksrc, rate); if (ret) { - pr_err("Couldn't register clock source.\n"); - goto err_remove_notifier; + dev_err(&pdev->dev, "Couldn't register clock source.\n"); + return ret; } return 0; - -err_remove_notifier: - clk_notifier_unregister(clk, &cs->timer.clk_rate_cb); -err_disable_clk: - clk_disable_unprepare(clk); -err_free: - kfree(cs); - return ret; } -static int sun5i_rate_cb_clkevt(struct notifier_block *nb, - unsigned long event, void *data) +static int sun5i_setup_clockevent(struct platform_device *pdev, + unsigned long rate, int irq) { - struct clk_notifier_data *ndata = data; - struct sun5i_timer *timer = to_sun5i_timer(nb); - struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer); - - if (event == POST_RATE_CHANGE) { - clockevents_update_freq(&ce->clkevt, ndata->new_rate); - ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ); - } - - return NOTIFY_DONE; -} - -static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base, - struct clk *clk, int irq) -{ - struct sun5i_timer_clkevt *ce; - unsigned long rate; + struct device *dev = &pdev->dev; + struct sun5i_timer *ce = platform_get_drvdata(pdev); + void __iomem *base = ce->base; int ret; u32 val; - ce = kzalloc(sizeof(*ce), GFP_KERNEL); - if (!ce) - return -ENOMEM; - - ret = clk_prepare_enable(clk); - if (ret) { - pr_err("Couldn't enable parent clock\n"); - goto err_free; - } - - rate = clk_get_rate(clk); - if (!rate) { - pr_err("Couldn't get parent clock rate\n"); - ret = -EINVAL; - goto err_disable_clk; - } - - ce->timer.base = base; - ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); - ce->timer.clk = clk; - ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt; - ce->timer.clk_rate_cb.next = NULL; - - ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb); - if (ret) { - pr_err("Unable to register clock notifier.\n"); - goto err_disable_clk; - } - - ce->clkevt.name = node->name; + ce->clkevt.name = dev->of_node->name; ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; ce->clkevt.set_next_event = sun5i_clkevt_next_event; ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown; @@ -313,60 +222,109 @@ static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem clockevents_config_and_register(&ce->clkevt, rate, TIMER_SYNC_TICKS, 0xffffffff); - ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, - "sun5i_timer0", ce); + ret = devm_request_irq(dev, irq, sun5i_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + "sun5i_timer0", ce); if (ret) { - pr_err("Unable to register interrupt\n"); - goto err_remove_notifier; + dev_err(dev, "Unable to register interrupt\n"); + return ret; } return 0; - -err_remove_notifier: - clk_notifier_unregister(clk, &ce->timer.clk_rate_cb); -err_disable_clk: - clk_disable_unprepare(clk); -err_free: - kfree(ce); - return ret; } -static int __init sun5i_timer_init(struct device_node *node) +static int sun5i_timer_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct sun5i_timer *st; struct reset_control *rstc; void __iomem *timer_base; struct clk *clk; + unsigned long rate; int irq, ret; - timer_base = of_io_request_and_map(node, 0, of_node_full_name(node)); + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + platform_set_drvdata(pdev, st); + + timer_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(timer_base)) { - pr_err("Can't map registers\n"); + dev_err(dev, "Can't map registers\n"); return PTR_ERR(timer_base); } - irq = irq_of_parse_and_map(node, 0); - if (irq <= 0) { - pr_err("Can't parse IRQ\n"); - return -EINVAL; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Can't get IRQ\n"); + return irq; } - clk = of_clk_get(node, 0); + clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(clk)) { - pr_err("Can't get timer clock\n"); + dev_err(dev, "Can't get timer clock\n"); return PTR_ERR(clk); } - rstc = of_reset_control_get(node, NULL); - if (!IS_ERR(rstc)) + rate = clk_get_rate(clk); + if (!rate) { + dev_err(dev, "Couldn't get parent clock rate\n"); + return -EINVAL; + } + + st->base = timer_base; + st->ticks_per_jiffy = DIV_ROUND_UP(rate, HZ); + st->clk = clk; + st->clk_rate_cb.notifier_call = sun5i_rate_cb; + st->clk_rate_cb.next = NULL; + + ret = devm_clk_notifier_register(dev, clk, &st->clk_rate_cb); + if (ret) { + dev_err(dev, "Unable to register clock notifier.\n"); + return ret; + } + + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (rstc) reset_control_deassert(rstc); - ret = sun5i_setup_clocksource(node, timer_base, clk, irq); + ret = sun5i_setup_clocksource(pdev, rate); if (ret) return ret; - return sun5i_setup_clockevent(node, timer_base, clk, irq); + ret = sun5i_setup_clockevent(pdev, rate, irq); + if (ret) + goto err_unreg_clocksource; + + return 0; + +err_unreg_clocksource: + clocksource_unregister(&st->clksrc); + return ret; } -TIMER_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer", - sun5i_timer_init); -TIMER_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer", - sun5i_timer_init); + +static void sun5i_timer_remove(struct platform_device *pdev) +{ + struct sun5i_timer *st = platform_get_drvdata(pdev); + + clocksource_unregister(&st->clksrc); +} + +static const struct of_device_id sun5i_timer_of_match[] = { + { .compatible = "allwinner,sun5i-a13-hstimer" }, + { .compatible = "allwinner,sun7i-a20-hstimer" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sun5i_timer_of_match); + +static struct platform_driver sun5i_timer_driver = { + .probe = sun5i_timer_probe, + .remove_new = sun5i_timer_remove, + .driver = { + .name = "sun5i-timer", + .of_match_table = sun5i_timer_of_match, + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sun5i_timer_driver); diff --git a/drivers/comedi/Kconfig b/drivers/comedi/Kconfig index 7a8d402f05be1668b1d084ad08f4834c8560cd5b..9af280735cbaa1db0d59dc154bcb3be7c1cc7df1 100644 --- a/drivers/comedi/Kconfig +++ b/drivers/comedi/Kconfig @@ -67,7 +67,6 @@ config COMEDI_TEST config COMEDI_PARPORT tristate "Parallel port support" - depends on HAS_IOPORT help Enable support for the standard parallel port. A cheap and easy way to get a few more digital I/O lines. Steal @@ -80,7 +79,6 @@ config COMEDI_PARPORT config COMEDI_SSV_DNP tristate "SSV Embedded Systems DIL/Net-PC support" depends on X86_32 || COMPILE_TEST - depends on HAS_IOPORT help Enable support for SSV Embedded Systems DIL/Net-PC @@ -91,7 +89,6 @@ endif # COMEDI_MISC_DRIVERS menuconfig COMEDI_ISA_DRIVERS bool "Comedi ISA and PC/104 drivers" - depends on ISA help Enable comedi ISA and PC/104 drivers to be built @@ -103,8 +100,7 @@ if COMEDI_ISA_DRIVERS config COMEDI_PCL711 tristate "Advantech PCL-711/711b and ADlink ACL-8112 ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-711 and 711b, ADlink ACL-8112 @@ -165,9 +161,8 @@ config COMEDI_PCL730 config COMEDI_PCL812 tristate "Advantech PCL-812/813 and ADlink ACL-8112/8113/8113/8216" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-812/PG, PCL-813/B, ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216, ICP DAS A-821PGH/PGL/PGL-NDA, @@ -178,9 +173,8 @@ config COMEDI_PCL812 config COMEDI_PCL816 tristate "Advantech PCL-814 and PCL-816 ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-814 and PCL-816 ISA cards @@ -189,9 +183,8 @@ config COMEDI_PCL816 config COMEDI_PCL818 tristate "Advantech PCL-718 and PCL-818 ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-818 ISA cards PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818 and PCL-718 @@ -210,7 +203,7 @@ config COMEDI_PCM3724 config COMEDI_AMPLC_DIO200_ISA tristate "Amplicon PC212E/PC214E/PC215E/PC218E/PC272E" - depends on COMEDI_AMPLC_DIO200 + select COMEDI_AMPLC_DIO200 help Enable support for Amplicon PC212E, PC214E, PC215E, PC218E and PC272E ISA DIO boards @@ -262,8 +255,7 @@ config COMEDI_DAC02 config COMEDI_DAS16M1 tristate "MeasurementComputing CIO-DAS16/M1DAS-16 ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Measurement Computing CIO-DAS16/M1 ISA cards. @@ -273,7 +265,7 @@ config COMEDI_DAS16M1 config COMEDI_DAS08_ISA tristate "DAS-08 compatible ISA and PC/104 card support" - depends on COMEDI_DAS08 + select COMEDI_DAS08 help Enable support for Keithley Metrabyte/ComputerBoards DAS08 and compatible ISA and PC/104 cards: @@ -286,9 +278,8 @@ config COMEDI_DAS08_ISA config COMEDI_DAS16 tristate "DAS-16 compatible ISA and PC/104 card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Keithley Metrabyte/ComputerBoards DAS16 @@ -305,8 +296,7 @@ config COMEDI_DAS16 config COMEDI_DAS800 tristate "DAS800 and compatible ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Keithley Metrabyte DAS800 and compatible ISA cards Keithley Metrabyte DAS-800, DAS-801, DAS-802 @@ -318,9 +308,8 @@ config COMEDI_DAS800 config COMEDI_DAS1800 tristate "DAS1800 and compatible ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for DAS1800 and compatible ISA cards Keithley Metrabyte DAS-1701ST, DAS-1701ST-DA, DAS-1701/AO, @@ -334,8 +323,7 @@ config COMEDI_DAS1800 config COMEDI_DAS6402 tristate "DAS6402 and compatible ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for DAS6402 and compatible ISA cards Computerboards, Keithley Metrabyte DAS6402 and compatibles @@ -414,8 +402,7 @@ config COMEDI_FL512 config COMEDI_AIO_AIO12_8 tristate "I/O Products PC/104 AIO12-8 Analog I/O Board support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for I/O Products PC/104 AIO12-8 Analog I/O Board @@ -469,9 +456,8 @@ config COMEDI_ADQ12B config COMEDI_NI_AT_A2150 tristate "NI AT-A2150 ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for National Instruments AT-A2150 cards @@ -480,8 +466,7 @@ config COMEDI_NI_AT_A2150 config COMEDI_NI_AT_AO tristate "NI AT-AO-6/10 EISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for National Instruments AT-AO-6/10 cards @@ -512,7 +497,7 @@ config COMEDI_NI_ATMIO16D config COMEDI_NI_LABPC_ISA tristate "NI Lab-PC and compatibles ISA support" - depends on COMEDI_NI_LABPC + select COMEDI_NI_LABPC help Enable support for National Instruments Lab-PC and compatibles Lab-PC-1200, Lab-PC-1200AI, Lab-PC+. @@ -576,7 +561,7 @@ endif # COMEDI_ISA_DRIVERS menuconfig COMEDI_PCI_DRIVERS tristate "Comedi PCI drivers" - depends on PCI && HAS_IOPORT + depends on PCI help Enable support for comedi PCI drivers. @@ -725,8 +710,7 @@ config COMEDI_ADL_PCI8164 config COMEDI_ADL_PCI9111 tristate "ADLink PCI-9111HR support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for ADlink PCI9111 cards @@ -736,7 +720,7 @@ config COMEDI_ADL_PCI9111 config COMEDI_ADL_PCI9118 tristate "ADLink PCI-9118DG, PCI-9118HG, PCI-9118HR support" depends on HAS_DMA - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for ADlink PCI-9118DG, PCI-9118HG, PCI-9118HR cards @@ -745,8 +729,7 @@ config COMEDI_ADL_PCI9118 config COMEDI_ADV_PCI1710 tristate "Advantech PCI-171x and PCI-1731 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713 and PCI-1731 @@ -790,8 +773,7 @@ config COMEDI_ADV_PCI1760 config COMEDI_ADV_PCI_DIO tristate "Advantech PCI DIO card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Advantech PCI DIO cards @@ -804,7 +786,7 @@ config COMEDI_ADV_PCI_DIO config COMEDI_AMPLC_DIO200_PCI tristate "Amplicon PCI215/PCI272/PCIe215/PCIe236/PCIe296 DIO support" - depends on COMEDI_AMPLC_DIO200 + select COMEDI_AMPLC_DIO200 help Enable support for Amplicon PCI215, PCI272, PCIe215, PCIe236 and PCIe296 DIO boards. @@ -832,8 +814,7 @@ config COMEDI_AMPLC_PC263_PCI config COMEDI_AMPLC_PCI224 tristate "Amplicon PCI224 and PCI234 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Amplicon PCI224 and PCI234 AO boards @@ -842,8 +823,7 @@ config COMEDI_AMPLC_PCI224 config COMEDI_AMPLC_PCI230 tristate "Amplicon PCI230 and PCI260 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Amplicon PCI230 and PCI260 Multifunction I/O @@ -862,7 +842,7 @@ config COMEDI_CONTEC_PCI_DIO config COMEDI_DAS08_PCI tristate "DAS-08 PCI support" - depends on COMEDI_DAS08 + select COMEDI_DAS08 help Enable support for PCI DAS-08 cards. @@ -949,8 +929,7 @@ config COMEDI_CB_PCIDAS64 config COMEDI_CB_PCIDAS tristate "MeasurementComputing PCI-DAS support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for ComputerBoards/MeasurementComputing PCI-DAS with @@ -974,8 +953,7 @@ config COMEDI_CB_PCIDDA config COMEDI_CB_PCIMDAS tristate "MeasurementComputing PCIM-DAS1602/16, PCIe-DAS1602/16 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for ComputerBoards/MeasurementComputing PCI Migration @@ -995,8 +973,7 @@ config COMEDI_CB_PCIMDDA config COMEDI_ME4000 tristate "Meilhaus ME-4000 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Meilhaus PCI data acquisition cards ME-4650, ME-4670i, ME-4680, ME-4680i and ME-4680is @@ -1054,7 +1031,7 @@ config COMEDI_NI_670X config COMEDI_NI_LABPC_PCI tristate "NI Lab-PC PCI-1200 support" - depends on COMEDI_NI_LABPC + select COMEDI_NI_LABPC help Enable support for National Instruments Lab-PC PCI-1200. @@ -1076,7 +1053,6 @@ config COMEDI_NI_PCIDIO config COMEDI_NI_PCIMIO tristate "NI PCI-MIO-E series and M series support" depends on HAS_DMA - depends on HAS_IOPORT select COMEDI_NI_TIOCMD select COMEDI_8255 help @@ -1098,8 +1074,7 @@ config COMEDI_NI_PCIMIO config COMEDI_RTD520 tristate "Real Time Devices PCI4520/DM7520 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Real Time Devices PCI4520/DM7520 @@ -1139,8 +1114,7 @@ if COMEDI_PCMCIA_DRIVERS config COMEDI_CB_DAS16_CS tristate "CB DAS16 series PCMCIA support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for the ComputerBoards/MeasurementComputing PCMCIA cards DAS16/16, PCM-DAS16D/12 and PCM-DAS16s/16 @@ -1150,7 +1124,7 @@ config COMEDI_CB_DAS16_CS config COMEDI_DAS08_CS tristate "CB DAS08 PCMCIA support" - depends on COMEDI_DAS08 + select COMEDI_DAS08 help Enable support for the ComputerBoards/MeasurementComputing DAS-08 PCMCIA card @@ -1160,7 +1134,6 @@ config COMEDI_DAS08_CS config COMEDI_NI_DAQ_700_CS tristate "NI DAQCard-700 PCMCIA support" - depends on HAS_IOPORT help Enable support for the National Instruments PCMCIA DAQCard-700 DIO @@ -1169,7 +1142,6 @@ config COMEDI_NI_DAQ_700_CS config COMEDI_NI_DAQ_DIO24_CS tristate "NI DAQ-Card DIO-24 PCMCIA support" - depends on HAS_IOPORT select COMEDI_8255 help Enable support for the National Instruments PCMCIA DAQ-Card DIO-24 @@ -1179,7 +1151,7 @@ config COMEDI_NI_DAQ_DIO24_CS config COMEDI_NI_LABPC_CS tristate "NI DAQCard-1200 PCMCIA support" - depends on COMEDI_NI_LABPC + select COMEDI_NI_LABPC help Enable support for the National Instruments PCMCIA DAQCard-1200 @@ -1188,7 +1160,6 @@ config COMEDI_NI_LABPC_CS config COMEDI_NI_MIO_CS tristate "NI DAQCard E series PCMCIA support" - depends on HAS_IOPORT select COMEDI_NI_TIO select COMEDI_8255 help @@ -1201,7 +1172,6 @@ config COMEDI_NI_MIO_CS config COMEDI_QUATECH_DAQP_CS tristate "Quatech DAQP PCMCIA data capture card support" - depends on HAS_IOPORT help Enable support for the Quatech DAQP PCMCIA data capture cards DAQP-208 and DAQP-308 @@ -1278,14 +1248,12 @@ endif # COMEDI_USB_DRIVERS config COMEDI_8254 tristate - depends on HAS_IOPORT config COMEDI_8255 tristate config COMEDI_8255_SA tristate "Standalone 8255 support" - depends on HAS_IOPORT select COMEDI_8255 help Enable support for 8255 digital I/O as a standalone driver. @@ -1317,7 +1285,7 @@ config COMEDI_KCOMEDILIB called kcomedilib. config COMEDI_AMPLC_DIO200 - depends on COMEDI_8254 + select COMEDI_8254 tristate config COMEDI_AMPLC_PC236 @@ -1326,7 +1294,7 @@ config COMEDI_AMPLC_PC236 config COMEDI_DAS08 tristate - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 config COMEDI_ISADMA @@ -1334,8 +1302,7 @@ config COMEDI_ISADMA config COMEDI_NI_LABPC tristate - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 config COMEDI_NI_LABPC_ISADMA diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 62962ae84b77da1f3fd4f8b3f8bdf65bf328bc60..497bc05dca4df07d608e61c68507b9fd57f75e59 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -92,7 +92,7 @@ config MICROCHIP_TCB_CAPTURE config RZ_MTU3_CNT tristate "Renesas RZ/G2L MTU3a counter driver" - depends on RZ_MTU3 || COMPILE_TEST + depends on RZ_MTU3 help Enable support for MTU3a counter driver found on Renesas RZ/G2L alike SoCs. This IP supports both 16-bit and 32-bit phase counting mode diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a757f90aa9d62848f49ce9f60b8b3c1125f888dd..60ed89000e82dc29c08abc63a0bba90be6e6ad79 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -86,6 +86,7 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy); static int cpufreq_set_policy(struct cpufreq_policy *policy, struct cpufreq_governor *new_gov, unsigned int new_pol); +static bool cpufreq_boost_supported(void); /* * Two notifier lists: the "policy" list is involved in the @@ -455,8 +456,10 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy, policy->cur, policy->cpuinfo.max_freq); + spin_lock(&policy->transition_lock); policy->transition_ongoing = false; policy->transition_task = NULL; + spin_unlock(&policy->transition_lock); wake_up(&policy->transition_wait); } @@ -621,6 +624,40 @@ static ssize_t store_boost(struct kobject *kobj, struct kobj_attribute *attr, } define_one_global_rw(boost); +static ssize_t show_local_boost(struct cpufreq_policy *policy, char *buf) +{ + return sysfs_emit(buf, "%d\n", policy->boost_enabled); +} + +static ssize_t store_local_boost(struct cpufreq_policy *policy, + const char *buf, size_t count) +{ + int ret, enable; + + ret = kstrtoint(buf, 10, &enable); + if (ret || enable < 0 || enable > 1) + return -EINVAL; + + if (!cpufreq_driver->boost_enabled) + return -EINVAL; + + if (policy->boost_enabled == enable) + return count; + + cpus_read_lock(); + ret = cpufreq_driver->set_boost(policy, enable); + cpus_read_unlock(); + + if (ret) + return ret; + + policy->boost_enabled = enable; + + return count; +} + +static struct freq_attr local_boost = __ATTR(boost, 0644, show_local_boost, store_local_boost); + static struct cpufreq_governor *find_governor(const char *str_governor) { struct cpufreq_governor *t; @@ -1055,6 +1092,12 @@ static int cpufreq_add_dev_interface(struct cpufreq_policy *policy) return ret; } + if (cpufreq_boost_supported()) { + ret = sysfs_create_file(&policy->kobj, &local_boost.attr); + if (ret) + return ret; + } + return 0; } @@ -1943,16 +1986,16 @@ void cpufreq_resume(void) for_each_active_policy(policy) { if (cpufreq_driver->resume && cpufreq_driver->resume(policy)) { - pr_err("%s: Failed to resume driver: %p\n", __func__, - policy); + pr_err("%s: Failed to resume driver: %s\n", __func__, + cpufreq_driver->name); } else if (has_target()) { down_write(&policy->rwsem); ret = cpufreq_start_governor(policy); up_write(&policy->rwsem); if (ret) - pr_err("%s: Failed to start governor for policy: %p\n", - __func__, policy); + pr_err("%s: Failed to start governor for CPU%u's policy\n", + __func__, policy->cpu); } } } @@ -2716,6 +2759,8 @@ int cpufreq_boost_trigger_state(int state) ret = cpufreq_driver->set_boost(policy, state); if (ret) goto err_reset_state; + + policy->boost_enabled = state; } cpus_read_unlock(); diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 85da677c43d6bde7077a80e1562b6b4b9a7faf8f..af44ee6a64304f0e56265b9bbf5c00a60285726d 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -439,7 +439,7 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) ret = gov->init(dbs_data); if (ret) - goto free_policy_dbs_info; + goto free_dbs_data; /* * The sampling interval should not be less than the transition latency @@ -474,6 +474,8 @@ int cpufreq_dbs_governor_init(struct cpufreq_policy *policy) if (!have_governor_per_policy()) gov->gdbs_data = NULL; gov->exit(dbs_data); + +free_dbs_data: kfree(dbs_data); free_policy_dbs_info: diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index 84fe37def0f12fca02e249f9b81e2f474111d21f..6f8b5ea7aeae4f6e3ccdea324172d7eded2d7436 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -232,8 +232,8 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, status = ioread16(&pcch_hdr->status); iowrite16(0, &pcch_hdr->status); - cpufreq_freq_transition_end(policy, &freqs, status != CMD_COMPLETE); spin_unlock(&pcc_lock); + cpufreq_freq_transition_end(policy, &freqs, status != CMD_COMPLETE); if (status != CMD_COMPLETE) { pr_debug("target: FAILED for cpu %d, with status: 0x%x\n", diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c index d1c559879dcc00733cfb9abff6cffb64b0745659..40d055560e52fd2298fa3400384b686e37413b32 100644 --- a/drivers/cxl/acpi.c +++ b/drivers/cxl/acpi.c @@ -14,7 +14,7 @@ struct cxl_cxims_data { int nr_maps; - u64 xormaps[]; + u64 xormaps[] __counted_by(nr_maps); }; /* @@ -112,9 +112,9 @@ static int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg, GFP_KERNEL); if (!cximsd) return -ENOMEM; + cximsd->nr_maps = nr_maps; memcpy(cximsd->xormaps, cxims->xormap_list, nr_maps * sizeof(*cximsd->xormaps)); - cximsd->nr_maps = nr_maps; cxlrd->platform_data = cximsd; return 0; diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index ca60bb8114f22a4df6f175b0f5a942e51e3cb45d..4df4f614f490ef96d26f412a9cff42e52d246486 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -715,24 +715,25 @@ static void cxl_walk_cel(struct cxl_memdev_state *mds, size_t size, u8 *cel) for (i = 0; i < cel_entries; i++) { u16 opcode = le16_to_cpu(cel_entry[i].opcode); struct cxl_mem_command *cmd = cxl_mem_find_command(opcode); + int enabled = 0; - if (!cmd && (!cxl_is_poison_command(opcode) || - !cxl_is_security_command(opcode))) { - dev_dbg(dev, - "Opcode 0x%04x unsupported by driver\n", opcode); - continue; - } - - if (cmd) + if (cmd) { set_bit(cmd->info.id, mds->enabled_cmds); + enabled++; + } - if (cxl_is_poison_command(opcode)) + if (cxl_is_poison_command(opcode)) { cxl_set_poison_cmd_enabled(&mds->poison, opcode); + enabled++; + } - if (cxl_is_security_command(opcode)) + if (cxl_is_security_command(opcode)) { cxl_set_security_cmd_enabled(&mds->security, opcode); + enabled++; + } - dev_dbg(dev, "Opcode 0x%04x enabled\n", opcode); + dev_dbg(dev, "Opcode 0x%04x %s\n", opcode, + enabled ? "enabled" : "unsupported by driver"); } } diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 724be8448eb4a52542bc3e0cd6b16de508f7979f..7ca01a834e188c8f09f2676b8c79689279acbabe 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* Copyright(c) 2020 Intel Corporation. All rights reserved. */ +#include #include #include #include @@ -706,16 +707,20 @@ static int cxl_setup_comp_regs(struct device *dev, struct cxl_register_map *map, return cxl_setup_regs(map); } -static inline int cxl_port_setup_regs(struct cxl_port *port, - resource_size_t component_reg_phys) +static int cxl_port_setup_regs(struct cxl_port *port, + resource_size_t component_reg_phys) { + if (dev_is_platform(port->uport_dev)) + return 0; return cxl_setup_comp_regs(&port->dev, &port->comp_map, component_reg_phys); } -static inline int cxl_dport_setup_regs(struct cxl_dport *dport, - resource_size_t component_reg_phys) +static int cxl_dport_setup_regs(struct cxl_dport *dport, + resource_size_t component_reg_phys) { + if (dev_is_platform(dport->dport_dev)) + return 0; return cxl_setup_comp_regs(dport->dport_dev, &dport->comp_map, component_reg_phys); } diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c index e115ba382e0445dc6c8ec1881e0e95ec307c4918..6d63b8798c29921470e37c9bf0e9d116c834508e 100644 --- a/drivers/cxl/core/region.c +++ b/drivers/cxl/core/region.c @@ -717,13 +717,35 @@ static int match_free_decoder(struct device *dev, void *data) return 0; } +static int match_auto_decoder(struct device *dev, void *data) +{ + struct cxl_region_params *p = data; + struct cxl_decoder *cxld; + struct range *r; + + if (!is_switch_decoder(dev)) + return 0; + + cxld = to_cxl_decoder(dev); + r = &cxld->hpa_range; + + if (p->res && p->res->start == r->start && p->res->end == r->end) + return 1; + + return 0; +} + static struct cxl_decoder *cxl_region_find_decoder(struct cxl_port *port, struct cxl_region *cxlr) { struct device *dev; int id = 0; - dev = device_find_child(&port->dev, &id, match_free_decoder); + if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) + dev = device_find_child(&port->dev, &cxlr->params, + match_auto_decoder); + else + dev = device_find_child(&port->dev, &id, match_free_decoder); if (!dev) return NULL; /* @@ -1154,16 +1176,15 @@ static int cxl_port_setup_targets(struct cxl_port *port, } /* - * If @parent_port is masking address bits, pick the next unused address - * bit to route @port's targets. + * Interleave granularity is a multiple of @parent_port granularity. + * Multiplier is the parent port interleave ways. */ - if (parent_iw > 1 && cxl_rr->nr_targets > 1) { - u32 address_bit = max(peig + peiw, eiw + peig); - - eig = address_bit - eiw + 1; - } else { - eiw = peiw; - eig = peig; + rc = granularity_to_eig(parent_ig * parent_iw, &eig); + if (rc) { + dev_dbg(&cxlr->dev, + "%s: invalid granularity calculation (%d * %d)\n", + dev_name(&parent_port->dev), parent_ig, parent_iw); + return rc; } rc = eig_to_granularity(eig, &ig); diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c index 1cb1494c28fe82c36f0e75f9a1ee64f6ed77fc38..44a21ab7add51b70d17c645434934af347a93e58 100644 --- a/drivers/cxl/pci.c +++ b/drivers/cxl/pci.c @@ -529,7 +529,6 @@ static int cxl_pci_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, static int cxl_pci_ras_unmask(struct pci_dev *pdev) { - struct pci_host_bridge *host_bridge = pci_find_host_bridge(pdev->bus); struct cxl_dev_state *cxlds = pci_get_drvdata(pdev); void __iomem *addr; u32 orig_val, val, mask; @@ -541,9 +540,9 @@ static int cxl_pci_ras_unmask(struct pci_dev *pdev) return 0; } - /* BIOS has CXL error control */ - if (!host_bridge->native_cxl_error) - return -ENXIO; + /* BIOS has PCIe AER error control */ + if (!pcie_aer_is_native(pdev)) + return 0; rc = pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &cap); if (rc) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 08fdd0e2ed1bf01a4be5baf462fabb32dd5b88d3..4ccae1a3b8842787fd144a79eede2396b00a7b83 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -474,25 +474,6 @@ config MXS_DMA Support the MXS DMA engine. This engine including APBH-DMA and APBX-DMA is integrated into some Freescale chips. -config MX3_IPU - bool "MX3x Image Processing Unit support" - depends on ARCH_MXC - select DMA_ENGINE - default y - help - If you plan to use the Image Processing unit in the i.MX3x, say - Y here. If unsure, select Y. - -config MX3_IPU_IRQS - int "Number of dynamically mapped interrupts for IPU" - depends on MX3_IPU - range 2 137 - default 4 - help - Out of 137 interrupt sources on i.MX31 IPU only very few are used. - To avoid bloating the irq_desc[] array we allocate a sufficient - number of IRQ slots and map them dynamically to specific sources. - config NBPFAXI_DMA tristate "Renesas Type-AXI NBPF DMA support" select DMA_ENGINE @@ -699,7 +680,7 @@ config XGENE_DMA config XILINX_DMA tristate "Xilinx AXI DMAS Engine" - depends on (ARCH_ZYNQ || MICROBLAZE || ARM64) + depends on HAS_IOMEM select DMA_ENGINE help Enable support for Xilinx AXI VDMA Soft IP. diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a4fd1ce295102804560a8074e603e8efdf38a3a9..83553a97a010e157b285ad6c47557df31c8bc835 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -32,8 +32,10 @@ obj-$(CONFIG_DW_DMAC_CORE) += dw/ obj-$(CONFIG_DW_EDMA) += dw-edma/ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_FSL_DMA) += fsldma.o -obj-$(CONFIG_FSL_EDMA) += fsl-edma.o fsl-edma-common.o -obj-$(CONFIG_MCF_EDMA) += mcf-edma.o fsl-edma-common.o +fsl-edma-objs := fsl-edma-main.o fsl-edma-common.o +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o +mcf-edma-objs := mcf-edma-main.o fsl-edma-common.o +obj-$(CONFIG_MCF_EDMA) += mcf-edma.o obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o obj-$(CONFIG_FSL_RAID) += fsl_raid.o obj-$(CONFIG_HISI_DMA) += hisi_dma.o @@ -55,7 +57,6 @@ obj-$(CONFIG_MPC512X_DMA) += mpc512x_dma.o obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_MV_XOR_V2) += mv_xor_v2.o obj-$(CONFIG_MXS_DMA) += mxs-dma.o -obj-$(CONFIG_MX3_IPU) += ipu/ obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o obj-$(CONFIG_OWL_DMA) += owl-dma.o obj-$(CONFIG_PCH_DMA) += pch_dma.o diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c index 4cf8da77bdd91329012579de26bb7aa01f7979f4..3af795635c5ce698e95d91e97b6e07d05afc6736 100644 --- a/drivers/dma/apple-admac.c +++ b/drivers/dma/apple-admac.c @@ -10,8 +10,9 @@ #include #include #include -#include +#include #include +#include #include #include #include diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index ee3a219e3a897c56ac69d7a902ef90ccc0262822..b2876f67471fe149b154104f07b38ef93651835a 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c index 064761289a73891717909f87cfdc05f76115f4df..94ea35330eb5c182f049a06794bb8bd4c4a41185 100644 --- a/drivers/dma/bcm-sba-raid.c +++ b/drivers/dma/bcm-sba-raid.c @@ -35,7 +35,9 @@ #include #include #include -#include +#include +#include +#include #include #include diff --git a/drivers/dma/bestcomm/bestcomm.c b/drivers/dma/bestcomm/bestcomm.c index eabbcfcaa7cba1121368acb4c61379077d9dca15..80096f94032dc6e4c240f1b0793643b3fa5f4f65 100644 --- a/drivers/dma/bestcomm/bestcomm.c +++ b/drivers/dma/bestcomm/bestcomm.c @@ -14,9 +14,8 @@ #include #include #include -#include #include -#include +#include #include #include #include diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index 9c1a6e9a9c03084d622daef7d19bbd8955254547..adbd47bd6adfe4755e2c5a6571c546b9c83896dd 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 826b98284fa1f845698cc996035d5f6bb56fdcc5..b7388ae62d7f1fde1b24118a38c1d454974b2bd9 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1147,69 +1147,27 @@ int dma_async_device_register(struct dma_device *device) device->owner = device->dev->driver->owner; - if (dma_has_cap(DMA_MEMCPY, device->cap_mask) && !device->device_prep_dma_memcpy) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_MEMCPY"); - return -EIO; - } - - if (dma_has_cap(DMA_XOR, device->cap_mask) && !device->device_prep_dma_xor) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_XOR"); - return -EIO; - } - - if (dma_has_cap(DMA_XOR_VAL, device->cap_mask) && !device->device_prep_dma_xor_val) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_XOR_VAL"); - return -EIO; - } - - if (dma_has_cap(DMA_PQ, device->cap_mask) && !device->device_prep_dma_pq) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_PQ"); - return -EIO; - } - - if (dma_has_cap(DMA_PQ_VAL, device->cap_mask) && !device->device_prep_dma_pq_val) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_PQ_VAL"); - return -EIO; - } - - if (dma_has_cap(DMA_MEMSET, device->cap_mask) && !device->device_prep_dma_memset) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_MEMSET"); - return -EIO; - } - - if (dma_has_cap(DMA_INTERRUPT, device->cap_mask) && !device->device_prep_dma_interrupt) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_INTERRUPT"); - return -EIO; - } - - if (dma_has_cap(DMA_CYCLIC, device->cap_mask) && !device->device_prep_dma_cyclic) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_CYCLIC"); - return -EIO; - } - - if (dma_has_cap(DMA_INTERLEAVE, device->cap_mask) && !device->device_prep_interleaved_dma) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_INTERLEAVE"); - return -EIO; - } +#define CHECK_CAP(_name, _type) \ +{ \ + if (dma_has_cap(_type, device->cap_mask) && !device->device_prep_##_name) { \ + dev_err(device->dev, \ + "Device claims capability %s, but op is not defined\n", \ + __stringify(_type)); \ + return -EIO; \ + } \ +} + CHECK_CAP(dma_memcpy, DMA_MEMCPY); + CHECK_CAP(dma_xor, DMA_XOR); + CHECK_CAP(dma_xor_val, DMA_XOR_VAL); + CHECK_CAP(dma_pq, DMA_PQ); + CHECK_CAP(dma_pq_val, DMA_PQ_VAL); + CHECK_CAP(dma_memset, DMA_MEMSET); + CHECK_CAP(dma_interrupt, DMA_INTERRUPT); + CHECK_CAP(dma_cyclic, DMA_CYCLIC); + CHECK_CAP(interleaved_dma, DMA_INTERLEAVE); + +#undef CHECK_CAP if (!device->device_tx_status) { dev_err(device->dev, "Device tx_status is not defined\n"); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 796b6caf0babe185dfd4cfd59ee77f55fc64b65e..dd02f84e404d08fc3cce669455d79a3879b1fd42 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/dw/rzn1-dmamux.c b/drivers/dma/dw/rzn1-dmamux.c index f9912c3dd4d7c06c9712ddc8f3e1a32beb16948d..4fb8508419dbd8afdffb3cc57a21a0a76aae26ad 100644 --- a/drivers/dma/dw/rzn1-dmamux.c +++ b/drivers/dma/dw/rzn1-dmamux.c @@ -5,8 +5,10 @@ * Based on TI crossbar driver written by Peter Ujfalusi */ #include -#include +#include #include +#include +#include #include #include #include diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 5338a94f1a69f00be71d7371d3dc991b5ff82319..5c4a448a12541ad2ceab2651f22cc2acec53b23f 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1320,11 +1320,9 @@ static int __init ep93xx_dma_probe(struct platform_device *pdev) struct ep93xx_dma_platform_data *pdata = dev_get_platdata(&pdev->dev); struct ep93xx_dma_engine *edma; struct dma_device *dma_dev; - size_t edma_size; int ret, i; - edma_size = pdata->num_channels * sizeof(struct ep93xx_dma_chan); - edma = kzalloc(sizeof(*edma) + edma_size, GFP_KERNEL); + edma = kzalloc(struct_size(edma, channels, pdata->num_channels), GFP_KERNEL); if (!edma) return -ENOMEM; diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index a06a1575a2a5026c36fd7a4babd2eff3405242f4..a0f5741abcc4797e9fea0b259dcaf57290a975ba 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "fsl-edma-common.h" @@ -40,14 +42,73 @@ #define EDMA64_ERRH 0x28 #define EDMA64_ERRL 0x2c -#define EDMA_TCD 0x1000 +void fsl_edma_tx_chan_handler(struct fsl_edma_chan *fsl_chan) +{ + spin_lock(&fsl_chan->vchan.lock); + + if (!fsl_chan->edesc) { + /* terminate_all called before */ + spin_unlock(&fsl_chan->vchan.lock); + return; + } + + if (!fsl_chan->edesc->iscyclic) { + list_del(&fsl_chan->edesc->vdesc.node); + vchan_cookie_complete(&fsl_chan->edesc->vdesc); + fsl_chan->edesc = NULL; + fsl_chan->status = DMA_COMPLETE; + fsl_chan->idle = true; + } else { + vchan_cyclic_callback(&fsl_chan->edesc->vdesc); + } + + if (!fsl_chan->edesc) + fsl_edma_xfer_desc(fsl_chan); + + spin_unlock(&fsl_chan->vchan.lock); +} + +static void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan) +{ + u32 val, flags; + + flags = fsl_edma_drvflags(fsl_chan); + val = edma_readl_chreg(fsl_chan, ch_sbr); + /* Remote/local swapped wrongly on iMX8 QM Audio edma */ + if (flags & FSL_EDMA_DRV_QUIRK_SWAPPED) { + if (!fsl_chan->is_rxchan) + val |= EDMA_V3_CH_SBR_RD; + else + val |= EDMA_V3_CH_SBR_WR; + } else { + if (fsl_chan->is_rxchan) + val |= EDMA_V3_CH_SBR_RD; + else + val |= EDMA_V3_CH_SBR_WR; + } + + if (fsl_chan->is_remote) + val &= ~(EDMA_V3_CH_SBR_RD | EDMA_V3_CH_SBR_WR); + + edma_writel_chreg(fsl_chan, val, ch_sbr); + + if (flags & FSL_EDMA_DRV_HAS_CHMUX) + edma_writel_chreg(fsl_chan, fsl_chan->srcid, ch_mux); + + val = edma_readl_chreg(fsl_chan, ch_csr); + val |= EDMA_V3_CH_CSR_ERQ; + edma_writel_chreg(fsl_chan, val, ch_csr); +} static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) { struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - if (fsl_chan->edma->drvdata->version == v1) { + if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG) + return fsl_edma3_enable_request(fsl_chan); + + if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) { edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); edma_writeb(fsl_chan->edma, ch, regs->serq); } else { @@ -59,12 +120,29 @@ static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) } } +static void fsl_edma3_disable_request(struct fsl_edma_chan *fsl_chan) +{ + u32 val = edma_readl_chreg(fsl_chan, ch_csr); + u32 flags; + + flags = fsl_edma_drvflags(fsl_chan); + + if (flags & FSL_EDMA_DRV_HAS_CHMUX) + edma_writel_chreg(fsl_chan, 0, ch_mux); + + val &= ~EDMA_V3_CH_CSR_ERQ; + edma_writel_chreg(fsl_chan, val, ch_csr); +} + void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) { struct edma_regs *regs = &fsl_chan->edma->regs; u32 ch = fsl_chan->vchan.chan.chan_id; - if (fsl_chan->edma->drvdata->version == v1) { + if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG) + return fsl_edma3_disable_request(fsl_chan); + + if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) { edma_writeb(fsl_chan->edma, ch, regs->cerq); edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); } else { @@ -75,7 +153,6 @@ void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei); } } -EXPORT_SYMBOL_GPL(fsl_edma_disable_request); static void mux_configure8(struct fsl_edma_chan *fsl_chan, void __iomem *addr, u32 off, u32 slot, bool enable) @@ -112,36 +189,33 @@ void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, int endian_diff[4] = {3, 1, -1, -3}; u32 dmamux_nr = fsl_chan->edma->drvdata->dmamuxs; + if (!dmamux_nr) + return; + chans_per_mux = fsl_chan->edma->n_chans / dmamux_nr; ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; - if (fsl_chan->edma->drvdata->mux_swap) + if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_MUX_SWAP) ch_off += endian_diff[ch_off % 4]; muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; slot = EDMAMUX_CHCFG_SOURCE(slot); - if (fsl_chan->edma->drvdata->version == v3) + if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_CONFIG32) mux_configure32(fsl_chan, muxaddr, ch_off, slot, enable); else mux_configure8(fsl_chan, muxaddr, ch_off, slot, enable); } -EXPORT_SYMBOL_GPL(fsl_edma_chan_mux); static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) { - switch (addr_width) { - case 1: - return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT; - case 2: - return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT; - case 4: - return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; - case 8: - return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT; - default: - return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; - } + u32 val; + + if (addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + val = ffs(addr_width) - 1; + return val | (val << 8); } void fsl_edma_free_desc(struct virt_dma_desc *vdesc) @@ -155,7 +229,6 @@ void fsl_edma_free_desc(struct virt_dma_desc *vdesc) fsl_desc->tcd[i].ptcd); kfree(fsl_desc); } -EXPORT_SYMBOL_GPL(fsl_edma_free_desc); int fsl_edma_terminate_all(struct dma_chan *chan) { @@ -170,9 +243,12 @@ int fsl_edma_terminate_all(struct dma_chan *chan) vchan_get_all_descriptors(&fsl_chan->vchan, &head); spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + + if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_PD) + pm_runtime_allow(fsl_chan->pd_dev); + return 0; } -EXPORT_SYMBOL_GPL(fsl_edma_terminate_all); int fsl_edma_pause(struct dma_chan *chan) { @@ -188,7 +264,6 @@ int fsl_edma_pause(struct dma_chan *chan) spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); return 0; } -EXPORT_SYMBOL_GPL(fsl_edma_pause); int fsl_edma_resume(struct dma_chan *chan) { @@ -204,7 +279,6 @@ int fsl_edma_resume(struct dma_chan *chan) spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); return 0; } -EXPORT_SYMBOL_GPL(fsl_edma_resume); static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan) { @@ -265,36 +339,41 @@ int fsl_edma_slave_config(struct dma_chan *chan, return 0; } -EXPORT_SYMBOL_GPL(fsl_edma_slave_config); static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, struct virt_dma_desc *vdesc, bool in_progress) { struct fsl_edma_desc *edesc = fsl_chan->edesc; - struct edma_regs *regs = &fsl_chan->edma->regs; - u32 ch = fsl_chan->vchan.chan.chan_id; enum dma_transfer_direction dir = edesc->dirn; dma_addr_t cur_addr, dma_addr; size_t len, size; + u32 nbytes = 0; int i; /* calculate the total size in this desc */ - for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) - len += le32_to_cpu(edesc->tcd[i].vtcd->nbytes) - * le16_to_cpu(edesc->tcd[i].vtcd->biter); + for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) { + nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes); + if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE)) + nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes); + len += nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter); + } if (!in_progress) return len; if (dir == DMA_MEM_TO_DEV) - cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].saddr); + cur_addr = edma_read_tcdreg(fsl_chan, saddr); else - cur_addr = edma_readl(fsl_chan->edma, ®s->tcd[ch].daddr); + cur_addr = edma_read_tcdreg(fsl_chan, daddr); /* figure out the finished and calculate the residue */ for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { - size = le32_to_cpu(edesc->tcd[i].vtcd->nbytes) - * le16_to_cpu(edesc->tcd[i].vtcd->biter); + nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes); + if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE)) + nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes); + + size = nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter); + if (dir == DMA_MEM_TO_DEV) dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr); else @@ -340,14 +419,10 @@ enum dma_status fsl_edma_tx_status(struct dma_chan *chan, return fsl_chan->status; } -EXPORT_SYMBOL_GPL(fsl_edma_tx_status); static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, struct fsl_edma_hw_tcd *tcd) { - struct fsl_edma_engine *edma = fsl_chan->edma; - struct edma_regs *regs = &fsl_chan->edma->regs; - u32 ch = fsl_chan->vchan.chan.chan_id; u16 csr = 0; /* @@ -356,23 +431,22 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, * big- or little-endian obeying the eDMA engine model endian, * and this is performed from specific edma_write functions */ - edma_writew(edma, 0, ®s->tcd[ch].csr); + edma_write_tcdreg(fsl_chan, 0, csr); - edma_writel(edma, (s32)tcd->saddr, ®s->tcd[ch].saddr); - edma_writel(edma, (s32)tcd->daddr, ®s->tcd[ch].daddr); + edma_write_tcdreg(fsl_chan, tcd->saddr, saddr); + edma_write_tcdreg(fsl_chan, tcd->daddr, daddr); - edma_writew(edma, (s16)tcd->attr, ®s->tcd[ch].attr); - edma_writew(edma, tcd->soff, ®s->tcd[ch].soff); + edma_write_tcdreg(fsl_chan, tcd->attr, attr); + edma_write_tcdreg(fsl_chan, tcd->soff, soff); - edma_writel(edma, (s32)tcd->nbytes, ®s->tcd[ch].nbytes); - edma_writel(edma, (s32)tcd->slast, ®s->tcd[ch].slast); + edma_write_tcdreg(fsl_chan, tcd->nbytes, nbytes); + edma_write_tcdreg(fsl_chan, tcd->slast, slast); - edma_writew(edma, (s16)tcd->citer, ®s->tcd[ch].citer); - edma_writew(edma, (s16)tcd->biter, ®s->tcd[ch].biter); - edma_writew(edma, (s16)tcd->doff, ®s->tcd[ch].doff); + edma_write_tcdreg(fsl_chan, tcd->citer, citer); + edma_write_tcdreg(fsl_chan, tcd->biter, biter); + edma_write_tcdreg(fsl_chan, tcd->doff, doff); - edma_writel(edma, (s32)tcd->dlast_sga, - ®s->tcd[ch].dlast_sga); + edma_write_tcdreg(fsl_chan, tcd->dlast_sga, dlast_sga); if (fsl_chan->is_sw) { csr = le16_to_cpu(tcd->csr); @@ -380,16 +454,19 @@ static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, tcd->csr = cpu_to_le16(csr); } - edma_writew(edma, (s16)tcd->csr, ®s->tcd[ch].csr); + edma_write_tcdreg(fsl_chan, tcd->csr, csr); } static inline -void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, +void fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan, + struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer, u16 biter, u16 doff, u32 dlast_sga, bool major_int, bool disable_req, bool enable_sg) { + struct dma_slave_config *cfg = &fsl_chan->cfg; u16 csr = 0; + u32 burst; /* * eDMA hardware SGs require the TCDs to be stored in little @@ -404,6 +481,21 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, tcd->soff = cpu_to_le16(soff); + if (fsl_chan->is_multi_fifo) { + /* set mloff to support multiple fifo */ + burst = cfg->direction == DMA_DEV_TO_MEM ? + cfg->src_addr_width : cfg->dst_addr_width; + nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-(burst * 4)); + /* enable DMLOE/SMLOE */ + if (cfg->direction == DMA_MEM_TO_DEV) { + nbytes |= EDMA_V3_TCD_NBYTES_DMLOE; + nbytes &= ~EDMA_V3_TCD_NBYTES_SMLOE; + } else { + nbytes |= EDMA_V3_TCD_NBYTES_SMLOE; + nbytes &= ~EDMA_V3_TCD_NBYTES_DMLOE; + } + } + tcd->nbytes = cpu_to_le32(nbytes); tcd->slast = cpu_to_le32(slast); @@ -422,6 +514,12 @@ void fsl_edma_fill_tcd(struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, if (enable_sg) csr |= EDMA_TCD_CSR_E_SG; + if (fsl_chan->is_rxchan) + csr |= EDMA_TCD_CSR_ACTIVE; + + if (fsl_chan->is_sw) + csr |= EDMA_TCD_CSR_START; + tcd->csr = cpu_to_le16(csr); } @@ -461,6 +559,7 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); struct fsl_edma_desc *fsl_desc; dma_addr_t dma_buf_next; + bool major_int = true; int sg_len, i; u32 src_addr, dst_addr, last_sg, nbytes; u16 soff, doff, iter; @@ -504,23 +603,28 @@ struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( src_addr = dma_buf_next; dst_addr = fsl_chan->dma_dev_addr; soff = fsl_chan->cfg.dst_addr_width; - doff = 0; - } else { + doff = fsl_chan->is_multi_fifo ? 4 : 0; + } else if (direction == DMA_DEV_TO_MEM) { src_addr = fsl_chan->dma_dev_addr; dst_addr = dma_buf_next; - soff = 0; + soff = fsl_chan->is_multi_fifo ? 4 : 0; doff = fsl_chan->cfg.src_addr_width; + } else { + /* DMA_DEV_TO_DEV */ + src_addr = fsl_chan->cfg.src_addr; + dst_addr = fsl_chan->cfg.dst_addr; + soff = doff = 0; + major_int = false; } - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, dst_addr, + fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, fsl_chan->attr, soff, nbytes, 0, iter, - iter, doff, last_sg, true, false, true); + iter, doff, last_sg, major_int, false, true); dma_buf_next += period_len; } return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); } -EXPORT_SYMBOL_GPL(fsl_edma_prep_dma_cyclic); struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, @@ -564,23 +668,51 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( dst_addr = fsl_chan->dma_dev_addr; soff = fsl_chan->cfg.dst_addr_width; doff = 0; - } else { + } else if (direction == DMA_DEV_TO_MEM) { src_addr = fsl_chan->dma_dev_addr; dst_addr = sg_dma_address(sg); soff = 0; doff = fsl_chan->cfg.src_addr_width; + } else { + /* DMA_DEV_TO_DEV */ + src_addr = fsl_chan->cfg.src_addr; + dst_addr = fsl_chan->cfg.dst_addr; + soff = 0; + doff = 0; } + /* + * Choose the suitable burst length if sg_dma_len is not + * multiple of burst length so that the whole transfer length is + * multiple of minor loop(burst length). + */ + if (sg_dma_len(sg) % nbytes) { + u32 width = (direction == DMA_DEV_TO_MEM) ? doff : soff; + u32 burst = (direction == DMA_DEV_TO_MEM) ? + fsl_chan->cfg.src_maxburst : + fsl_chan->cfg.dst_maxburst; + int j; + + for (j = burst; j > 1; j--) { + if (!(sg_dma_len(sg) % (j * width))) { + nbytes = j * width; + break; + } + } + /* Set burst size as 1 if there's no suitable one */ + if (j == 1) + nbytes = width; + } iter = sg_dma_len(sg) / nbytes; if (i < sg_len - 1) { last_sg = fsl_desc->tcd[(i + 1)].ptcd; - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, + fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, fsl_chan->attr, soff, nbytes, 0, iter, iter, doff, last_sg, false, false, true); } else { last_sg = 0; - fsl_edma_fill_tcd(fsl_desc->tcd[i].vtcd, src_addr, + fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, fsl_chan->attr, soff, nbytes, 0, iter, iter, doff, last_sg, true, true, false); @@ -589,7 +721,6 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); } -EXPORT_SYMBOL_GPL(fsl_edma_prep_slave_sg); struct dma_async_tx_descriptor *fsl_edma_prep_memcpy(struct dma_chan *chan, dma_addr_t dma_dst, dma_addr_t dma_src, @@ -606,13 +737,12 @@ struct dma_async_tx_descriptor *fsl_edma_prep_memcpy(struct dma_chan *chan, fsl_chan->is_sw = true; /* To match with copy_align and max_seg_size so 1 tcd is enough */ - fsl_edma_fill_tcd(fsl_desc->tcd[0].vtcd, dma_src, dma_dst, - EDMA_TCD_ATTR_SSIZE_32BYTE | EDMA_TCD_ATTR_DSIZE_32BYTE, + fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[0].vtcd, dma_src, dma_dst, + fsl_edma_get_tcd_attr(DMA_SLAVE_BUSWIDTH_32_BYTES), 32, len, 0, 1, 1, 32, 0, true, true, false); return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); } -EXPORT_SYMBOL_GPL(fsl_edma_prep_memcpy); void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) { @@ -629,7 +759,6 @@ void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) fsl_chan->status = DMA_IN_PROGRESS; fsl_chan->idle = false; } -EXPORT_SYMBOL_GPL(fsl_edma_xfer_desc); void fsl_edma_issue_pending(struct dma_chan *chan) { @@ -649,7 +778,6 @@ void fsl_edma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); } -EXPORT_SYMBOL_GPL(fsl_edma_issue_pending); int fsl_edma_alloc_chan_resources(struct dma_chan *chan) { @@ -660,7 +788,6 @@ int fsl_edma_alloc_chan_resources(struct dma_chan *chan) 32, 0); return 0; } -EXPORT_SYMBOL_GPL(fsl_edma_alloc_chan_resources); void fsl_edma_free_chan_resources(struct dma_chan *chan) { @@ -683,7 +810,6 @@ void fsl_edma_free_chan_resources(struct dma_chan *chan) fsl_chan->tcd_pool = NULL; fsl_chan->is_sw = false; } -EXPORT_SYMBOL_GPL(fsl_edma_free_chan_resources); void fsl_edma_cleanup_vchan(struct dma_device *dmadev) { @@ -695,12 +821,10 @@ void fsl_edma_cleanup_vchan(struct dma_device *dmadev) tasklet_kill(&chan->vchan.task); } } -EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan); /* - * On the 32 channels Vybrid/mpc577x edma version (here called "v1"), - * register offsets are different compared to ColdFire mcf5441x 64 channels - * edma (here called "v2"). + * On the 32 channels Vybrid/mpc577x edma version, register offsets are + * different compared to ColdFire mcf5441x 64 channels edma. * * This function sets up register offsets as per proper declared version * so must be called in xxx_edma_probe() just after setting the @@ -708,41 +832,30 @@ EXPORT_SYMBOL_GPL(fsl_edma_cleanup_vchan); */ void fsl_edma_setup_regs(struct fsl_edma_engine *edma) { + bool is64 = !!(edma->drvdata->flags & FSL_EDMA_DRV_EDMA64); + edma->regs.cr = edma->membase + EDMA_CR; edma->regs.es = edma->membase + EDMA_ES; edma->regs.erql = edma->membase + EDMA_ERQ; edma->regs.eeil = edma->membase + EDMA_EEI; - edma->regs.serq = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_SERQ : EDMA_SERQ); - edma->regs.cerq = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_CERQ : EDMA_CERQ); - edma->regs.seei = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_SEEI : EDMA_SEEI); - edma->regs.ceei = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_CEEI : EDMA_CEEI); - edma->regs.cint = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_CINT : EDMA_CINT); - edma->regs.cerr = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_CERR : EDMA_CERR); - edma->regs.ssrt = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_SSRT : EDMA_SSRT); - edma->regs.cdne = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_CDNE : EDMA_CDNE); - edma->regs.intl = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_INTL : EDMA_INTR); - edma->regs.errl = edma->membase + ((edma->drvdata->version == v2) ? - EDMA64_ERRL : EDMA_ERR); - - if (edma->drvdata->version == v2) { + edma->regs.serq = edma->membase + (is64 ? EDMA64_SERQ : EDMA_SERQ); + edma->regs.cerq = edma->membase + (is64 ? EDMA64_CERQ : EDMA_CERQ); + edma->regs.seei = edma->membase + (is64 ? EDMA64_SEEI : EDMA_SEEI); + edma->regs.ceei = edma->membase + (is64 ? EDMA64_CEEI : EDMA_CEEI); + edma->regs.cint = edma->membase + (is64 ? EDMA64_CINT : EDMA_CINT); + edma->regs.cerr = edma->membase + (is64 ? EDMA64_CERR : EDMA_CERR); + edma->regs.ssrt = edma->membase + (is64 ? EDMA64_SSRT : EDMA_SSRT); + edma->regs.cdne = edma->membase + (is64 ? EDMA64_CDNE : EDMA_CDNE); + edma->regs.intl = edma->membase + (is64 ? EDMA64_INTL : EDMA_INTR); + edma->regs.errl = edma->membase + (is64 ? EDMA64_ERRL : EDMA_ERR); + + if (is64) { edma->regs.erqh = edma->membase + EDMA64_ERQH; edma->regs.eeih = edma->membase + EDMA64_EEIH; edma->regs.errh = edma->membase + EDMA64_ERRH; edma->regs.inth = edma->membase + EDMA64_INTH; } - - edma->regs.tcd = edma->membase + EDMA_TCD; } -EXPORT_SYMBOL_GPL(fsl_edma_setup_regs); MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/fsl-edma-common.h b/drivers/dma/fsl-edma-common.h index 004ec4a6bc86de0bcd5a96f9d111d288cf289cf2..3cc0cc8fc2d05d41b953ae55a4b38dab235eccfe 100644 --- a/drivers/dma/fsl-edma-common.h +++ b/drivers/dma/fsl-edma-common.h @@ -29,16 +29,6 @@ #define EDMA_TCD_ATTR_DMOD(x) (((x) & GENMASK(4, 0)) << 3) #define EDMA_TCD_ATTR_SSIZE(x) (((x) & GENMASK(2, 0)) << 8) #define EDMA_TCD_ATTR_SMOD(x) (((x) & GENMASK(4, 0)) << 11) -#define EDMA_TCD_ATTR_DSIZE_8BIT 0 -#define EDMA_TCD_ATTR_DSIZE_16BIT BIT(0) -#define EDMA_TCD_ATTR_DSIZE_32BIT BIT(1) -#define EDMA_TCD_ATTR_DSIZE_64BIT (BIT(0) | BIT(1)) -#define EDMA_TCD_ATTR_DSIZE_32BYTE (BIT(2) | BIT(0)) -#define EDMA_TCD_ATTR_SSIZE_8BIT 0 -#define EDMA_TCD_ATTR_SSIZE_16BIT (EDMA_TCD_ATTR_DSIZE_16BIT << 8) -#define EDMA_TCD_ATTR_SSIZE_32BIT (EDMA_TCD_ATTR_DSIZE_32BIT << 8) -#define EDMA_TCD_ATTR_SSIZE_64BIT (EDMA_TCD_ATTR_DSIZE_64BIT << 8) -#define EDMA_TCD_ATTR_SSIZE_32BYTE (EDMA_TCD_ATTR_DSIZE_32BYTE << 8) #define EDMA_TCD_CITER_CITER(x) ((x) & GENMASK(14, 0)) #define EDMA_TCD_BITER_BITER(x) ((x) & GENMASK(14, 0)) @@ -52,16 +42,32 @@ #define EDMA_TCD_CSR_ACTIVE BIT(6) #define EDMA_TCD_CSR_DONE BIT(7) +#define EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(x) ((x) & GENMASK(9, 0)) +#define EDMA_V3_TCD_NBYTES_MLOFF(x) (x << 10) +#define EDMA_V3_TCD_NBYTES_DMLOE (1 << 30) +#define EDMA_V3_TCD_NBYTES_SMLOE (1 << 31) + #define EDMAMUX_CHCFG_DIS 0x0 #define EDMAMUX_CHCFG_ENBL 0x80 #define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F) #define DMAMUX_NR 2 +#define EDMA_TCD 0x1000 + #define FSL_EDMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) + +#define EDMA_V3_CH_SBR_RD BIT(22) +#define EDMA_V3_CH_SBR_WR BIT(21) +#define EDMA_V3_CH_CSR_ERQ BIT(0) +#define EDMA_V3_CH_CSR_EARQ BIT(1) +#define EDMA_V3_CH_CSR_EEI BIT(2) +#define EDMA_V3_CH_CSR_DONE BIT(30) +#define EDMA_V3_CH_CSR_ACTIVE BIT(31) + enum fsl_edma_pm_state { RUNNING = 0, SUSPENDED, @@ -81,6 +87,18 @@ struct fsl_edma_hw_tcd { __le16 biter; }; +struct fsl_edma3_ch_reg { + __le32 ch_csr; + __le32 ch_es; + __le32 ch_int; + __le32 ch_sbr; + __le32 ch_pri; + __le32 ch_mux; + __le32 ch_mattr; /* edma4, reserved for edma3 */ + __le32 ch_reserved; + struct fsl_edma_hw_tcd tcd; +} __packed; + /* * These are iomem pointers, for both v32 and v64. */ @@ -103,7 +121,6 @@ struct edma_regs { void __iomem *intl; void __iomem *errh; void __iomem *errl; - struct fsl_edma_hw_tcd __iomem *tcd; }; struct fsl_edma_sw_tcd { @@ -126,7 +143,20 @@ struct fsl_edma_chan { dma_addr_t dma_dev_addr; u32 dma_dev_size; enum dma_data_direction dma_dir; - char chan_name[16]; + char chan_name[32]; + struct fsl_edma_hw_tcd __iomem *tcd; + u32 real_count; + struct work_struct issue_worker; + struct platform_device *pdev; + struct device *pd_dev; + u32 srcid; + struct clk *clk; + int priority; + int hw_chanid; + int txirq; + bool is_rxchan; + bool is_remote; + bool is_multi_fifo; }; struct fsl_edma_desc { @@ -138,17 +168,32 @@ struct fsl_edma_desc { struct fsl_edma_sw_tcd tcd[]; }; -enum edma_version { - v1, /* 32ch, Vybrid, mpc57x, etc */ - v2, /* 64ch Coldfire */ - v3, /* 32ch, i.mx7ulp */ -}; +#define FSL_EDMA_DRV_HAS_DMACLK BIT(0) +#define FSL_EDMA_DRV_MUX_SWAP BIT(1) +#define FSL_EDMA_DRV_CONFIG32 BIT(2) +#define FSL_EDMA_DRV_WRAP_IO BIT(3) +#define FSL_EDMA_DRV_EDMA64 BIT(4) +#define FSL_EDMA_DRV_HAS_PD BIT(5) +#define FSL_EDMA_DRV_HAS_CHCLK BIT(6) +#define FSL_EDMA_DRV_HAS_CHMUX BIT(7) +/* imx8 QM audio edma remote local swapped */ +#define FSL_EDMA_DRV_QUIRK_SWAPPED BIT(8) +/* control and status register is in tcd address space, edma3 reg layout */ +#define FSL_EDMA_DRV_SPLIT_REG BIT(9) +#define FSL_EDMA_DRV_BUS_8BYTE BIT(10) +#define FSL_EDMA_DRV_DEV_TO_DEV BIT(11) +#define FSL_EDMA_DRV_ALIGN_64BYTE BIT(12) + +#define FSL_EDMA_DRV_EDMA3 (FSL_EDMA_DRV_SPLIT_REG | \ + FSL_EDMA_DRV_BUS_8BYTE | \ + FSL_EDMA_DRV_DEV_TO_DEV | \ + FSL_EDMA_DRV_ALIGN_64BYTE) struct fsl_edma_drvdata { - enum edma_version version; - u32 dmamuxs; - bool has_dmaclk; - bool mux_swap; + u32 dmamuxs; /* only used before v3 */ + u32 chreg_off; + u32 chreg_space_sz; + u32 flags; int (*setup_irq)(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma); }; @@ -159,6 +204,7 @@ struct fsl_edma_engine { void __iomem *muxbase[DMAMUX_NR]; struct clk *muxclk[DMAMUX_NR]; struct clk *dmaclk; + struct clk *chclk; struct mutex fsl_edma_mutex; const struct fsl_edma_drvdata *drvdata; u32 n_chans; @@ -166,9 +212,28 @@ struct fsl_edma_engine { int errirq; bool big_endian; struct edma_regs regs; + u64 chan_masked; struct fsl_edma_chan chans[]; }; +#define edma_read_tcdreg(chan, __name) \ +(sizeof(chan->tcd->__name) == sizeof(u32) ? \ + edma_readl(chan->edma, &chan->tcd->__name) : \ + edma_readw(chan->edma, &chan->tcd->__name)) + +#define edma_write_tcdreg(chan, val, __name) \ +(sizeof(chan->tcd->__name) == sizeof(u32) ? \ + edma_writel(chan->edma, (u32 __force)val, &chan->tcd->__name) : \ + edma_writew(chan->edma, (u16 __force)val, &chan->tcd->__name)) + +#define edma_readl_chreg(chan, __name) \ + edma_readl(chan->edma, \ + (void __iomem *)&(container_of(chan->tcd, struct fsl_edma3_ch_reg, tcd)->__name)) + +#define edma_writel_chreg(chan, val, __name) \ + edma_writel(chan->edma, val, \ + (void __iomem *)&(container_of(chan->tcd, struct fsl_edma3_ch_reg, tcd)->__name)) + /* * R/W functions for big- or little-endian registers: * The eDMA controller's endian is independent of the CPU core's endian. @@ -183,6 +248,14 @@ static inline u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) return ioread32(addr); } +static inline u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr) +{ + if (edma->big_endian) + return ioread16be(addr); + else + return ioread16(addr); +} + static inline void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr) { @@ -217,11 +290,23 @@ static inline struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) return container_of(chan, struct fsl_edma_chan, vchan.chan); } +static inline u32 fsl_edma_drvflags(struct fsl_edma_chan *fsl_chan) +{ + return fsl_chan->edma->drvdata->flags; +} + static inline struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd) { return container_of(vd, struct fsl_edma_desc, vdesc); } +static inline void fsl_edma_err_chan_handler(struct fsl_edma_chan *fsl_chan) +{ + fsl_chan->status = DMA_ERROR; + fsl_chan->idle = true; +} + +void fsl_edma_tx_chan_handler(struct fsl_edma_chan *fsl_chan); void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan); void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, unsigned int slot, bool enable); diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma-main.c similarity index 62% rename from drivers/dma/fsl-edma.c rename to drivers/dma/fsl-edma-main.c index e40769666e3933f649692ddb29a8e4968817eacb..63d48d046f046fbdee054e64c97c8cf2808fa5c2 100644 --- a/drivers/dma/fsl-edma.c +++ b/drivers/dma/fsl-edma-main.c @@ -18,9 +18,15 @@ #include #include #include +#include +#include #include "fsl-edma-common.h" +#define ARGS_RX BIT(0) +#define ARGS_REMOTE BIT(1) +#define ARGS_MULTI_FIFO BIT(2) + static void fsl_edma_synchronize(struct dma_chan *chan) { struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); @@ -33,7 +39,6 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) struct fsl_edma_engine *fsl_edma = dev_id; unsigned int intr, ch; struct edma_regs *regs = &fsl_edma->regs; - struct fsl_edma_chan *fsl_chan; intr = edma_readl(fsl_edma, regs->intl); if (!intr) @@ -42,33 +47,25 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) for (ch = 0; ch < fsl_edma->n_chans; ch++) { if (intr & (0x1 << ch)) { edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), regs->cint); + fsl_edma_tx_chan_handler(&fsl_edma->chans[ch]); + } + } + return IRQ_HANDLED; +} - fsl_chan = &fsl_edma->chans[ch]; - - spin_lock(&fsl_chan->vchan.lock); +static irqreturn_t fsl_edma3_tx_handler(int irq, void *dev_id) +{ + struct fsl_edma_chan *fsl_chan = dev_id; + unsigned int intr; - if (!fsl_chan->edesc) { - /* terminate_all called before */ - spin_unlock(&fsl_chan->vchan.lock); - continue; - } + intr = edma_readl_chreg(fsl_chan, ch_int); + if (!intr) + return IRQ_HANDLED; - if (!fsl_chan->edesc->iscyclic) { - list_del(&fsl_chan->edesc->vdesc.node); - vchan_cookie_complete(&fsl_chan->edesc->vdesc); - fsl_chan->edesc = NULL; - fsl_chan->status = DMA_COMPLETE; - fsl_chan->idle = true; - } else { - vchan_cyclic_callback(&fsl_chan->edesc->vdesc); - } + edma_writel_chreg(fsl_chan, 1, ch_int); - if (!fsl_chan->edesc) - fsl_edma_xfer_desc(fsl_chan); + fsl_edma_tx_chan_handler(fsl_chan); - spin_unlock(&fsl_chan->vchan.lock); - } - } return IRQ_HANDLED; } @@ -86,8 +83,7 @@ static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) if (err & (0x1 << ch)) { fsl_edma_disable_request(&fsl_edma->chans[ch]); edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), regs->cerr); - fsl_edma->chans[ch].status = DMA_ERROR; - fsl_edma->chans[ch].idle = true; + fsl_edma_err_chan_handler(&fsl_edma->chans[ch]); } } return IRQ_HANDLED; @@ -134,11 +130,58 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, return NULL; } +static struct dma_chan *fsl_edma3_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; + struct dma_chan *chan, *_chan; + struct fsl_edma_chan *fsl_chan; + bool b_chmux; + int i; + + if (dma_spec->args_count != 3) + return NULL; + + b_chmux = !!(fsl_edma->drvdata->flags & FSL_EDMA_DRV_HAS_CHMUX); + + mutex_lock(&fsl_edma->fsl_edma_mutex); + list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, + device_node) { + + if (chan->client_count) + continue; + + fsl_chan = to_fsl_edma_chan(chan); + i = fsl_chan - fsl_edma->chans; + + chan = dma_get_slave_channel(chan); + chan->device->privatecnt++; + fsl_chan->priority = dma_spec->args[1]; + fsl_chan->is_rxchan = dma_spec->args[2] & ARGS_RX; + fsl_chan->is_remote = dma_spec->args[2] & ARGS_REMOTE; + fsl_chan->is_multi_fifo = dma_spec->args[2] & ARGS_MULTI_FIFO; + + if (!b_chmux && i == dma_spec->args[0]) { + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return chan; + } else if (b_chmux && !fsl_chan->srcid) { + /* if controller support channel mux, choose a free channel */ + fsl_chan->srcid = dma_spec->args[0]; + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return chan; + } + } + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return NULL; +} + static int fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) { int ret; + edma_writel(fsl_edma, ~0, fsl_edma->regs.intl); + fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx"); if (fsl_edma->txirq < 0) return fsl_edma->txirq; @@ -173,6 +216,37 @@ fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma return 0; } +static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) +{ + int ret; + int i; + + for (i = 0; i < fsl_edma->n_chans; i++) { + + struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; + + if (fsl_edma->chan_masked & BIT(i)) + continue; + + /* request channel irq */ + fsl_chan->txirq = platform_get_irq(pdev, i); + if (fsl_chan->txirq < 0) { + dev_err(&pdev->dev, "Can't get chan %d's irq.\n", i); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, fsl_chan->txirq, + fsl_edma3_tx_handler, IRQF_SHARED, + fsl_chan->chan_name, fsl_chan); + if (ret) { + dev_err(&pdev->dev, "Can't register chan%d's IRQ.\n", i); + return -EINVAL; + } + } + + return 0; +} + static int fsl_edma2_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) @@ -180,6 +254,8 @@ fsl_edma2_irq_init(struct platform_device *pdev, int i, ret, irq; int count; + edma_writel(fsl_edma, ~0, fsl_edma->regs.intl); + count = platform_irq_count(pdev); dev_dbg(&pdev->dev, "%s Found %d interrupts\r\n", __func__, count); if (count <= 2) { @@ -197,8 +273,6 @@ fsl_edma2_irq_init(struct platform_device *pdev, if (irq < 0) return -ENXIO; - sprintf(fsl_edma->chans[i].chan_name, "eDMA2-CH%02d", i); - /* The last IRQ is for eDMA err */ if (i == count - 1) ret = devm_request_irq(&pdev->dev, irq, @@ -236,33 +310,110 @@ static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks) } static struct fsl_edma_drvdata vf610_data = { - .version = v1, .dmamuxs = DMAMUX_NR, + .flags = FSL_EDMA_DRV_WRAP_IO, + .chreg_off = EDMA_TCD, + .chreg_space_sz = sizeof(struct fsl_edma_hw_tcd), .setup_irq = fsl_edma_irq_init, }; static struct fsl_edma_drvdata ls1028a_data = { - .version = v1, .dmamuxs = DMAMUX_NR, - .mux_swap = true, + .flags = FSL_EDMA_DRV_MUX_SWAP | FSL_EDMA_DRV_WRAP_IO, + .chreg_off = EDMA_TCD, + .chreg_space_sz = sizeof(struct fsl_edma_hw_tcd), .setup_irq = fsl_edma_irq_init, }; static struct fsl_edma_drvdata imx7ulp_data = { - .version = v3, .dmamuxs = 1, - .has_dmaclk = true, + .chreg_off = EDMA_TCD, + .chreg_space_sz = sizeof(struct fsl_edma_hw_tcd), + .flags = FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_CONFIG32, .setup_irq = fsl_edma2_irq_init, }; +static struct fsl_edma_drvdata imx8qm_data = { + .flags = FSL_EDMA_DRV_HAS_PD | FSL_EDMA_DRV_EDMA3, + .chreg_space_sz = 0x10000, + .chreg_off = 0x10000, + .setup_irq = fsl_edma3_irq_init, +}; + +static struct fsl_edma_drvdata imx8qm_audio_data = { + .flags = FSL_EDMA_DRV_QUIRK_SWAPPED | FSL_EDMA_DRV_HAS_PD | FSL_EDMA_DRV_EDMA3, + .chreg_space_sz = 0x10000, + .chreg_off = 0x10000, + .setup_irq = fsl_edma3_irq_init, +}; + +static struct fsl_edma_drvdata imx93_data3 = { + .flags = FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA3, + .chreg_space_sz = 0x10000, + .chreg_off = 0x10000, + .setup_irq = fsl_edma3_irq_init, +}; + +static struct fsl_edma_drvdata imx93_data4 = { + .flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA3, + .chreg_space_sz = 0x8000, + .chreg_off = 0x10000, + .setup_irq = fsl_edma3_irq_init, +}; + static const struct of_device_id fsl_edma_dt_ids[] = { { .compatible = "fsl,vf610-edma", .data = &vf610_data}, { .compatible = "fsl,ls1028a-edma", .data = &ls1028a_data}, { .compatible = "fsl,imx7ulp-edma", .data = &imx7ulp_data}, + { .compatible = "fsl,imx8qm-edma", .data = &imx8qm_data}, + { .compatible = "fsl,imx8qm-adma", .data = &imx8qm_audio_data}, + { .compatible = "fsl,imx93-edma3", .data = &imx93_data3}, + { .compatible = "fsl,imx93-edma4", .data = &imx93_data4}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); +static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) +{ + struct fsl_edma_chan *fsl_chan; + struct device_link *link; + struct device *pd_chan; + struct device *dev; + int i; + + dev = &pdev->dev; + + for (i = 0; i < fsl_edma->n_chans; i++) { + if (fsl_edma->chan_masked & BIT(i)) + continue; + + fsl_chan = &fsl_edma->chans[i]; + + pd_chan = dev_pm_domain_attach_by_id(dev, i); + if (IS_ERR_OR_NULL(pd_chan)) { + dev_err(dev, "Failed attach pd %d\n", i); + return -EINVAL; + } + + link = device_link_add(dev, pd_chan, DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (IS_ERR(link)) { + dev_err(dev, "Failed to add device_link to %d: %ld\n", i, + PTR_ERR(link)); + return -EINVAL; + } + + fsl_chan->pd_dev = pd_chan; + + pm_runtime_use_autosuspend(fsl_chan->pd_dev); + pm_runtime_set_autosuspend_delay(fsl_chan->pd_dev, 200); + pm_runtime_set_active(fsl_chan->pd_dev); + } + + return 0; +} + static int fsl_edma_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -270,9 +421,9 @@ static int fsl_edma_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct fsl_edma_engine *fsl_edma; const struct fsl_edma_drvdata *drvdata = NULL; - struct fsl_edma_chan *fsl_chan; + u32 chan_mask[2] = {0, 0}; struct edma_regs *regs; - int len, chans; + int chans; int ret, i; if (of_id) @@ -288,8 +439,8 @@ static int fsl_edma_probe(struct platform_device *pdev) return ret; } - len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans; - fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + fsl_edma = devm_kzalloc(&pdev->dev, struct_size(fsl_edma, chans, chans), + GFP_KERNEL); if (!fsl_edma) return -ENOMEM; @@ -301,26 +452,42 @@ static int fsl_edma_probe(struct platform_device *pdev) if (IS_ERR(fsl_edma->membase)) return PTR_ERR(fsl_edma->membase); - fsl_edma_setup_regs(fsl_edma); - regs = &fsl_edma->regs; + if (!(drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)) { + fsl_edma_setup_regs(fsl_edma); + regs = &fsl_edma->regs; + } - if (drvdata->has_dmaclk) { - fsl_edma->dmaclk = devm_clk_get(&pdev->dev, "dma"); + if (drvdata->flags & FSL_EDMA_DRV_HAS_DMACLK) { + fsl_edma->dmaclk = devm_clk_get_enabled(&pdev->dev, "dma"); if (IS_ERR(fsl_edma->dmaclk)) { dev_err(&pdev->dev, "Missing DMA block clock.\n"); return PTR_ERR(fsl_edma->dmaclk); } + } - ret = clk_prepare_enable(fsl_edma->dmaclk); - if (ret) { - dev_err(&pdev->dev, "DMA clk block failed.\n"); - return ret; + if (drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK) { + fsl_edma->chclk = devm_clk_get_enabled(&pdev->dev, "mp"); + if (IS_ERR(fsl_edma->chclk)) { + dev_err(&pdev->dev, "Missing MP block clock.\n"); + return PTR_ERR(fsl_edma->chclk); } } + ret = of_property_read_variable_u32_array(np, "dma-channel-mask", chan_mask, 1, 2); + + if (ret > 0) { + fsl_edma->chan_masked = chan_mask[1]; + fsl_edma->chan_masked <<= 32; + fsl_edma->chan_masked |= chan_mask[0]; + } + for (i = 0; i < fsl_edma->drvdata->dmamuxs; i++) { char clkname[32]; + /* eDMAv3 mux register move to TCD area if ch_mux exist */ + if (drvdata->flags & FSL_EDMA_DRV_SPLIT_REG) + break; + fsl_edma->muxbase[i] = devm_platform_ioremap_resource(pdev, 1 + i); if (IS_ERR(fsl_edma->muxbase[i])) { @@ -330,26 +497,32 @@ static int fsl_edma_probe(struct platform_device *pdev) } sprintf(clkname, "dmamux%d", i); - fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname); + fsl_edma->muxclk[i] = devm_clk_get_enabled(&pdev->dev, clkname); if (IS_ERR(fsl_edma->muxclk[i])) { dev_err(&pdev->dev, "Missing DMAMUX block clock.\n"); /* on error: disable all previously enabled clks */ - fsl_disable_clocks(fsl_edma, i); return PTR_ERR(fsl_edma->muxclk[i]); } - - ret = clk_prepare_enable(fsl_edma->muxclk[i]); - if (ret) - /* on error: disable all previously enabled clks */ - fsl_disable_clocks(fsl_edma, i); - } fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); + if (drvdata->flags & FSL_EDMA_DRV_HAS_PD) { + ret = fsl_edma3_attach_pd(pdev, fsl_edma); + if (ret) + return ret; + } + INIT_LIST_HEAD(&fsl_edma->dma_dev.channels); for (i = 0; i < fsl_edma->n_chans; i++) { struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; + int len; + + if (fsl_edma->chan_masked & BIT(i)) + continue; + + snprintf(fsl_chan->chan_name, sizeof(fsl_chan->chan_name), "%s-CH%02d", + dev_name(&pdev->dev), i); fsl_chan->edma = fsl_edma; fsl_chan->pm_state = RUNNING; @@ -357,13 +530,19 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_chan->idle = true; fsl_chan->dma_dir = DMA_NONE; fsl_chan->vchan.desc_free = fsl_edma_free_desc; + + len = (drvdata->flags & FSL_EDMA_DRV_SPLIT_REG) ? + offsetof(struct fsl_edma3_ch_reg, tcd) : 0; + fsl_chan->tcd = fsl_edma->membase + + i * drvdata->chreg_space_sz + drvdata->chreg_off + len; + + fsl_chan->pdev = pdev; vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); - edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); + edma_write_tcdreg(fsl_chan, 0, csr); fsl_edma_chan_mux(fsl_chan, 0, false); } - edma_writel(fsl_edma, ~0, regs->intl); ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma); if (ret) return ret; @@ -391,33 +570,47 @@ static int fsl_edma_probe(struct platform_device *pdev) fsl_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; fsl_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; + + if (drvdata->flags & FSL_EDMA_DRV_BUS_8BYTE) { + fsl_edma->dma_dev.src_addr_widths |= BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + fsl_edma->dma_dev.dst_addr_widths |= BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + } + fsl_edma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + if (drvdata->flags & FSL_EDMA_DRV_DEV_TO_DEV) + fsl_edma->dma_dev.directions |= BIT(DMA_DEV_TO_DEV); + + fsl_edma->dma_dev.copy_align = drvdata->flags & FSL_EDMA_DRV_ALIGN_64BYTE ? + DMAENGINE_ALIGN_64_BYTES : + DMAENGINE_ALIGN_32_BYTES; - fsl_edma->dma_dev.copy_align = DMAENGINE_ALIGN_32_BYTES; /* Per worst case 'nbytes = 1' take CITER as the max_seg_size */ dma_set_max_seg_size(fsl_edma->dma_dev.dev, 0x3fff); + fsl_edma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; + platform_set_drvdata(pdev, fsl_edma); ret = dma_async_device_register(&fsl_edma->dma_dev); if (ret) { dev_err(&pdev->dev, "Can't register Freescale eDMA engine. (%d)\n", ret); - fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs); return ret; } - ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma); + ret = of_dma_controller_register(np, + drvdata->flags & FSL_EDMA_DRV_SPLIT_REG ? fsl_edma3_xlate : fsl_edma_xlate, + fsl_edma); if (ret) { dev_err(&pdev->dev, "Can't register Freescale eDMA of_dma. (%d)\n", ret); dma_async_device_unregister(&fsl_edma->dma_dev); - fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs); return ret; } /* enable round robin arbitration */ - edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); + if (!(drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)) + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr); return 0; } @@ -470,7 +663,7 @@ static int fsl_edma_resume_early(struct device *dev) for (i = 0; i < fsl_edma->n_chans; i++) { fsl_chan = &fsl_edma->chans[i]; fsl_chan->pm_state = RUNNING; - edma_writew(fsl_edma, 0x0, ®s->tcd[i].csr); + edma_write_tcdreg(fsl_chan, 0, csr); if (fsl_chan->slave_id != 0) fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true); } diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c index eddb2688f234060cbfdb12e6e31a4f1ec6dc2f7d..a8cc8a4bc6102c4b94ae08693de596d8597cf832 100644 --- a/drivers/dma/fsl-qdma.c +++ b/drivers/dma/fsl-qdma.c @@ -13,10 +13,10 @@ #include #include -#include -#include +#include #include #include +#include #include "virt-dma.h" #include "fsldma.h" diff --git a/drivers/dma/fsl_raid.c b/drivers/dma/fsl_raid.c index fdf3500d96a9e1667f2d2902ae30c0939893c034..0b9ca93ce3dca72addb8e82a439c227c9d672311 100644 --- a/drivers/dma/fsl_raid.c +++ b/drivers/dma/fsl_raid.c @@ -60,9 +60,10 @@ */ #include #include +#include #include -#include #include +#include #include #include #include diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index f8459cc5315dfa49c079b578b94c1bf06acc5e8d..ddcf736d283d8775f14d5b509042053cdda2bb02 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -28,9 +28,10 @@ #include #include #include +#include #include #include -#include +#include #include #include "dmaengine.h" #include "fsldma.h" diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c index 9a15f0d12c79989006dd78110d51f83b433b3b9d..22d6f4e455b7973cae80208ccfcd638f3e0e0dca 100644 --- a/drivers/dma/idxd/device.c +++ b/drivers/dma/idxd/device.c @@ -299,21 +299,6 @@ void idxd_wqs_unmap_portal(struct idxd_device *idxd) } } -static void __idxd_wq_set_priv_locked(struct idxd_wq *wq, int priv) -{ - struct idxd_device *idxd = wq->idxd; - union wqcfg wqcfg; - unsigned int offset; - - offset = WQCFG_OFFSET(idxd, wq->id, WQCFG_PRIVL_IDX); - spin_lock(&idxd->dev_lock); - wqcfg.bits[WQCFG_PRIVL_IDX] = ioread32(idxd->reg_base + offset); - wqcfg.priv = priv; - wq->wqcfg->bits[WQCFG_PRIVL_IDX] = wqcfg.bits[WQCFG_PRIVL_IDX]; - iowrite32(wqcfg.bits[WQCFG_PRIVL_IDX], idxd->reg_base + offset); - spin_unlock(&idxd->dev_lock); -} - static void __idxd_wq_set_pasid_locked(struct idxd_wq *wq, int pasid) { struct idxd_device *idxd = wq->idxd; @@ -784,8 +769,6 @@ static int idxd_device_evl_setup(struct idxd_device *idxd) goto err_alloc; } - memset(addr, 0, size); - spin_lock(&evl->lock); evl->log = addr; evl->dma = dma_addr; @@ -1421,15 +1404,14 @@ int drv_enable_wq(struct idxd_wq *wq) } /* - * In the event that the WQ is configurable for pasid and priv bits. - * For kernel wq, the driver should setup the pasid, pasid_en, and priv bit. - * However, for non-kernel wq, the driver should only set the pasid_en bit for - * shared wq. A dedicated wq that is not 'kernel' type will configure pasid and + * In the event that the WQ is configurable for pasid, the driver + * should setup the pasid, pasid_en bit. This is true for both kernel + * and user shared workqueues. There is no need to setup priv bit in + * that in-kernel DMA will also do user privileged requests. + * A dedicated wq that is not 'kernel' type will configure pasid and * pasid_en later on so there is no need to setup. */ if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) { - int priv = 0; - if (wq_pasid_enabled(wq)) { if (is_idxd_wq_kernel(wq) || wq_shared(wq)) { u32 pasid = wq_dedicated(wq) ? idxd->pasid : 0; @@ -1437,10 +1419,6 @@ int drv_enable_wq(struct idxd_wq *wq) __idxd_wq_set_pasid_locked(wq, pasid); } } - - if (is_idxd_wq_kernel(wq)) - priv = 1; - __idxd_wq_set_priv_locked(wq, priv); } rc = 0; @@ -1548,6 +1526,15 @@ int idxd_device_drv_probe(struct idxd_dev *idxd_dev) if (rc < 0) return -ENXIO; + /* + * System PASID is preserved across device disable/enable cycle, but + * genconfig register content gets cleared during device reset. We + * need to re-enable user interrupts for kernel work queue completion + * IRQ to function. + */ + if (idxd->pasid != IOMMU_PASID_INVALID) + idxd_set_user_intr(idxd, 1); + rc = idxd_device_evl_setup(idxd); if (rc < 0) { idxd->cmd_status = IDXD_SCMD_DEV_EVL_ERR; diff --git a/drivers/dma/idxd/dma.c b/drivers/dma/idxd/dma.c index eb35ca313684596831a60e662004538f6b5b25f4..07623fb0f52fc2bb160224ecf5d0dc0e625e2b00 100644 --- a/drivers/dma/idxd/dma.c +++ b/drivers/dma/idxd/dma.c @@ -75,9 +75,10 @@ static inline void idxd_prep_desc_common(struct idxd_wq *wq, hw->xfer_size = len; /* * For dedicated WQ, this field is ignored and HW will use the WQCFG.priv - * field instead. This field should be set to 1 for kernel descriptors. + * field instead. This field should be set to 0 for kernel descriptors + * since kernel DMA on VT-d supports "user" privilege only. */ - hw->priv = 1; + hw->priv = 0; hw->completion_addr = compl; } diff --git a/drivers/dma/idxd/idxd.h b/drivers/dma/idxd/idxd.h index 5428a2e1b1ec4c482558f4c844895c1f2cc6c03a..e269ca1f48625513fb03df0f678919db5ccc3c48 100644 --- a/drivers/dma/idxd/idxd.h +++ b/drivers/dma/idxd/idxd.h @@ -473,6 +473,15 @@ static inline struct idxd_device *ie_to_idxd(struct idxd_irq_entry *ie) return container_of(ie, struct idxd_device, ie); } +static inline void idxd_set_user_intr(struct idxd_device *idxd, bool enable) +{ + union gencfg_reg reg; + + reg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET); + reg.user_int_en = enable; + iowrite32(reg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET); +} + extern struct bus_type dsa_bus_type; extern bool support_enqcmd; @@ -651,8 +660,6 @@ int idxd_register_bus_type(void); void idxd_unregister_bus_type(void); int idxd_register_devices(struct idxd_device *idxd); void idxd_unregister_devices(struct idxd_device *idxd); -int idxd_register_driver(void); -void idxd_unregister_driver(void); void idxd_wqs_quiesce(struct idxd_device *idxd); bool idxd_queue_int_handle_resubmit(struct idxd_desc *desc); void multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count); @@ -664,8 +671,6 @@ void idxd_mask_error_interrupts(struct idxd_device *idxd); void idxd_unmask_error_interrupts(struct idxd_device *idxd); /* device control */ -int idxd_register_idxd_drv(void); -void idxd_unregister_idxd_drv(void); int idxd_device_drv_probe(struct idxd_dev *idxd_dev); void idxd_device_drv_remove(struct idxd_dev *idxd_dev); int drv_enable_wq(struct idxd_wq *wq); @@ -710,7 +715,6 @@ int idxd_enqcmds(struct idxd_wq *wq, void __iomem *portal, const void *desc); /* dmaengine */ int idxd_register_dma_device(struct idxd_device *idxd); void idxd_unregister_dma_device(struct idxd_device *idxd); -void idxd_parse_completion_status(u8 status, enum dmaengine_tx_result *res); void idxd_dma_complete_txd(struct idxd_desc *desc, enum idxd_complete_type comp_type, bool free_desc); diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index 1aa823974cdac6c0458ea2a5029166fda2e29553..0eb1c827a215f9b5ff556dbf74652acf8fc7ed99 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -550,14 +550,59 @@ static struct idxd_device *idxd_alloc(struct pci_dev *pdev, struct idxd_driver_d static int idxd_enable_system_pasid(struct idxd_device *idxd) { - return -EOPNOTSUPP; + struct pci_dev *pdev = idxd->pdev; + struct device *dev = &pdev->dev; + struct iommu_domain *domain; + ioasid_t pasid; + int ret; + + /* + * Attach a global PASID to the DMA domain so that we can use ENQCMDS + * to submit work on buffers mapped by DMA API. + */ + domain = iommu_get_domain_for_dev(dev); + if (!domain) + return -EPERM; + + pasid = iommu_alloc_global_pasid(dev); + if (pasid == IOMMU_PASID_INVALID) + return -ENOSPC; + + /* + * DMA domain is owned by the driver, it should support all valid + * types such as DMA-FQ, identity, etc. + */ + ret = iommu_attach_device_pasid(domain, dev, pasid); + if (ret) { + dev_err(dev, "failed to attach device pasid %d, domain type %d", + pasid, domain->type); + iommu_free_global_pasid(pasid); + return ret; + } + + /* Since we set user privilege for kernel DMA, enable completion IRQ */ + idxd_set_user_intr(idxd, 1); + idxd->pasid = pasid; + + return ret; } static void idxd_disable_system_pasid(struct idxd_device *idxd) { + struct pci_dev *pdev = idxd->pdev; + struct device *dev = &pdev->dev; + struct iommu_domain *domain; + + domain = iommu_get_domain_for_dev(dev); + if (!domain) + return; + + iommu_detach_device_pasid(domain, dev, idxd->pasid); + iommu_free_global_pasid(idxd->pasid); - iommu_sva_unbind_device(idxd->sva); + idxd_set_user_intr(idxd, 0); idxd->sva = NULL; + idxd->pasid = IOMMU_PASID_INVALID; } static int idxd_enable_sva(struct pci_dev *pdev) @@ -600,8 +645,9 @@ static int idxd_probe(struct idxd_device *idxd) } else { set_bit(IDXD_FLAG_USER_PASID_ENABLED, &idxd->flags); - if (idxd_enable_system_pasid(idxd)) - dev_warn(dev, "No in-kernel DMA with PASID.\n"); + rc = idxd_enable_system_pasid(idxd); + if (rc) + dev_warn(dev, "No in-kernel DMA with PASID. %d\n", rc); else set_bit(IDXD_FLAG_PASID_ENABLED, &idxd->flags); } diff --git a/drivers/dma/idxd/perfmon.c b/drivers/dma/idxd/perfmon.c index d73004f47cf4b40f0cddf8a57907500884b69e29..fdda6d60426295baf11b191186b1d3956c1d94ae 100644 --- a/drivers/dma/idxd/perfmon.c +++ b/drivers/dma/idxd/perfmon.c @@ -245,12 +245,11 @@ static void perfmon_pmu_event_update(struct perf_event *event) int shift = 64 - idxd->idxd_pmu->counter_width; struct hw_perf_event *hwc = &event->hw; + prev_raw_count = local64_read(&hwc->prev_count); do { - prev_raw_count = local64_read(&hwc->prev_count); new_raw_count = perfmon_pmu_read_counter(event); - } while (local64_cmpxchg(&hwc->prev_count, prev_raw_count, - new_raw_count) != prev_raw_count); - + } while (!local64_try_cmpxchg(&hwc->prev_count, + &prev_raw_count, new_raw_count)); n = (new_raw_count << shift); p = (prev_raw_count << shift); diff --git a/drivers/dma/idxd/sysfs.c b/drivers/dma/idxd/sysfs.c index 293739ac55969e0a26fcf3adcc6b049583938e33..7caba90d85b31e2d2519e7ff564f2c106e51ccc2 100644 --- a/drivers/dma/idxd/sysfs.c +++ b/drivers/dma/idxd/sysfs.c @@ -948,13 +948,6 @@ static ssize_t wq_name_store(struct device *dev, if (strlen(buf) > WQ_NAME_SIZE || strlen(buf) == 0) return -EINVAL; - /* - * This is temporarily placed here until we have SVM support for - * dmaengine. - */ - if (wq->type == IDXD_WQT_KERNEL && device_pasid_enabled(wq->idxd)) - return -EOPNOTSUPP; - input = kstrndup(buf, count, GFP_KERNEL); if (!input) return -ENOMEM; @@ -1095,8 +1088,8 @@ static ssize_t wq_ats_disable_store(struct device *dev, struct device_attribute if (wq->state != IDXD_WQ_DISABLED) return -EPERM; - if (!idxd->hw.wq_cap.wq_ats_support) - return -EOPNOTSUPP; + if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) + return -EPERM; rc = kstrtobool(buf, &ats_dis); if (rc < 0) @@ -1131,8 +1124,8 @@ static ssize_t wq_prs_disable_store(struct device *dev, struct device_attribute if (wq->state != IDXD_WQ_DISABLED) return -EPERM; - if (!idxd->hw.wq_cap.wq_prs_support) - return -EOPNOTSUPP; + if (!test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags)) + return -EPERM; rc = kstrtobool(buf, &prs_dis); if (rc < 0) @@ -1288,12 +1281,9 @@ static struct attribute *idxd_wq_attributes[] = { NULL, }; -static bool idxd_wq_attr_op_config_invisible(struct attribute *attr, - struct idxd_device *idxd) -{ - return attr == &dev_attr_wq_op_config.attr && - !idxd->hw.wq_cap.op_config; -} +/* A WQ attr is invisible if the feature is not supported in WQCAP. */ +#define idxd_wq_attr_invisible(name, cap_field, a, idxd) \ + ((a) == &dev_attr_wq_##name.attr && !(idxd)->hw.wq_cap.cap_field) static bool idxd_wq_attr_max_batch_size_invisible(struct attribute *attr, struct idxd_device *idxd) @@ -1303,13 +1293,6 @@ static bool idxd_wq_attr_max_batch_size_invisible(struct attribute *attr, idxd->data->type == IDXD_TYPE_IAX; } -static bool idxd_wq_attr_wq_prs_disable_invisible(struct attribute *attr, - struct idxd_device *idxd) -{ - return attr == &dev_attr_wq_prs_disable.attr && - !idxd->hw.wq_cap.wq_prs_support; -} - static umode_t idxd_wq_attr_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -1317,13 +1300,16 @@ static umode_t idxd_wq_attr_visible(struct kobject *kobj, struct idxd_wq *wq = confdev_to_wq(dev); struct idxd_device *idxd = wq->idxd; - if (idxd_wq_attr_op_config_invisible(attr, idxd)) + if (idxd_wq_attr_invisible(op_config, op_config, attr, idxd)) return 0; if (idxd_wq_attr_max_batch_size_invisible(attr, idxd)) return 0; - if (idxd_wq_attr_wq_prs_disable_invisible(attr, idxd)) + if (idxd_wq_attr_invisible(prs_disable, wq_prs_support, attr, idxd)) + return 0; + + if (idxd_wq_attr_invisible(ats_disable, wq_ats_support, attr, idxd)) return 0; return attr->mode; @@ -1480,7 +1466,7 @@ static ssize_t pasid_enabled_show(struct device *dev, { struct idxd_device *idxd = confdev_to_idxd(dev); - return sysfs_emit(buf, "%u\n", device_pasid_enabled(idxd)); + return sysfs_emit(buf, "%u\n", device_user_pasid_enabled(idxd)); } static DEVICE_ATTR_RO(pasid_enabled); diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c index ad084552640ffd8c250cdd1daad7fe8142de6807..9be0d3226e1992463e4920447c445ef103064e8f 100644 --- a/drivers/dma/img-mdc-dma.c +++ b/drivers/dma/img-mdc-dma.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index f040751690af1f0ee19ddcbe5eda9dde0e3b64a0..114f254b9f5057c290608c2b168535f3058ac55f 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 7a912f90c2a9a90ab4e87f97f07724f368f0087f..51012bd399002d7fc45820a84321d12817efe389 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/drivers/dma/ioat/dca.c b/drivers/dma/ioat/dca.c index 289c59ed74b9770f7ab7bee036059a6aac223b77..17f6b636711360bf2adebb470f02d1dcf136a81e 100644 --- a/drivers/dma/ioat/dca.c +++ b/drivers/dma/ioat/dca.c @@ -51,7 +51,7 @@ /* pack PCI B/D/F into a u16 */ static inline u16 dcaid_from_pcidev(struct pci_dev *pci) { - return (pci->bus->number << 8) | pci->devfn; + return pci_dev_id(pci); } static int dca_enabled_in_bios(struct pci_dev *pdev) diff --git a/drivers/dma/ioat/dma.h b/drivers/dma/ioat/dma.h index 35e06b3826034a99416a9139b0da39cba9c433e0..a180171087a872e1a593572ad40802bc6d375f5a 100644 --- a/drivers/dma/ioat/dma.h +++ b/drivers/dma/ioat/dma.h @@ -74,6 +74,7 @@ struct ioatdma_device { struct dca_provider *dca; enum ioat_irq_mode irq_mode; u32 cap; + int chancnt; /* shadow version for CB3.3 chan reset errata workaround */ u64 msixtba0; diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c index c4602bfc9c74916d935e49f553e291e1e01a8ddc..9c364e92cb828da1833a6ec024196e448981ab79 100644 --- a/drivers/dma/ioat/init.c +++ b/drivers/dma/ioat/init.c @@ -420,7 +420,7 @@ int ioat_dma_setup_interrupts(struct ioatdma_device *ioat_dma) msix: /* The number of MSI-X vectors should equal the number of channels */ - msixcnt = ioat_dma->dma_dev.chancnt; + msixcnt = ioat_dma->chancnt; for (i = 0; i < msixcnt; i++) ioat_dma->msix_entries[i].entry = i; @@ -511,7 +511,7 @@ static int ioat_probe(struct ioatdma_device *ioat_dma) dma_cap_set(DMA_MEMCPY, dma->cap_mask); dma->dev = &pdev->dev; - if (!dma->chancnt) { + if (!ioat_dma->chancnt) { dev_err(dev, "channel enumeration error\n"); goto err_setup_interrupts; } @@ -567,15 +567,16 @@ static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma) struct device *dev = &ioat_dma->pdev->dev; struct dma_device *dma = &ioat_dma->dma_dev; u8 xfercap_log; + int chancnt; int i; INIT_LIST_HEAD(&dma->channels); - dma->chancnt = readb(ioat_dma->reg_base + IOAT_CHANCNT_OFFSET); - dma->chancnt &= 0x1f; /* bits [4:0] valid */ - if (dma->chancnt > ARRAY_SIZE(ioat_dma->idx)) { + chancnt = readb(ioat_dma->reg_base + IOAT_CHANCNT_OFFSET); + chancnt &= 0x1f; /* bits [4:0] valid */ + if (chancnt > ARRAY_SIZE(ioat_dma->idx)) { dev_warn(dev, "(%d) exceeds max supported channels (%zu)\n", - dma->chancnt, ARRAY_SIZE(ioat_dma->idx)); - dma->chancnt = ARRAY_SIZE(ioat_dma->idx); + chancnt, ARRAY_SIZE(ioat_dma->idx)); + chancnt = ARRAY_SIZE(ioat_dma->idx); } xfercap_log = readb(ioat_dma->reg_base + IOAT_XFERCAP_OFFSET); xfercap_log &= 0x1f; /* bits [4:0] valid */ @@ -583,7 +584,7 @@ static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma) return; dev_dbg(dev, "%s: xfercap = %d\n", __func__, 1 << xfercap_log); - for (i = 0; i < dma->chancnt; i++) { + for (i = 0; i < chancnt; i++) { ioat_chan = kzalloc(sizeof(*ioat_chan), GFP_KERNEL); if (!ioat_chan) break; @@ -596,7 +597,7 @@ static void ioat_enumerate_channels(struct ioatdma_device *ioat_dma) break; } } - dma->chancnt = i; + ioat_dma->chancnt = i; } /** diff --git a/drivers/dma/ipu/Makefile b/drivers/dma/ipu/Makefile deleted file mode 100644 index c79ff116daf610e184f3912600437bb18fb82b10..0000000000000000000000000000000000000000 --- a/drivers/dma/ipu/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-y += ipu_irq.o ipu_idmac.o diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c deleted file mode 100644 index d799b99c18bdafc90cbb03500c89048b2350cb9f..0000000000000000000000000000000000000000 --- a/drivers/dma/ipu/ipu_idmac.c +++ /dev/null @@ -1,1801 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, - * - * Copyright (C) 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../dmaengine.h" -#include "ipu_intern.h" - -#define FS_VF_IN_VALID 0x00000002 -#define FS_ENC_IN_VALID 0x00000001 - -static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, - bool wait_for_stop); - -/* - * There can be only one, we could allocate it dynamically, but then we'd have - * to add an extra parameter to some functions, and use something as ugly as - * struct ipu *ipu = to_ipu(to_idmac(ichan->dma_chan.device)); - * in the ISR - */ -static struct ipu ipu_data; - -#define to_ipu(id) container_of(id, struct ipu, idmac) - -static u32 __idmac_read_icreg(struct ipu *ipu, unsigned long reg) -{ - return __raw_readl(ipu->reg_ic + reg); -} - -#define idmac_read_icreg(ipu, reg) __idmac_read_icreg(ipu, reg - IC_CONF) - -static void __idmac_write_icreg(struct ipu *ipu, u32 value, unsigned long reg) -{ - __raw_writel(value, ipu->reg_ic + reg); -} - -#define idmac_write_icreg(ipu, v, reg) __idmac_write_icreg(ipu, v, reg - IC_CONF) - -static u32 idmac_read_ipureg(struct ipu *ipu, unsigned long reg) -{ - return __raw_readl(ipu->reg_ipu + reg); -} - -static void idmac_write_ipureg(struct ipu *ipu, u32 value, unsigned long reg) -{ - __raw_writel(value, ipu->reg_ipu + reg); -} - -/***************************************************************************** - * IPU / IC common functions - */ -static void dump_idmac_reg(struct ipu *ipu) -{ - dev_dbg(ipu->dev, "IDMAC_CONF 0x%x, IC_CONF 0x%x, IDMAC_CHA_EN 0x%x, " - "IDMAC_CHA_PRI 0x%x, IDMAC_CHA_BUSY 0x%x\n", - idmac_read_icreg(ipu, IDMAC_CONF), - idmac_read_icreg(ipu, IC_CONF), - idmac_read_icreg(ipu, IDMAC_CHA_EN), - idmac_read_icreg(ipu, IDMAC_CHA_PRI), - idmac_read_icreg(ipu, IDMAC_CHA_BUSY)); - dev_dbg(ipu->dev, "BUF0_RDY 0x%x, BUF1_RDY 0x%x, CUR_BUF 0x%x, " - "DB_MODE 0x%x, TASKS_STAT 0x%x\n", - idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), - idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), - idmac_read_ipureg(ipu, IPU_CHA_CUR_BUF), - idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL), - idmac_read_ipureg(ipu, IPU_TASKS_STAT)); -} - -static uint32_t bytes_per_pixel(enum pixel_fmt fmt) -{ - switch (fmt) { - case IPU_PIX_FMT_GENERIC: /* generic data */ - case IPU_PIX_FMT_RGB332: - case IPU_PIX_FMT_YUV420P: - case IPU_PIX_FMT_YUV422P: - default: - return 1; - case IPU_PIX_FMT_RGB565: - case IPU_PIX_FMT_YUYV: - case IPU_PIX_FMT_UYVY: - return 2; - case IPU_PIX_FMT_BGR24: - case IPU_PIX_FMT_RGB24: - return 3; - case IPU_PIX_FMT_GENERIC_32: /* generic data */ - case IPU_PIX_FMT_BGR32: - case IPU_PIX_FMT_RGB32: - case IPU_PIX_FMT_ABGR32: - return 4; - } -} - -/* Enable direct write to memory by the Camera Sensor Interface */ -static void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel) -{ - uint32_t ic_conf, mask; - - switch (channel) { - case IDMAC_IC_0: - mask = IC_CONF_PRPENC_EN; - break; - case IDMAC_IC_7: - mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; - break; - default: - return; - } - ic_conf = idmac_read_icreg(ipu, IC_CONF) | mask; - idmac_write_icreg(ipu, ic_conf, IC_CONF); -} - -/* Called under spin_lock_irqsave(&ipu_data.lock) */ -static void ipu_ic_disable_task(struct ipu *ipu, enum ipu_channel channel) -{ - uint32_t ic_conf, mask; - - switch (channel) { - case IDMAC_IC_0: - mask = IC_CONF_PRPENC_EN; - break; - case IDMAC_IC_7: - mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; - break; - default: - return; - } - ic_conf = idmac_read_icreg(ipu, IC_CONF) & ~mask; - idmac_write_icreg(ipu, ic_conf, IC_CONF); -} - -static uint32_t ipu_channel_status(struct ipu *ipu, enum ipu_channel channel) -{ - uint32_t stat = TASK_STAT_IDLE; - uint32_t task_stat_reg = idmac_read_ipureg(ipu, IPU_TASKS_STAT); - - switch (channel) { - case IDMAC_IC_7: - stat = (task_stat_reg & TSTAT_CSI2MEM_MASK) >> - TSTAT_CSI2MEM_OFFSET; - break; - case IDMAC_IC_0: - case IDMAC_SDC_0: - case IDMAC_SDC_1: - default: - break; - } - return stat; -} - -struct chan_param_mem_planar { - /* Word 0 */ - u32 xv:10; - u32 yv:10; - u32 xb:12; - - u32 yb:12; - u32 res1:2; - u32 nsb:1; - u32 lnpb:6; - u32 ubo_l:11; - - u32 ubo_h:15; - u32 vbo_l:17; - - u32 vbo_h:9; - u32 res2:3; - u32 fw:12; - u32 fh_l:8; - - u32 fh_h:4; - u32 res3:28; - - /* Word 1 */ - u32 eba0; - - u32 eba1; - - u32 bpp:3; - u32 sl:14; - u32 pfs:3; - u32 bam:3; - u32 res4:2; - u32 npb:6; - u32 res5:1; - - u32 sat:2; - u32 res6:30; -} __attribute__ ((packed)); - -struct chan_param_mem_interleaved { - /* Word 0 */ - u32 xv:10; - u32 yv:10; - u32 xb:12; - - u32 yb:12; - u32 sce:1; - u32 res1:1; - u32 nsb:1; - u32 lnpb:6; - u32 sx:10; - u32 sy_l:1; - - u32 sy_h:9; - u32 ns:10; - u32 sm:10; - u32 sdx_l:3; - - u32 sdx_h:2; - u32 sdy:5; - u32 sdrx:1; - u32 sdry:1; - u32 sdr1:1; - u32 res2:2; - u32 fw:12; - u32 fh_l:8; - - u32 fh_h:4; - u32 res3:28; - - /* Word 1 */ - u32 eba0; - - u32 eba1; - - u32 bpp:3; - u32 sl:14; - u32 pfs:3; - u32 bam:3; - u32 res4:2; - u32 npb:6; - u32 res5:1; - - u32 sat:2; - u32 scc:1; - u32 ofs0:5; - u32 ofs1:5; - u32 ofs2:5; - u32 ofs3:5; - u32 wid0:3; - u32 wid1:3; - u32 wid2:3; - - u32 wid3:3; - u32 dec_sel:1; - u32 res6:28; -} __attribute__ ((packed)); - -union chan_param_mem { - struct chan_param_mem_planar pp; - struct chan_param_mem_interleaved ip; -}; - -static void ipu_ch_param_set_plane_offset(union chan_param_mem *params, - u32 u_offset, u32 v_offset) -{ - params->pp.ubo_l = u_offset & 0x7ff; - params->pp.ubo_h = u_offset >> 11; - params->pp.vbo_l = v_offset & 0x1ffff; - params->pp.vbo_h = v_offset >> 17; -} - -static void ipu_ch_param_set_size(union chan_param_mem *params, - uint32_t pixel_fmt, uint16_t width, - uint16_t height, uint16_t stride) -{ - u32 u_offset; - u32 v_offset; - - params->pp.fw = width - 1; - params->pp.fh_l = height - 1; - params->pp.fh_h = (height - 1) >> 8; - params->pp.sl = stride - 1; - - switch (pixel_fmt) { - case IPU_PIX_FMT_GENERIC: - /*Represents 8-bit Generic data */ - params->pp.bpp = 3; - params->pp.pfs = 7; - params->pp.npb = 31; - params->pp.sat = 2; /* SAT = use 32-bit access */ - break; - case IPU_PIX_FMT_GENERIC_32: - /*Represents 32-bit Generic data */ - params->pp.bpp = 0; - params->pp.pfs = 7; - params->pp.npb = 7; - params->pp.sat = 2; /* SAT = use 32-bit access */ - break; - case IPU_PIX_FMT_RGB565: - params->ip.bpp = 2; - params->ip.pfs = 4; - params->ip.npb = 15; - params->ip.sat = 2; /* SAT = 32-bit access */ - params->ip.ofs0 = 0; /* Red bit offset */ - params->ip.ofs1 = 5; /* Green bit offset */ - params->ip.ofs2 = 11; /* Blue bit offset */ - params->ip.ofs3 = 16; /* Alpha bit offset */ - params->ip.wid0 = 4; /* Red bit width - 1 */ - params->ip.wid1 = 5; /* Green bit width - 1 */ - params->ip.wid2 = 4; /* Blue bit width - 1 */ - break; - case IPU_PIX_FMT_BGR24: - params->ip.bpp = 1; /* 24 BPP & RGB PFS */ - params->ip.pfs = 4; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - params->ip.ofs0 = 0; /* Red bit offset */ - params->ip.ofs1 = 8; /* Green bit offset */ - params->ip.ofs2 = 16; /* Blue bit offset */ - params->ip.ofs3 = 24; /* Alpha bit offset */ - params->ip.wid0 = 7; /* Red bit width - 1 */ - params->ip.wid1 = 7; /* Green bit width - 1 */ - params->ip.wid2 = 7; /* Blue bit width - 1 */ - break; - case IPU_PIX_FMT_RGB24: - params->ip.bpp = 1; /* 24 BPP & RGB PFS */ - params->ip.pfs = 4; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - params->ip.ofs0 = 16; /* Red bit offset */ - params->ip.ofs1 = 8; /* Green bit offset */ - params->ip.ofs2 = 0; /* Blue bit offset */ - params->ip.ofs3 = 24; /* Alpha bit offset */ - params->ip.wid0 = 7; /* Red bit width - 1 */ - params->ip.wid1 = 7; /* Green bit width - 1 */ - params->ip.wid2 = 7; /* Blue bit width - 1 */ - break; - case IPU_PIX_FMT_BGRA32: - case IPU_PIX_FMT_BGR32: - case IPU_PIX_FMT_ABGR32: - params->ip.bpp = 0; - params->ip.pfs = 4; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - params->ip.ofs0 = 8; /* Red bit offset */ - params->ip.ofs1 = 16; /* Green bit offset */ - params->ip.ofs2 = 24; /* Blue bit offset */ - params->ip.ofs3 = 0; /* Alpha bit offset */ - params->ip.wid0 = 7; /* Red bit width - 1 */ - params->ip.wid1 = 7; /* Green bit width - 1 */ - params->ip.wid2 = 7; /* Blue bit width - 1 */ - params->ip.wid3 = 7; /* Alpha bit width - 1 */ - break; - case IPU_PIX_FMT_RGBA32: - case IPU_PIX_FMT_RGB32: - params->ip.bpp = 0; - params->ip.pfs = 4; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - params->ip.ofs0 = 24; /* Red bit offset */ - params->ip.ofs1 = 16; /* Green bit offset */ - params->ip.ofs2 = 8; /* Blue bit offset */ - params->ip.ofs3 = 0; /* Alpha bit offset */ - params->ip.wid0 = 7; /* Red bit width - 1 */ - params->ip.wid1 = 7; /* Green bit width - 1 */ - params->ip.wid2 = 7; /* Blue bit width - 1 */ - params->ip.wid3 = 7; /* Alpha bit width - 1 */ - break; - case IPU_PIX_FMT_UYVY: - params->ip.bpp = 2; - params->ip.pfs = 6; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - break; - case IPU_PIX_FMT_YUV420P2: - case IPU_PIX_FMT_YUV420P: - params->ip.bpp = 3; - params->ip.pfs = 3; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - u_offset = stride * height; - v_offset = u_offset + u_offset / 4; - ipu_ch_param_set_plane_offset(params, u_offset, v_offset); - break; - case IPU_PIX_FMT_YVU422P: - params->ip.bpp = 3; - params->ip.pfs = 2; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - v_offset = stride * height; - u_offset = v_offset + v_offset / 2; - ipu_ch_param_set_plane_offset(params, u_offset, v_offset); - break; - case IPU_PIX_FMT_YUV422P: - params->ip.bpp = 3; - params->ip.pfs = 2; - params->ip.npb = 7; - params->ip.sat = 2; /* SAT = 32-bit access */ - u_offset = stride * height; - v_offset = u_offset + u_offset / 2; - ipu_ch_param_set_plane_offset(params, u_offset, v_offset); - break; - default: - dev_err(ipu_data.dev, - "mx3 ipu: unimplemented pixel format %d\n", pixel_fmt); - break; - } - - params->pp.nsb = 1; -} - -static void ipu_ch_param_set_buffer(union chan_param_mem *params, - dma_addr_t buf0, dma_addr_t buf1) -{ - params->pp.eba0 = buf0; - params->pp.eba1 = buf1; -} - -static void ipu_ch_param_set_rotation(union chan_param_mem *params, - enum ipu_rotate_mode rotate) -{ - params->pp.bam = rotate; -} - -static void ipu_write_param_mem(uint32_t addr, uint32_t *data, - uint32_t num_words) -{ - for (; num_words > 0; num_words--) { - dev_dbg(ipu_data.dev, - "write param mem - addr = 0x%08X, data = 0x%08X\n", - addr, *data); - idmac_write_ipureg(&ipu_data, addr, IPU_IMA_ADDR); - idmac_write_ipureg(&ipu_data, *data++, IPU_IMA_DATA); - addr++; - if ((addr & 0x7) == 5) { - addr &= ~0x7; /* set to word 0 */ - addr += 8; /* increment to next row */ - } - } -} - -static int calc_resize_coeffs(uint32_t in_size, uint32_t out_size, - uint32_t *resize_coeff, - uint32_t *downsize_coeff) -{ - uint32_t temp_size; - uint32_t temp_downsize; - - *resize_coeff = 1 << 13; - *downsize_coeff = 1 << 13; - - /* Cannot downsize more than 8:1 */ - if (out_size << 3 < in_size) - return -EINVAL; - - /* compute downsizing coefficient */ - temp_downsize = 0; - temp_size = in_size; - while (temp_size >= out_size * 2 && temp_downsize < 2) { - temp_size >>= 1; - temp_downsize++; - } - *downsize_coeff = temp_downsize; - - /* - * compute resizing coefficient using the following formula: - * resize_coeff = M*(SI -1)/(SO - 1) - * where M = 2^13, SI - input size, SO - output size - */ - *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); - if (*resize_coeff >= 16384L) { - dev_err(ipu_data.dev, "Warning! Overflow on resize coeff.\n"); - *resize_coeff = 0x3FFF; - } - - dev_dbg(ipu_data.dev, "resizing from %u -> %u pixels, " - "downsize=%u, resize=%u.%lu (reg=%u)\n", in_size, out_size, - *downsize_coeff, *resize_coeff >= 8192L ? 1 : 0, - ((*resize_coeff & 0x1FFF) * 10000L) / 8192L, *resize_coeff); - - return 0; -} - -static enum ipu_color_space format_to_colorspace(enum pixel_fmt fmt) -{ - switch (fmt) { - case IPU_PIX_FMT_RGB565: - case IPU_PIX_FMT_BGR24: - case IPU_PIX_FMT_RGB24: - case IPU_PIX_FMT_BGR32: - case IPU_PIX_FMT_RGB32: - return IPU_COLORSPACE_RGB; - default: - return IPU_COLORSPACE_YCBCR; - } -} - -static int ipu_ic_init_prpenc(struct ipu *ipu, - union ipu_channel_param *params, bool src_is_csi) -{ - uint32_t reg, ic_conf; - uint32_t downsize_coeff, resize_coeff; - enum ipu_color_space in_fmt, out_fmt; - - /* Setup vertical resizing */ - calc_resize_coeffs(params->video.in_height, - params->video.out_height, - &resize_coeff, &downsize_coeff); - reg = (downsize_coeff << 30) | (resize_coeff << 16); - - /* Setup horizontal resizing */ - calc_resize_coeffs(params->video.in_width, - params->video.out_width, - &resize_coeff, &downsize_coeff); - reg |= (downsize_coeff << 14) | resize_coeff; - - /* Setup color space conversion */ - in_fmt = format_to_colorspace(params->video.in_pixel_fmt); - out_fmt = format_to_colorspace(params->video.out_pixel_fmt); - - /* - * Colourspace conversion unsupported yet - see _init_csc() in - * Freescale sources - */ - if (in_fmt != out_fmt) { - dev_err(ipu->dev, "Colourspace conversion unsupported!\n"); - return -EOPNOTSUPP; - } - - idmac_write_icreg(ipu, reg, IC_PRP_ENC_RSC); - - ic_conf = idmac_read_icreg(ipu, IC_CONF); - - if (src_is_csi) - ic_conf &= ~IC_CONF_RWS_EN; - else - ic_conf |= IC_CONF_RWS_EN; - - idmac_write_icreg(ipu, ic_conf, IC_CONF); - - return 0; -} - -static uint32_t dma_param_addr(uint32_t dma_ch) -{ - /* Channel Parameter Memory */ - return 0x10000 | (dma_ch << 4); -} - -static void ipu_channel_set_priority(struct ipu *ipu, enum ipu_channel channel, - bool prio) -{ - u32 reg = idmac_read_icreg(ipu, IDMAC_CHA_PRI); - - if (prio) - reg |= 1UL << channel; - else - reg &= ~(1UL << channel); - - idmac_write_icreg(ipu, reg, IDMAC_CHA_PRI); - - dump_idmac_reg(ipu); -} - -static uint32_t ipu_channel_conf_mask(enum ipu_channel channel) -{ - uint32_t mask; - - switch (channel) { - case IDMAC_IC_0: - case IDMAC_IC_7: - mask = IPU_CONF_CSI_EN | IPU_CONF_IC_EN; - break; - case IDMAC_SDC_0: - case IDMAC_SDC_1: - mask = IPU_CONF_SDC_EN | IPU_CONF_DI_EN; - break; - default: - mask = 0; - break; - } - - return mask; -} - -/** - * ipu_enable_channel() - enable an IPU channel. - * @idmac: IPU DMAC context. - * @ichan: IDMAC channel. - * @return: 0 on success or negative error code on failure. - */ -static int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan) -{ - struct ipu *ipu = to_ipu(idmac); - enum ipu_channel channel = ichan->dma_chan.chan_id; - uint32_t reg; - unsigned long flags; - - spin_lock_irqsave(&ipu->lock, flags); - - /* Reset to buffer 0 */ - idmac_write_ipureg(ipu, 1UL << channel, IPU_CHA_CUR_BUF); - ichan->active_buffer = 0; - ichan->status = IPU_CHANNEL_ENABLED; - - switch (channel) { - case IDMAC_SDC_0: - case IDMAC_SDC_1: - case IDMAC_IC_7: - ipu_channel_set_priority(ipu, channel, true); - break; - default: - break; - } - - reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); - - idmac_write_icreg(ipu, reg | (1UL << channel), IDMAC_CHA_EN); - - ipu_ic_enable_task(ipu, channel); - - spin_unlock_irqrestore(&ipu->lock, flags); - return 0; -} - -/** - * ipu_init_channel_buffer() - initialize a buffer for logical IPU channel. - * @ichan: IDMAC channel. - * @pixel_fmt: pixel format of buffer. Pixel format is a FOURCC ASCII code. - * @width: width of buffer in pixels. - * @height: height of buffer in pixels. - * @stride: stride length of buffer in pixels. - * @rot_mode: rotation mode of buffer. A rotation setting other than - * IPU_ROTATE_VERT_FLIP should only be used for input buffers of - * rotation channels. - * @phyaddr_0: buffer 0 physical address. - * @phyaddr_1: buffer 1 physical address. Setting this to a value other than - * NULL enables double buffering mode. - * @return: 0 on success or negative error code on failure. - */ -static int ipu_init_channel_buffer(struct idmac_channel *ichan, - enum pixel_fmt pixel_fmt, - uint16_t width, uint16_t height, - uint32_t stride, - enum ipu_rotate_mode rot_mode, - dma_addr_t phyaddr_0, dma_addr_t phyaddr_1) -{ - enum ipu_channel channel = ichan->dma_chan.chan_id; - struct idmac *idmac = to_idmac(ichan->dma_chan.device); - struct ipu *ipu = to_ipu(idmac); - union chan_param_mem params = {}; - unsigned long flags; - uint32_t reg; - uint32_t stride_bytes; - - stride_bytes = stride * bytes_per_pixel(pixel_fmt); - - if (stride_bytes % 4) { - dev_err(ipu->dev, - "Stride length must be 32-bit aligned, stride = %d, bytes = %d\n", - stride, stride_bytes); - return -EINVAL; - } - - /* IC channel's stride must be a multiple of 8 pixels */ - if ((channel <= IDMAC_IC_13) && (stride % 8)) { - dev_err(ipu->dev, "Stride must be 8 pixel multiple\n"); - return -EINVAL; - } - - /* Build parameter memory data for DMA channel */ - ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes); - ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1); - ipu_ch_param_set_rotation(¶ms, rot_mode); - - spin_lock_irqsave(&ipu->lock, flags); - - ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)¶ms, 10); - - reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); - - if (phyaddr_1) - reg |= 1UL << channel; - else - reg &= ~(1UL << channel); - - idmac_write_ipureg(ipu, reg, IPU_CHA_DB_MODE_SEL); - - ichan->status = IPU_CHANNEL_READY; - - spin_unlock_irqrestore(&ipu->lock, flags); - - return 0; -} - -/** - * ipu_select_buffer() - mark a channel's buffer as ready. - * @channel: channel ID. - * @buffer_n: buffer number to mark ready. - */ -static void ipu_select_buffer(enum ipu_channel channel, int buffer_n) -{ - /* No locking - this is a write-one-to-set register, cleared by IPU */ - if (buffer_n == 0) - /* Mark buffer 0 as ready. */ - idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF0_RDY); - else - /* Mark buffer 1 as ready. */ - idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF1_RDY); -} - -/** - * ipu_update_channel_buffer() - update physical address of a channel buffer. - * @ichan: IDMAC channel. - * @buffer_n: buffer number to update. - * 0 or 1 are the only valid values. - * @phyaddr: buffer physical address. - */ -/* Called under spin_lock(_irqsave)(&ichan->lock) */ -static void ipu_update_channel_buffer(struct idmac_channel *ichan, - int buffer_n, dma_addr_t phyaddr) -{ - enum ipu_channel channel = ichan->dma_chan.chan_id; - uint32_t reg; - unsigned long flags; - - spin_lock_irqsave(&ipu_data.lock, flags); - - if (buffer_n == 0) { - reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); - if (reg & (1UL << channel)) { - ipu_ic_disable_task(&ipu_data, channel); - ichan->status = IPU_CHANNEL_READY; - } - - /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ - idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + - 0x0008UL, IPU_IMA_ADDR); - idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); - } else { - reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); - if (reg & (1UL << channel)) { - ipu_ic_disable_task(&ipu_data, channel); - ichan->status = IPU_CHANNEL_READY; - } - - /* Check if double-buffering is already enabled */ - reg = idmac_read_ipureg(&ipu_data, IPU_CHA_DB_MODE_SEL); - - if (!(reg & (1UL << channel))) - idmac_write_ipureg(&ipu_data, reg | (1UL << channel), - IPU_CHA_DB_MODE_SEL); - - /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 1) */ - idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + - 0x0009UL, IPU_IMA_ADDR); - idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); - } - - spin_unlock_irqrestore(&ipu_data.lock, flags); -} - -/* Called under spin_lock_irqsave(&ichan->lock) */ -static int ipu_submit_buffer(struct idmac_channel *ichan, - struct idmac_tx_desc *desc, struct scatterlist *sg, int buf_idx) -{ - unsigned int chan_id = ichan->dma_chan.chan_id; - struct device *dev = &ichan->dma_chan.dev->device; - - if (async_tx_test_ack(&desc->txd)) - return -EINTR; - - /* - * On first invocation this shouldn't be necessary, the call to - * ipu_init_channel_buffer() above will set addresses for us, so we - * could make it conditional on status >= IPU_CHANNEL_ENABLED, but - * doing it again shouldn't hurt either. - */ - ipu_update_channel_buffer(ichan, buf_idx, sg_dma_address(sg)); - - ipu_select_buffer(chan_id, buf_idx); - dev_dbg(dev, "Updated sg %p on channel 0x%x buffer %d\n", - sg, chan_id, buf_idx); - - return 0; -} - -/* Called under spin_lock_irqsave(&ichan->lock) */ -static int ipu_submit_channel_buffers(struct idmac_channel *ichan, - struct idmac_tx_desc *desc) -{ - struct scatterlist *sg; - int i, ret = 0; - - for (i = 0, sg = desc->sg; i < 2 && sg; i++) { - if (!ichan->sg[i]) { - ichan->sg[i] = sg; - - ret = ipu_submit_buffer(ichan, desc, sg, i); - if (ret < 0) - return ret; - - sg = sg_next(sg); - } - } - - return ret; -} - -static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) -{ - struct idmac_tx_desc *desc = to_tx_desc(tx); - struct idmac_channel *ichan = to_idmac_chan(tx->chan); - struct idmac *idmac = to_idmac(tx->chan->device); - struct ipu *ipu = to_ipu(idmac); - struct device *dev = &ichan->dma_chan.dev->device; - dma_cookie_t cookie; - unsigned long flags; - int ret; - - /* Sanity check */ - if (!list_empty(&desc->list)) { - /* The descriptor doesn't belong to client */ - dev_err(dev, "Descriptor %p not prepared!\n", tx); - return -EBUSY; - } - - mutex_lock(&ichan->chan_mutex); - - async_tx_clear_ack(tx); - - if (ichan->status < IPU_CHANNEL_READY) { - struct idmac_video_param *video = &ichan->params.video; - /* - * Initial buffer assignment - the first two sg-entries from - * the descriptor will end up in the IDMAC buffers - */ - dma_addr_t dma_1 = sg_is_last(desc->sg) ? 0 : - sg_dma_address(&desc->sg[1]); - - WARN_ON(ichan->sg[0] || ichan->sg[1]); - - cookie = ipu_init_channel_buffer(ichan, - video->out_pixel_fmt, - video->out_width, - video->out_height, - video->out_stride, - IPU_ROTATE_NONE, - sg_dma_address(&desc->sg[0]), - dma_1); - if (cookie < 0) - goto out; - } - - dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]); - - cookie = dma_cookie_assign(tx); - - /* ipu->lock can be taken under ichan->lock, but not v.v. */ - spin_lock_irqsave(&ichan->lock, flags); - - list_add_tail(&desc->list, &ichan->queue); - /* submit_buffers() atomically verifies and fills empty sg slots */ - ret = ipu_submit_channel_buffers(ichan, desc); - - spin_unlock_irqrestore(&ichan->lock, flags); - - if (ret < 0) { - cookie = ret; - goto dequeue; - } - - if (ichan->status < IPU_CHANNEL_ENABLED) { - ret = ipu_enable_channel(idmac, ichan); - if (ret < 0) { - cookie = ret; - goto dequeue; - } - } - - dump_idmac_reg(ipu); - -dequeue: - if (cookie < 0) { - spin_lock_irqsave(&ichan->lock, flags); - list_del_init(&desc->list); - spin_unlock_irqrestore(&ichan->lock, flags); - tx->cookie = cookie; - ichan->dma_chan.cookie = cookie; - } - -out: - mutex_unlock(&ichan->chan_mutex); - - return cookie; -} - -/* Called with ichan->chan_mutex held */ -static int idmac_desc_alloc(struct idmac_channel *ichan, int n) -{ - struct idmac_tx_desc *desc = - vmalloc(array_size(n, sizeof(struct idmac_tx_desc))); - struct idmac *idmac = to_idmac(ichan->dma_chan.device); - - if (!desc) - return -ENOMEM; - - /* No interrupts, just disable the tasklet for a moment */ - tasklet_disable(&to_ipu(idmac)->tasklet); - - ichan->n_tx_desc = n; - ichan->desc = desc; - INIT_LIST_HEAD(&ichan->queue); - INIT_LIST_HEAD(&ichan->free_list); - - while (n--) { - struct dma_async_tx_descriptor *txd = &desc->txd; - - memset(txd, 0, sizeof(*txd)); - dma_async_tx_descriptor_init(txd, &ichan->dma_chan); - txd->tx_submit = idmac_tx_submit; - - list_add(&desc->list, &ichan->free_list); - - desc++; - } - - tasklet_enable(&to_ipu(idmac)->tasklet); - - return 0; -} - -/** - * ipu_init_channel() - initialize an IPU channel. - * @idmac: IPU DMAC context. - * @ichan: pointer to the channel object. - * @return 0 on success or negative error code on failure. - */ -static int ipu_init_channel(struct idmac *idmac, struct idmac_channel *ichan) -{ - union ipu_channel_param *params = &ichan->params; - uint32_t ipu_conf; - enum ipu_channel channel = ichan->dma_chan.chan_id; - unsigned long flags; - uint32_t reg; - struct ipu *ipu = to_ipu(idmac); - int ret = 0, n_desc = 0; - - dev_dbg(ipu->dev, "init channel = %d\n", channel); - - if (channel != IDMAC_SDC_0 && channel != IDMAC_SDC_1 && - channel != IDMAC_IC_7) - return -EINVAL; - - spin_lock_irqsave(&ipu->lock, flags); - - switch (channel) { - case IDMAC_IC_7: - n_desc = 16; - reg = idmac_read_icreg(ipu, IC_CONF); - idmac_write_icreg(ipu, reg & ~IC_CONF_CSI_MEM_WR_EN, IC_CONF); - break; - case IDMAC_IC_0: - n_desc = 16; - reg = idmac_read_ipureg(ipu, IPU_FS_PROC_FLOW); - idmac_write_ipureg(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW); - ret = ipu_ic_init_prpenc(ipu, params, true); - break; - case IDMAC_SDC_0: - case IDMAC_SDC_1: - n_desc = 4; - break; - default: - break; - } - - ipu->channel_init_mask |= 1L << channel; - - /* Enable IPU sub module */ - ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) | - ipu_channel_conf_mask(channel); - idmac_write_ipureg(ipu, ipu_conf, IPU_CONF); - - spin_unlock_irqrestore(&ipu->lock, flags); - - if (n_desc && !ichan->desc) - ret = idmac_desc_alloc(ichan, n_desc); - - dump_idmac_reg(ipu); - - return ret; -} - -/** - * ipu_uninit_channel() - uninitialize an IPU channel. - * @idmac: IPU DMAC context. - * @ichan: pointer to the channel object. - */ -static void ipu_uninit_channel(struct idmac *idmac, struct idmac_channel *ichan) -{ - enum ipu_channel channel = ichan->dma_chan.chan_id; - unsigned long flags; - uint32_t reg; - unsigned long chan_mask = 1UL << channel; - uint32_t ipu_conf; - struct ipu *ipu = to_ipu(idmac); - - spin_lock_irqsave(&ipu->lock, flags); - - if (!(ipu->channel_init_mask & chan_mask)) { - dev_err(ipu->dev, "Channel already uninitialized %d\n", - channel); - spin_unlock_irqrestore(&ipu->lock, flags); - return; - } - - /* Reset the double buffer */ - reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); - idmac_write_ipureg(ipu, reg & ~chan_mask, IPU_CHA_DB_MODE_SEL); - - ichan->sec_chan_en = false; - - switch (channel) { - case IDMAC_IC_7: - reg = idmac_read_icreg(ipu, IC_CONF); - idmac_write_icreg(ipu, reg & ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN), - IC_CONF); - break; - case IDMAC_IC_0: - reg = idmac_read_icreg(ipu, IC_CONF); - idmac_write_icreg(ipu, reg & ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1), - IC_CONF); - break; - case IDMAC_SDC_0: - case IDMAC_SDC_1: - default: - break; - } - - ipu->channel_init_mask &= ~(1L << channel); - - ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) & - ~ipu_channel_conf_mask(channel); - idmac_write_ipureg(ipu, ipu_conf, IPU_CONF); - - spin_unlock_irqrestore(&ipu->lock, flags); - - ichan->n_tx_desc = 0; - vfree(ichan->desc); - ichan->desc = NULL; -} - -/** - * ipu_disable_channel() - disable an IPU channel. - * @idmac: IPU DMAC context. - * @ichan: channel object pointer. - * @wait_for_stop: flag to set whether to wait for channel end of frame or - * return immediately. - * @return: 0 on success or negative error code on failure. - */ -static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, - bool wait_for_stop) -{ - enum ipu_channel channel = ichan->dma_chan.chan_id; - struct ipu *ipu = to_ipu(idmac); - uint32_t reg; - unsigned long flags; - unsigned long chan_mask = 1UL << channel; - unsigned int timeout; - - if (wait_for_stop && channel != IDMAC_SDC_1 && channel != IDMAC_SDC_0) { - timeout = 40; - /* This waiting always fails. Related to spurious irq problem */ - while ((idmac_read_icreg(ipu, IDMAC_CHA_BUSY) & chan_mask) || - (ipu_channel_status(ipu, channel) == TASK_STAT_ACTIVE)) { - timeout--; - msleep(10); - - if (!timeout) { - dev_dbg(ipu->dev, - "Warning: timeout waiting for channel %u to " - "stop: buf0_rdy = 0x%08X, buf1_rdy = 0x%08X, " - "busy = 0x%08X, tstat = 0x%08X\n", channel, - idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), - idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), - idmac_read_icreg(ipu, IDMAC_CHA_BUSY), - idmac_read_ipureg(ipu, IPU_TASKS_STAT)); - break; - } - } - dev_dbg(ipu->dev, "timeout = %d * 10ms\n", 40 - timeout); - } - /* SDC BG and FG must be disabled before DMA is disabled */ - if (wait_for_stop && (channel == IDMAC_SDC_0 || - channel == IDMAC_SDC_1)) { - for (timeout = 5; - timeout && !ipu_irq_status(ichan->eof_irq); timeout--) - msleep(5); - } - - spin_lock_irqsave(&ipu->lock, flags); - - /* Disable IC task */ - ipu_ic_disable_task(ipu, channel); - - /* Disable DMA channel(s) */ - reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); - idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN); - - spin_unlock_irqrestore(&ipu->lock, flags); - - return 0; -} - -static struct scatterlist *idmac_sg_next(struct idmac_channel *ichan, - struct idmac_tx_desc **desc, struct scatterlist *sg) -{ - struct scatterlist *sgnew = sg ? sg_next(sg) : NULL; - - if (sgnew) - /* next sg-element in this list */ - return sgnew; - - if ((*desc)->list.next == &ichan->queue) - /* No more descriptors on the queue */ - return NULL; - - /* Fetch next descriptor */ - *desc = list_entry((*desc)->list.next, struct idmac_tx_desc, list); - return (*desc)->sg; -} - -/* - * We have several possibilities here: - * current BUF next BUF - * - * not last sg next not last sg - * not last sg next last sg - * last sg first sg from next descriptor - * last sg NULL - * - * Besides, the descriptor queue might be empty or not. We process all these - * cases carefully. - */ -static irqreturn_t idmac_interrupt(int irq, void *dev_id) -{ - struct idmac_channel *ichan = dev_id; - struct device *dev = &ichan->dma_chan.dev->device; - unsigned int chan_id = ichan->dma_chan.chan_id; - struct scatterlist **sg, *sgnext, *sgnew = NULL; - /* Next transfer descriptor */ - struct idmac_tx_desc *desc, *descnew; - bool done = false; - u32 ready0, ready1, curbuf, err; - struct dmaengine_desc_callback cb; - - /* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */ - - dev_dbg(dev, "IDMAC irq %d, buf %d\n", irq, ichan->active_buffer); - - spin_lock(&ipu_data.lock); - - ready0 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); - ready1 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); - curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); - err = idmac_read_ipureg(&ipu_data, IPU_INT_STAT_4); - - if (err & (1 << chan_id)) { - idmac_write_ipureg(&ipu_data, 1 << chan_id, IPU_INT_STAT_4); - spin_unlock(&ipu_data.lock); - /* - * Doing this - * ichan->sg[0] = ichan->sg[1] = NULL; - * you can force channel re-enable on the next tx_submit(), but - * this is dirty - think about descriptors with multiple - * sg elements. - */ - dev_warn(dev, "NFB4EOF on channel %d, ready %x, %x, cur %x\n", - chan_id, ready0, ready1, curbuf); - return IRQ_HANDLED; - } - spin_unlock(&ipu_data.lock); - - /* Other interrupts do not interfere with this channel */ - spin_lock(&ichan->lock); - if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || - (!ichan->active_buffer && (ready0 >> chan_id) & 1) - )) { - spin_unlock(&ichan->lock); - dev_dbg(dev, - "IRQ with active buffer still ready on channel %x, " - "active %d, ready %x, %x!\n", chan_id, - ichan->active_buffer, ready0, ready1); - return IRQ_NONE; - } - - if (unlikely(list_empty(&ichan->queue))) { - ichan->sg[ichan->active_buffer] = NULL; - spin_unlock(&ichan->lock); - dev_err(dev, - "IRQ without queued buffers on channel %x, active %d, " - "ready %x, %x!\n", chan_id, - ichan->active_buffer, ready0, ready1); - return IRQ_NONE; - } - - /* - * active_buffer is a software flag, it shows which buffer we are - * currently expecting back from the hardware, IDMAC should be - * processing the other buffer already - */ - sg = &ichan->sg[ichan->active_buffer]; - sgnext = ichan->sg[!ichan->active_buffer]; - - if (!*sg) { - spin_unlock(&ichan->lock); - return IRQ_HANDLED; - } - - desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list); - descnew = desc; - - dev_dbg(dev, "IDMAC irq %d, dma %#llx, next dma %#llx, current %d, curbuf %#x\n", - irq, (u64)sg_dma_address(*sg), - sgnext ? (u64)sg_dma_address(sgnext) : 0, - ichan->active_buffer, curbuf); - - /* Find the descriptor of sgnext */ - sgnew = idmac_sg_next(ichan, &descnew, *sg); - if (sgnext != sgnew) - dev_err(dev, "Submitted buffer %p, next buffer %p\n", sgnext, sgnew); - - /* - * if sgnext == NULL sg must be the last element in a scatterlist and - * queue must be empty - */ - if (unlikely(!sgnext)) { - if (!WARN_ON(sg_next(*sg))) - dev_dbg(dev, "Underrun on channel %x\n", chan_id); - ichan->sg[!ichan->active_buffer] = sgnew; - - if (unlikely(sgnew)) { - ipu_submit_buffer(ichan, descnew, sgnew, !ichan->active_buffer); - } else { - spin_lock(&ipu_data.lock); - ipu_ic_disable_task(&ipu_data, chan_id); - spin_unlock(&ipu_data.lock); - ichan->status = IPU_CHANNEL_READY; - /* Continue to check for complete descriptor */ - } - } - - /* Calculate and submit the next sg element */ - sgnew = idmac_sg_next(ichan, &descnew, sgnew); - - if (unlikely(!sg_next(*sg)) || !sgnext) { - /* - * Last element in scatterlist done, remove from the queue, - * _init for debugging - */ - list_del_init(&desc->list); - done = true; - } - - *sg = sgnew; - - if (likely(sgnew) && - ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { - dmaengine_desc_get_callback(&descnew->txd, &cb); - - list_del_init(&descnew->list); - spin_unlock(&ichan->lock); - - dmaengine_desc_callback_invoke(&cb, NULL); - spin_lock(&ichan->lock); - } - - /* Flip the active buffer - even if update above failed */ - ichan->active_buffer = !ichan->active_buffer; - if (done) - dma_cookie_complete(&desc->txd); - - dmaengine_desc_get_callback(&desc->txd, &cb); - - spin_unlock(&ichan->lock); - - if (done && (desc->txd.flags & DMA_PREP_INTERRUPT)) - dmaengine_desc_callback_invoke(&cb, NULL); - - return IRQ_HANDLED; -} - -static void ipu_gc_tasklet(struct tasklet_struct *t) -{ - struct ipu *ipu = from_tasklet(ipu, t, tasklet); - int i; - - for (i = 0; i < IPU_CHANNELS_NUM; i++) { - struct idmac_channel *ichan = ipu->channel + i; - struct idmac_tx_desc *desc; - unsigned long flags; - struct scatterlist *sg; - int j, k; - - for (j = 0; j < ichan->n_tx_desc; j++) { - desc = ichan->desc + j; - spin_lock_irqsave(&ichan->lock, flags); - if (async_tx_test_ack(&desc->txd)) { - list_move(&desc->list, &ichan->free_list); - for_each_sg(desc->sg, sg, desc->sg_len, k) { - if (ichan->sg[0] == sg) - ichan->sg[0] = NULL; - else if (ichan->sg[1] == sg) - ichan->sg[1] = NULL; - } - async_tx_clear_ack(&desc->txd); - } - spin_unlock_irqrestore(&ichan->lock, flags); - } - } -} - -/* Allocate and initialise a transfer descriptor. */ -static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, - struct scatterlist *sgl, unsigned int sg_len, - enum dma_transfer_direction direction, unsigned long tx_flags, - void *context) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - struct idmac_tx_desc *desc = NULL; - struct dma_async_tx_descriptor *txd = NULL; - unsigned long flags; - - /* We only can handle these three channels so far */ - if (chan->chan_id != IDMAC_SDC_0 && chan->chan_id != IDMAC_SDC_1 && - chan->chan_id != IDMAC_IC_7) - return NULL; - - if (!is_slave_direction(direction)) { - dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); - return NULL; - } - - mutex_lock(&ichan->chan_mutex); - - spin_lock_irqsave(&ichan->lock, flags); - if (!list_empty(&ichan->free_list)) { - desc = list_entry(ichan->free_list.next, - struct idmac_tx_desc, list); - - list_del_init(&desc->list); - - desc->sg_len = sg_len; - desc->sg = sgl; - txd = &desc->txd; - txd->flags = tx_flags; - } - spin_unlock_irqrestore(&ichan->lock, flags); - - mutex_unlock(&ichan->chan_mutex); - - tasklet_schedule(&to_ipu(to_idmac(chan->device))->tasklet); - - return txd; -} - -/* Re-select the current buffer and re-activate the channel */ -static void idmac_issue_pending(struct dma_chan *chan) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - struct idmac *idmac = to_idmac(chan->device); - struct ipu *ipu = to_ipu(idmac); - unsigned long flags; - - /* This is not always needed, but doesn't hurt either */ - spin_lock_irqsave(&ipu->lock, flags); - ipu_select_buffer(chan->chan_id, ichan->active_buffer); - spin_unlock_irqrestore(&ipu->lock, flags); - - /* - * Might need to perform some parts of initialisation from - * ipu_enable_channel(), but not all, we do not want to reset to buffer - * 0, don't need to set priority again either, but re-enabling the task - * and the channel might be a good idea. - */ -} - -static int idmac_pause(struct dma_chan *chan) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - struct idmac *idmac = to_idmac(chan->device); - struct ipu *ipu = to_ipu(idmac); - struct list_head *list, *tmp; - unsigned long flags; - - mutex_lock(&ichan->chan_mutex); - - spin_lock_irqsave(&ipu->lock, flags); - ipu_ic_disable_task(ipu, chan->chan_id); - - /* Return all descriptors into "prepared" state */ - list_for_each_safe(list, tmp, &ichan->queue) - list_del_init(list); - - ichan->sg[0] = NULL; - ichan->sg[1] = NULL; - - spin_unlock_irqrestore(&ipu->lock, flags); - - ichan->status = IPU_CHANNEL_INITIALIZED; - - mutex_unlock(&ichan->chan_mutex); - - return 0; -} - -static int __idmac_terminate_all(struct dma_chan *chan) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - struct idmac *idmac = to_idmac(chan->device); - struct ipu *ipu = to_ipu(idmac); - unsigned long flags; - int i; - - ipu_disable_channel(idmac, ichan, - ichan->status >= IPU_CHANNEL_ENABLED); - - tasklet_disable(&ipu->tasklet); - - /* ichan->queue is modified in ISR, have to spinlock */ - spin_lock_irqsave(&ichan->lock, flags); - list_splice_init(&ichan->queue, &ichan->free_list); - - if (ichan->desc) - for (i = 0; i < ichan->n_tx_desc; i++) { - struct idmac_tx_desc *desc = ichan->desc + i; - if (list_empty(&desc->list)) - /* Descriptor was prepared, but not submitted */ - list_add(&desc->list, &ichan->free_list); - - async_tx_clear_ack(&desc->txd); - } - - ichan->sg[0] = NULL; - ichan->sg[1] = NULL; - spin_unlock_irqrestore(&ichan->lock, flags); - - tasklet_enable(&ipu->tasklet); - - ichan->status = IPU_CHANNEL_INITIALIZED; - - return 0; -} - -static int idmac_terminate_all(struct dma_chan *chan) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - int ret; - - mutex_lock(&ichan->chan_mutex); - - ret = __idmac_terminate_all(chan); - - mutex_unlock(&ichan->chan_mutex); - - return ret; -} - -#ifdef DEBUG -static irqreturn_t ic_sof_irq(int irq, void *dev_id) -{ - struct idmac_channel *ichan = dev_id; - printk(KERN_DEBUG "Got SOF IRQ %d on Channel %d\n", - irq, ichan->dma_chan.chan_id); - disable_irq_nosync(irq); - return IRQ_HANDLED; -} - -static irqreturn_t ic_eof_irq(int irq, void *dev_id) -{ - struct idmac_channel *ichan = dev_id; - printk(KERN_DEBUG "Got EOF IRQ %d on Channel %d\n", - irq, ichan->dma_chan.chan_id); - disable_irq_nosync(irq); - return IRQ_HANDLED; -} - -static int ic_sof = -EINVAL, ic_eof = -EINVAL; -#endif - -static int idmac_alloc_chan_resources(struct dma_chan *chan) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - struct idmac *idmac = to_idmac(chan->device); - int ret; - - /* dmaengine.c now guarantees to only offer free channels */ - BUG_ON(chan->client_count > 1); - WARN_ON(ichan->status != IPU_CHANNEL_FREE); - - dma_cookie_init(chan); - - ret = ipu_irq_map(chan->chan_id); - if (ret < 0) - goto eimap; - - ichan->eof_irq = ret; - - /* - * Important to first disable the channel, because maybe someone - * used it before us, e.g., the bootloader - */ - ipu_disable_channel(idmac, ichan, true); - - ret = ipu_init_channel(idmac, ichan); - if (ret < 0) - goto eichan; - - ret = request_irq(ichan->eof_irq, idmac_interrupt, 0, - ichan->eof_name, ichan); - if (ret < 0) - goto erirq; - -#ifdef DEBUG - if (chan->chan_id == IDMAC_IC_7) { - ic_sof = ipu_irq_map(69); - if (ic_sof > 0) { - ret = request_irq(ic_sof, ic_sof_irq, 0, "IC SOF", ichan); - if (ret) - dev_err(&chan->dev->device, "request irq failed for IC SOF"); - } - ic_eof = ipu_irq_map(70); - if (ic_eof > 0) { - ret = request_irq(ic_eof, ic_eof_irq, 0, "IC EOF", ichan); - if (ret) - dev_err(&chan->dev->device, "request irq failed for IC EOF"); - } - } -#endif - - ichan->status = IPU_CHANNEL_INITIALIZED; - - dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n", - chan->chan_id, ichan->eof_irq); - - return ret; - -erirq: - ipu_uninit_channel(idmac, ichan); -eichan: - ipu_irq_unmap(chan->chan_id); -eimap: - return ret; -} - -static void idmac_free_chan_resources(struct dma_chan *chan) -{ - struct idmac_channel *ichan = to_idmac_chan(chan); - struct idmac *idmac = to_idmac(chan->device); - - mutex_lock(&ichan->chan_mutex); - - __idmac_terminate_all(chan); - - if (ichan->status > IPU_CHANNEL_FREE) { -#ifdef DEBUG - if (chan->chan_id == IDMAC_IC_7) { - if (ic_sof > 0) { - free_irq(ic_sof, ichan); - ipu_irq_unmap(69); - ic_sof = -EINVAL; - } - if (ic_eof > 0) { - free_irq(ic_eof, ichan); - ipu_irq_unmap(70); - ic_eof = -EINVAL; - } - } -#endif - free_irq(ichan->eof_irq, ichan); - ipu_irq_unmap(chan->chan_id); - } - - ichan->status = IPU_CHANNEL_FREE; - - ipu_uninit_channel(idmac, ichan); - - mutex_unlock(&ichan->chan_mutex); - - tasklet_schedule(&to_ipu(idmac)->tasklet); -} - -static enum dma_status idmac_tx_status(struct dma_chan *chan, - dma_cookie_t cookie, struct dma_tx_state *txstate) -{ - return dma_cookie_status(chan, cookie, txstate); -} - -static int __init ipu_idmac_init(struct ipu *ipu) -{ - struct idmac *idmac = &ipu->idmac; - struct dma_device *dma = &idmac->dma; - int i; - - dma_cap_set(DMA_SLAVE, dma->cap_mask); - dma_cap_set(DMA_PRIVATE, dma->cap_mask); - - /* Compulsory common fields */ - dma->dev = ipu->dev; - dma->device_alloc_chan_resources = idmac_alloc_chan_resources; - dma->device_free_chan_resources = idmac_free_chan_resources; - dma->device_tx_status = idmac_tx_status; - dma->device_issue_pending = idmac_issue_pending; - - /* Compulsory for DMA_SLAVE fields */ - dma->device_prep_slave_sg = idmac_prep_slave_sg; - dma->device_pause = idmac_pause; - dma->device_terminate_all = idmac_terminate_all; - - INIT_LIST_HEAD(&dma->channels); - for (i = 0; i < IPU_CHANNELS_NUM; i++) { - struct idmac_channel *ichan = ipu->channel + i; - struct dma_chan *dma_chan = &ichan->dma_chan; - - spin_lock_init(&ichan->lock); - mutex_init(&ichan->chan_mutex); - - ichan->status = IPU_CHANNEL_FREE; - ichan->sec_chan_en = false; - snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i); - - dma_chan->device = &idmac->dma; - dma_cookie_init(dma_chan); - dma_chan->chan_id = i; - list_add_tail(&dma_chan->device_node, &dma->channels); - } - - idmac_write_icreg(ipu, 0x00000070, IDMAC_CONF); - - return dma_async_device_register(&idmac->dma); -} - -static void ipu_idmac_exit(struct ipu *ipu) -{ - int i; - struct idmac *idmac = &ipu->idmac; - - for (i = 0; i < IPU_CHANNELS_NUM; i++) { - struct idmac_channel *ichan = ipu->channel + i; - - idmac_terminate_all(&ichan->dma_chan); - } - - dma_async_device_unregister(&idmac->dma); -} - -/***************************************************************************** - * IPU common probe / remove - */ - -static int __init ipu_probe(struct platform_device *pdev) -{ - struct resource *mem_ipu, *mem_ic; - int ret; - - spin_lock_init(&ipu_data.lock); - - mem_ipu = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mem_ic = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!mem_ipu || !mem_ic) - return -EINVAL; - - ipu_data.dev = &pdev->dev; - - platform_set_drvdata(pdev, &ipu_data); - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - goto err_noirq; - - ipu_data.irq_fn = ret; - ret = platform_get_irq(pdev, 1); - if (ret < 0) - goto err_noirq; - - ipu_data.irq_err = ret; - - dev_dbg(&pdev->dev, "fn irq %u, err irq %u\n", - ipu_data.irq_fn, ipu_data.irq_err); - - /* Remap IPU common registers */ - ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu)); - if (!ipu_data.reg_ipu) { - ret = -ENOMEM; - goto err_ioremap_ipu; - } - - /* Remap Image Converter and Image DMA Controller registers */ - ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic)); - if (!ipu_data.reg_ic) { - ret = -ENOMEM; - goto err_ioremap_ic; - } - - /* Get IPU clock */ - ipu_data.ipu_clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(ipu_data.ipu_clk)) { - ret = PTR_ERR(ipu_data.ipu_clk); - goto err_clk_get; - } - - /* Make sure IPU HSP clock is running */ - clk_prepare_enable(ipu_data.ipu_clk); - - /* Disable all interrupts */ - idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_1); - idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_2); - idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_3); - idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_4); - idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_5); - - dev_dbg(&pdev->dev, "%s @ 0x%08lx, fn irq %u, err irq %u\n", pdev->name, - (unsigned long)mem_ipu->start, ipu_data.irq_fn, ipu_data.irq_err); - - ret = ipu_irq_attach_irq(&ipu_data, pdev); - if (ret < 0) - goto err_attach_irq; - - /* Initialize DMA engine */ - ret = ipu_idmac_init(&ipu_data); - if (ret < 0) - goto err_idmac_init; - - tasklet_setup(&ipu_data.tasklet, ipu_gc_tasklet); - - ipu_data.dev = &pdev->dev; - - dev_dbg(ipu_data.dev, "IPU initialized\n"); - - return 0; - -err_idmac_init: -err_attach_irq: - ipu_irq_detach_irq(&ipu_data, pdev); - clk_disable_unprepare(ipu_data.ipu_clk); - clk_put(ipu_data.ipu_clk); -err_clk_get: - iounmap(ipu_data.reg_ic); -err_ioremap_ic: - iounmap(ipu_data.reg_ipu); -err_ioremap_ipu: -err_noirq: - dev_err(&pdev->dev, "Failed to probe IPU: %d\n", ret); - return ret; -} - -static int ipu_remove(struct platform_device *pdev) -{ - struct ipu *ipu = platform_get_drvdata(pdev); - - ipu_idmac_exit(ipu); - ipu_irq_detach_irq(ipu, pdev); - clk_disable_unprepare(ipu->ipu_clk); - clk_put(ipu->ipu_clk); - iounmap(ipu->reg_ic); - iounmap(ipu->reg_ipu); - tasklet_kill(&ipu->tasklet); - - return 0; -} - -/* - * We need two MEM resources - with IPU-common and Image Converter registers, - * including PF_CONF and IDMAC_* registers, and two IRQs - function and error - */ -static struct platform_driver ipu_platform_driver = { - .driver = { - .name = "ipu-core", - }, - .remove = ipu_remove, -}; - -static int __init ipu_init(void) -{ - return platform_driver_probe(&ipu_platform_driver, ipu_probe); -} -subsys_initcall(ipu_init); - -MODULE_DESCRIPTION("IPU core driver"); -MODULE_AUTHOR("Guennadi Liakhovetski "); -MODULE_ALIAS("platform:ipu-core"); diff --git a/drivers/dma/ipu/ipu_intern.h b/drivers/dma/ipu/ipu_intern.h deleted file mode 100644 index e7ec1dec3edfd98b1f801161e35b48f4f8c8a16d..0000000000000000000000000000000000000000 --- a/drivers/dma/ipu/ipu_intern.h +++ /dev/null @@ -1,173 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, - * - * Copyright (C) 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. - */ - -#ifndef _IPU_INTERN_H_ -#define _IPU_INTERN_H_ - -#include -#include -#include - -/* IPU Common registers */ -#define IPU_CONF 0x00 -#define IPU_CHA_BUF0_RDY 0x04 -#define IPU_CHA_BUF1_RDY 0x08 -#define IPU_CHA_DB_MODE_SEL 0x0C -#define IPU_CHA_CUR_BUF 0x10 -#define IPU_FS_PROC_FLOW 0x14 -#define IPU_FS_DISP_FLOW 0x18 -#define IPU_TASKS_STAT 0x1C -#define IPU_IMA_ADDR 0x20 -#define IPU_IMA_DATA 0x24 -#define IPU_INT_CTRL_1 0x28 -#define IPU_INT_CTRL_2 0x2C -#define IPU_INT_CTRL_3 0x30 -#define IPU_INT_CTRL_4 0x34 -#define IPU_INT_CTRL_5 0x38 -#define IPU_INT_STAT_1 0x3C -#define IPU_INT_STAT_2 0x40 -#define IPU_INT_STAT_3 0x44 -#define IPU_INT_STAT_4 0x48 -#define IPU_INT_STAT_5 0x4C -#define IPU_BRK_CTRL_1 0x50 -#define IPU_BRK_CTRL_2 0x54 -#define IPU_BRK_STAT 0x58 -#define IPU_DIAGB_CTRL 0x5C - -/* IPU_CONF Register bits */ -#define IPU_CONF_CSI_EN 0x00000001 -#define IPU_CONF_IC_EN 0x00000002 -#define IPU_CONF_ROT_EN 0x00000004 -#define IPU_CONF_PF_EN 0x00000008 -#define IPU_CONF_SDC_EN 0x00000010 -#define IPU_CONF_ADC_EN 0x00000020 -#define IPU_CONF_DI_EN 0x00000040 -#define IPU_CONF_DU_EN 0x00000080 -#define IPU_CONF_PXL_ENDIAN 0x00000100 - -/* Image Converter Registers */ -#define IC_CONF 0x88 -#define IC_PRP_ENC_RSC 0x8C -#define IC_PRP_VF_RSC 0x90 -#define IC_PP_RSC 0x94 -#define IC_CMBP_1 0x98 -#define IC_CMBP_2 0x9C -#define PF_CONF 0xA0 -#define IDMAC_CONF 0xA4 -#define IDMAC_CHA_EN 0xA8 -#define IDMAC_CHA_PRI 0xAC -#define IDMAC_CHA_BUSY 0xB0 - -/* Image Converter Register bits */ -#define IC_CONF_PRPENC_EN 0x00000001 -#define IC_CONF_PRPENC_CSC1 0x00000002 -#define IC_CONF_PRPENC_ROT_EN 0x00000004 -#define IC_CONF_PRPVF_EN 0x00000100 -#define IC_CONF_PRPVF_CSC1 0x00000200 -#define IC_CONF_PRPVF_CSC2 0x00000400 -#define IC_CONF_PRPVF_CMB 0x00000800 -#define IC_CONF_PRPVF_ROT_EN 0x00001000 -#define IC_CONF_PP_EN 0x00010000 -#define IC_CONF_PP_CSC1 0x00020000 -#define IC_CONF_PP_CSC2 0x00040000 -#define IC_CONF_PP_CMB 0x00080000 -#define IC_CONF_PP_ROT_EN 0x00100000 -#define IC_CONF_IC_GLB_LOC_A 0x10000000 -#define IC_CONF_KEY_COLOR_EN 0x20000000 -#define IC_CONF_RWS_EN 0x40000000 -#define IC_CONF_CSI_MEM_WR_EN 0x80000000 - -#define IDMA_CHAN_INVALID 0x000000FF -#define IDMA_IC_0 0x00000001 -#define IDMA_IC_1 0x00000002 -#define IDMA_IC_2 0x00000004 -#define IDMA_IC_3 0x00000008 -#define IDMA_IC_4 0x00000010 -#define IDMA_IC_5 0x00000020 -#define IDMA_IC_6 0x00000040 -#define IDMA_IC_7 0x00000080 -#define IDMA_IC_8 0x00000100 -#define IDMA_IC_9 0x00000200 -#define IDMA_IC_10 0x00000400 -#define IDMA_IC_11 0x00000800 -#define IDMA_IC_12 0x00001000 -#define IDMA_IC_13 0x00002000 -#define IDMA_SDC_BG 0x00004000 -#define IDMA_SDC_FG 0x00008000 -#define IDMA_SDC_MASK 0x00010000 -#define IDMA_SDC_PARTIAL 0x00020000 -#define IDMA_ADC_SYS1_WR 0x00040000 -#define IDMA_ADC_SYS2_WR 0x00080000 -#define IDMA_ADC_SYS1_CMD 0x00100000 -#define IDMA_ADC_SYS2_CMD 0x00200000 -#define IDMA_ADC_SYS1_RD 0x00400000 -#define IDMA_ADC_SYS2_RD 0x00800000 -#define IDMA_PF_QP 0x01000000 -#define IDMA_PF_BSP 0x02000000 -#define IDMA_PF_Y_IN 0x04000000 -#define IDMA_PF_U_IN 0x08000000 -#define IDMA_PF_V_IN 0x10000000 -#define IDMA_PF_Y_OUT 0x20000000 -#define IDMA_PF_U_OUT 0x40000000 -#define IDMA_PF_V_OUT 0x80000000 - -#define TSTAT_PF_H264_PAUSE 0x00000001 -#define TSTAT_CSI2MEM_MASK 0x0000000C -#define TSTAT_CSI2MEM_OFFSET 2 -#define TSTAT_VF_MASK 0x00000600 -#define TSTAT_VF_OFFSET 9 -#define TSTAT_VF_ROT_MASK 0x000C0000 -#define TSTAT_VF_ROT_OFFSET 18 -#define TSTAT_ENC_MASK 0x00000180 -#define TSTAT_ENC_OFFSET 7 -#define TSTAT_ENC_ROT_MASK 0x00030000 -#define TSTAT_ENC_ROT_OFFSET 16 -#define TSTAT_PP_MASK 0x00001800 -#define TSTAT_PP_OFFSET 11 -#define TSTAT_PP_ROT_MASK 0x00300000 -#define TSTAT_PP_ROT_OFFSET 20 -#define TSTAT_PF_MASK 0x00C00000 -#define TSTAT_PF_OFFSET 22 -#define TSTAT_ADCSYS1_MASK 0x03000000 -#define TSTAT_ADCSYS1_OFFSET 24 -#define TSTAT_ADCSYS2_MASK 0x0C000000 -#define TSTAT_ADCSYS2_OFFSET 26 - -#define TASK_STAT_IDLE 0 -#define TASK_STAT_ACTIVE 1 -#define TASK_STAT_WAIT4READY 2 - -struct idmac { - struct dma_device dma; -}; - -struct ipu { - void __iomem *reg_ipu; - void __iomem *reg_ic; - unsigned int irq_fn; /* IPU Function IRQ to the CPU */ - unsigned int irq_err; /* IPU Error IRQ to the CPU */ - unsigned int irq_base; /* Beginning of the IPU IRQ range */ - unsigned long channel_init_mask; - spinlock_t lock; - struct clk *ipu_clk; - struct device *dev; - struct idmac idmac; - struct idmac_channel channel[IPU_CHANNELS_NUM]; - struct tasklet_struct tasklet; -}; - -#define to_idmac(d) container_of(d, struct idmac, dma) - -extern int ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev); -extern void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev); - -extern bool ipu_irq_status(uint32_t irq); -extern int ipu_irq_map(unsigned int source); -extern int ipu_irq_unmap(unsigned int source); - -#endif diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c deleted file mode 100644 index 97d9a6f04f2a2b201e9df71e6ad9afe450ad135d..0000000000000000000000000000000000000000 --- a/drivers/dma/ipu/ipu_irq.c +++ /dev/null @@ -1,367 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2008 - * Guennadi Liakhovetski, DENX Software Engineering, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ipu_intern.h" - -/* - * Register read / write - shall be inlined by the compiler - */ -static u32 ipu_read_reg(struct ipu *ipu, unsigned long reg) -{ - return __raw_readl(ipu->reg_ipu + reg); -} - -static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg) -{ - __raw_writel(value, ipu->reg_ipu + reg); -} - - -/* - * IPU IRQ chip driver - */ - -#define IPU_IRQ_NR_FN_BANKS 3 -#define IPU_IRQ_NR_ERR_BANKS 2 -#define IPU_IRQ_NR_BANKS (IPU_IRQ_NR_FN_BANKS + IPU_IRQ_NR_ERR_BANKS) - -struct ipu_irq_bank { - unsigned int control; - unsigned int status; - struct ipu *ipu; -}; - -static struct ipu_irq_bank irq_bank[IPU_IRQ_NR_BANKS] = { - /* 3 groups of functional interrupts */ - { - .control = IPU_INT_CTRL_1, - .status = IPU_INT_STAT_1, - }, { - .control = IPU_INT_CTRL_2, - .status = IPU_INT_STAT_2, - }, { - .control = IPU_INT_CTRL_3, - .status = IPU_INT_STAT_3, - }, - /* 2 groups of error interrupts */ - { - .control = IPU_INT_CTRL_4, - .status = IPU_INT_STAT_4, - }, { - .control = IPU_INT_CTRL_5, - .status = IPU_INT_STAT_5, - }, -}; - -struct ipu_irq_map { - unsigned int irq; - int source; - struct ipu_irq_bank *bank; - struct ipu *ipu; -}; - -static struct ipu_irq_map irq_map[CONFIG_MX3_IPU_IRQS]; -/* Protects allocations from the above array of maps */ -static DEFINE_MUTEX(map_lock); -/* Protects register accesses and individual mappings */ -static DEFINE_RAW_SPINLOCK(bank_lock); - -static struct ipu_irq_map *src2map(unsigned int src) -{ - int i; - - for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) - if (irq_map[i].source == src) - return irq_map + i; - - return NULL; -} - -static void ipu_irq_unmask(struct irq_data *d) -{ - struct ipu_irq_map *map = irq_data_get_irq_chip_data(d); - struct ipu_irq_bank *bank; - uint32_t reg; - unsigned long lock_flags; - - raw_spin_lock_irqsave(&bank_lock, lock_flags); - - bank = map->bank; - if (!bank) { - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); - pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq); - return; - } - - reg = ipu_read_reg(bank->ipu, bank->control); - reg |= (1UL << (map->source & 31)); - ipu_write_reg(bank->ipu, reg, bank->control); - - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); -} - -static void ipu_irq_mask(struct irq_data *d) -{ - struct ipu_irq_map *map = irq_data_get_irq_chip_data(d); - struct ipu_irq_bank *bank; - uint32_t reg; - unsigned long lock_flags; - - raw_spin_lock_irqsave(&bank_lock, lock_flags); - - bank = map->bank; - if (!bank) { - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); - pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq); - return; - } - - reg = ipu_read_reg(bank->ipu, bank->control); - reg &= ~(1UL << (map->source & 31)); - ipu_write_reg(bank->ipu, reg, bank->control); - - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); -} - -static void ipu_irq_ack(struct irq_data *d) -{ - struct ipu_irq_map *map = irq_data_get_irq_chip_data(d); - struct ipu_irq_bank *bank; - unsigned long lock_flags; - - raw_spin_lock_irqsave(&bank_lock, lock_flags); - - bank = map->bank; - if (!bank) { - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); - pr_err("IPU: %s(%u) - unmapped!\n", __func__, d->irq); - return; - } - - ipu_write_reg(bank->ipu, 1UL << (map->source & 31), bank->status); - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); -} - -/** - * ipu_irq_status() - returns the current interrupt status of the specified IRQ. - * @irq: interrupt line to get status for. - * @return: true if the interrupt is pending/asserted or false if the - * interrupt is not pending. - */ -bool ipu_irq_status(unsigned int irq) -{ - struct ipu_irq_map *map = irq_get_chip_data(irq); - struct ipu_irq_bank *bank; - unsigned long lock_flags; - bool ret; - - raw_spin_lock_irqsave(&bank_lock, lock_flags); - bank = map->bank; - ret = bank && ipu_read_reg(bank->ipu, bank->status) & - (1UL << (map->source & 31)); - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); - - return ret; -} - -/** - * ipu_irq_map() - map an IPU interrupt source to an IRQ number - * @source: interrupt source bit position (see below) - * @return: mapped IRQ number or negative error code - * - * The source parameter has to be explained further. On i.MX31 IPU has 137 IRQ - * sources, they are broken down in 5 32-bit registers, like 32, 32, 24, 32, 17. - * However, the source argument of this function is not the sequence number of - * the possible IRQ, but rather its bit position. So, first interrupt in fourth - * register has source number 96, and not 88. This makes calculations easier, - * and also provides forward compatibility with any future IPU implementations - * with any interrupt bit assignments. - */ -int ipu_irq_map(unsigned int source) -{ - int i, ret = -ENOMEM; - struct ipu_irq_map *map; - - might_sleep(); - - mutex_lock(&map_lock); - map = src2map(source); - if (map) { - pr_err("IPU: Source %u already mapped to IRQ %u\n", source, map->irq); - ret = -EBUSY; - goto out; - } - - for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) { - if (irq_map[i].source < 0) { - unsigned long lock_flags; - - raw_spin_lock_irqsave(&bank_lock, lock_flags); - irq_map[i].source = source; - irq_map[i].bank = irq_bank + source / 32; - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); - - ret = irq_map[i].irq; - pr_debug("IPU: mapped source %u to IRQ %u\n", - source, ret); - break; - } - } -out: - mutex_unlock(&map_lock); - - if (ret < 0) - pr_err("IPU: couldn't map source %u: %d\n", source, ret); - - return ret; -} - -/** - * ipu_irq_unmap() - unmap an IPU interrupt source - * @source: interrupt source bit position (see ipu_irq_map()) - * @return: 0 or negative error code - */ -int ipu_irq_unmap(unsigned int source) -{ - int i, ret = -EINVAL; - - might_sleep(); - - mutex_lock(&map_lock); - for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) { - if (irq_map[i].source == source) { - unsigned long lock_flags; - - pr_debug("IPU: unmapped source %u from IRQ %u\n", - source, irq_map[i].irq); - - raw_spin_lock_irqsave(&bank_lock, lock_flags); - irq_map[i].source = -EINVAL; - irq_map[i].bank = NULL; - raw_spin_unlock_irqrestore(&bank_lock, lock_flags); - - ret = 0; - break; - } - } - mutex_unlock(&map_lock); - - return ret; -} - -/* Chained IRQ handler for IPU function and error interrupt */ -static void ipu_irq_handler(struct irq_desc *desc) -{ - struct ipu *ipu = irq_desc_get_handler_data(desc); - u32 status; - int i, line; - - for (i = 0; i < IPU_IRQ_NR_BANKS; i++) { - struct ipu_irq_bank *bank = irq_bank + i; - - raw_spin_lock(&bank_lock); - status = ipu_read_reg(ipu, bank->status); - /* - * Don't think we have to clear all interrupts here, they will - * be acked by ->handle_irq() (handle_level_irq). However, we - * might want to clear unhandled interrupts after the loop... - */ - status &= ipu_read_reg(ipu, bank->control); - raw_spin_unlock(&bank_lock); - while ((line = ffs(status))) { - struct ipu_irq_map *map; - unsigned int irq; - - line--; - status &= ~(1UL << line); - - raw_spin_lock(&bank_lock); - map = src2map(32 * i + line); - if (!map) { - raw_spin_unlock(&bank_lock); - pr_err("IPU: Interrupt on unmapped source %u bank %d\n", - line, i); - continue; - } - irq = map->irq; - raw_spin_unlock(&bank_lock); - generic_handle_irq(irq); - } - } -} - -static struct irq_chip ipu_irq_chip = { - .name = "ipu_irq", - .irq_ack = ipu_irq_ack, - .irq_mask = ipu_irq_mask, - .irq_unmask = ipu_irq_unmask, -}; - -/* Install the IRQ handler */ -int __init ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev) -{ - unsigned int irq, i; - int irq_base = irq_alloc_descs(-1, 0, CONFIG_MX3_IPU_IRQS, - numa_node_id()); - - if (irq_base < 0) - return irq_base; - - for (i = 0; i < IPU_IRQ_NR_BANKS; i++) - irq_bank[i].ipu = ipu; - - for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) { - int ret; - - irq = irq_base + i; - ret = irq_set_chip(irq, &ipu_irq_chip); - if (ret < 0) - return ret; - ret = irq_set_chip_data(irq, irq_map + i); - if (ret < 0) - return ret; - irq_map[i].ipu = ipu; - irq_map[i].irq = irq; - irq_map[i].source = -EINVAL; - irq_set_handler(irq, handle_level_irq); - irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE); - } - - irq_set_chained_handler_and_data(ipu->irq_fn, ipu_irq_handler, ipu); - - irq_set_chained_handler_and_data(ipu->irq_err, ipu_irq_handler, ipu); - - ipu->irq_base = irq_base; - - return 0; -} - -void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev) -{ - unsigned int irq, irq_base; - - irq_base = ipu->irq_base; - - irq_set_chained_handler_and_data(ipu->irq_fn, NULL, NULL); - - irq_set_chained_handler_and_data(ipu->irq_err, NULL, NULL); - - for (irq = irq_base; irq < irq_base + CONFIG_MX3_IPU_IRQS; irq++) { - irq_set_status_flags(irq, IRQ_NOREQUEST); - irq_set_chip(irq, NULL); - irq_set_chip_data(irq, NULL); - } -} diff --git a/drivers/dma/lgm/lgm-dma.c b/drivers/dma/lgm/lgm-dma.c index 1709d159af7e019e04fe015fa38690c55ef71fe4..4117c7b67e9c2ebc20b1ee37a1ae868dce0d0b9f 100644 --- a/drivers/dma/lgm/lgm-dma.c +++ b/drivers/dma/lgm/lgm-dma.c @@ -1732,9 +1732,4 @@ static struct platform_driver intel_ldma_driver = { * registered DMA channels and DMA capabilities to clients before their * initialization. */ -static int __init intel_ldma_init(void) -{ - return platform_driver_register(&intel_ldma_driver); -} - -device_initcall(intel_ldma_init); +builtin_platform_driver(intel_ldma_driver); diff --git a/drivers/dma/lpc18xx-dmamux.c b/drivers/dma/lpc18xx-dmamux.c index df98cae8792baee3bcb514a2b2080dbb5b3c98ad..2b6436f4b193743dfd31341022b613712c9bf69b 100644 --- a/drivers/dma/lpc18xx-dmamux.c +++ b/drivers/dma/lpc18xx-dmamux.c @@ -12,8 +12,10 @@ #include #include #include -#include +#include #include +#include +#include #include #include diff --git a/drivers/dma/mcf-edma.c b/drivers/dma/mcf-edma-main.c similarity index 88% rename from drivers/dma/mcf-edma.c rename to drivers/dma/mcf-edma-main.c index 9413fad08a60ca0503c87bda291c94dbb01a0c6b..b359421ee9ea52e2a31e786a6c0d59f4551be5b3 100644 --- a/drivers/dma/mcf-edma.c +++ b/drivers/dma/mcf-edma-main.c @@ -19,7 +19,6 @@ static irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id) struct fsl_edma_engine *mcf_edma = dev_id; struct edma_regs *regs = &mcf_edma->regs; unsigned int ch; - struct fsl_edma_chan *mcf_chan; u64 intmap; intmap = ioread32(regs->inth); @@ -31,31 +30,7 @@ static irqreturn_t mcf_edma_tx_handler(int irq, void *dev_id) for (ch = 0; ch < mcf_edma->n_chans; ch++) { if (intmap & BIT(ch)) { iowrite8(EDMA_MASK_CH(ch), regs->cint); - - mcf_chan = &mcf_edma->chans[ch]; - - spin_lock(&mcf_chan->vchan.lock); - - if (!mcf_chan->edesc) { - /* terminate_all called before */ - spin_unlock(&mcf_chan->vchan.lock); - continue; - } - - if (!mcf_chan->edesc->iscyclic) { - list_del(&mcf_chan->edesc->vdesc.node); - vchan_cookie_complete(&mcf_chan->edesc->vdesc); - mcf_chan->edesc = NULL; - mcf_chan->status = DMA_COMPLETE; - mcf_chan->idle = true; - } else { - vchan_cyclic_callback(&mcf_chan->edesc->vdesc); - } - - if (!mcf_chan->edesc) - fsl_edma_xfer_desc(mcf_chan); - - spin_unlock(&mcf_chan->vchan.lock); + fsl_edma_tx_chan_handler(&mcf_edma->chans[ch]); } } @@ -76,8 +51,7 @@ static irqreturn_t mcf_edma_err_handler(int irq, void *dev_id) if (err & BIT(ch)) { fsl_edma_disable_request(&mcf_edma->chans[ch]); iowrite8(EDMA_CERR_CERR(ch), regs->cerr); - mcf_edma->chans[ch].status = DMA_ERROR; - mcf_edma->chans[ch].idle = true; + fsl_edma_err_chan_handler(&mcf_edma->chans[ch]); } } @@ -172,7 +146,7 @@ static void mcf_edma_irq_free(struct platform_device *pdev, } static struct fsl_edma_drvdata mcf_data = { - .version = v2, + .flags = FSL_EDMA_DRV_EDMA64, .setup_irq = mcf_edma_irq_init, }; @@ -180,9 +154,8 @@ static int mcf_edma_probe(struct platform_device *pdev) { struct mcf_edma_platform_data *pdata; struct fsl_edma_engine *mcf_edma; - struct fsl_edma_chan *mcf_chan; struct edma_regs *regs; - int ret, i, len, chans; + int ret, i, chans; pdata = dev_get_platdata(&pdev->dev); if (!pdata) { @@ -197,8 +170,8 @@ static int mcf_edma_probe(struct platform_device *pdev) chans = pdata->dma_channels; } - len = sizeof(*mcf_edma) + sizeof(*mcf_chan) * chans; - mcf_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + mcf_edma = devm_kzalloc(&pdev->dev, struct_size(mcf_edma, chans, chans), + GFP_KERNEL); if (!mcf_edma) return -ENOMEM; @@ -227,7 +200,9 @@ static int mcf_edma_probe(struct platform_device *pdev) mcf_chan->dma_dir = DMA_NONE; mcf_chan->vchan.desc_free = fsl_edma_free_desc; vchan_init(&mcf_chan->vchan, &mcf_edma->dma_dev); - iowrite32(0x0, ®s->tcd[i].csr); + mcf_chan->tcd = mcf_edma->membase + EDMA_TCD + + i * sizeof(struct fsl_edma_hw_tcd); + iowrite32(0x0, &mcf_chan->tcd->csr); } iowrite32(~0, regs->inth); diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cqdma.c index 9ae92b8940efe30863d1f33a8a0e6205472d32e2..324b7387b1b922d3528a2de9e38fbecac2d97d00 100644 --- a/drivers/dma/mediatek/mtk-cqdma.c +++ b/drivers/dma/mediatek/mtk-cqdma.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hsdma.c index 69cc61c0b262b3c84e51f588a437fb2d67f1e3d6..64120767d9838c9c3eb93326ef85c405fdbed91f 100644 --- a/drivers/dma/mediatek/mtk-hsdma.c +++ b/drivers/dma/mediatek/mtk-hsdma.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/mediatek/mtk-uart-apdma.c b/drivers/dma/mediatek/mtk-uart-apdma.c index a1517ef1f4a0185700343797ef05d8ef6810ed0a..c51dc017b48a84fc7d7f20d1c7ec0c1010a6159d 100644 --- a/drivers/dma/mediatek/mtk-uart-apdma.c +++ b/drivers/dma/mediatek/mtk-uart-apdma.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -551,7 +550,6 @@ static int mtk_uart_apdma_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); - pm_runtime_set_active(&pdev->dev); rc = dma_async_device_register(&mtkd->ddev); if (rc) diff --git a/drivers/dma/mpc512x_dma.c b/drivers/dma/mpc512x_dma.c index 4a51fdbf5aa9c3a0156b5a8949ca5187cabc0755..1104017320b888ba3058475d9e1eae3bc03061ea 100644 --- a/drivers/dma/mpc512x_dma.c +++ b/drivers/dma/mpc512x_dma.c @@ -36,11 +36,11 @@ #include #include #include +#include #include -#include #include #include -#include +#include #include diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index acc4d53e4630d144fafca99e1f58b2608bbdc19f..cfb9962417ef68e976ae03c3c6f3054dc89bd1e6 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/nbpfaxi.c b/drivers/dma/nbpfaxi.c index e72e8c10355ea2a91986c4072cb9ca4d30a9ff4f..0b2f96fd8bf0c607bb08a7bb1dafb191bf11b4df 100644 --- a/drivers/dma/nbpfaxi.c +++ b/drivers/dma/nbpfaxi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index b6e0ac8314e5cd278a0d3555805146a21bd206b3..384476757c5e3a47502c23dbadc2049b627dcf34 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -20,8 +20,9 @@ #include #include #include -#include +#include #include +#include #include #include "virt-dma.h" @@ -1116,7 +1117,7 @@ static int owl_dma_probe(struct platform_device *pdev) dev_info(&pdev->dev, "dma-channels %d, dma-requests %d\n", nr_channels, nr_requests); - od->devid = (enum owl_dma_id)of_device_get_match_data(&pdev->dev); + od->devid = (uintptr_t)of_device_get_match_data(&pdev->dev); od->nr_pchans = nr_channels; od->nr_vchans = nr_requests; diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 686c270ef7100c442b693535ecba9df71c4f78cc..f9b82dff33871c35a9291f65b68320838fba9f27 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include "adma.h" diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c index 932628b319c812f2d9af6cfe1ce353a7d4c7d815..1c93864e0e4d41798a1580e645fb38ab9eb5a2ca 100644 --- a/drivers/dma/qcom/gpi.c +++ b/drivers/dma/qcom/gpi.c @@ -2160,8 +2160,7 @@ static int gpi_probe(struct platform_device *pdev) return -ENOMEM; gpi_dev->dev = &pdev->dev; - gpi_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gpi_dev->regs = devm_ioremap_resource(gpi_dev->dev, gpi_dev->res); + gpi_dev->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &gpi_dev->res); if (IS_ERR(gpi_dev->regs)) return PTR_ERR(gpi_dev->regs); gpi_dev->ee_base = gpi_dev->regs; diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 344525c3a32fafbb7995b09bf2163bf1674fa95e..834ae519c15de052eacc2d16c2097a0f6707bf28 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -45,12 +45,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -765,17 +765,15 @@ static int hidma_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - trca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); - trca = devm_ioremap_resource(&pdev->dev, trca_resource); + trca = devm_platform_get_and_ioremap_resource(pdev, 0, &trca_resource); if (IS_ERR(trca)) { - rc = -ENOMEM; + rc = PTR_ERR(trca); goto bailout; } - evca_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1); - evca = devm_ioremap_resource(&pdev->dev, evca_resource); + evca = devm_platform_get_and_ioremap_resource(pdev, 1, &evca_resource); if (IS_ERR(evca)) { - rc = -ENOMEM; + rc = PTR_ERR(evca); goto bailout; } @@ -785,7 +783,7 @@ static int hidma_probe(struct platform_device *pdev) */ chirq = platform_get_irq(pdev, 0); if (chirq < 0) { - rc = -ENODEV; + rc = chirq; goto bailout; } diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c index 05e96b31d871694222e67be6db13a6016aa0269f..1d675f31252b89ad8ba7ed57256d01d01d6b0999 100644 --- a/drivers/dma/qcom/hidma_mgmt.c +++ b/drivers/dma/qcom/hidma_mgmt.c @@ -176,10 +176,9 @@ static int hidma_mgmt_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - virtaddr = devm_ioremap_resource(&pdev->dev, res); + virtaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(virtaddr)) { - rc = -ENOMEM; + rc = PTR_ERR(virtaddr); goto out; } diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 9479f29692d3e3d7244b08e26fd8424b06245ec9..f777addda8bac068d97da1d3a744924368213daa 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -9,6 +9,7 @@ * Copyright 2012 Javier Martin, Vista Silicon */ +#include #include #include #include @@ -145,8 +146,8 @@ struct rz_dmac { #define CHCFG_REQD BIT(3) #define CHCFG_SEL(bits) ((bits) & 0x07) #define CHCFG_MEM_COPY (0x80400008) -#define CHCFG_FILL_DDS(a) (((a) << 16) & GENMASK(19, 16)) -#define CHCFG_FILL_SDS(a) (((a) << 12) & GENMASK(15, 12)) +#define CHCFG_FILL_DDS_MASK GENMASK(19, 16) +#define CHCFG_FILL_SDS_MASK GENMASK(15, 12) #define CHCFG_FILL_TM(a) (((a) & BIT(5)) << 22) #define CHCFG_FILL_AM(a) (((a) & GENMASK(4, 2)) << 6) #define CHCFG_FILL_LVL(a) (((a) & BIT(1)) << 5) @@ -607,13 +608,15 @@ static int rz_dmac_config(struct dma_chan *chan, if (val == CHCFG_DS_INVALID) return -EINVAL; - channel->chcfg |= CHCFG_FILL_DDS(val); + channel->chcfg &= ~CHCFG_FILL_DDS_MASK; + channel->chcfg |= FIELD_PREP(CHCFG_FILL_DDS_MASK, val); val = rz_dmac_ds_to_val_mapping(config->src_addr_width); if (val == CHCFG_DS_INVALID) return -EINVAL; - channel->chcfg |= CHCFG_FILL_SDS(val); + channel->chcfg &= ~CHCFG_FILL_SDS_MASK; + channel->chcfg |= FIELD_PREP(CHCFG_FILL_SDS_MASK, val); return 0; } @@ -947,7 +950,6 @@ static int rz_dmac_probe(struct platform_device *pdev) dma_register_err: of_dma_controller_free(pdev->dev.of_node); err: - reset_control_assert(dmac->rstc); channel_num = i ? i - 1 : 0; for (i = 0; i < channel_num; i++) { struct rz_dmac_chan *channel = &dmac->channels[i]; @@ -958,6 +960,7 @@ static int rz_dmac_probe(struct platform_device *pdev) channel->lmdesc.base_dma); } + reset_control_assert(dmac->rstc); err_pm_runtime_put: pm_runtime_put(&pdev->dev); err_pm_disable: @@ -971,6 +974,8 @@ static int rz_dmac_remove(struct platform_device *pdev) struct rz_dmac *dmac = platform_get_drvdata(pdev); unsigned int i; + dma_async_device_unregister(&dmac->engine); + of_dma_controller_free(pdev->dev.of_node); for (i = 0; i < dmac->n_channels; i++) { struct rz_dmac_chan *channel = &dmac->channels[i]; @@ -979,8 +984,6 @@ static int rz_dmac_remove(struct platform_device *pdev) channel->lmdesc.base, channel->lmdesc.base_dma); } - of_dma_controller_free(pdev->dev.of_node); - dma_async_device_unregister(&dmac->engine); reset_control_assert(dmac->rstc); pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 5aafe548ca5f308200de9e555580e44e8bcb234a..00067b29e23223abcec60f1847bdb2c07189fbf6 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -678,7 +677,7 @@ static int sh_dmae_probe(struct platform_device *pdev) int err, errirq, i, irq_cnt = 0, irqres = 0, irq_cap = 0; struct sh_dmae_device *shdev; struct dma_device *dma_dev; - struct resource *chan, *dmars, *errirq_res, *chanirq_res; + struct resource *dmars, *errirq_res, *chanirq_res; if (pdev->dev.of_node) pdata = of_device_get_match_data(&pdev->dev); @@ -689,7 +688,6 @@ static int sh_dmae_probe(struct platform_device *pdev) if (!pdata || !pdata->channel_num) return -ENODEV; - chan = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* DMARS area is optional */ dmars = platform_get_resource(pdev, IORESOURCE_MEM, 1); /* @@ -709,7 +707,7 @@ static int sh_dmae_probe(struct platform_device *pdev) * requested with the IRQF_SHARED flag */ errirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!chan || !errirq_res) + if (!errirq_res) return -ENODEV; shdev = devm_kzalloc(&pdev->dev, sizeof(struct sh_dmae_device), @@ -719,7 +717,7 @@ static int sh_dmae_probe(struct platform_device *pdev) dma_dev = &shdev->shdma_dev.dma_dev; - shdev->chan_reg = devm_ioremap_resource(&pdev->dev, chan); + shdev->chan_reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(shdev->chan_reg)) return PTR_ERR(shdev->chan_reg); if (dmars) { diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 2b639adb48ba501d207c5668cd0d7b25653fa7c8..168aa0bd73a0dd3b7f8224c9c5768945e15b9902 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index 825001bde42c4bf249c15edb2eab8a2f4320d1a2..89e82508c13392c721de92969922cb001483b60d 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -3590,6 +3590,10 @@ static int __init d40_probe(struct platform_device *pdev) spin_lock_init(&base->lcla_pool.lock); base->irq = platform_get_irq(pdev, 0); + if (base->irq < 0) { + ret = base->irq; + goto destroy_cache; + } ret = request_irq(base->irq, d40_handle_interrupt, 0, D40_NAME, base); if (ret) { diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 37674029cb427cd76518dcc6d9095e456992177c..5c36811aa1342c8df4f340dbbaeadbd77c8ef3bf 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -1581,8 +1581,7 @@ static int stm32_dma_probe(struct platform_device *pdev) dd = &dmadev->ddev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dmadev->base = devm_ioremap_resource(&pdev->dev, res); + dmadev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(dmadev->base)) return PTR_ERR(dmadev->base); diff --git a/drivers/dma/stm32-dmamux.c b/drivers/dma/stm32-dmamux.c index e415bd9f4f2b6a35a234b6a09aa031fd686356ac..8d77e2a7939a0d0a1c8d12d89db81a1cb5309deb 100644 --- a/drivers/dma/stm32-dmamux.c +++ b/drivers/dma/stm32-dmamux.c @@ -15,8 +15,10 @@ #include #include #include -#include +#include #include +#include +#include #include #include #include diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index 1d0e9dd72ab39223353fc09ba77cd8f1f8b0cdf5..0de234022c6d6d24f5cce496cc196bb1263dffe2 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index ebfd29888b2f547144a0a2ad7b1f9f2c9c825a66..2469efddf540153d40bd4706e00f30874b94612f 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -14,8 +14,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 8f67f453a4922c2434cbfc84d378f84db7d45557..33b10100110096f37b7a05f423d3a5b14539cb2f 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index cc6b91f48979921ccec9c62dbf2ccb0ff9088438..063022f9df7633a2581de0d13f287781ccf9d2d8 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index b970040360717873c10580971303da691eeb9b63..e557bada151073385f04fe0624c19da28eb7255a 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -8,9 +8,10 @@ #include #include #include -#include +#include #include #include +#include #include #include diff --git a/drivers/dma/ti/dma-crossbar.c b/drivers/dma/ti/dma-crossbar.c index f744ddbbbad7fa35e41bfd39915feb2d9cfcae73..7f17ee87a6dce36958fbccecbadf6debba84395e 100644 --- a/drivers/dma/ti/dma-crossbar.c +++ b/drivers/dma/ti/dma-crossbar.c @@ -3,14 +3,15 @@ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com * Author: Peter Ujfalusi */ +#include #include #include #include #include #include -#include -#include +#include #include +#include #define TI_XBAR_DRA7 0 #define TI_XBAR_AM335X 1 diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index 9ea91c640c324d0b61fc0295761e9e927d1fde06..aa8e2e8ac260981d037f94b8995eb4c7b949d127 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c index 789193ed038650b5d73f64fd8db8cab84c031936..c278d5facf7d8bc6696e32d7f7ebc38c58b67afc 100644 --- a/drivers/dma/ti/k3-udma-glue.c +++ b/drivers/dma/ti/k3-udma-glue.c @@ -558,6 +558,9 @@ int k3_udma_glue_tx_get_irq(struct k3_udma_glue_tx_channel *tx_chn) tx_chn->virq = k3_ringacc_get_ring_irq_num(tx_chn->ringtxcq); } + if (!tx_chn->virq) + return -ENXIO; + return tx_chn->virq; } EXPORT_SYMBOL_GPL(k3_udma_glue_tx_get_irq); diff --git a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c index 85e00701473cb121ab668c97a2b5c875e082615a..05228bf00033329f9e466d3492ea7a26358f5f3c 100644 --- a/drivers/dma/ti/k3-udma-private.c +++ b/drivers/dma/ti/k3-udma-private.c @@ -3,6 +3,8 @@ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com * Author: Peter Ujfalusi */ +#include +#include int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread) { diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index eb4dc5fffe6468d3b32ba7e4eed614bb21dc7708..30fd2f386f36a1ada7fc5cbe6a4359eb7019a559 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/dma/ti/omap-dma.c b/drivers/dma/ti/omap-dma.c index 02e1c08c596d1d9de84d293017d3e86083526c96..cf96cf915c0c7483a6edf3e46048c90b8fc43a76 100644 --- a/drivers/dma/ti/omap-dma.c +++ b/drivers/dma/ti/omap-dma.c @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include #include "../virt-dma.h" diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c index 3589b4ef50b83f597b20d11ad4ed8e11a6634c0c..bb4ff8c86733bf358b1ecb53142ea8c7b2efb176 100644 --- a/drivers/dma/xgene-dma.c +++ b/drivers/dma/xgene-dma.c @@ -18,8 +18,9 @@ #include #include #include +#include #include -#include +#include #include "dmaengine.h" diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index ac09f0e5f58d88929a7dd9452b9044ce82e083d3..0a3b2e22f23dbbf339f8ca28b32b6e57942abc78 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -41,10 +41,10 @@ #include #include #include -#include +#include #include -#include #include +#include #include #include #include @@ -173,12 +173,15 @@ #define XILINX_DMA_MAX_TRANS_LEN_MAX 23 #define XILINX_DMA_V2_MAX_TRANS_LEN_MAX 26 #define XILINX_DMA_CR_COALESCE_MAX GENMASK(23, 16) +#define XILINX_DMA_CR_DELAY_MAX GENMASK(31, 24) #define XILINX_DMA_CR_CYCLIC_BD_EN_MASK BIT(4) #define XILINX_DMA_CR_COALESCE_SHIFT 16 +#define XILINX_DMA_CR_DELAY_SHIFT 24 #define XILINX_DMA_BD_SOP BIT(27) #define XILINX_DMA_BD_EOP BIT(26) +#define XILINX_DMA_BD_COMP_MASK BIT(31) #define XILINX_DMA_COALESCE_MAX 255 -#define XILINX_DMA_NUM_DESCS 255 +#define XILINX_DMA_NUM_DESCS 512 #define XILINX_DMA_NUM_APP_WORDS 5 /* AXI CDMA Specific Registers/Offsets */ @@ -410,6 +413,7 @@ struct xilinx_dma_tx_descriptor { * @stop_transfer: Differentiate b/w DMA IP's quiesce * @tdest: TDEST value for mcdma * @has_vflip: S2MM vertical flip + * @irq_delay: Interrupt delay timeout */ struct xilinx_dma_chan { struct xilinx_dma_device *xdev; @@ -448,6 +452,7 @@ struct xilinx_dma_chan { int (*stop_transfer)(struct xilinx_dma_chan *chan); u16 tdest; bool has_vflip; + u8 irq_delay; }; /** @@ -493,6 +498,7 @@ struct xilinx_dma_config { * @s2mm_chan_id: DMA s2mm channel identifier * @mm2s_chan_id: DMA mm2s channel identifier * @max_buffer_len: Max buffer length + * @has_axistream_connected: AXI DMA connected to AXI Stream IP */ struct xilinx_dma_device { void __iomem *regs; @@ -511,6 +517,7 @@ struct xilinx_dma_device { u32 s2mm_chan_id; u32 mm2s_chan_id; u32 max_buffer_len; + bool has_axistream_connected; }; /* Macros */ @@ -623,6 +630,29 @@ static inline void xilinx_aximcdma_buf(struct xilinx_dma_chan *chan, } } +/** + * xilinx_dma_get_metadata_ptr- Populate metadata pointer and payload length + * @tx: async transaction descriptor + * @payload_len: metadata payload length + * @max_len: metadata max length + * Return: The app field pointer. + */ +static void *xilinx_dma_get_metadata_ptr(struct dma_async_tx_descriptor *tx, + size_t *payload_len, size_t *max_len) +{ + struct xilinx_dma_tx_descriptor *desc = to_dma_tx_descriptor(tx); + struct xilinx_axidma_tx_segment *seg; + + *max_len = *payload_len = sizeof(u32) * XILINX_DMA_NUM_APP_WORDS; + seg = list_first_entry(&desc->segments, + struct xilinx_axidma_tx_segment, node); + return seg->hw.app; +} + +static struct dma_descriptor_metadata_ops xilinx_dma_metadata_ops = { + .get_ptr = xilinx_dma_get_metadata_ptr, +}; + /* ----------------------------------------------------------------------------- * Descriptors and segments alloc and free */ @@ -1535,6 +1565,9 @@ static void xilinx_dma_start_transfer(struct xilinx_dma_chan *chan) if (chan->has_sg) xilinx_write(chan, XILINX_DMA_REG_CURDESC, head_desc->async_tx.phys); + reg &= ~XILINX_DMA_CR_DELAY_MAX; + reg |= chan->irq_delay << XILINX_DMA_CR_DELAY_SHIFT; + dma_ctrl_write(chan, XILINX_DMA_REG_DMACR, reg); xilinx_dma_start(chan); @@ -1683,6 +1716,14 @@ static void xilinx_dma_complete_descriptor(struct xilinx_dma_chan *chan) return; list_for_each_entry_safe(desc, next, &chan->active_list, node) { + if (chan->xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { + struct xilinx_axidma_tx_segment *seg; + + seg = list_last_entry(&desc->segments, + struct xilinx_axidma_tx_segment, node); + if (!(seg->hw.status & XILINX_DMA_BD_COMP_MASK) && chan->has_sg) + break; + } if (chan->has_sg && chan->xdev->dma_config->dmatype != XDMA_TYPE_VDMA) desc->residue = xilinx_dma_get_residue(chan, desc); @@ -1816,7 +1857,7 @@ static irqreturn_t xilinx_mcdma_irq_handler(int irq, void *data) spin_unlock(&chan->lock); } - tasklet_schedule(&chan->tasklet); + tasklet_hi_schedule(&chan->tasklet); return IRQ_HANDLED; } @@ -1864,15 +1905,8 @@ static irqreturn_t xilinx_dma_irq_handler(int irq, void *data) } } - if (status & XILINX_DMA_DMASR_DLY_CNT_IRQ) { - /* - * Device takes too long to do the transfer when user requires - * responsiveness. - */ - dev_dbg(chan->dev, "Inter-packet latency too long\n"); - } - - if (status & XILINX_DMA_DMASR_FRM_CNT_IRQ) { + if (status & (XILINX_DMA_DMASR_FRM_CNT_IRQ | + XILINX_DMA_DMASR_DLY_CNT_IRQ)) { spin_lock(&chan->lock); xilinx_dma_complete_descriptor(chan); chan->idle = true; @@ -2221,6 +2255,9 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg( segment->hw.control |= XILINX_DMA_BD_EOP; } + if (chan->xdev->has_axistream_connected) + desc->async_tx.metadata_ops = &xilinx_dma_metadata_ops; + return &desc->async_tx; error: @@ -2796,6 +2833,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev, /* Retrieve the channel properties from the device tree */ has_dre = of_property_read_bool(node, "xlnx,include-dre"); + of_property_read_u8(node, "xlnx,irq-delay", &chan->irq_delay); + chan->genlock = of_property_read_bool(node, "xlnx,genlock-mode"); err = of_property_read_u32(node, "xlnx,datawidth", &value); @@ -3067,6 +3106,11 @@ static int xilinx_dma_probe(struct platform_device *pdev) } } + if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { + xdev->has_axistream_connected = + of_property_read_bool(node, "xlnx,axistream-connected"); + } + if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) { err = of_property_read_u32(node, "xlnx,num-fstores", &num_frames); @@ -3092,6 +3136,10 @@ static int xilinx_dma_probe(struct platform_device *pdev) else xdev->ext_addr = false; + /* Set metadata mode */ + if (xdev->has_axistream_connected) + xdev->common.desc_metadata_modes = DESC_METADATA_ENGINE; + /* Set the dma mask bits */ err = dma_set_mask_and_coherent(xdev->dev, DMA_BIT_MASK(addr_width)); if (err < 0) { diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index 9360f43b8e0f3cba6931e92f89d98d5b0c312856..bd8c3cc2eaab6f84e5485d9e4b9a9d36e86003b7 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -11,8 +11,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index a3104e35412c150b65fb5e9c213893ad34e37e5c..aa597cda0d88741334c68bf1f9f23fcf8e43c292 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -1211,7 +1211,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) * without actually having a link. */ create: - device = kzalloc(sizeof(*device), GFP_KERNEL); + device = kzalloc(sizeof(*device), GFP_ATOMIC); if (device == NULL) break; diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 88466b663482f1a867c51fd103772fc06296cc27..f40c81534381219b1d968a4351391eb27ca02758 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -101,7 +101,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color) { struct fw_node *node; - node = kzalloc(struct_size(node, ports, port_count), GFP_KERNEL); + node = kzalloc(struct_size(node, ports, port_count), GFP_ATOMIC); if (node == NULL) return NULL; diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c index 26db5b8dfc1ef5952a02ee32869adaa2184ebe19..749868b9e80d66146d5ca33215358deff20a4a35 100644 --- a/drivers/firewire/sbp2.c +++ b/drivers/firewire/sbp2.c @@ -81,7 +81,8 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device " * * - power condition * Set the power condition field in the START STOP UNIT commands sent by - * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on). + * sd_mod on suspend, resume, and shutdown (if manage_system_start_stop or + * manage_runtime_start_stop is on). * Some disks need this to spin down or to resume properly. * * - override internal blacklist @@ -1517,8 +1518,10 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev) sdev->use_10_for_rw = 1; - if (sbp2_param_exclusive_login) - sdev->manage_start_stop = 1; + if (sbp2_param_exclusive_login) { + sdev->manage_system_start_stop = true; + sdev->manage_runtime_start_stop = true; + } if (sdev->type == TYPE_ROM) sdev->use_10_for_ms = 1; diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 2109cd178ff706f3ec7b0db0735d18519ba639cb..121f4fc903cd57b17e2dd6835a31570f4edf3774 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -397,6 +397,19 @@ static u32 ffa_get_num_pages_sg(struct scatterlist *sg) return num_pages; } +static u8 ffa_memory_attributes_get(u32 func_id) +{ + /* + * For the memory lend or donate operation, if the receiver is a PE or + * a proxy endpoint, the owner/sender must not specify the attributes + */ + if (func_id == FFA_FN_NATIVE(MEM_LEND) || + func_id == FFA_MEM_LEND) + return 0; + + return FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK | FFA_MEM_INNER_SHAREABLE; +} + static int ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, struct ffa_mem_ops_args *args) @@ -413,8 +426,7 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize, mem_region->tag = args->tag; mem_region->flags = args->flags; mem_region->sender_id = drv_info->vm_id; - mem_region->attributes = FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK | - FFA_MEM_INNER_SHAREABLE; + mem_region->attributes = ffa_memory_attributes_get(func_id); ep_mem_access = &mem_region->ep_mem_access[0]; for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) { diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index c0cd556fbaaec6946a98b2785990c2edaa14b699..30dedd6ebfde61f54fef878cdfb5b6eadd2a70c1 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -1080,6 +1080,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) if (!pinfo) return -ENOMEM; + pinfo->version = version; + ret = scmi_perf_attributes_get(ph, pinfo); if (ret) return ret; @@ -1104,8 +1106,6 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) if (ret) return ret; - pinfo->version = version; - return ph->set_priv(ph, pinfo); } diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c index 49b70c70dc6960b79cc69e5f382a9fdd3e8b9c2e..79d4254d1f9bc537be6ad3e958928b89d40efba3 100644 --- a/drivers/firmware/cirrus/cs_dsp.c +++ b/drivers/firmware/cirrus/cs_dsp.c @@ -1863,15 +1863,15 @@ static int cs_dsp_adsp2_setup_algs(struct cs_dsp *dsp) return PTR_ERR(adsp2_alg); for (i = 0; i < n_algs; i++) { - cs_dsp_info(dsp, - "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", - i, be32_to_cpu(adsp2_alg[i].alg.id), - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, - be32_to_cpu(adsp2_alg[i].xm), - be32_to_cpu(adsp2_alg[i].ym), - be32_to_cpu(adsp2_alg[i].zm)); + cs_dsp_dbg(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); alg_region = cs_dsp_create_region(dsp, WMFW_ADSP2_XM, adsp2_alg[i].alg.id, @@ -1996,14 +1996,14 @@ static int cs_dsp_halo_setup_algs(struct cs_dsp *dsp) return PTR_ERR(halo_alg); for (i = 0; i < n_algs; i++) { - cs_dsp_info(dsp, - "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", - i, be32_to_cpu(halo_alg[i].alg.id), - (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, - (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, - be32_to_cpu(halo_alg[i].alg.ver) & 0xff, - be32_to_cpu(halo_alg[i].xm_base), - be32_to_cpu(halo_alg[i].ym_base)); + cs_dsp_dbg(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x\n", + i, be32_to_cpu(halo_alg[i].alg.id), + (be32_to_cpu(halo_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(halo_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(halo_alg[i].alg.ver) & 0xff, + be32_to_cpu(halo_alg[i].xm_base), + be32_to_cpu(halo_alg[i].ym_base)); ret = cs_dsp_halo_create_regions(dsp, halo_alg[i].alg.id, halo_alg[i].alg.ver, diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 1599f11768426d8ff72c9c2cfc9387728af2efa3..ce20a60676f018361ee9a1a47fba0e2583e6e9a7 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -623,6 +623,34 @@ static __init int match_config_table(const efi_guid_t *guid, return 0; } +/** + * reserve_unaccepted - Map and reserve unaccepted configuration table + * @unaccepted: Pointer to unaccepted memory table + * + * memblock_add() makes sure that the table is mapped in direct mapping. During + * normal boot it happens automatically because the table is allocated from + * usable memory. But during crashkernel boot only memory specifically reserved + * for crash scenario is mapped. memblock_add() forces the table to be mapped + * in crashkernel case. + * + * Align the range to the nearest page borders. Ranges smaller than page size + * are not going to be mapped. + * + * memblock_reserve() makes sure that future allocations will not touch the + * table. + */ + +static __init void reserve_unaccepted(struct efi_unaccepted_memory *unaccepted) +{ + phys_addr_t start, size; + + start = PAGE_ALIGN_DOWN(efi.unaccepted); + size = PAGE_ALIGN(sizeof(*unaccepted) + unaccepted->size); + + memblock_add(start, size); + memblock_reserve(start, size); +} + int __init efi_config_parse_tables(const efi_config_table_t *config_tables, int count, const efi_config_table_type_t *arch_tables) @@ -751,11 +779,9 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, unaccepted = early_memremap(efi.unaccepted, sizeof(*unaccepted)); if (unaccepted) { - unsigned long size; if (unaccepted->version == 1) { - size = sizeof(*unaccepted) + unaccepted->size; - memblock_reserve(efi.unaccepted, size); + reserve_unaccepted(unaccepted); } else { efi.unaccepted = EFI_INVALID_TABLE_ADDR; } diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 92389a5481ffe5787a34cc9a986db51dfbe3a24d..a1157c2a7170402922fa526d84a3353f9fbb053c 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -86,10 +86,10 @@ lib-$(CONFIG_EFI_GENERIC_STUB) += efi-stub.o string.o intrinsics.o systable.o \ screen_info.o efi-stub-entry.o lib-$(CONFIG_ARM) += arm32-stub.o -lib-$(CONFIG_ARM64) += arm64.o arm64-stub.o smbios.o +lib-$(CONFIG_ARM64) += kaslr.o arm64.o arm64-stub.o smbios.o lib-$(CONFIG_X86) += x86-stub.o lib-$(CONFIG_X86_64) += x86-5lvl.o -lib-$(CONFIG_RISCV) += riscv.o riscv-stub.o +lib-$(CONFIG_RISCV) += kaslr.o riscv.o riscv-stub.o lib-$(CONFIG_LOONGARCH) += loongarch.o loongarch-stub.o CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 8c40fc89f5f992092955919f4481d6dbdd0078e9..452b7ccd330e0b208fd3fa1be0eda2fa138366c5 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -14,42 +14,6 @@ #include "efistub.h" -/* - * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail - * to provide space, and fail to zero it). Check for this condition by double - * checking that the first and the last byte of the image are covered by the - * same EFI memory map entry. - */ -static bool check_image_region(u64 base, u64 size) -{ - struct efi_boot_memmap *map; - efi_status_t status; - bool ret = false; - int map_offset; - - status = efi_get_memory_map(&map, false); - if (status != EFI_SUCCESS) - return false; - - for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) { - efi_memory_desc_t *md = (void *)map->map + map_offset; - u64 end = md->phys_addr + md->num_pages * EFI_PAGE_SIZE; - - /* - * Find the region that covers base, and return whether - * it covers base+size bytes. - */ - if (base >= md->phys_addr && base < end) { - ret = (base + size) <= end; - break; - } - } - - efi_bs_call(free_pool, map); - - return ret; -} - efi_status_t handle_kernel_image(unsigned long *image_addr, unsigned long *image_size, unsigned long *reserve_addr, @@ -59,31 +23,6 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, { efi_status_t status; unsigned long kernel_size, kernel_codesize, kernel_memsize; - u32 phys_seed = 0; - u64 min_kimg_align = efi_get_kimg_min_align(); - - if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) { - efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; - void *p; - - if (efi_nokaslr) { - efi_info("KASLR disabled on kernel command line\n"); - } else if (efi_bs_call(handle_protocol, image_handle, - &li_fixed_proto, &p) == EFI_SUCCESS) { - efi_info("Image placement fixed by loader\n"); - } else { - status = efi_get_random_bytes(sizeof(phys_seed), - (u8 *)&phys_seed); - if (status == EFI_NOT_FOUND) { - efi_info("EFI_RNG_PROTOCOL unavailable\n"); - efi_nokaslr = true; - } else if (status != EFI_SUCCESS) { - efi_err("efi_get_random_bytes() failed (0x%lx)\n", - status); - efi_nokaslr = true; - } - } - } if (image->image_base != _text) { efi_err("FIRMWARE BUG: efi_loaded_image_t::image_base has bogus value\n"); @@ -98,50 +37,15 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, kernel_codesize = __inittext_end - _text; kernel_memsize = kernel_size + (_end - _edata); *reserve_size = kernel_memsize; + *image_addr = (unsigned long)_text; - if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { - /* - * If KASLR is enabled, and we have some randomness available, - * locate the kernel at a randomized offset in physical memory. - */ - status = efi_random_alloc(*reserve_size, min_kimg_align, - reserve_addr, phys_seed, - EFI_LOADER_CODE, EFI_ALLOC_LIMIT); - if (status != EFI_SUCCESS) - efi_warn("efi_random_alloc() failed: 0x%lx\n", status); - } else { - status = EFI_OUT_OF_RESOURCES; - } - - if (status != EFI_SUCCESS) { - if (!check_image_region((u64)_text, kernel_memsize)) { - efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n"); - } else if (IS_ALIGNED((u64)_text, min_kimg_align) && - (u64)_end < EFI_ALLOC_LIMIT) { - /* - * Just execute from wherever we were loaded by the - * UEFI PE/COFF loader if the placement is suitable. - */ - *image_addr = (u64)_text; - *reserve_size = 0; - return EFI_SUCCESS; - } - - status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, - ULONG_MAX, min_kimg_align, - EFI_LOADER_CODE); - - if (status != EFI_SUCCESS) { - efi_err("Failed to relocate kernel\n"); - *reserve_size = 0; - return status; - } - } - - *image_addr = *reserve_addr; - memcpy((void *)*image_addr, _text, kernel_size); - caches_clean_inval_pou(*image_addr, *image_addr + kernel_codesize); - efi_remap_image(*image_addr, *reserve_size, kernel_codesize); + status = efi_kaslr_relocate_kernel(image_addr, + reserve_addr, reserve_size, + kernel_size, kernel_codesize, + kernel_memsize, + efi_kaslr_get_phys_seed(image_handle)); + if (status != EFI_SUCCESS) + return status; return EFI_SUCCESS; } @@ -159,3 +63,8 @@ unsigned long primary_entry_offset(void) */ return (char *)primary_entry - _text; } + +void efi_icache_sync(unsigned long start, unsigned long end) +{ + caches_clean_inval_pou(start, end); +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 9823f6fb3e01f71878e9d1179bd3eee971955692..212687c30d79c4b0b307af0b8d3c7b52502e6a95 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -1133,6 +1133,14 @@ const u8 *__efi_get_smbios_string(const struct efi_smbios_record *record, void efi_remap_image(unsigned long image_base, unsigned alloc_size, unsigned long code_size); +efi_status_t efi_kaslr_relocate_kernel(unsigned long *image_addr, + unsigned long *reserve_addr, + unsigned long *reserve_size, + unsigned long kernel_size, + unsigned long kernel_codesize, + unsigned long kernel_memsize, + u32 phys_seed); +u32 efi_kaslr_get_phys_seed(efi_handle_t image_handle); asmlinkage efi_status_t __efiapi efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab); diff --git a/drivers/firmware/efi/libstub/kaslr.c b/drivers/firmware/efi/libstub/kaslr.c new file mode 100644 index 0000000000000000000000000000000000000000..62d63f7a2645bf82525d79b5d8825e9bea023404 --- /dev/null +++ b/drivers/firmware/efi/libstub/kaslr.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Helper functions used by the EFI stub on multiple + * architectures to deal with physical address space randomization. + */ +#include + +#include "efistub.h" + +/** + * efi_kaslr_get_phys_seed() - Get random seed for physical kernel KASLR + * @image_handle: Handle to the image + * + * If KASLR is not disabled, obtain a random seed using EFI_RNG_PROTOCOL + * that will be used to move the kernel physical mapping. + * + * Return: the random seed + */ +u32 efi_kaslr_get_phys_seed(efi_handle_t image_handle) +{ + efi_status_t status; + u32 phys_seed; + efi_guid_t li_fixed_proto = LINUX_EFI_LOADED_IMAGE_FIXED_GUID; + void *p; + + if (!IS_ENABLED(CONFIG_RANDOMIZE_BASE)) + return 0; + + if (efi_nokaslr) { + efi_info("KASLR disabled on kernel command line\n"); + } else if (efi_bs_call(handle_protocol, image_handle, + &li_fixed_proto, &p) == EFI_SUCCESS) { + efi_info("Image placement fixed by loader\n"); + } else { + status = efi_get_random_bytes(sizeof(phys_seed), + (u8 *)&phys_seed); + if (status == EFI_SUCCESS) { + return phys_seed; + } else if (status == EFI_NOT_FOUND) { + efi_info("EFI_RNG_PROTOCOL unavailable\n"); + efi_nokaslr = true; + } else if (status != EFI_SUCCESS) { + efi_err("efi_get_random_bytes() failed (0x%lx)\n", + status); + efi_nokaslr = true; + } + } + + return 0; +} + +/* + * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail + * to provide space, and fail to zero it). Check for this condition by double + * checking that the first and the last byte of the image are covered by the + * same EFI memory map entry. + */ +static bool check_image_region(u64 base, u64 size) +{ + struct efi_boot_memmap *map; + efi_status_t status; + bool ret = false; + int map_offset; + + status = efi_get_memory_map(&map, false); + if (status != EFI_SUCCESS) + return false; + + for (map_offset = 0; map_offset < map->map_size; map_offset += map->desc_size) { + efi_memory_desc_t *md = (void *)map->map + map_offset; + u64 end = md->phys_addr + md->num_pages * EFI_PAGE_SIZE; + + /* + * Find the region that covers base, and return whether + * it covers base+size bytes. + */ + if (base >= md->phys_addr && base < end) { + ret = (base + size) <= end; + break; + } + } + + efi_bs_call(free_pool, map); + + return ret; +} + +/** + * efi_kaslr_relocate_kernel() - Relocate the kernel (random if KASLR enabled) + * @image_addr: Pointer to the current kernel location + * @reserve_addr: Pointer to the relocated kernel location + * @reserve_size: Size of the relocated kernel + * @kernel_size: Size of the text + data + * @kernel_codesize: Size of the text + * @kernel_memsize: Size of the text + data + bss + * @phys_seed: Random seed used for the relocation + * + * If KASLR is not enabled, this function relocates the kernel to a fixed + * address (or leave it as its current location). If KASLR is enabled, the + * kernel physical location is randomized using the seed in parameter. + * + * Return: status code, EFI_SUCCESS if relocation is successful + */ +efi_status_t efi_kaslr_relocate_kernel(unsigned long *image_addr, + unsigned long *reserve_addr, + unsigned long *reserve_size, + unsigned long kernel_size, + unsigned long kernel_codesize, + unsigned long kernel_memsize, + u32 phys_seed) +{ + efi_status_t status; + u64 min_kimg_align = efi_get_kimg_min_align(); + + if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) { + /* + * If KASLR is enabled, and we have some randomness available, + * locate the kernel at a randomized offset in physical memory. + */ + status = efi_random_alloc(*reserve_size, min_kimg_align, + reserve_addr, phys_seed, + EFI_LOADER_CODE, EFI_ALLOC_LIMIT); + if (status != EFI_SUCCESS) + efi_warn("efi_random_alloc() failed: 0x%lx\n", status); + } else { + status = EFI_OUT_OF_RESOURCES; + } + + if (status != EFI_SUCCESS) { + if (!check_image_region(*image_addr, kernel_memsize)) { + efi_err("FIRMWARE BUG: Image BSS overlaps adjacent EFI memory region\n"); + } else if (IS_ALIGNED(*image_addr, min_kimg_align) && + (unsigned long)_end < EFI_ALLOC_LIMIT) { + /* + * Just execute from wherever we were loaded by the + * UEFI PE/COFF loader if the placement is suitable. + */ + *reserve_size = 0; + return EFI_SUCCESS; + } + + status = efi_allocate_pages_aligned(*reserve_size, reserve_addr, + ULONG_MAX, min_kimg_align, + EFI_LOADER_CODE); + + if (status != EFI_SUCCESS) { + efi_err("Failed to relocate kernel\n"); + *reserve_size = 0; + return status; + } + } + + memcpy((void *)*reserve_addr, (void *)*image_addr, kernel_size); + *image_addr = *reserve_addr; + efi_icache_sync(*image_addr, *image_addr + kernel_codesize); + efi_remap_image(*image_addr, *reserve_size, kernel_codesize); + + return status; +} diff --git a/drivers/firmware/efi/libstub/riscv-stub.c b/drivers/firmware/efi/libstub/riscv-stub.c index 145c9f0ba21702104a6173aa2075f69d5bf693aa..c96d6dcee86c20656e8b6c9504734c66b26854c1 100644 --- a/drivers/firmware/efi/libstub/riscv-stub.c +++ b/drivers/firmware/efi/libstub/riscv-stub.c @@ -30,32 +30,29 @@ efi_status_t handle_kernel_image(unsigned long *image_addr, efi_loaded_image_t *image, efi_handle_t image_handle) { - unsigned long kernel_size = 0; - unsigned long preferred_addr; + unsigned long kernel_size, kernel_codesize, kernel_memsize; efi_status_t status; kernel_size = _edata - _start; + kernel_codesize = __init_text_end - _start; + kernel_memsize = kernel_size + (_end - _edata); *image_addr = (unsigned long)_start; - *image_size = kernel_size + (_end - _edata); - - /* - * RISC-V kernel maps PAGE_OFFSET virtual address to the same physical - * address where kernel is booted. That's why kernel should boot from - * as low as possible to avoid wastage of memory. Currently, dram_base - * is occupied by the firmware. So the preferred address for kernel to - * boot is next aligned address. If preferred address is not available, - * relocate_kernel will fall back to efi_low_alloc_above to allocate - * lowest possible memory region as long as the address and size meets - * the alignment constraints. - */ - preferred_addr = EFI_KIMG_PREFERRED_ADDRESS; - status = efi_relocate_kernel(image_addr, kernel_size, *image_size, - preferred_addr, efi_get_kimg_min_align(), - 0x0); + *image_size = kernel_memsize; + *reserve_size = *image_size; + status = efi_kaslr_relocate_kernel(image_addr, + reserve_addr, reserve_size, + kernel_size, kernel_codesize, kernel_memsize, + efi_kaslr_get_phys_seed(image_handle)); if (status != EFI_SUCCESS) { efi_err("Failed to relocate kernel\n"); *image_size = 0; } + return status; } + +void efi_icache_sync(unsigned long start, unsigned long end) +{ + asm volatile ("fence.i" ::: "memory"); +} diff --git a/drivers/firmware/efi/libstub/unaccepted_memory.c b/drivers/firmware/efi/libstub/unaccepted_memory.c index ca61f4733ea5869356489a8f2709a23cbf591601..9a655f30ba47db013c03f2f73da88d6405507c7f 100644 --- a/drivers/firmware/efi/libstub/unaccepted_memory.c +++ b/drivers/firmware/efi/libstub/unaccepted_memory.c @@ -62,7 +62,7 @@ efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc, bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start, EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE); - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, sizeof(*unaccepted_table) + bitmap_size, (void **)&unaccepted_table); if (status != EFI_SUCCESS) { diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c index 3dba590a2a9579c44e8cc498b62e47744eb29f06..508eab346fc6763965c00bfc9f9fa614faedde18 100644 --- a/drivers/firmware/imx/imx-dsp.c +++ b/drivers/firmware/imx/imx-dsp.c @@ -114,6 +114,7 @@ static int imx_dsp_setup_channels(struct imx_dsp_ipc *dsp_ipc) dsp_chan->idx = i % 2; dsp_chan->ch = mbox_request_channel_byname(cl, chan_name); if (IS_ERR(dsp_chan->ch)) { + kfree(dsp_chan->name); ret = PTR_ERR(dsp_chan->ch); if (ret != -EPROBE_DEFER) dev_err(dev, "Failed to request mbox chan %s ret %d\n", diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index da33bbbdacb956c914342a3ed297f069346f48a7..58f107194fdafdfae6d86318fdaf2f206065757a 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -973,7 +973,7 @@ static int aspeed_gpio_set_config(struct gpio_chip *chip, unsigned int offset, else if (param == PIN_CONFIG_BIAS_DISABLE || param == PIN_CONFIG_BIAS_PULL_DOWN || param == PIN_CONFIG_DRIVE_STRENGTH) - return pinctrl_gpio_set_config(offset, config); + return pinctrl_gpio_set_config(chip->base + offset, config); else if (param == PIN_CONFIG_DRIVE_OPEN_DRAIN || param == PIN_CONFIG_DRIVE_OPEN_SOURCE) /* Return -ENOTSUPP to trigger emulation, as per datasheet */ diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c index 2b9b7be9b8fdd892cae6a82e071795fef3d49d9e..01c0fd0a9d8c63959a0aeee445944165aaef3e5e 100644 --- a/drivers/gpio/gpio-pmic-eic-sprd.c +++ b/drivers/gpio/gpio-pmic-eic-sprd.c @@ -352,6 +352,7 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev) pmic_eic->chip.set_config = sprd_pmic_eic_set_config; pmic_eic->chip.set = sprd_pmic_eic_set; pmic_eic->chip.get = sprd_pmic_eic_get; + pmic_eic->chip.can_sleep = true; irq = &pmic_eic->chip.irq; gpio_irq_chip_set_chip(irq, &pmic_eic_irq_chip); diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 7e9f7a32d3ee58ea047fe942a062bba0395a2af5..cae9661862fe1ddade8676596244d4a474fbf8ce 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -237,6 +237,7 @@ static bool pxa_gpio_has_pinctrl(void) switch (gpio_type) { case PXA3XX_GPIO: case MMP2_GPIO: + case MMP_GPIO: return false; default: diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index 271db3639a78209e00d45d558d6354bb0161e27f..44bf1709a6488cd7e61eca8e56463f670bbee4b8 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -685,52 +686,32 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page) return sprintf(page, "%c\n", live ? '1' : '0'); } -static char **gpio_sim_make_line_names(struct gpio_sim_bank *bank, - unsigned int *line_names_size) +static unsigned int gpio_sim_get_line_names_size(struct gpio_sim_bank *bank) { - unsigned int max_offset = 0; - bool has_line_names = false; struct gpio_sim_line *line; - char **line_names; + unsigned int size = 0; list_for_each_entry(line, &bank->line_list, siblings) { - if (line->offset >= bank->num_lines) + if (!line->name || (line->offset >= bank->num_lines)) continue; - if (line->name) { - if (line->offset > max_offset) - max_offset = line->offset; - - /* - * max_offset can stay at 0 so it's not an indicator - * of whether line names were configured at all. - */ - has_line_names = true; - } + size = max(size, line->offset + 1); } - if (!has_line_names) - /* - * This is not an error - NULL means, there are no line - * names configured. - */ - return NULL; - - *line_names_size = max_offset + 1; + return size; +} - line_names = kcalloc(*line_names_size, sizeof(*line_names), GFP_KERNEL); - if (!line_names) - return ERR_PTR(-ENOMEM); +static void +gpio_sim_set_line_names(struct gpio_sim_bank *bank, char **line_names) +{ + struct gpio_sim_line *line; list_for_each_entry(line, &bank->line_list, siblings) { - if (line->offset >= bank->num_lines) + if (!line->name || (line->offset >= bank->num_lines)) continue; - if (line->name && (line->offset <= max_offset)) - line_names[line->offset] = line->name; + line_names[line->offset] = line->name; } - - return line_names; } static void gpio_sim_remove_hogs(struct gpio_sim_device *dev) @@ -834,7 +815,7 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, struct fwnode_handle *parent) { struct property_entry properties[GPIO_SIM_PROP_MAX]; - unsigned int prop_idx = 0, line_names_size = 0; + unsigned int prop_idx = 0, line_names_size; char **line_names __free(kfree) = NULL; memset(properties, 0, sizeof(properties)); @@ -845,14 +826,19 @@ gpio_sim_make_bank_swnode(struct gpio_sim_bank *bank, properties[prop_idx++] = PROPERTY_ENTRY_STRING("gpio-sim,label", bank->label); - line_names = gpio_sim_make_line_names(bank, &line_names_size); - if (IS_ERR(line_names)) - return ERR_CAST(line_names); + line_names_size = gpio_sim_get_line_names_size(bank); + if (line_names_size) { + line_names = kcalloc(line_names_size, sizeof(*line_names), + GFP_KERNEL); + if (!line_names) + return ERR_PTR(-ENOMEM); + + gpio_sim_set_line_names(bank, line_names); - if (line_names) properties[prop_idx++] = PROPERTY_ENTRY_STRING_ARRAY_LEN( "gpio-line-names", line_names, line_names_size); + } return fwnode_create_software_node(properties, parent); } diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index 78f8790168ae1e4bad29229970cec99ff6f52d9b..f96d260a4a19d8b788dbf92aef7da5c4e0bfcb63 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -195,7 +195,7 @@ static int tb10x_gpio_probe(struct platform_device *pdev) handle_edge_irq, IRQ_NOREQUEST, IRQ_NOPROBE, IRQ_GC_INIT_MASK_CACHE); if (ret) - return ret; + goto err_remove_domain; gc = tb10x_gpio->domain->gc->gc[0]; gc->reg_base = tb10x_gpio->base; @@ -209,6 +209,10 @@ static int tb10x_gpio_probe(struct platform_device *pdev) } return 0; + +err_remove_domain: + irq_domain_remove(tb10x_gpio->domain); + return ret; } static int tb10x_gpio_remove(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index bbd9e919119921e5e77a3aeac2b0e0cc337f05c9..fad979797486534d3cc6a1437fdca765baeb7b47 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -43,9 +43,10 @@ static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, unsigned offset, bool enabled) { struct timbgpio *tgpio = gpiochip_get_data(gpio); + unsigned long flags; u32 reg; - spin_lock(&tgpio->lock); + spin_lock_irqsave(&tgpio->lock, flags); reg = ioread32(tgpio->membase + offset); if (enabled) @@ -54,7 +55,7 @@ static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, reg &= ~(1 << index); iowrite32(reg, tgpio->membase + offset); - spin_unlock(&tgpio->lock); + spin_unlock_irqrestore(&tgpio->lock, flags); return 0; } diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index 0a7264aabe48834d5d1830340caab0bdd752fc77..324e942c0650bb9f33bc2d14b5e93df228e40801 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -575,6 +575,26 @@ static int zynq_gpio_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int zynq_gpio_irq_reqres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + int ret; + + ret = pm_runtime_resume_and_get(chip->parent); + if (ret < 0) + return ret; + + return gpiochip_reqres_irq(chip, d->hwirq); +} + +static void zynq_gpio_irq_relres(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + gpiochip_relres_irq(chip, d->hwirq); + pm_runtime_put(chip->parent); +} + /* irq chip descriptor */ static const struct irq_chip zynq_gpio_level_irqchip = { .name = DRIVER_NAME, @@ -584,9 +604,10 @@ static const struct irq_chip zynq_gpio_level_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED | IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static const struct irq_chip zynq_gpio_edge_irqchip = { @@ -597,8 +618,9 @@ static const struct irq_chip zynq_gpio_edge_irqchip = { .irq_unmask = zynq_gpio_irq_unmask, .irq_set_type = zynq_gpio_set_irq_type, .irq_set_wake = zynq_gpio_set_wake, + .irq_request_resources = zynq_gpio_irq_reqres, + .irq_release_resources = zynq_gpio_irq_relres, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ab9ef1c2034912d8fc330f6009a81dc31759f5b5..3caa020391c75283c9145d18fb3be151496620af 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -136,7 +136,7 @@ config DRM_FBDEV_EMULATION bool "Enable legacy fbdev support for your modesetting driver" depends on DRM select FRAMEBUFFER_CONSOLE_DETECT_PRIMARY if FRAMEBUFFER_CONSOLE - default y + default FB help Choose this option if you have a need for the legacy fbdev support. Note that this support also provides the linux console diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index dc2d53081e806ffcdb05bc32e46cb76cdaed056c..a79d53bdbe136a8a99afe83530d5666d2edb405f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1293,7 +1293,6 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, void amdgpu_device_pci_config_reset(struct amdgpu_device *adev); int amdgpu_device_pci_reset(struct amdgpu_device *adev); bool amdgpu_device_need_post(struct amdgpu_device *adev); -bool amdgpu_sg_display_supported(struct amdgpu_device *adev); bool amdgpu_device_pcie_dynamic_switching_supported(void); bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev); bool amdgpu_device_aspm_support_quirk(void); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index df633e9ce9205fc6357d269d851e4c1ed34e4b7b..25d5fda5b243e3c598c141952f9537fe3c9652d7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -442,9 +442,7 @@ void amdgpu_amdkfd_get_local_mem_info(struct amdgpu_device *adev, mem_info->local_mem_size_public, mem_info->local_mem_size_private); - if (amdgpu_sriov_vf(adev)) - mem_info->mem_clk_max = adev->clock.default_mclk / 100; - else if (adev->pm.dpm_enabled) { + if (adev->pm.dpm_enabled) { if (amdgpu_emu_mode == 1) mem_info->mem_clk_max = 0; else @@ -463,9 +461,7 @@ uint64_t amdgpu_amdkfd_get_gpu_clock_counter(struct amdgpu_device *adev) uint32_t amdgpu_amdkfd_get_max_engine_clock_in_mhz(struct amdgpu_device *adev) { /* the sclk is in quantas of 10kHz */ - if (amdgpu_sriov_vf(adev)) - return adev->clock.default_sclk / 100; - else if (adev->pm.dpm_enabled) + if (adev->pm.dpm_enabled) return amdgpu_dpm_get_sclk(adev, false) / 100; else return 100; @@ -482,7 +478,7 @@ void amdgpu_amdkfd_get_cu_info(struct amdgpu_device *adev, struct kfd_cu_info *c cu_info->cu_active_number = acu_info.number; cu_info->cu_ao_mask = acu_info.ao_cu_mask; memcpy(&cu_info->cu_bitmap[0], &acu_info.bitmap[0], - sizeof(acu_info.bitmap)); + sizeof(cu_info->cu_bitmap)); cu_info->num_shader_engines = adev->gfx.config.max_shader_engines; cu_info->num_shader_arrays_per_engine = adev->gfx.config.max_sh_per_se; cu_info->num_cu_per_sh = adev->gfx.config.max_cu_per_sh; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c index f1f2c24de081eb1f300b2664a53f221591f50e06..69810b3f1c63613f6686f181cdeccf5d9513c2d9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c @@ -980,8 +980,7 @@ void kgd_gfx_v10_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst) + uint32_t *reg_data) { *reg_data = wait_times; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h index ecaead24e8c96bbe288fffcac96b067f33fb1589..67bcaa3d4226410279a6bce1b62b7e0cd206259f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h @@ -55,5 +55,4 @@ void kgd_gfx_v10_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c index fa5ee96f884545c6fe6aec9573441399fbb3ef9a..3c45a188b701ab0c406481146ff1942576acc413 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c @@ -1103,8 +1103,7 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst) + uint32_t *reg_data) { *reg_data = wait_times; @@ -1120,8 +1119,7 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, SCH_WAVE, grace_period); - *reg_offset = SOC15_REG_OFFSET(GC, GET_INST(GC, inst), - mmCP_IQ_WAIT_TIME2); + *reg_offset = SOC15_REG_OFFSET(GC, 0, mmCP_IQ_WAIT_TIME2); } void kgd_gfx_v9_program_trap_handler_settings(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h index 936e501908cef3760865ed6d888127a59bbffcbc..ce424615f59b5bc753c54b8dc83bf1161fca407e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h @@ -100,5 +100,4 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index 73ee14f7a9a4b83cfea610dbf8427adaa7da8b40..dce9e7d5e4ec672827f574fb64816ca205ef96ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -1776,7 +1776,7 @@ static ssize_t amdgpu_atombios_get_vbios_version(struct device *dev, struct amdgpu_device *adev = drm_to_adev(ddev); struct atom_context *ctx = adev->mode_info.atom_context; - return sysfs_emit(buf, "%s\n", ctx->vbios_ver_str); + return sysfs_emit(buf, "%s\n", ctx->vbios_pn); } static DEVICE_ATTR(vbios_version, 0444, amdgpu_atombios_get_vbios_version, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c index 835980e94b9e88401bd14f05afac025469a41fb3..fb2681dd6b338c222eaa0431cce61f940ad239b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c @@ -217,6 +217,7 @@ union umc_info { struct atom_umc_info_v3_1 v31; struct atom_umc_info_v3_2 v32; struct atom_umc_info_v3_3 v33; + struct atom_umc_info_v4_0 v40; }; union vram_info { @@ -508,9 +509,8 @@ bool amdgpu_atomfirmware_mem_ecc_supported(struct amdgpu_device *adev) if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, &size, &frev, &crev, &data_offset)) { + umc_info = (union umc_info *)(mode_info->atom_context->bios + data_offset); if (frev == 3) { - umc_info = (union umc_info *) - (mode_info->atom_context->bios + data_offset); switch (crev) { case 1: umc_config = le32_to_cpu(umc_info->v31.umc_config); @@ -533,6 +533,20 @@ bool amdgpu_atomfirmware_mem_ecc_supported(struct amdgpu_device *adev) /* unsupported crev */ return false; } + } else if (frev == 4) { + switch (crev) { + case 0: + umc_config1 = le32_to_cpu(umc_info->v40.umc_config1); + ecc_default_enabled = + (umc_config1 & UMC_CONFIG1__ENABLE_ECC_CAPABLE) ? true : false; + break; + default: + /* unsupported crev */ + return false; + } + } else { + /* unsupported frev */ + return false; } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 49dd9aa8da70883871c9859717f590df32fc34fa..efdb1c48f431530d01fd25eb581033472a5b5239 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -127,7 +127,6 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, { struct drm_gem_object *gobj; unsigned long size; - int r; gobj = drm_gem_object_lookup(p->filp, data->handle); if (gobj == NULL) @@ -137,23 +136,14 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, drm_gem_object_put(gobj); size = amdgpu_bo_size(p->uf_bo); - if (size != PAGE_SIZE || (data->offset + 8) > size) { - r = -EINVAL; - goto error_unref; - } + if (size != PAGE_SIZE || data->offset > (size - 8)) + return -EINVAL; - if (amdgpu_ttm_tt_get_usermm(p->uf_bo->tbo.ttm)) { - r = -EINVAL; - goto error_unref; - } + if (amdgpu_ttm_tt_get_usermm(p->uf_bo->tbo.ttm)) + return -EINVAL; *offset = data->offset; - return 0; - -error_unref: - amdgpu_bo_unref(&p->uf_bo); - return r; } static int amdgpu_cs_p1_bo_handles(struct amdgpu_cs_parser *p, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index e77f048c99d85ee74d1efe2354358185af80ca6d..2b8356699f235dc2e18f991b70e18de56200e5c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -885,13 +885,20 @@ static void amdgpu_block_invalid_wreg(struct amdgpu_device *adev, */ static int amdgpu_device_asic_init(struct amdgpu_device *adev) { + int ret; + amdgpu_asic_pre_asic_init(adev); if (adev->ip_versions[GC_HWIP][0] == IP_VERSION(9, 4, 3) || - adev->ip_versions[GC_HWIP][0] >= IP_VERSION(11, 0, 0)) - return amdgpu_atomfirmware_asic_init(adev, true); - else + adev->ip_versions[GC_HWIP][0] >= IP_VERSION(11, 0, 0)) { + amdgpu_psp_wait_for_bootloader(adev); + ret = amdgpu_atomfirmware_asic_init(adev, true); + return ret; + } else { return amdgpu_atom_asic_init(adev->mode_info.atom_context); + } + + return 0; } /** @@ -1237,32 +1244,6 @@ bool amdgpu_device_need_post(struct amdgpu_device *adev) return true; } -/* - * On APUs with >= 64GB white flickering has been observed w/ SG enabled. - * Disable S/G on such systems until we have a proper fix. - * https://gitlab.freedesktop.org/drm/amd/-/issues/2354 - * https://gitlab.freedesktop.org/drm/amd/-/issues/2735 - */ -bool amdgpu_sg_display_supported(struct amdgpu_device *adev) -{ - switch (amdgpu_sg_display) { - case -1: - break; - case 0: - return false; - case 1: - return true; - default: - return false; - } - if ((totalram_pages() << (PAGE_SHIFT - 10)) + - (adev->gmc.real_vram_size / 1024) >= 64000000) { - DRM_WARN("Disabling S/G due to >=64GB RAM\n"); - return false; - } - return true; -} - /* * Intel hosts such as Raptor Lake and Sapphire Rapids don't support dynamic * speed switching. Until we have confirmation from Intel that a specific host @@ -2112,7 +2093,7 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) adev->flags |= AMD_IS_PX; if (!(adev->flags & AMD_IS_APU)) { - parent = pci_upstream_bridge(adev->pdev); + parent = pcie_find_root_port(adev->pdev); adev->has_pr3 = parent ? pci_pr3_present(parent) : false; } @@ -4694,9 +4675,12 @@ int amdgpu_device_mode1_reset(struct amdgpu_device *adev) } if (ret) - dev_err(adev->dev, "GPU mode1 reset failed\n"); + goto mode1_reset_failed; amdgpu_device_load_pci_state(adev->pdev); + ret = amdgpu_psp_wait_for_bootloader(adev); + if (ret) + goto mode1_reset_failed; /* wait for asic to come out of reset */ for (i = 0; i < adev->usec_timeout; i++) { @@ -4707,7 +4691,17 @@ int amdgpu_device_mode1_reset(struct amdgpu_device *adev) udelay(1); } + if (i >= adev->usec_timeout) { + ret = -ETIMEDOUT; + goto mode1_reset_failed; + } + amdgpu_atombios_scratch_regs_engine_hung(adev, false); + + return 0; + +mode1_reset_failed: + dev_err(adev->dev, "GPU mode1 reset failed\n"); return ret; } @@ -4849,7 +4843,7 @@ static void amdgpu_reset_capture_coredumpm(struct amdgpu_device *adev) struct drm_device *dev = adev_to_drm(adev); ktime_get_ts64(&adev->reset_time); - dev_coredumpm(dev->dev, THIS_MODULE, adev, 0, GFP_KERNEL, + dev_coredumpm(dev->dev, THIS_MODULE, adev, 0, GFP_NOWAIT, amdgpu_devcoredump_read, amdgpu_devcoredump_free); } #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 74ffe6581c8548fe81d47f33893ef74fb4ff960a..7d5e7ad28ba82a62c65d91ee37f8521e5c08a0d1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -1390,6 +1390,7 @@ union gc_info { struct gc_info_v1_1 v1_1; struct gc_info_v1_2 v1_2; struct gc_info_v2_0 v2; + struct gc_info_v2_1 v2_1; }; static int amdgpu_discovery_get_gfx_info(struct amdgpu_device *adev) @@ -1465,6 +1466,15 @@ static int amdgpu_discovery_get_gfx_info(struct amdgpu_device *adev) adev->gfx.config.num_sc_per_sh = le32_to_cpu(gc_info->v2.gc_num_sc_per_se) / le32_to_cpu(gc_info->v2.gc_num_sh_per_se); adev->gfx.config.num_packer_per_sc = le32_to_cpu(gc_info->v2.gc_num_packer_per_sc); + if (gc_info->v2.header.version_minor == 1) { + adev->gfx.config.gc_num_tcp_per_sa = le32_to_cpu(gc_info->v2_1.gc_num_tcp_per_sh); + adev->gfx.config.gc_tcp_size_per_cu = le32_to_cpu(gc_info->v2_1.gc_tcp_size_per_cu); + adev->gfx.config.gc_num_sdp_interface = le32_to_cpu(gc_info->v2_1.gc_num_sdp_interface); /* per XCD */ + adev->gfx.config.gc_num_cu_per_sqc = le32_to_cpu(gc_info->v2_1.gc_num_cu_per_sqc); + adev->gfx.config.gc_l1_instruction_cache_size_per_sqc = le32_to_cpu(gc_info->v2_1.gc_instruction_cache_size_per_sqc); + adev->gfx.config.gc_l1_data_cache_size_per_sqc = le32_to_cpu(gc_info->v2_1.gc_scalar_data_cache_size_per_sqc); + adev->gfx.config.gc_tcc_size = le32_to_cpu(gc_info->v2_1.gc_tcc_size); /* per XCD */ + } break; default: dev_err(adev->dev, @@ -1478,6 +1488,7 @@ static int amdgpu_discovery_get_gfx_info(struct amdgpu_device *adev) union mall_info { struct mall_info_v1_0 v1; + struct mall_info_v2_0 v2; }; static int amdgpu_discovery_get_mall_info(struct amdgpu_device *adev) @@ -1518,6 +1529,10 @@ static int amdgpu_discovery_get_mall_info(struct amdgpu_device *adev) adev->gmc.mall_size = mall_size; adev->gmc.m_half_use = half_use; break; + case 2: + mall_size_per_umc = le32_to_cpu(mall_info->v2.mall_size_per_umc); + adev->gmc.mall_size = mall_size_per_umc * adev->gmc.num_umc; + break; default: dev_err(adev->dev, "Unhandled MALL info table %d.%d\n", diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index d20dd3f852fc3e957f1e989a0a0f62d47baf7136..363e6a2cad8c205d2ce50afbffd408e8098c9bd9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -38,6 +38,8 @@ #include #include #include +#include +#include #include #include #include @@ -532,11 +534,29 @@ bool amdgpu_display_ddc_probe(struct amdgpu_connector *amdgpu_connector, return true; } +static int amdgpu_dirtyfb(struct drm_framebuffer *fb, struct drm_file *file, + unsigned int flags, unsigned int color, + struct drm_clip_rect *clips, unsigned int num_clips) +{ + + if (file) + return -ENOSYS; + + return drm_atomic_helper_dirtyfb(fb, file, flags, color, clips, + num_clips); +} + static const struct drm_framebuffer_funcs amdgpu_fb_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, }; +static const struct drm_framebuffer_funcs amdgpu_fb_funcs_atomic = { + .destroy = drm_gem_fb_destroy, + .create_handle = drm_gem_fb_create_handle, + .dirty = amdgpu_dirtyfb +}; + uint32_t amdgpu_display_supported_domains(struct amdgpu_device *adev, uint64_t bo_flags) { @@ -1139,7 +1159,11 @@ static int amdgpu_display_gem_fb_verify_and_init(struct drm_device *dev, if (ret) goto err; - ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs); + if (drm_drv_uses_atomic_modeset(dev)) + ret = drm_framebuffer_init(dev, &rfb->base, + &amdgpu_fb_funcs_atomic); + else + ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs); if (ret) goto err; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c index 9c66d98af6d86ae41870fb1cb2d18b26787d9f29..7cd0dfaeee206c15d9ef069947085d3088f95816 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c @@ -170,6 +170,7 @@ int amdgpu_fru_get_product_info(struct amdgpu_device *adev) csum += pia[size - 1]; if (csum) { DRM_ERROR("Bad Product Info Area checksum: 0x%02x", csum); + kfree(pia); return -EIO; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index a4ff515ce8966ae4e12aad8997d6113b3d63540c..0ca95c4d4bfbe1634b270ac5afe04aec57636347 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -43,6 +43,7 @@ #define AMDGPU_GFX_LBPW_DISABLED_MODE 0x00000008L #define AMDGPU_MAX_GC_INSTANCES 8 +#define KGD_MAX_QUEUES 128 #define AMDGPU_MAX_GFX_QUEUES KGD_MAX_QUEUES #define AMDGPU_MAX_COMPUTE_QUEUES KGD_MAX_QUEUES @@ -241,6 +242,9 @@ struct amdgpu_gfx_config { uint32_t gc_gl1c_per_sa; uint32_t gc_gl1c_size_per_instance; uint32_t gc_gl2c_per_gpu; + uint32_t gc_tcp_size_per_cu; + uint32_t gc_num_cu_per_sqc; + uint32_t gc_tcc_size; }; struct amdgpu_cu_info { @@ -254,7 +258,7 @@ struct amdgpu_cu_info { uint32_t number; uint32_t ao_cu_mask; uint32_t ao_cu_bitmap[4][4]; - uint32_t bitmap[4][4]; + uint32_t bitmap[AMDGPU_MAX_GC_INSTANCES][4][4]; }; struct amdgpu_gfx_ras { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 99f4df133ed3e8f21a77f2636e41c18a82634473..d30dc0b718c73ee8c703592b56d41c62f87d9c19 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -839,7 +839,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) memcpy(&dev_info->cu_ao_bitmap[0], &adev->gfx.cu_info.ao_cu_bitmap[0], sizeof(adev->gfx.cu_info.ao_cu_bitmap)); memcpy(&dev_info->cu_bitmap[0], &adev->gfx.cu_info.bitmap[0], - sizeof(adev->gfx.cu_info.bitmap)); + sizeof(dev_info->cu_bitmap)); dev_info->vram_type = adev->gmc.vram_type; dev_info->vram_bit_width = adev->gmc.vram_width; dev_info->vce_harvest_config = adev->vce.harvest_config; @@ -940,12 +940,17 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) struct atom_context *atom_context; atom_context = adev->mode_info.atom_context; - memcpy(vbios_info.name, atom_context->name, sizeof(atom_context->name)); - memcpy(vbios_info.vbios_pn, atom_context->vbios_pn, sizeof(atom_context->vbios_pn)); - vbios_info.version = atom_context->version; - memcpy(vbios_info.vbios_ver_str, atom_context->vbios_ver_str, - sizeof(atom_context->vbios_ver_str)); - memcpy(vbios_info.date, atom_context->date, sizeof(atom_context->date)); + if (atom_context) { + memcpy(vbios_info.name, atom_context->name, + sizeof(atom_context->name)); + memcpy(vbios_info.vbios_pn, atom_context->vbios_pn, + sizeof(atom_context->vbios_pn)); + vbios_info.version = atom_context->version; + memcpy(vbios_info.vbios_ver_str, atom_context->vbios_ver_str, + sizeof(atom_context->vbios_ver_str)); + memcpy(vbios_info.date, atom_context->date, + sizeof(atom_context->date)); + } return copy_to_user(out, &vbios_info, min((size_t)size, sizeof(vbios_info))) ? -EFAULT : 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index 8fdca54bb8a11cb4b2d1d46ad1baab74a431029b..429ef212c1f25b4865953083ef32196f0e3628a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -2078,6 +2078,17 @@ int psp_securedisplay_invoke(struct psp_context *psp, uint32_t ta_cmd_id) } /* SECUREDISPLAY end */ +int amdgpu_psp_wait_for_bootloader(struct amdgpu_device *adev) +{ + struct psp_context *psp = &adev->psp; + int ret = 0; + + if (!amdgpu_sriov_vf(adev) && psp->funcs && psp->funcs->wait_for_bootloader != NULL) + ret = psp->funcs->wait_for_bootloader(psp); + + return ret; +} + static int psp_hw_start(struct psp_context *psp) { struct amdgpu_device *adev = psp->adev; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h index 3384eb94fde07a0e8be4be545387ee620a1af51f..3e67ed63e63853fadb3da7452592074687aa7dda 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h @@ -109,6 +109,7 @@ enum psp_reg_prog_id { struct psp_funcs { int (*init_microcode)(struct psp_context *psp); + int (*wait_for_bootloader)(struct psp_context *psp); int (*bootloader_load_kdb)(struct psp_context *psp); int (*bootloader_load_spl)(struct psp_context *psp); int (*bootloader_load_sysdrv)(struct psp_context *psp); @@ -533,4 +534,6 @@ int psp_spatial_partition(struct psp_context *psp, int mode); int is_psp_fw_valid(struct psp_bin_desc bin); +int amdgpu_psp_wait_for_bootloader(struct amdgpu_device *adev); + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 7689395e44fd419e1e2a5f19ccbe5b00e618afce..163445baa4fc80a432209247e43c4fb76d4da979 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -764,7 +764,7 @@ int amdgpu_ras_feature_enable(struct amdgpu_device *adev, { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); union ta_ras_cmd_input *info; - int ret = 0; + int ret; if (!con) return -EINVAL; @@ -773,7 +773,7 @@ int amdgpu_ras_feature_enable(struct amdgpu_device *adev, if (enable && head->block != AMDGPU_RAS_BLOCK__GFX && !amdgpu_ras_is_feature_allowed(adev, head)) - goto out; + return 0; /* Only enable gfx ras feature from host side */ if (head->block == AMDGPU_RAS_BLOCK__GFX && @@ -801,16 +801,17 @@ int amdgpu_ras_feature_enable(struct amdgpu_device *adev, enable ? "enable":"disable", get_ras_block_str(head), amdgpu_ras_is_poison_mode_supported(adev), ret); - goto out; + kfree(info); + return ret; } + + kfree(info); } /* setup the obj */ __amdgpu_ras_feature_enable(adev, head, enable); -out: - if (head->block == AMDGPU_RAS_BLOCK__GFX) - kfree(info); - return ret; + + return 0; } /* Only used in device probe stage and called only once. */ @@ -1052,7 +1053,8 @@ int amdgpu_ras_query_error_status(struct amdgpu_device *adev, info->ce_count = obj->err_data.ce_count; if (err_data.ce_count) { - if (adev->smuio.funcs && + if (!adev->aid_mask && + adev->smuio.funcs && adev->smuio.funcs->get_socket_id && adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " @@ -1072,7 +1074,8 @@ int amdgpu_ras_query_error_status(struct amdgpu_device *adev, } } if (err_data.ue_count) { - if (adev->smuio.funcs && + if (!adev->aid_mask && + adev->smuio.funcs && adev->smuio.funcs->get_socket_id && adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " @@ -2399,6 +2402,7 @@ static bool amdgpu_ras_asic_supported(struct amdgpu_device *adev) if (amdgpu_sriov_vf(adev)) { switch (adev->ip_versions[MP0_HWIP][0]) { case IP_VERSION(13, 0, 2): + case IP_VERSION(13, 0, 6): return true; default: return false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c index 4764d2171f92e918bf89098121e82d0c1525d628..595d5e535aca635099dd31775025017b6f3f4d3e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c @@ -158,9 +158,10 @@ static bool __is_ras_eeprom_supported(struct amdgpu_device *adev) case IP_VERSION(11, 0, 7): /* Sienna cichlid */ case IP_VERSION(13, 0, 0): case IP_VERSION(13, 0, 2): /* Aldebaran */ - case IP_VERSION(13, 0, 6): case IP_VERSION(13, 0, 10): return true; + case IP_VERSION(13, 0, 6): + return (adev->gmc.is_app_apu) ? false : true; default: return false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index c6b4337eb20c3b38b0426fdacb95aee6c528c283..10df731998b22f873611092942008560ec67394d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -81,7 +81,7 @@ int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, unsigned int size) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, true, 0); + GFP_KERNEL, false, 0); if (IS_ERR(sa)) { *sa_bo = NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 0aee9c8288a2b8eb88054177b52e8a8ce0404215..9032d7a24d7cd7cddfafdc494f131d9ba87a16c6 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -9449,7 +9449,7 @@ static int gfx_v10_0_get_cu_info(struct amdgpu_device *adev, gfx_v10_0_set_user_wgp_inactive_bitmap_per_sh( adev, disable_masks[i * 2 + j]); bitmap = gfx_v10_0_get_cu_active_bitmap_per_sh(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 5c3db694afa87a99382f68044b798d401d0dda5e..762d7a19f1be163fa2cbf83bd5271a32cbc043a1 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -6368,7 +6368,7 @@ static int gfx_v11_0_get_cu_info(struct amdgpu_device *adev, * SE6: {SH0,SH1} --> {bitmap[2][2], bitmap[2][3]} * SE7: {SH0,SH1} --> {bitmap[3][2], bitmap[3][3]} */ - cu_info->bitmap[i % 4][j + (i / 4) * 2] = bitmap; + cu_info->bitmap[0][i % 4][j + (i / 4) * 2] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index da6caff78c22be23ba918c49f4334c97236c0104..34f9211b26793ff705aef4ab75e926dfba1f1dbb 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -3577,7 +3577,7 @@ static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev) gfx_v6_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v6_0_get_cu_enabled(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 90b034b173c1cc9385c4668eb478c93e88b93ed1..c2faf6b4c2fced463cc24598cf10c9775a9663ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -5119,7 +5119,7 @@ static void gfx_v7_0_get_cu_info(struct amdgpu_device *adev) gfx_v7_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v7_0_get_cu_active_bitmap(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 51c1745c83697b04a7eab230f5a64847fceabed3..885ebd703260f05a92aa15928910e171112b2cc7 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -7121,7 +7121,7 @@ static void gfx_v8_0_get_cu_info(struct amdgpu_device *adev) gfx_v8_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v8_0_get_cu_active_bitmap(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 458faf657042e3d6a2b8af49b508db2867bf3295..fd61574a737cb17cd843ea4b81c0c10b0f0b1661 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1499,7 +1499,7 @@ static void gfx_v9_0_init_always_on_cu_mask(struct amdgpu_device *adev) amdgpu_gfx_select_se_sh(adev, i, j, 0xffffffff, 0); for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { - if (cu_info->bitmap[i][j] & mask) { + if (cu_info->bitmap[0][i][j] & mask) { if (counter == pg_always_on_cu_num) WREG32_SOC15(GC, 0, mmRLC_PG_ALWAYS_ON_CU_MASK, cu_bitmap); if (counter < always_on_cu_num) @@ -7233,7 +7233,7 @@ static int gfx_v9_0_get_cu_info(struct amdgpu_device *adev, * SE6,SH0 --> bitmap[2][1] * SE7,SH0 --> bitmap[3][1] */ - cu_info->bitmap[i % 4][j + i / 4] = bitmap; + cu_info->bitmap[0][i % 4][j + i / 4] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 57ed4e5c294c8fb5cb0571f77eee8b3efb5d2566..18ce5fe45f6f863ac33e51e8eaa019025baf82a2 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -203,6 +203,9 @@ static void gfx_v9_4_3_init_golden_registers(struct amdgpu_device *adev) if (adev->rev_id == 0) { WREG32_FIELD15_PREREG(GC, dev_inst, TCP_UTCL1_CNTL1, REDUCE_FIFO_DEPTH_BY_2, 2); + } else { + WREG32_FIELD15_PREREG(GC, dev_inst, TCP_UTCL1_CNTL2, + SPARE, 0x1); } } } @@ -860,11 +863,15 @@ static int gfx_v9_4_3_sw_init(void *handle) if (r) return r; - r = amdgpu_gfx_sysfs_init(adev); + r = amdgpu_gfx_ras_sw_init(adev); if (r) return r; - return amdgpu_gfx_ras_sw_init(adev); + + if (!amdgpu_sriov_vf(adev)) + r = amdgpu_gfx_sysfs_init(adev); + + return r; } static int gfx_v9_4_3_sw_fini(void *handle) @@ -885,7 +892,8 @@ static int gfx_v9_4_3_sw_fini(void *handle) gfx_v9_4_3_mec_fini(adev); amdgpu_bo_unref(&adev->gfx.rlc.clear_state_obj); gfx_v9_4_3_free_microcode(adev); - amdgpu_gfx_sysfs_fini(adev); + if (!amdgpu_sriov_vf(adev)) + amdgpu_gfx_sysfs_fini(adev); return 0; } @@ -2219,15 +2227,6 @@ static void gfx_v9_4_3_xcc_update_sram_fgcg(struct amdgpu_device *adev, WREG32_SOC15(GC, GET_INST(GC, xcc_id), regRLC_CGTT_MGCG_OVERRIDE, data); - def = data = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regRLC_CLK_CNTL); - - if (enable) - data &= ~RLC_CLK_CNTL__RLC_SRAM_CLK_GATER_OVERRIDE_MASK; - else - data |= RLC_CLK_CNTL__RLC_SRAM_CLK_GATER_OVERRIDE_MASK; - - if (def != data) - WREG32_SOC15(GC, GET_INST(GC, xcc_id), regRLC_CLK_CNTL, data); } static void gfx_v9_4_3_xcc_update_repeater_fgcg(struct amdgpu_device *adev, @@ -4048,7 +4047,8 @@ static void gfx_v9_4_3_inst_enable_watchdog_timer(struct amdgpu_device *adev, uint32_t i; uint32_t data; - data = REG_SET_FIELD(0, SQ_TIMEOUT_CONFIG, TIMEOUT_FATAL_DISABLE, + data = RREG32_SOC15(GC, GET_INST(GC, 0), regSQ_TIMEOUT_CONFIG); + data = REG_SET_FIELD(data, SQ_TIMEOUT_CONFIG, TIMEOUT_FATAL_DISABLE, amdgpu_watchdog_timer.timeout_fatal_disable ? 1 : 0); if (amdgpu_watchdog_timer.timeout_fatal_disable && @@ -4259,7 +4259,7 @@ static void gfx_v9_4_3_set_gds_init(struct amdgpu_device *adev) } static void gfx_v9_4_3_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, - u32 bitmap) + u32 bitmap, int xcc_id) { u32 data; @@ -4269,15 +4269,15 @@ static void gfx_v9_4_3_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, data = bitmap << GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; data &= GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; - WREG32_SOC15(GC, GET_INST(GC, 0), regGC_USER_SHADER_ARRAY_CONFIG, data); + WREG32_SOC15(GC, GET_INST(GC, xcc_id), regGC_USER_SHADER_ARRAY_CONFIG, data); } -static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev) +static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev, int xcc_id) { u32 data, mask; - data = RREG32_SOC15(GC, GET_INST(GC, 0), regCC_GC_SHADER_ARRAY_CONFIG); - data |= RREG32_SOC15(GC, GET_INST(GC, 0), regGC_USER_SHADER_ARRAY_CONFIG); + data = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCC_GC_SHADER_ARRAY_CONFIG); + data |= RREG32_SOC15(GC, GET_INST(GC, xcc_id), regGC_USER_SHADER_ARRAY_CONFIG); data &= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; data >>= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; @@ -4290,7 +4290,7 @@ static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev) static int gfx_v9_4_3_get_cu_info(struct amdgpu_device *adev, struct amdgpu_cu_info *cu_info) { - int i, j, k, counter, active_cu_number = 0; + int i, j, k, counter, xcc_id, active_cu_number = 0; u32 mask, bitmap, ao_bitmap, ao_cu_mask = 0; unsigned disable_masks[4 * 4]; @@ -4309,46 +4309,38 @@ static int gfx_v9_4_3_get_cu_info(struct amdgpu_device *adev, adev->gfx.config.max_sh_per_se); mutex_lock(&adev->grbm_idx_mutex); - for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { - for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { - mask = 1; - ao_bitmap = 0; - counter = 0; - gfx_v9_4_3_xcc_select_se_sh(adev, i, j, 0xffffffff, 0); - gfx_v9_4_3_set_user_cu_inactive_bitmap( - adev, disable_masks[i * adev->gfx.config.max_sh_per_se + j]); - bitmap = gfx_v9_4_3_get_cu_active_bitmap(adev); - - /* - * The bitmap(and ao_cu_bitmap) in cu_info structure is - * 4x4 size array, and it's usually suitable for Vega - * ASICs which has 4*2 SE/SH layout. - * But for Arcturus, SE/SH layout is changed to 8*1. - * To mostly reduce the impact, we make it compatible - * with current bitmap array as below: - * SE4,SH0 --> bitmap[0][1] - * SE5,SH0 --> bitmap[1][1] - * SE6,SH0 --> bitmap[2][1] - * SE7,SH0 --> bitmap[3][1] - */ - cu_info->bitmap[i % 4][j + i / 4] = bitmap; - - for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { - if (bitmap & mask) { - if (counter < adev->gfx.config.max_cu_per_sh) - ao_bitmap |= mask; - counter++; + for (xcc_id = 0; xcc_id < NUM_XCC(adev->gfx.xcc_mask); xcc_id++) { + for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { + for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { + mask = 1; + ao_bitmap = 0; + counter = 0; + gfx_v9_4_3_xcc_select_se_sh(adev, i, j, 0xffffffff, xcc_id); + gfx_v9_4_3_set_user_cu_inactive_bitmap( + adev, + disable_masks[i * adev->gfx.config.max_sh_per_se + j], + xcc_id); + bitmap = gfx_v9_4_3_get_cu_active_bitmap(adev, xcc_id); + + cu_info->bitmap[xcc_id][i][j] = bitmap; + + for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { + if (bitmap & mask) { + if (counter < adev->gfx.config.max_cu_per_sh) + ao_bitmap |= mask; + counter++; + } + mask <<= 1; } - mask <<= 1; + active_cu_number += counter; + if (i < 2 && j < 2) + ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8)); + cu_info->ao_cu_bitmap[i][j] = ao_bitmap; } - active_cu_number += counter; - if (i < 2 && j < 2) - ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8)); - cu_info->ao_cu_bitmap[i % 4][j + i / 4] = ao_bitmap; } + gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, + xcc_id); } - gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, - 0); mutex_unlock(&adev->grbm_idx_mutex); cu_info->number = active_cu_number; diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c index 15612915bb6c7ef3c11f9251219836b670610397..1de79d660285d796637a95e4120d8a1d604ef493 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -360,8 +360,10 @@ static int jpeg_v4_0_3_hw_fini(void *handle) cancel_delayed_work_sync(&adev->jpeg.idle_work); - if (adev->jpeg.cur_state != AMD_PG_STATE_GATE) - ret = jpeg_v4_0_3_set_powergating_state(adev, AMD_PG_STATE_GATE); + if (!amdgpu_sriov_vf(adev)) { + if (adev->jpeg.cur_state != AMD_PG_STATE_GATE) + ret = jpeg_v4_0_3_set_powergating_state(adev, AMD_PG_STATE_GATE); + } return ret; } diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c index d5ed9e0e1a5f157fd90bcb0bd429041cb3d3eeb2..e5b5b0f4940f4cb517e0bd6b3de4067e73426218 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c @@ -345,6 +345,9 @@ static void nbio_v4_3_init_registers(struct amdgpu_device *adev) data &= ~RCC_DEV0_EPF2_STRAP2__STRAP_NO_SOFT_RESET_DEV0_F2_MASK; WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF2_STRAP2, data); } + if (amdgpu_sriov_vf(adev)) + adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, + regBIF_BX_DEV0_EPF0_VF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2; } static u32 nbio_v4_3_get_rom_offset(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c index 9ea072374cb7b2ebf7f390d1fa52769c3627ff27..f85eec05d2181594763eb3a4062aa9c85414f073 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c @@ -437,6 +437,24 @@ static void nbio_v7_9_init_registers(struct amdgpu_device *adev) XCC_DOORBELL_FENCE__SHUB_SLV_MODE_MASK); } + + if (!amdgpu_sriov_vf(adev)) { + u32 baco_cntl; + for_each_inst(i, adev->aid_mask) { + baco_cntl = RREG32_SOC15(NBIO, i, regBIF_BX0_BACO_CNTL); + if (baco_cntl & (BIF_BX0_BACO_CNTL__BACO_DUMMY_EN_MASK | + BIF_BX0_BACO_CNTL__BACO_EN_MASK)) { + baco_cntl &= ~( + BIF_BX0_BACO_CNTL__BACO_DUMMY_EN_MASK | + BIF_BX0_BACO_CNTL__BACO_EN_MASK); + dev_dbg(adev->dev, + "Unsetting baco dummy mode %x", + baco_cntl); + WREG32_SOC15(NBIO, i, regBIF_BX0_BACO_CNTL, + baco_cntl); + } + } + } } static u64 nbio_v7_9_get_pcie_replay_count(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c index 10b17bd5aebe501ae1b61f603a6f3ba12e21099f..469eed084976c73af5f82fb989bda67f36592098 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c @@ -133,12 +133,32 @@ static bool psp_v13_0_is_sos_alive(struct psp_context *psp) return sol_reg != 0x0; } -static int psp_v13_0_wait_for_bootloader(struct psp_context *psp) +static int psp_v13_0_wait_for_vmbx_ready(struct psp_context *psp) { struct amdgpu_device *adev = psp->adev; + int retry_loop, ret; - int ret; - int retry_loop; + for (retry_loop = 0; retry_loop < 70; retry_loop++) { + /* Wait for bootloader to signify that is + ready having bit 31 of C2PMSG_33 set to 1 */ + ret = psp_wait_for( + psp, SOC15_REG_OFFSET(MP0, 0, regMP0_SMN_C2PMSG_33), + 0x80000000, 0xffffffff, false); + + if (ret == 0) + break; + } + + if (ret) + dev_warn(adev->dev, "Bootloader wait timed out"); + + return ret; +} + +static int psp_v13_0_wait_for_bootloader(struct psp_context *psp) +{ + struct amdgpu_device *adev = psp->adev; + int retry_loop, ret; /* Wait for bootloader to signify that it is ready having bit 31 of * C2PMSG_35 set to 1. All other bits are expected to be cleared. @@ -157,6 +177,19 @@ static int psp_v13_0_wait_for_bootloader(struct psp_context *psp) return ret; } +static int psp_v13_0_wait_for_bootloader_steady_state(struct psp_context *psp) +{ + struct amdgpu_device *adev = psp->adev; + + if (adev->ip_versions[MP0_HWIP][0] == IP_VERSION(13, 0, 6)) { + psp_v13_0_wait_for_vmbx_ready(psp); + + return psp_v13_0_wait_for_bootloader(psp); + } + + return 0; +} + static int psp_v13_0_bootloader_load_component(struct psp_context *psp, struct psp_bin_desc *bin_desc, enum psp_bootloader_cmd bl_cmd) @@ -714,6 +747,7 @@ static int psp_v13_0_fatal_error_recovery_quirk(struct psp_context *psp) static const struct psp_funcs psp_v13_0_funcs = { .init_microcode = psp_v13_0_init_microcode, + .wait_for_bootloader = psp_v13_0_wait_for_bootloader_steady_state, .bootloader_load_kdb = psp_v13_0_bootloader_load_kdb, .bootloader_load_spl = psp_v13_0_bootloader_load_spl, .bootloader_load_sysdrv = psp_v13_0_bootloader_load_sysdrv, diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index c45721ca916e40fda80c27e2ec7d699834acb110..f5be40d7ba3679dd9b8998d4b2aae3776e74742f 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -559,8 +559,10 @@ soc15_asic_reset_method(struct amdgpu_device *adev) */ if (amdgpu_gpu_recovery == 4 || amdgpu_gpu_recovery == 5) return AMD_RESET_METHOD_MODE2; + else if (!(adev->flags & AMD_IS_APU)) + return AMD_RESET_METHOD_MODE1; else - return AMD_RESET_METHOD_NONE; + return AMD_RESET_METHOD_MODE2; default: break; } diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 40d23738ee4ec0528fb7a13e3978654e055233ea..8b2ff2b281b0ade2f37d309476f20eb8a10c732a 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -766,7 +766,7 @@ static int soc21_common_hw_init(void *handle) * for the purpose of expose those registers * to process space */ - if (adev->nbio.funcs->remap_hdp_registers) + if (adev->nbio.funcs->remap_hdp_registers && !amdgpu_sriov_vf(adev)) adev->nbio.funcs->remap_hdp_registers(adev); /* enable the doorbell aperture */ adev->nbio.funcs->enable_doorbell_aperture(adev, true); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 86fb7ac7982a1d633d83eeec8e6fde1065a02b35..f76b7aee5c0a12b9a65ccba84e573b0078c0d2fd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -2087,7 +2087,8 @@ static int kfd_create_vcrat_image_gpu(void *pcrat_image, amdgpu_amdkfd_get_cu_info(kdev->adev, &cu_info); cu->num_simd_per_cu = cu_info.simd_per_cu; - cu->num_simd_cores = cu_info.simd_per_cu * cu_info.cu_active_number; + cu->num_simd_cores = cu_info.simd_per_cu * + (cu_info.cu_active_number / kdev->kfd->num_nodes); cu->max_waves_simd = cu_info.max_waves_per_simd; cu->wave_front_size = cu_info.wave_front_size; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.h b/drivers/gpu/drm/amd/amdkfd/kfd_crat.h index 387a8ef493855adbfe8718858d1ff876cc487613..74c2d7a0d6285715339482b975f946779def8e58 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.h @@ -79,6 +79,10 @@ struct crat_header { #define CRAT_SUBTYPE_IOLINK_AFFINITY 5 #define CRAT_SUBTYPE_MAX 6 +/* + * Do not change the value of CRAT_SIBLINGMAP_SIZE from 32 + * as it breaks the ABI. + */ #define CRAT_SIBLINGMAP_SIZE 32 /* diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index b166f30f083e0aad6498bd96f603dcfad3977296..0d3d538b64ebc319d705bcdde28ab9179c3575b4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -216,7 +216,7 @@ static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q, if (q->wptr_bo) { wptr_addr_off = (uint64_t)q->properties.write_ptr & (PAGE_SIZE - 1); - queue_input.wptr_mc_addr = ((uint64_t)q->wptr_bo->tbo.resource->start << PAGE_SHIFT) + wptr_addr_off; + queue_input.wptr_mc_addr = amdgpu_bo_gpu_offset(q->wptr_bo) + wptr_addr_off; } queue_input.is_kfd_process = 1; @@ -1677,8 +1677,7 @@ static int start_cpsch(struct device_queue_manager *dqm) dqm->dev->kfd2kgd->build_grace_period_packet_info( dqm->dev->adev, dqm->wait_times, grace_period, ®_offset, - &dqm->wait_times, - ffs(dqm->dev->xcc_mask) - 1); + &dqm->wait_times); } dqm_unlock(dqm); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c index c2e0b79dcc6dabe598fa023a2afba8cbcb9f0b87..7b38537c7c99bdb401ea4666d907c23253ef089e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c @@ -162,6 +162,7 @@ void __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd, return NULL; *doorbell_off = amdgpu_doorbell_index_on_bar(kfd->adev, kfd->doorbells, inx); + inx *= 2; pr_debug("Get kernel queue doorbell\n" " doorbell offset == 0x%08X\n" @@ -176,6 +177,7 @@ void kfd_release_kernel_doorbell(struct kfd_dev *kfd, u32 __iomem *db_addr) unsigned int inx; inx = (unsigned int)(db_addr - kfd->doorbell_kernel_ptr); + inx /= 2; mutex_lock(&kfd->doorbell_mutex); __clear_bit(inx, kfd->doorbell_bitmap); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c index f0731a6a5306cd14b6ed2e3e660cb2a45ade3499..830396b1c3b145ea4e5757d79b56361a0a607013 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c @@ -384,7 +384,7 @@ static void event_interrupt_wq_v9(struct kfd_node *dev, default: break; } - kfd_signal_event_interrupt(pasid, context_id0 & 0xffffff, 24); + kfd_signal_event_interrupt(pasid, sq_int_data, 24); } else if (source_id == SOC15_INTSRC_CP_BAD_OPCODE) { kfd_set_dbg_ev_from_interrupt(dev, pasid, KFD_DEBUG_DOORBELL_ID(context_id0), diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c index d01bb57733b368c4ed692c873027352b3aa865a3..447829c22295c6cf6c7570a592c0ef33ba0211c8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c @@ -97,18 +97,22 @@ void free_mqd_hiq_sdma(struct mqd_manager *mm, void *mqd, void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, const uint32_t *cu_mask, uint32_t cu_mask_count, - uint32_t *se_mask) + uint32_t *se_mask, uint32_t inst) { struct kfd_cu_info cu_info; uint32_t cu_per_sh[KFD_MAX_NUM_SE][KFD_MAX_NUM_SH_PER_SE] = {0}; bool wgp_mode_req = KFD_GC_VERSION(mm->dev) >= IP_VERSION(10, 0, 0); uint32_t en_mask = wgp_mode_req ? 0x3 : 0x1; - int i, se, sh, cu, cu_bitmap_sh_mul, inc = wgp_mode_req ? 2 : 1; + int i, se, sh, cu, cu_bitmap_sh_mul, cu_inc = wgp_mode_req ? 2 : 1; + uint32_t cu_active_per_node; + int inc = cu_inc * NUM_XCC(mm->dev->xcc_mask); + int xcc_inst = inst + ffs(mm->dev->xcc_mask) - 1; amdgpu_amdkfd_get_cu_info(mm->dev->adev, &cu_info); - if (cu_mask_count > cu_info.cu_active_number) - cu_mask_count = cu_info.cu_active_number; + cu_active_per_node = cu_info.cu_active_number / mm->dev->kfd->num_nodes; + if (cu_mask_count > cu_active_per_node) + cu_mask_count = cu_active_per_node; /* Exceeding these bounds corrupts the stack and indicates a coding error. * Returning with no CU's enabled will hang the queue, which should be @@ -141,7 +145,8 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, for (se = 0; se < cu_info.num_shader_engines; se++) for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) cu_per_sh[se][sh] = hweight32( - cu_info.cu_bitmap[se % 4][sh + (se / 4) * cu_bitmap_sh_mul]); + cu_info.cu_bitmap[xcc_inst][se % 4][sh + (se / 4) * + cu_bitmap_sh_mul]); /* Symmetrically map cu_mask to all SEs & SHs: * se_mask programs up to 2 SH in the upper and lower 16 bits. @@ -164,20 +169,33 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, * cu_mask[0] bit8 -> se_mask[0] bit1 (SE0,SH0,CU1) * ... * + * For GFX 9.4.3, the following code only looks at a + * subset of the cu_mask corresponding to the inst parameter. + * If we have n XCCs under one GPU node + * cu_mask[0] bit0 -> XCC0 se_mask[0] bit0 (XCC0,SE0,SH0,CU0) + * cu_mask[0] bit1 -> XCC1 se_mask[0] bit0 (XCC1,SE0,SH0,CU0) + * .. + * cu_mask[0] bitn -> XCCn se_mask[0] bit0 (XCCn,SE0,SH0,CU0) + * cu_mask[0] bit n+1 -> XCC0 se_mask[1] bit0 (XCC0,SE1,SH0,CU0) + * + * For example, if there are 6 XCCs under 1 KFD node, this code + * running for each inst, will look at the bits as: + * inst, inst + 6, inst + 12... + * * First ensure all CUs are disabled, then enable user specified CUs. */ for (i = 0; i < cu_info.num_shader_engines; i++) se_mask[i] = 0; - i = 0; - for (cu = 0; cu < 16; cu += inc) { + i = inst; + for (cu = 0; cu < 16; cu += cu_inc) { for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) { for (se = 0; se < cu_info.num_shader_engines; se++) { if (cu_per_sh[se][sh] > cu) { if (cu_mask[i / 32] & (en_mask << (i % 32))) se_mask[se] |= en_mask << (cu + sh * 16); i += inc; - if (i == cu_mask_count) + if (i >= cu_mask_count) return; } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h index 23158db7da035d9820b8b14007826f75f69f3cc1..57bf5e513f4d16198a6dda43f27f422e4df5f44a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h @@ -138,7 +138,7 @@ void free_mqd_hiq_sdma(struct mqd_manager *mm, void *mqd, void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, const uint32_t *cu_mask, uint32_t cu_mask_count, - uint32_t *se_mask); + uint32_t *se_mask, uint32_t inst); int kfd_hiq_load_mqd_kiq(struct mqd_manager *mm, void *mqd, uint32_t pipe_id, uint32_t queue_id, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c index ee1d32d957f2b1bc3757778506ed55a8264588e1..1a4a69943c714069ba5a2b314fabdd6849e74a6a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c @@ -52,7 +52,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index 83699392c8089cd12d8bafa274c72e6e0d8bcca3..8b7fed91352696cf2b5cafab0680ad0737fa95ee 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -52,7 +52,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index 2319467d2d9575cb365055a4ee4fab83f3cfda3a..15277f1d5cf0a9d9eb694ccaeec540e467ab774a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -71,7 +71,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, } mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m->compute_static_thread_mgmt_se0 = se_mask[0]; m->compute_static_thread_mgmt_se1 = se_mask[1]; @@ -321,6 +321,43 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, return 0; } +static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst) +{ + struct v11_compute_mqd *m; + + m = get_mqd(mqd); + + memcpy(mqd_dst, m, sizeof(struct v11_compute_mqd)); +} + +static void restore_mqd(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, const u32 ctl_stack_size) +{ + uint64_t addr; + struct v11_compute_mqd *m; + + m = (struct v11_compute_mqd *) mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memcpy(m, mqd_src, sizeof(*m)); + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + m->cp_hqd_pq_doorbell_control = + qp->doorbell_off << + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", + m->cp_hqd_pq_doorbell_control); + + qp->is_active = 0; +} + + static void init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, struct queue_properties *q) @@ -457,6 +494,9 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->is_occupied = kfd_is_occupied_cp; mqd->mqd_size = sizeof(struct v11_compute_mqd); mqd->get_wave_state = get_wave_state; + mqd->mqd_stride = kfd_mqd_stride; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; #endif @@ -472,6 +512,7 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->destroy_mqd = destroy_hiq_mqd; mqd->is_occupied = kfd_is_occupied_cp; mqd->mqd_size = sizeof(struct v11_compute_mqd); + mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; #endif @@ -500,7 +541,10 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->update_mqd = update_mqd_sdma; mqd->destroy_mqd = kfd_destroy_mqd_sdma; mqd->is_occupied = kfd_is_occupied_sdma; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; mqd->mqd_size = sizeof(struct v11_sdma_mqd); + mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd_sdma; #endif diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index e23d32f356077225f06b76008b8b75cc6ab22a87..42d881809dc70e230133674e4b12f6f68567837a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -60,7 +60,7 @@ static inline struct v9_sdma_mqd *get_sdma_mqd(void *mqd) } static void update_cu_mask(struct mqd_manager *mm, void *mqd, - struct mqd_update_info *minfo) + struct mqd_update_info *minfo, uint32_t inst) { struct v9_mqd *m; uint32_t se_mask[KFD_MAX_NUM_SE] = {0}; @@ -69,27 +69,36 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, inst); m = get_mqd(mqd); + m->compute_static_thread_mgmt_se0 = se_mask[0]; m->compute_static_thread_mgmt_se1 = se_mask[1]; m->compute_static_thread_mgmt_se2 = se_mask[2]; m->compute_static_thread_mgmt_se3 = se_mask[3]; - m->compute_static_thread_mgmt_se4 = se_mask[4]; - m->compute_static_thread_mgmt_se5 = se_mask[5]; - m->compute_static_thread_mgmt_se6 = se_mask[6]; - m->compute_static_thread_mgmt_se7 = se_mask[7]; - - pr_debug("update cu mask to %#x %#x %#x %#x %#x %#x %#x %#x\n", - m->compute_static_thread_mgmt_se0, - m->compute_static_thread_mgmt_se1, - m->compute_static_thread_mgmt_se2, - m->compute_static_thread_mgmt_se3, - m->compute_static_thread_mgmt_se4, - m->compute_static_thread_mgmt_se5, - m->compute_static_thread_mgmt_se6, - m->compute_static_thread_mgmt_se7); + if (KFD_GC_VERSION(mm->dev) != IP_VERSION(9, 4, 3)) { + m->compute_static_thread_mgmt_se4 = se_mask[4]; + m->compute_static_thread_mgmt_se5 = se_mask[5]; + m->compute_static_thread_mgmt_se6 = se_mask[6]; + m->compute_static_thread_mgmt_se7 = se_mask[7]; + + pr_debug("update cu mask to %#x %#x %#x %#x %#x %#x %#x %#x\n", + m->compute_static_thread_mgmt_se0, + m->compute_static_thread_mgmt_se1, + m->compute_static_thread_mgmt_se2, + m->compute_static_thread_mgmt_se3, + m->compute_static_thread_mgmt_se4, + m->compute_static_thread_mgmt_se5, + m->compute_static_thread_mgmt_se6, + m->compute_static_thread_mgmt_se7); + } else { + pr_debug("inst: %u, update cu mask to %#x %#x %#x %#x\n", + inst, m->compute_static_thread_mgmt_se0, + m->compute_static_thread_mgmt_se1, + m->compute_static_thread_mgmt_se2, + m->compute_static_thread_mgmt_se3); + } } static void set_priority(struct v9_mqd *m, struct queue_properties *q) @@ -290,7 +299,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, if (mm->dev->kfd->cwsr_enabled && q->ctx_save_restore_area_address) m->cp_hqd_ctx_save_control = 0; - update_cu_mask(mm, mqd, minfo); + if (KFD_GC_VERSION(mm->dev) != IP_VERSION(9, 4, 3)) + update_cu_mask(mm, mqd, minfo, 0); set_priority(m, q); q->is_active = QUEUE_IS_ACTIVE(*q); @@ -676,6 +686,8 @@ static void update_mqd_v9_4_3(struct mqd_manager *mm, void *mqd, m = get_mqd(mqd + size * xcc); update_mqd(mm, m, q, minfo); + update_cu_mask(mm, mqd, minfo, xcc); + if (q->format == KFD_QUEUE_FORMAT_AQL) { switch (xcc) { case 0: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index 657c378229808fdfb2518203269fb81c744e53d0..3e1a574d4ea66981b9a5243211bd451def7aee94 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -55,7 +55,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c index 8ce6f5200905164a433e04c290c7f63f6ca05f5a..1a03173e23133799d1d6be4a064a7fde0bb25923 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c @@ -299,8 +299,7 @@ static int pm_set_grace_period_v9(struct packet_manager *pm, pm->dqm->wait_times, grace_period, ®_offset, - ®_data, - 0); + ®_data); if (grace_period == USE_DEFAULT_GRACE_PERIOD) reg_data = pm->dqm->wait_times; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 3d9ce44d88da572faa590a3277063ecd0854d9bb..fa24e1852493dc8fb77b35efdb0e5768a34b172e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -1466,8 +1466,7 @@ void kfd_flush_tlb(struct kfd_process_device *pdd, enum TLB_FLUSH_TYPE type); static inline bool kfd_flush_tlb_after_unmap(struct kfd_dev *dev) { - return KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 3) || - KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 2) || + return KFD_GC_VERSION(dev) > IP_VERSION(9, 4, 2) || (KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 1) && dev->sdma_fw_version >= 18) || KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 0); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 011561605983a23833d6b321957b340b75e4921f..bb16b795d1bc2ac2885db4a46e76bc632e0972d2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1686,6 +1686,8 @@ static int svm_range_validate_and_map(struct mm_struct *mm, WRITE_ONCE(p->svms.faulting_task, NULL); if (r) { pr_debug("failed %d to get svm range pages\n", r); + if (r == -EBUSY) + r = -EAGAIN; goto unreserve_out; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index ff98fded953499017a90b57c066570c09721e7e0..c8c75ff7cea80d3ba77dd23d16e1cea9213cc8df 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -450,8 +450,7 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr, sysfs_show_32bit_prop(buffer, offs, "cpu_cores_count", dev->node_props.cpu_cores_count); sysfs_show_32bit_prop(buffer, offs, "simd_count", - dev->gpu ? (dev->node_props.simd_count * - NUM_XCC(dev->gpu->xcc_mask)) : 0); + dev->gpu ? dev->node_props.simd_count : 0); sysfs_show_32bit_prop(buffer, offs, "mem_banks_count", dev->node_props.mem_banks_count); sysfs_show_32bit_prop(buffer, offs, "caches_count", @@ -1597,14 +1596,17 @@ static int fill_in_l1_pcache(struct kfd_cache_properties **props_ext, static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, struct kfd_gpu_cache_info *pcache_info, struct kfd_cu_info *cu_info, - int cache_type, unsigned int cu_processor_id) + int cache_type, unsigned int cu_processor_id, + struct kfd_node *knode) { unsigned int cu_sibling_map_mask; int first_active_cu; - int i, j, k; + int i, j, k, xcc, start, end; struct kfd_cache_properties *pcache = NULL; - cu_sibling_map_mask = cu_info->cu_bitmap[0][0]; + start = ffs(knode->xcc_mask) - 1; + end = start + NUM_XCC(knode->xcc_mask); + cu_sibling_map_mask = cu_info->cu_bitmap[start][0][0]; cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); first_active_cu = ffs(cu_sibling_map_mask); @@ -1639,16 +1641,18 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, cu_sibling_map_mask = cu_sibling_map_mask >> (first_active_cu - 1); k = 0; - for (i = 0; i < cu_info->num_shader_engines; i++) { - for (j = 0; j < cu_info->num_shader_arrays_per_engine; j++) { - pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF); - pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF); - pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF); - pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); - k += 4; - - cu_sibling_map_mask = cu_info->cu_bitmap[i % 4][j + i / 4]; - cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); + for (xcc = start; xcc < end; xcc++) { + for (i = 0; i < cu_info->num_shader_engines; i++) { + for (j = 0; j < cu_info->num_shader_arrays_per_engine; j++) { + pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF); + pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF); + pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF); + pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); + k += 4; + + cu_sibling_map_mask = cu_info->cu_bitmap[xcc][i % 4][j + i / 4]; + cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); + } } } pcache->sibling_map_size = k; @@ -1666,7 +1670,7 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct kfd_node *kdev) { struct kfd_gpu_cache_info *pcache_info = NULL; - int i, j, k; + int i, j, k, xcc, start, end; int ct = 0; unsigned int cu_processor_id; int ret; @@ -1700,37 +1704,42 @@ static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct * then it will consider only one CU from * the shared unit */ + start = ffs(kdev->xcc_mask) - 1; + end = start + NUM_XCC(kdev->xcc_mask); + for (ct = 0; ct < num_of_cache_types; ct++) { cu_processor_id = gpu_processor_id; if (pcache_info[ct].cache_level == 1) { - for (i = 0; i < pcu_info->num_shader_engines; i++) { - for (j = 0; j < pcu_info->num_shader_arrays_per_engine; j++) { - for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { + for (xcc = start; xcc < end; xcc++) { + for (i = 0; i < pcu_info->num_shader_engines; i++) { + for (j = 0; j < pcu_info->num_shader_arrays_per_engine; j++) { + for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { - ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, - pcu_info->cu_bitmap[i % 4][j + i / 4], ct, + ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, + pcu_info->cu_bitmap[xcc][i % 4][j + i / 4], ct, cu_processor_id, k); - if (ret < 0) - break; + if (ret < 0) + break; - if (!ret) { - num_of_entries++; - list_add_tail(&props_ext->list, &dev->cache_props); - } + if (!ret) { + num_of_entries++; + list_add_tail(&props_ext->list, &dev->cache_props); + } - /* Move to next CU block */ - num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <= - pcu_info->num_cu_per_sh) ? - pcache_info[ct].num_cu_shared : - (pcu_info->num_cu_per_sh - k); - cu_processor_id += num_cu_shared; + /* Move to next CU block */ + num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <= + pcu_info->num_cu_per_sh) ? + pcache_info[ct].num_cu_shared : + (pcu_info->num_cu_per_sh - k); + cu_processor_id += num_cu_shared; + } } } } } else { ret = fill_in_l2_l3_pcache(&props_ext, pcache_info, - pcu_info, ct, cu_processor_id); + pcu_info, ct, cu_processor_id, kdev); if (ret < 0) break; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.h b/drivers/gpu/drm/amd/amdkfd/kfd_topology.h index dea32a9e55060e8deb983e888290b19cdf58e2fd..27386ce9a021dad9a15fb7d9ac38deb32f36e8f3 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.h @@ -89,7 +89,7 @@ struct kfd_mem_properties { struct attribute attr; }; -#define CACHE_SIBLINGMAP_SIZE 64 +#define CACHE_SIBLINGMAP_SIZE 128 struct kfd_cache_properties { struct list_head list; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 268cb99a4c4bc46d70ef0a5aeaab49dff6a4c3fd..868946dd7ef12602842ab389cef64a4275ff10da 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -65,6 +65,7 @@ #include "amdgpu_dm_debugfs.h" #endif #include "amdgpu_dm_psr.h" +#include "amdgpu_dm_replay.h" #include "ivsrcid/ivsrcid_vislands30.h" @@ -1273,11 +1274,15 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_ pt_base = amdgpu_gmc_pd_addr(adev->gart.bo); - page_table_start.high_part = (u32)(adev->gmc.gart_start >> 44) & 0xF; - page_table_start.low_part = (u32)(adev->gmc.gart_start >> 12); - page_table_end.high_part = (u32)(adev->gmc.gart_end >> 44) & 0xF; - page_table_end.low_part = (u32)(adev->gmc.gart_end >> 12); - page_table_base.high_part = upper_32_bits(pt_base) & 0xF; + page_table_start.high_part = upper_32_bits(adev->gmc.gart_start >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_start.low_part = lower_32_bits(adev->gmc.gart_start >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_end.high_part = upper_32_bits(adev->gmc.gart_end >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_end.low_part = lower_32_bits(adev->gmc.gart_end >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_base.high_part = upper_32_bits(pt_base); page_table_base.low_part = lower_32_bits(pt_base); pa_config->system_aperture.start_addr = (uint64_t)logical_addr_low << 18; @@ -1639,8 +1644,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) } break; } - if (init_data.flags.gpu_vm_support) - init_data.flags.gpu_vm_support = amdgpu_sg_display_supported(adev); + if (init_data.flags.gpu_vm_support && + (amdgpu_sg_display == 0)) + init_data.flags.gpu_vm_support = false; if (init_data.flags.gpu_vm_support) adev->mode_info.gpu_vm_support = true; @@ -2334,14 +2340,62 @@ static int dm_late_init(void *handle) return detect_mst_link_for_all_connectors(adev_to_drm(adev)); } +static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret; + u8 guid[16]; + u64 tmp64; + + mutex_lock(&mgr->lock); + if (!mgr->mst_primary) + goto out_fail; + + if (drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd) < 0) { + drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); + goto out_fail; + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | + DP_UP_REQ_EN | + DP_UPSTREAM_IS_SRC); + if (ret < 0) { + drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n"); + goto out_fail; + } + + /* Some hubs forget their guids after they resume */ + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); + if (ret != 16) { + drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); + goto out_fail; + } + + if (memchr_inv(guid, 0, 16) == NULL) { + tmp64 = get_jiffies_64(); + memcpy(&guid[0], &tmp64, sizeof(u64)); + memcpy(&guid[8], &tmp64, sizeof(u64)); + + ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16); + + if (ret != 16) { + drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n"); + goto out_fail; + } + } + + memcpy(mgr->mst_primary->guid, guid, 16); + +out_fail: + mutex_unlock(&mgr->lock); +} + static void s3_handle_mst(struct drm_device *dev, bool suspend) { struct amdgpu_dm_connector *aconnector; struct drm_connector *connector; struct drm_connector_list_iter iter; struct drm_dp_mst_topology_mgr *mgr; - int ret; - bool need_hotplug = false; drm_connector_list_iter_begin(dev, &iter); drm_for_each_connector_iter(connector, &iter) { @@ -2363,18 +2417,15 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend) if (!dp_is_lttpr_present(aconnector->dc_link)) try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD); - ret = drm_dp_mst_topology_mgr_resume(mgr, true); - if (ret < 0) { - dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, - aconnector->dc_link); - need_hotplug = true; - } + /* TODO: move resume_mst_branch_status() into drm mst resume again + * once topology probing work is pulled out from mst resume into mst + * resume 2nd step. mst resume 2nd step should be called after old + * state getting restored (i.e. drm_atomic_helper_resume()). + */ + resume_mst_branch_status(mgr); } } drm_connector_list_iter_end(&iter); - - if (need_hotplug) - drm_kms_helper_hotplug_event(dev); } static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev) @@ -2768,7 +2819,8 @@ static int dm_resume(void *handle) struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state); enum dc_connection_type new_connection_type = dc_connection_none; struct dc_state *dc_state; - int i, r, j; + int i, r, j, ret; + bool need_hotplug = false; if (amdgpu_in_reset(adev)) { dc_state = dm->cached_dc_state; @@ -2866,7 +2918,7 @@ static int dm_resume(void *handle) continue; /* - * this is the case when traversing through already created + * this is the case when traversing through already created end sink * MST connectors, should be skipped */ if (aconnector && aconnector->mst_root) @@ -2926,6 +2978,27 @@ static int dm_resume(void *handle) dm->cached_state = NULL; + /* Do mst topology probing after resuming cached state*/ + drm_connector_list_iter_begin(ddev, &iter); + drm_for_each_connector_iter(connector, &iter) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type != dc_connection_mst_branch || + aconnector->mst_root) + continue; + + ret = drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr, true); + + if (ret < 0) { + dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, + aconnector->dc_link); + need_hotplug = true; + } + } + drm_connector_list_iter_end(&iter); + + if (need_hotplug) + drm_kms_helper_hotplug_event(ddev); + amdgpu_dm_irq_resume_late(adev); amdgpu_dm_smu_write_watermarks_table(adev); @@ -4265,6 +4338,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) enum dc_connection_type new_connection_type = dc_connection_none; const struct dc_plane_cap *plane; bool psr_feature_enabled = false; + bool replay_feature_enabled = false; int max_overlay = dm->dc->caps.max_slave_planes; dm->display_indexes_num = dm->dc->caps.max_streams; @@ -4374,6 +4448,20 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) } } + if (!(amdgpu_dc_debug_mask & DC_DISABLE_REPLAY)) { + switch (adev->ip_versions[DCE_HWIP][0]) { + case IP_VERSION(3, 1, 4): + case IP_VERSION(3, 1, 5): + case IP_VERSION(3, 1, 6): + case IP_VERSION(3, 2, 0): + case IP_VERSION(3, 2, 1): + replay_feature_enabled = true; + break; + default: + replay_feature_enabled = amdgpu_dc_feature_mask & DC_REPLAY_MASK; + break; + } + } /* loops over all connectors on the board */ for (i = 0; i < link_cnt; i++) { struct dc_link *link = NULL; @@ -4422,6 +4510,12 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) amdgpu_dm_update_connector_after_detect(aconnector); setup_backlight_device(dm, aconnector); + /* + * Disable psr if replay can be enabled + */ + if (replay_feature_enabled && amdgpu_dm_setup_replay(link, aconnector)) + psr_feature_enabled = false; + if (psr_feature_enabled) amdgpu_dm_set_psr_caps(link); @@ -6004,8 +6098,6 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, if (recalculate_timing) drm_mode_set_crtcinfo(&saved_mode, 0); - else - drm_mode_set_crtcinfo(&mode, 0); /* * If scaling is enabled and refresh rate didn't change @@ -6567,6 +6659,8 @@ enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connec goto fail; } + drm_mode_set_crtcinfo(mode, 0); + stream = create_validate_stream_for_sink(aconnector, mode, to_dm_connector_state(connector->state), NULL); @@ -8051,7 +8145,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bundle->surface_updates[planes_count].plane_info = &bundle->plane_infos[planes_count]; - if (acrtc_state->stream->link->psr_settings.psr_feature_enabled) { + if (acrtc_state->stream->link->psr_settings.psr_feature_enabled || + acrtc_state->stream->link->replay_settings.replay_feature_enabled) { fill_dc_dirty_rects(plane, old_plane_state, new_plane_state, new_crtc_state, &bundle->flip_addrs[planes_count], diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index a2d34be82613c5614f1b6cecb3f987121ae0afd0..9e4cc5eeda767e36bd591dff20adf2202dd93119 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -620,7 +620,7 @@ struct amdgpu_hdmi_vsdb_info { unsigned int max_refresh_rate_hz; /** - * @replay mode: Replay supported + * @replay_mode: Replay supported */ bool replay_mode; }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 30d4c6fd95f531de021e54657662c605f8543663..97b7a0b8a1c26c38c035d04f5a0b2ab6c843b5f3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -29,6 +29,7 @@ #include "dc.h" #include "amdgpu.h" #include "amdgpu_dm_psr.h" +#include "amdgpu_dm_replay.h" #include "amdgpu_dm_crtc.h" #include "amdgpu_dm_plane.h" #include "amdgpu_dm_trace.h" @@ -123,7 +124,12 @@ static void vblank_control_worker(struct work_struct *work) * fill_dc_dirty_rects(). */ if (vblank_work->stream && vblank_work->stream->link) { - if (vblank_work->enable) { + /* + * Prioritize replay, instead of psr + */ + if (vblank_work->stream->link->replay_settings.replay_feature_enabled) + amdgpu_dm_replay_enable(vblank_work->stream, false); + else if (vblank_work->enable) { if (vblank_work->stream->link->psr_settings.psr_version < DC_PSR_VERSION_SU_1 && vblank_work->stream->link->psr_settings.psr_allow_active) amdgpu_dm_psr_disable(vblank_work->stream); @@ -132,6 +138,7 @@ static void vblank_control_worker(struct work_struct *work) #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY !amdgpu_dm_crc_window_is_activated(&vblank_work->acrtc->base) && #endif + vblank_work->stream->link->panel_config.psr.disallow_replay && vblank_work->acrtc->dm_irq_params.allow_psr_entry) { amdgpu_dm_psr_enable(vblank_work->stream); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index 8eeca160d4344c47d3dec9e65374f2eb8e5ff150..cc74dd69acf2ba27bf21eb1820f72ea610339f27 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1269,6 +1269,13 @@ void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane, attributes.rotation_angle = 0; attributes.attribute_flags.value = 0; + /* Enable cursor degamma ROM on DCN3+ for implicit sRGB degamma in DRM + * legacy gamma setup. + */ + if (crtc_state->cm_is_degamma_srgb && + adev->dm.dc->caps.color.dpp.gamma_corr) + attributes.attribute_flags.bits.ENABLE_CURSOR_DEGAMMA = 1; + attributes.pitch = afb->base.pitches[0] / afb->base.format->cpp[0]; if (crtc_state->stream) { @@ -1468,6 +1475,15 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm, drm_plane_create_blend_mode_property(plane, blend_caps); } + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + drm_plane_create_zpos_immutable_property(plane, 0); + } else if (plane->type == DRM_PLANE_TYPE_OVERLAY) { + unsigned int zpos = 1 + drm_plane_index(plane); + drm_plane_create_zpos_property(plane, zpos, 1, 254); + } else if (plane->type == DRM_PLANE_TYPE_CURSOR) { + drm_plane_create_zpos_immutable_property(plane, 255); + } + if (plane->type == DRM_PLANE_TYPE_PRIMARY && plane_cap && (plane_cap->pixel_format_support.nv12 || diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile index 69ffd4424dc7b0bcd0c3def9a06f7902e7c82281..1b8c2aef4633c75048065b623025c19b81699806 100644 --- a/drivers/gpu/drm/amd/display/dc/Makefile +++ b/drivers/gpu/drm/amd/display/dc/Makefile @@ -78,3 +78,4 @@ DC_EDID += dc_edid_parser.o AMD_DISPLAY_DMUB = $(addprefix $(AMDDALPATH)/dc/,$(DC_DMUB)) AMD_DISPLAY_EDID = $(addprefix $(AMDDALPATH)/dc/,$(DC_EDID)) AMD_DISPLAY_FILES += $(AMD_DISPLAY_DMUB) $(AMD_DISPLAY_EDID) + diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c index c435f7632e8e8b76b868a26fec65d40f938ee6b6..5ee87965a07818a67916326ff277a1626aba6e04 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn20/dcn20_clk_mgr.c @@ -157,7 +157,7 @@ void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr, struct int32_t N; int32_t j; - if (!pipe_ctx->stream) + if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER)) continue; /* Virtual encoders don't have this function */ if (!stream_enc->funcs->get_fifo_cal_average_level) @@ -188,7 +188,7 @@ void dcn20_update_clocks_update_dentist(struct clk_mgr_internal *clk_mgr, struct int32_t N; int32_t j; - if (!pipe_ctx->stream) + if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER)) continue; /* Virtual encoders don't have this function */ if (!stream_enc->funcs->get_fifo_cal_average_level) diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c index 3e0da873cf4c8f99df9058cdae693d1d1a5dbf70..1042cf1a3ab04b9fb5d8e4cbe0283c4a06148a44 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_smu.c @@ -32,6 +32,7 @@ #define MAX_INSTANCE 6 #define MAX_SEGMENT 6 +#define SMU_REGISTER_WRITE_RETRY_COUNT 5 struct IP_BASE_INSTANCE { unsigned int segment[MAX_SEGMENT]; @@ -132,6 +133,8 @@ static int dcn315_smu_send_msg_with_param( unsigned int msg_id, unsigned int param) { uint32_t result; + uint32_t i = 0; + uint32_t read_back_data; result = dcn315_smu_wait_for_response(clk_mgr, 10, 200000); @@ -148,10 +151,19 @@ static int dcn315_smu_send_msg_with_param( /* Set the parameter register for the SMU message, unit is Mhz */ REG_WRITE(MP1_SMN_C2PMSG_37, param); - /* Trigger the message transaction by writing the message ID */ - generic_write_indirect_reg(CTX, - REG_NBIO(RSMU_INDEX), REG_NBIO(RSMU_DATA), - mmMP1_C2PMSG_3, msg_id); + for (i = 0; i < SMU_REGISTER_WRITE_RETRY_COUNT; i++) { + /* Trigger the message transaction by writing the message ID */ + generic_write_indirect_reg(CTX, + REG_NBIO(RSMU_INDEX), REG_NBIO(RSMU_DATA), + mmMP1_C2PMSG_3, msg_id); + read_back_data = generic_read_indirect_reg(CTX, + REG_NBIO(RSMU_INDEX), REG_NBIO(RSMU_DATA), + mmMP1_C2PMSG_3); + if (read_back_data == msg_id) + break; + udelay(2); + smu_print("SMU msg id write fail %x times. \n", i + 1); + } result = dcn315_smu_wait_for_response(clk_mgr, 10, 200000); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index 984b52923534056cc3e4a76e115e3edbd0ce2332..e9345f6554dbcb06cce61b98e1b68c0432f967ce 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -355,7 +355,7 @@ static void dcn32_update_clocks_update_dentist( int32_t N; int32_t j; - if (!pipe_ctx->stream) + if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER)) continue; /* Virtual encoders don't have this function */ if (!stream_enc->funcs->get_fifo_cal_average_level) @@ -401,7 +401,7 @@ static void dcn32_update_clocks_update_dentist( int32_t N; int32_t j; - if (!pipe_ctx->stream) + if (!resource_is_pipe_type(pipe_ctx, OTG_MASTER)) continue; /* Virtual encoders don't have this function */ if (!stream_enc->funcs->get_fifo_cal_average_level) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 566d7045b2dea80a6670b4af8cb253c1e455f1ca..3a9077b60029ba9626301d204c35443290664ba9 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -2073,12 +2073,12 @@ enum dc_status dc_commit_streams(struct dc *dc, } } - /* Check for case where we are going from odm 2:1 to max - * pipe scenario. For these cases, we will call - * commit_minimal_transition_state() to exit out of odm 2:1 - * first before processing new streams + /* ODM Combine 2:1 power optimization is only applied for single stream + * scenario, it uses extra pipes than needed to reduce power consumption + * We need to switch off this feature to make room for new streams. */ - if (stream_count == dc->res_pool->pipe_count) { + if (stream_count > dc->current_state->stream_count && + dc->current_state->stream_count == 1) { for (i = 0; i < dc->res_pool->pipe_count; i++) { pipe = &dc->current_state->res_ctx.pipe_ctx[i]; if (pipe->next_odm_pipe) @@ -3501,6 +3501,45 @@ static void commit_planes_for_stream_fast(struct dc *dc, top_pipe_to_program->stream->update_flags.raw = 0; } +static void wait_for_outstanding_hw_updates(struct dc *dc, const struct dc_state *dc_context) +{ +/* + * This function calls HWSS to wait for any potentially double buffered + * operations to complete. It should be invoked as a pre-amble prior + * to full update programming before asserting any HW locks. + */ + int pipe_idx; + int opp_inst; + int opp_count = dc->res_pool->pipe_count; + struct hubp *hubp; + int mpcc_inst; + const struct pipe_ctx *pipe_ctx; + + for (pipe_idx = 0; pipe_idx < dc->res_pool->pipe_count; pipe_idx++) { + pipe_ctx = &dc_context->res_ctx.pipe_ctx[pipe_idx]; + + if (!pipe_ctx->stream) + continue; + + if (pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear) + pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear(pipe_ctx->stream_res.tg); + + hubp = pipe_ctx->plane_res.hubp; + if (!hubp) + continue; + + mpcc_inst = hubp->inst; + // MPCC inst is equal to pipe index in practice + for (opp_inst = 0; opp_inst < opp_count; opp_inst++) { + if (dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst]) { + dc->res_pool->mpc->funcs->wait_for_idle(dc->res_pool->mpc, mpcc_inst); + dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst] = false; + break; + } + } + } +} + static void commit_planes_for_stream(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, @@ -3519,24 +3558,9 @@ static void commit_planes_for_stream(struct dc *dc, // dc->current_state anymore, so we have to cache it before we apply // the new SubVP context subvp_prev_use = false; - - dc_z10_restore(dc); - - if (update_type == UPDATE_TYPE_FULL) { - /* wait for all double-buffer activity to clear on all pipes */ - int pipe_idx; - - for (pipe_idx = 0; pipe_idx < dc->res_pool->pipe_count; pipe_idx++) { - struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[pipe_idx]; - - if (!pipe_ctx->stream) - continue; - - if (pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear) - pipe_ctx->stream_res.tg->funcs->wait_drr_doublebuffer_pending_clear(pipe_ctx->stream_res.tg); - } - } + if (update_type == UPDATE_TYPE_FULL) + wait_for_outstanding_hw_updates(dc, context); if (update_type == UPDATE_TYPE_FULL) { dc_allow_idle_optimizations(dc, false); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c index 30c0644d4418fb418ebdbde8f49e503469125732..be5a6d008b290db14ba043feb2b7ad5c428d49df 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c @@ -169,11 +169,23 @@ static void add_link_enc_assignment( /* Return first available DIG link encoder. */ static enum engine_id find_first_avail_link_enc( const struct dc_context *ctx, - const struct dc_state *state) + const struct dc_state *state, + enum engine_id eng_id_requested) { enum engine_id eng_id = ENGINE_ID_UNKNOWN; int i; + if (eng_id_requested != ENGINE_ID_UNKNOWN) { + + for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { + eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i]; + if (eng_id == eng_id_requested) + return eng_id; + } + } + + eng_id = ENGINE_ID_UNKNOWN; + for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i]; if (eng_id != ENGINE_ID_UNKNOWN) @@ -287,7 +299,7 @@ void link_enc_cfg_link_encs_assign( struct dc_stream_state *streams[], uint8_t stream_count) { - enum engine_id eng_id = ENGINE_ID_UNKNOWN; + enum engine_id eng_id = ENGINE_ID_UNKNOWN, eng_id_req = ENGINE_ID_UNKNOWN; int i; int j; @@ -377,8 +389,14 @@ void link_enc_cfg_link_encs_assign( * assigned to that endpoint. */ link_enc = get_link_enc_used_by_link(state, stream->link); - if (link_enc == NULL) - eng_id = find_first_avail_link_enc(stream->ctx, state); + if (link_enc == NULL) { + + if (stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + stream->link->dpia_preferred_eng_id != ENGINE_ID_UNKNOWN) + eng_id_req = stream->link->dpia_preferred_eng_id; + + eng_id = find_first_avail_link_enc(stream->ctx, state, eng_id_req); + } else eng_id = link_enc->preferred_engine; @@ -402,7 +420,9 @@ void link_enc_cfg_link_encs_assign( DC_LOG_DEBUG("%s: CUR %s(%d) - enc_id(%d)\n", __func__, assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA", - assignment.ep_id.link_id.enum_id - 1, + assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? + assignment.ep_id.link_id.enum_id : + assignment.ep_id.link_id.enum_id - 1, assignment.eng_id); } for (i = 0; i < MAX_PIPES; i++) { @@ -413,7 +433,9 @@ void link_enc_cfg_link_encs_assign( DC_LOG_DEBUG("%s: NEW %s(%d) - enc_id(%d)\n", __func__, assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA", - assignment.ep_id.link_id.enum_id - 1, + assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? + assignment.ep_id.link_id.enum_id : + assignment.ep_id.link_id.enum_id - 1, assignment.eng_id); } @@ -478,7 +500,6 @@ struct dc_link *link_enc_cfg_get_link_using_link_enc( if (stream) link = stream->link; - // dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id); return link; } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 0d0bef8eb331d8f35ca0978e0615e187a5198431..31e3183497a7f50a1e141286b1183f3ad76909a5 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1496,6 +1496,7 @@ struct dc_link { * object creation. */ enum engine_id eng_id; + enum engine_id dpia_preferred_eng_id; bool test_pattern_enabled; enum dp_test_pattern current_test_pattern; diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index ad967b58d7bec6ee727b6209767df3bd87c55300..2a6157555fd1e4617c4f7320c49b367eab1d543f 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -964,7 +964,9 @@ void dce110_edp_backlight_control( return; } - if (link->panel_cntl) { + if (link->panel_cntl && !(link->dpcd_sink_ext_caps.bits.oled || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) { bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl); if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) { @@ -1176,12 +1178,15 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx) dto_params.otg_inst = tg->inst; dto_params.timing = &pipe_ctx->stream->timing; dp_hpo_inst = pipe_ctx->stream_res.hpo_dp_stream_enc->inst; - dccg->funcs->set_dtbclk_dto(dccg, &dto_params); - dccg->funcs->disable_symclk32_se(dccg, dp_hpo_inst); - dccg->funcs->set_dpstreamclk(dccg, REFCLK, tg->inst, dp_hpo_inst); - } else if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST && dccg->funcs->disable_symclk_se) + if (dccg) { + dccg->funcs->set_dtbclk_dto(dccg, &dto_params); + dccg->funcs->disable_symclk32_se(dccg, dp_hpo_inst); + dccg->funcs->set_dpstreamclk(dccg, REFCLK, tg->inst, dp_hpo_inst); + } + } else if (dccg && dccg->funcs->disable_symclk_se) { dccg->funcs->disable_symclk_se(dccg, stream_enc->stream_enc_inst, link_enc->transmitter - TRANSMITTER_UNIPHY_A); + } if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { /* TODO: This looks like a bug to me as we are disabling HPO IO when @@ -2656,11 +2661,11 @@ void dce110_prepare_bandwidth( struct clk_mgr *dccg = dc->clk_mgr; dce110_set_safe_displaymarks(&context->res_ctx, dc->res_pool); - - dccg->funcs->update_clocks( - dccg, - context, - false); + if (dccg) + dccg->funcs->update_clocks( + dccg, + context, + false); } void dce110_optimize_bandwidth( @@ -2671,10 +2676,11 @@ void dce110_optimize_bandwidth( dce110_set_displaymarks(dc, context); - dccg->funcs->update_clocks( - dccg, - context, - true); + if (dccg) + dccg->funcs->update_clocks( + dccg, + context, + true); } static void dce110_program_front_end_for_pipe( diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c index 65fa9e21ad9c47b5be9df9d60b3fe6a65a87a6d4..aeadc587433fd5a1410123b8529e60c48496a292 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c @@ -1106,29 +1106,6 @@ void dcn20_blank_pixel_data( v_active, offset); - if (!blank && dc->debug.enable_single_display_2to1_odm_policy) { - /* when exiting dynamic ODM need to reinit DPG state for unused pipes */ - struct pipe_ctx *old_odm_pipe = dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx].next_odm_pipe; - - odm_pipe = pipe_ctx->next_odm_pipe; - - while (old_odm_pipe) { - if (!odm_pipe || old_odm_pipe->pipe_idx != odm_pipe->pipe_idx) - dc->hwss.set_disp_pattern_generator(dc, - old_odm_pipe, - CONTROLLER_DP_TEST_PATTERN_VIDEOMODE, - CONTROLLER_DP_COLOR_SPACE_UDEFINED, - COLOR_DEPTH_888, - NULL, - 0, - 0, - 0); - old_odm_pipe = old_odm_pipe->next_odm_pipe; - if (odm_pipe) - odm_pipe = odm_pipe->next_odm_pipe; - } - } - if (!blank) if (stream_res->abm) { dc->hwss.set_pipe(pipe_ctx); @@ -1584,17 +1561,6 @@ static void dcn20_update_dchubp_dpp( || plane_state->update_flags.bits.global_alpha_change || plane_state->update_flags.bits.per_pixel_alpha_change) { // MPCC inst is equal to pipe index in practice - int mpcc_inst = hubp->inst; - int opp_inst; - int opp_count = dc->res_pool->pipe_count; - - for (opp_inst = 0; opp_inst < opp_count; opp_inst++) { - if (dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst]) { - dc->res_pool->mpc->funcs->wait_for_idle(dc->res_pool->mpc, mpcc_inst); - dc->res_pool->opps[opp_inst]->mpcc_disconnect_pending[mpcc_inst] = false; - break; - } - } hws->funcs.update_mpcc(dc, pipe_ctx); } @@ -1722,11 +1688,16 @@ static void dcn20_program_pipe( struct dc_state *context) { struct dce_hwseq *hws = dc->hwseq; - /* Only need to unblank on top pipe */ - if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->stream->update_flags.bits.abm_level) - && !pipe_ctx->top_pipe && !pipe_ctx->prev_odm_pipe) - hws->funcs.blank_pixel_data(dc, pipe_ctx, !pipe_ctx->plane_state->visible); + /* Only need to unblank on top pipe */ + if (resource_is_pipe_type(pipe_ctx, OTG_MASTER)) { + if (pipe_ctx->update_flags.bits.enable || + pipe_ctx->update_flags.bits.odm || + pipe_ctx->stream->update_flags.bits.abm_level) + hws->funcs.blank_pixel_data(dc, pipe_ctx, + !pipe_ctx->plane_state || + !pipe_ctx->plane_state->visible); + } /* Only update TG on top pipe */ if (pipe_ctx->update_flags.bits.global_sync && !pipe_ctx->top_pipe @@ -2721,8 +2692,6 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) struct dce_hwseq *hws = dc->hwseq; unsigned int k1_div = PIXEL_RATE_DIV_NA; unsigned int k2_div = PIXEL_RATE_DIV_NA; - struct link_encoder *link_enc = link_enc_cfg_get_link_enc(pipe_ctx->stream->link); - struct stream_encoder *stream_enc = pipe_ctx->stream_res.stream_enc; if (dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { if (dc->hwseq->funcs.setup_hpo_hw_control) @@ -2742,10 +2711,8 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx) dto_params.timing = &pipe_ctx->stream->timing; dto_params.ref_dtbclk_khz = dc->clk_mgr->funcs->get_dtb_ref_clk_frequency(dc->clk_mgr); dccg->funcs->set_dtbclk_dto(dccg, &dto_params); - } else if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST && dccg->funcs->enable_symclk_se) - dccg->funcs->enable_symclk_se(dccg, - stream_enc->stream_enc_inst, link_enc->transmitter - TRANSMITTER_UNIPHY_A); - + } else { + } if (hws->funcs.calculate_dccg_k1_k2_values && dc->res_pool->dccg->funcs->set_pixel_rate_div) { hws->funcs.calculate_dccg_k1_k2_values(pipe_ctx, &k1_div, &k2_div); diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c index 6cef62d7a2e5d79b4dd08515923663c32979d0b6..255713ec29bb0fc794445ff3226bd5d4968b302c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c @@ -987,3 +987,20 @@ void dcn30_prepare_bandwidth(struct dc *dc, } } +void dcn30_set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_params *params) +{ + unsigned int i; + unsigned int triggers = 0; + + if (params->triggers.surface_update) + triggers |= 0x100; + if (params->triggers.cursor_update) + triggers |= 0x8; + if (params->triggers.force_trigger) + triggers |= 0x1; + + for (i = 0; i < num_pipes; i++) + pipe_ctx[i]->stream_res.tg->funcs->set_static_screen_control(pipe_ctx[i]->stream_res.tg, + triggers, params->num_frames); +} diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h index a24a8e33a3d289e8b99a78fa0d377263fbedde2e..ce19c54097f8be5a99983e4e54de6449a90e5a14 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.h @@ -87,5 +87,7 @@ void dcn30_set_hubp_blank(const struct dc *dc, void dcn30_prepare_bandwidth(struct dc *dc, struct dc_state *context); +void dcn30_set_static_screen_control(struct pipe_ctx **pipe_ctx, + int num_pipes, const struct dc_static_screen_params *params); #endif /* __DC_HWSS_DCN30_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c index 3d19acaa12f3ce3e5b20092fa44767379a7fe97e..0de8b2783cf682d6576646b8b53248783302ba14 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_init.c @@ -64,7 +64,7 @@ static const struct hw_sequencer_funcs dcn30_funcs = { .update_bandwidth = dcn20_update_bandwidth, .set_drr = dcn10_set_drr, .get_position = dcn10_get_position, - .set_static_screen_control = dcn10_set_static_screen_control, + .set_static_screen_control = dcn30_set_static_screen_control, .setup_stereo = dcn10_setup_stereo, .set_avmute = dcn30_set_avmute, .log_hw_state = dcn10_log_hw_state, diff --git a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c index 257df8660b4cafef2a64231487729bbd2d4b3486..61205cdbe2d5abdb73d3249501b12873dae8243a 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c @@ -75,6 +75,7 @@ static const struct hw_sequencer_funcs dcn301_funcs = { .get_hw_state = dcn10_get_hw_state, .clear_status_bits = dcn10_clear_status_bits, .wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect, + .edp_backlight_control = dce110_edp_backlight_control, .edp_power_control = dce110_edp_power_control, .edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready, .set_cursor_position = dcn10_set_cursor_position, diff --git a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_init.c b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_init.c index fc25cc300a175b980daa8e7646541342e06b0a9f..1d7bc1e39afe4c5400077bc261ad1338cd62f6cd 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn31/dcn31_init.c @@ -67,7 +67,7 @@ static const struct hw_sequencer_funcs dcn31_funcs = { .update_bandwidth = dcn20_update_bandwidth, .set_drr = dcn10_set_drr, .get_position = dcn10_get_position, - .set_static_screen_control = dcn10_set_static_screen_control, + .set_static_screen_control = dcn30_set_static_screen_control, .setup_stereo = dcn10_setup_stereo, .set_avmute = dcn30_set_avmute, .log_hw_state = dcn10_log_hw_state, diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c index ca8fe55c33b8ddf737200bce0bbee23b76ee789d..4ef85c3a0688f96058053fbd80b66fcda41710d9 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c @@ -69,7 +69,7 @@ static const struct hw_sequencer_funcs dcn314_funcs = { .update_bandwidth = dcn20_update_bandwidth, .set_drr = dcn10_set_drr, .get_position = dcn10_get_position, - .set_static_screen_control = dcn10_set_static_screen_control, + .set_static_screen_control = dcn30_set_static_screen_control, .setup_stereo = dcn10_setup_stereo, .set_avmute = dcn30_set_avmute, .log_hw_state = dcn10_log_hw_state, diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c index 1c1fb2fa082296908074f93b350d64259bf9f99f..004beed9bd444c7df896f07305726f2266fd652c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c @@ -1032,6 +1032,28 @@ static const struct dce_i2c_mask i2c_masks = { I2C_COMMON_MASK_SH_LIST_DCN30(_MASK) }; +/* ========================================================== */ + +/* + * DPIA index | Preferred Encoder | Host Router + * 0 | C | 0 + * 1 | First Available | 0 + * 2 | D | 1 + * 3 | First Available | 1 + */ +/* ========================================================== */ +static const enum engine_id dpia_to_preferred_enc_id_table[] = { + ENGINE_ID_DIGC, + ENGINE_ID_DIGC, + ENGINE_ID_DIGD, + ENGINE_ID_DIGD +}; + +static enum engine_id dcn314_get_preferred_eng_id_dpia(unsigned int dpia_index) +{ + return dpia_to_preferred_enc_id_table[dpia_index]; +} + static struct dce_i2c_hw *dcn31_i2c_hw_create( struct dc_context *ctx, uint32_t inst) @@ -1785,6 +1807,7 @@ static struct resource_funcs dcn314_res_pool_funcs = { .update_bw_bounding_box = dcn314_update_bw_bounding_box, .patch_unknown_plane_state = dcn20_patch_unknown_plane_state, .get_panel_config_defaults = dcn314_get_panel_config_defaults, + .get_preferred_eng_id_dpia = dcn314_get_preferred_eng_id_dpia, }; static struct clock_source *dcn30_clock_source_create( diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c index 777b2fac20c4efa8a65f71131967ba603b4c04be..c7417147dff19bf06b35dcc25d777c80c9034a60 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c @@ -65,7 +65,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = { .update_bandwidth = dcn20_update_bandwidth, .set_drr = dcn10_set_drr, .get_position = dcn10_get_position, - .set_static_screen_control = dcn10_set_static_screen_control, + .set_static_screen_control = dcn30_set_static_screen_control, .setup_stereo = dcn10_setup_stereo, .set_avmute = dcn30_set_avmute, .log_hw_state = dcn10_log_hw_state, diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mpc.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mpc.c index 3082da04a63db76d8b15cd7f62bf639e18f083d4..1d052f08aff5e16ffc10a9695bca927a64ea68f5 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_mpc.c @@ -75,7 +75,7 @@ void mpc32_power_on_blnd_lut( if (power_on) { REG_UPDATE(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], MPCC_MCM_1DLUT_MEM_PWR_FORCE, 0); REG_WAIT(MPCC_MCM_MEM_PWR_CTRL[mpcc_id], MPCC_MCM_1DLUT_MEM_PWR_STATE, 0, 1, 5); - } else { + } else if (!mpc->ctx->dc->debug.disable_mem_low_power) { ASSERT(false); /* TODO: change to mpc * dpp_base->ctx->dc->optimized_required = true; diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c index 935cd23e6a01ab80efd07196e466337a852b4373..f9d601c8c72176350ae0ddf6397dda4816d43617 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c @@ -2564,18 +2564,128 @@ static int find_optimal_free_pipe_as_secondary_dpp_pipe( return free_pipe_idx; } +static struct pipe_ctx *find_idle_secondary_pipe_check_mpo( + struct resource_context *res_ctx, + const struct resource_pool *pool, + const struct pipe_ctx *primary_pipe) +{ + int i; + struct pipe_ctx *secondary_pipe = NULL; + struct pipe_ctx *next_odm_mpo_pipe = NULL; + int primary_index, preferred_pipe_idx; + struct pipe_ctx *old_primary_pipe = NULL; + + /* + * Modified from find_idle_secondary_pipe + * With windowed MPO and ODM, we want to avoid the case where we want a + * free pipe for the left side but the free pipe is being used on the + * right side. + * Add check on current_state if the primary_pipe is the left side, + * to check the right side ( primary_pipe->next_odm_pipe ) to see if + * it is using a pipe for MPO ( primary_pipe->next_odm_pipe->bottom_pipe ) + * - If so, then don't use this pipe + * EXCEPTION - 3 plane ( 2 MPO plane ) case + * - in this case, the primary pipe has already gotten a free pipe for the + * MPO window in the left + * - when it tries to get a free pipe for the MPO window on the right, + * it will see that it is already assigned to the right side + * ( primary_pipe->next_odm_pipe ). But in this case, we want this + * free pipe, since it will be for the right side. So add an + * additional condition, that skipping the free pipe on the right only + * applies if the primary pipe has no bottom pipe currently assigned + */ + if (primary_pipe) { + primary_index = primary_pipe->pipe_idx; + old_primary_pipe = &primary_pipe->stream->ctx->dc->current_state->res_ctx.pipe_ctx[primary_index]; + if ((old_primary_pipe->next_odm_pipe) && (old_primary_pipe->next_odm_pipe->bottom_pipe) + && (!primary_pipe->bottom_pipe)) + next_odm_mpo_pipe = old_primary_pipe->next_odm_pipe->bottom_pipe; + + preferred_pipe_idx = (pool->pipe_count - 1) - primary_pipe->pipe_idx; + if ((res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) && + !(next_odm_mpo_pipe && next_odm_mpo_pipe->pipe_idx == preferred_pipe_idx)) { + secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx]; + secondary_pipe->pipe_idx = preferred_pipe_idx; + } + } + + /* + * search backwards for the second pipe to keep pipe + * assignment more consistent + */ + if (!secondary_pipe) + for (i = pool->pipe_count - 1; i >= 0; i--) { + if ((res_ctx->pipe_ctx[i].stream == NULL) && + !(next_odm_mpo_pipe && next_odm_mpo_pipe->pipe_idx == i)) { + secondary_pipe = &res_ctx->pipe_ctx[i]; + secondary_pipe->pipe_idx = i; + break; + } + } + + return secondary_pipe; +} + +static struct pipe_ctx *dcn32_acquire_idle_pipe_for_head_pipe_in_layer( + struct dc_state *state, + const struct resource_pool *pool, + struct dc_stream_state *stream, + const struct pipe_ctx *head_pipe) +{ + struct resource_context *res_ctx = &state->res_ctx; + struct pipe_ctx *idle_pipe, *pipe; + struct resource_context *old_ctx = &stream->ctx->dc->current_state->res_ctx; + int head_index; + + if (!head_pipe) + ASSERT(0); + + /* + * Modified from dcn20_acquire_idle_pipe_for_layer + * Check if head_pipe in old_context already has bottom_pipe allocated. + * - If so, check if that pipe is available in the current context. + * -- If so, reuse pipe from old_context + */ + head_index = head_pipe->pipe_idx; + pipe = &old_ctx->pipe_ctx[head_index]; + if (pipe->bottom_pipe && res_ctx->pipe_ctx[pipe->bottom_pipe->pipe_idx].stream == NULL) { + idle_pipe = &res_ctx->pipe_ctx[pipe->bottom_pipe->pipe_idx]; + idle_pipe->pipe_idx = pipe->bottom_pipe->pipe_idx; + } else { + idle_pipe = find_idle_secondary_pipe_check_mpo(res_ctx, pool, head_pipe); + if (!idle_pipe) + return NULL; + } + + idle_pipe->stream = head_pipe->stream; + idle_pipe->stream_res.tg = head_pipe->stream_res.tg; + idle_pipe->stream_res.opp = head_pipe->stream_res.opp; + + idle_pipe->plane_res.hubp = pool->hubps[idle_pipe->pipe_idx]; + idle_pipe->plane_res.ipp = pool->ipps[idle_pipe->pipe_idx]; + idle_pipe->plane_res.dpp = pool->dpps[idle_pipe->pipe_idx]; + idle_pipe->plane_res.mpcc_inst = pool->dpps[idle_pipe->pipe_idx]->inst; + + return idle_pipe; +} + struct pipe_ctx *dcn32_acquire_free_pipe_as_secondary_dpp_pipe( const struct dc_state *cur_ctx, struct dc_state *new_ctx, const struct resource_pool *pool, const struct pipe_ctx *opp_head_pipe) { - int free_pipe_idx = - find_optimal_free_pipe_as_secondary_dpp_pipe( - &cur_ctx->res_ctx, &new_ctx->res_ctx, - pool, opp_head_pipe); + + int free_pipe_idx; struct pipe_ctx *free_pipe; + if (!opp_head_pipe->stream->ctx->dc->config.enable_windowed_mpo_odm) + return dcn32_acquire_idle_pipe_for_head_pipe_in_layer( + new_ctx, pool, opp_head_pipe->stream, opp_head_pipe); + + free_pipe_idx = find_optimal_free_pipe_as_secondary_dpp_pipe( + &cur_ctx->res_ctx, &new_ctx->res_ctx, + pool, opp_head_pipe); if (free_pipe_idx >= 0) { free_pipe = &new_ctx->res_ctx.pipe_ctx[free_pipe_idx]; free_pipe->pipe_idx = free_pipe_idx; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c index 8afda5ecc0cd3f28f07d9d821822d7943a547c31..5805fb02af14e37c9af22710dc85de9606b86e17 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c @@ -1099,6 +1099,11 @@ void dcn20_calculate_dlg_params(struct dc *dc, context->res_ctx.pipe_ctx[i].plane_res.bw.dppclk_khz = pipes[pipe_idx].clks_cfg.dppclk_mhz * 1000; context->res_ctx.pipe_ctx[i].pipe_dlg_param = pipes[pipe_idx].pipe.dest; + if (dc->ctx->dce_version < DCN_VERSION_3_1 && + context->res_ctx.pipe_ctx[i].stream->adaptive_sync_infopacket.valid) + dcn20_adjust_freesync_v_startup( + &context->res_ctx.pipe_ctx[i].stream->timing, + &context->res_ctx.pipe_ctx[i].pipe_dlg_param.vstartup_start); pipe_idx++; } @@ -1927,7 +1932,6 @@ static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *co int vlevel = 0; int pipe_split_from[MAX_PIPES]; int pipe_cnt = 0; - int i = 0; display_e2e_pipe_params_st *pipes = kzalloc(dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st), GFP_ATOMIC); DC_LOGGER_INIT(dc->ctx->logger); @@ -1951,15 +1955,6 @@ static bool dcn20_validate_bandwidth_internal(struct dc *dc, struct dc_state *co dcn20_calculate_wm(dc, context, pipes, &pipe_cnt, pipe_split_from, vlevel, fast_validate); dcn20_calculate_dlg_params(dc, context, pipes, pipe_cnt, vlevel); - for (i = 0; i < dc->res_pool->pipe_count; i++) { - if (!context->res_ctx.pipe_ctx[i].stream) - continue; - if (context->res_ctx.pipe_ctx[i].stream->adaptive_sync_infopacket.valid) - dcn20_adjust_freesync_v_startup( - &context->res_ctx.pipe_ctx[i].stream->timing, - &context->res_ctx.pipe_ctx[i].pipe_dlg_param.vstartup_start); - } - BW_VAL_TRACE_END_WATERMARKS(); goto validate_out; @@ -2232,7 +2227,6 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, int vlevel = 0; int pipe_split_from[MAX_PIPES]; int pipe_cnt = 0; - int i = 0; display_e2e_pipe_params_st *pipes = kzalloc(dc->res_pool->pipe_count * sizeof(display_e2e_pipe_params_st), GFP_ATOMIC); DC_LOGGER_INIT(dc->ctx->logger); @@ -2261,15 +2255,6 @@ bool dcn21_validate_bandwidth_fp(struct dc *dc, dcn21_calculate_wm(dc, context, pipes, &pipe_cnt, pipe_split_from, vlevel, fast_validate); dcn20_calculate_dlg_params(dc, context, pipes, pipe_cnt, vlevel); - for (i = 0; i < dc->res_pool->pipe_count; i++) { - if (!context->res_ctx.pipe_ctx[i].stream) - continue; - if (context->res_ctx.pipe_ctx[i].stream->adaptive_sync_infopacket.valid) - dcn20_adjust_freesync_v_startup( - &context->res_ctx.pipe_ctx[i].stream->timing, - &context->res_ctx.pipe_ctx[i].pipe_dlg_param.vstartup_start); - } - BW_VAL_TRACE_END_WATERMARKS(); goto validate_out; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c index 07adb614366eb5c71964ea0b5b4f3701a7427f2a..fb21572750e8098a3f794217d9d4ed8478ab4349 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c @@ -293,6 +293,17 @@ static unsigned int micro_sec_to_vert_lines(unsigned int num_us, struct dc_crtc_ return num_lines; } +static unsigned int get_vertical_back_porch(struct dc_crtc_timing *timing) +{ + unsigned int v_active = 0, v_blank = 0, v_back_porch = 0; + + v_active = timing->v_border_top + timing->v_addressable + timing->v_border_bottom; + v_blank = timing->v_total - v_active; + v_back_porch = v_blank - timing->v_front_porch - timing->v_sync_width; + + return v_back_porch; +} + int dcn314_populate_dml_pipes_from_context_fpu(struct dc *dc, struct dc_state *context, display_e2e_pipe_params_st *pipes, bool fast_validate) @@ -310,6 +321,7 @@ int dcn314_populate_dml_pipes_from_context_fpu(struct dc *dc, struct dc_state *c for (i = 0, pipe_cnt = 0; i < dc->res_pool->pipe_count; i++) { struct dc_crtc_timing *timing; unsigned int num_lines = 0; + unsigned int v_back_porch = 0; if (!res_ctx->pipe_ctx[i].stream) continue; @@ -323,9 +335,16 @@ int dcn314_populate_dml_pipes_from_context_fpu(struct dc *dc, struct dc_state *c else pipes[pipe_cnt].pipe.dest.vtotal = timing->v_total; + v_back_porch = get_vertical_back_porch(timing); + pipes[pipe_cnt].pipe.dest.vblank_nom = timing->v_total - pipes[pipe_cnt].pipe.dest.vactive; pipes[pipe_cnt].pipe.dest.vblank_nom = min(pipes[pipe_cnt].pipe.dest.vblank_nom, num_lines); - pipes[pipe_cnt].pipe.dest.vblank_nom = max(pipes[pipe_cnt].pipe.dest.vblank_nom, timing->v_sync_width); + // vblank_nom should not smaller than (VSync (timing->v_sync_width + v_back_porch) + 2) + // + 2 is because + // 1 -> VStartup_start should be 1 line before VSync + // 1 -> always reserve 1 line between start of vblank to vstartup signal + pipes[pipe_cnt].pipe.dest.vblank_nom = + max(pipes[pipe_cnt].pipe.dest.vblank_nom, timing->v_sync_width + v_back_porch + 2); pipes[pipe_cnt].pipe.dest.vblank_nom = min(pipes[pipe_cnt].pipe.dest.vblank_nom, max_allowed_vblank_nom); if (pipe->plane_state && diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index 027aec70c070f8d566986a3222117ae7e39acb3d..eaad1260bfd1808c40bea358ed410051ef5440d8 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -65,6 +65,7 @@ struct resource_context; struct clk_bw_params; struct resource_funcs { + enum engine_id (*get_preferred_eng_id_dpia)(unsigned int dpia_index); void (*destroy)(struct resource_pool **pool); void (*link_init)(struct dc_link *link); struct panel_cntl*(*panel_cntl_create)( diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index 195ca9e52edaaba87a8bd679b55eb643abc044eb..0895742a3102418152eff96f2939d29d116652c0 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -791,6 +791,10 @@ static bool construct_dpia(struct dc_link *link, /* Set dpia port index : 0 to number of dpia ports */ link->ddc_hw_inst = init_params->connector_index; + // Assign Dpia preferred eng_id + if (link->dc->res_pool->funcs->get_preferred_eng_id_dpia) + link->dpia_preferred_eng_id = link->dc->res_pool->funcs->get_preferred_eng_id_dpia(link->ddc_hw_inst); + /* TODO: Create link encoder */ link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c index dbd60811f95da4a8d17f76784758ce363f3e0058..ef3a674090211ce285e566e4012c69d340ecc55f 100644 --- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c +++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c @@ -338,7 +338,9 @@ static void apply_below_the_range(struct core_freesync *core_freesync, * - Delta for CEIL: delta_from_mid_point_in_us_1 * - Delta for FLOOR: delta_from_mid_point_in_us_2 */ - if ((last_render_time_in_us / mid_point_frames_ceil) < in_out_vrr->min_duration_in_us) { + if (mid_point_frames_ceil && + (last_render_time_in_us / mid_point_frames_ceil) < + in_out_vrr->min_duration_in_us) { /* Check for out of range. * If using CEIL produces a value that is out of range, * then we are forced to use FLOOR. @@ -385,8 +387,9 @@ static void apply_below_the_range(struct core_freesync *core_freesync, /* Either we've calculated the number of frames to insert, * or we need to insert min duration frames */ - if (last_render_time_in_us / frames_to_insert < - in_out_vrr->min_duration_in_us){ + if (frames_to_insert && + (last_render_time_in_us / frames_to_insert) < + in_out_vrr->min_duration_in_us){ frames_to_insert -= (frames_to_insert > 1) ? 1 : 0; } diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index abe829bbd54af668e113a4b2724e4ed72ae57511..67d7b7ee8a2a02ddb78e33565dedbccff13c628f 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -240,6 +240,7 @@ enum DC_FEATURE_MASK { DC_DISABLE_LTTPR_DP2_0 = (1 << 6), //0x40, disabled by default DC_PSR_ALLOW_SMU_OPT = (1 << 7), //0x80, disabled by default DC_PSR_ALLOW_MULTI_DISP_OPT = (1 << 8), //0x100, disabled by default + DC_REPLAY_MASK = (1 << 9), //0x200, disabled by default for dcn < 3.1.4 }; enum DC_DEBUG_MASK { @@ -250,6 +251,7 @@ enum DC_DEBUG_MASK { DC_DISABLE_PSR = 0x10, DC_FORCE_SUBVP_MCLK_SWITCH = 0x20, DC_DISABLE_MPO = 0x40, + DC_DISABLE_REPLAY = 0x50, DC_ENABLE_DPIA_TRACE = 0x80, }; diff --git a/drivers/gpu/drm/amd/include/atomfirmware.h b/drivers/gpu/drm/amd/include/atomfirmware.h index e68c1e280322ace66fd825d6350cf463bf7e6f22..fa7d6ced786f1985ca4ba1d55371d5b1b1ca4486 100644 --- a/drivers/gpu/drm/amd/include/atomfirmware.h +++ b/drivers/gpu/drm/amd/include/atomfirmware.h @@ -3117,6 +3117,24 @@ enum atom_umc_config1_def { UMC_CONFIG1__ENABLE_ECC_CAPABLE = 0x00010000, }; +struct atom_umc_info_v4_0 { + struct atom_common_table_header table_header; + uint32_t ucode_reserved[5]; + uint8_t umcip_min_ver; + uint8_t umcip_max_ver; + uint8_t vram_type; + uint8_t umc_config; + uint32_t mem_refclk_10khz; + uint32_t clk_reserved[4]; + uint32_t golden_reserved; + uint32_t umc_config1; + uint32_t reserved[2]; + uint8_t channel_num; + uint8_t channel_width; + uint8_t channel_reserve[2]; + uint8_t umc_info_reserved[16]; +}; + /* *************************************************************************** Data Table vram_info structure diff --git a/drivers/gpu/drm/amd/include/discovery.h b/drivers/gpu/drm/amd/include/discovery.h index f43e29722ef7104b1ef2a1370ac64f02a508d764..7a9d473d0917b0d0c7a6b91bfd341003b699e180 100644 --- a/drivers/gpu/drm/amd/include/discovery.h +++ b/drivers/gpu/drm/amd/include/discovery.h @@ -30,7 +30,7 @@ #define GC_TABLE_ID 0x4347 #define HARVEST_TABLE_SIGNATURE 0x56524148 #define VCN_INFO_TABLE_ID 0x004E4356 -#define MALL_INFO_TABLE_ID 0x4D414C4C +#define MALL_INFO_TABLE_ID 0x4C4C414D typedef enum { @@ -280,6 +280,36 @@ struct gc_info_v2_0 { uint32_t gc_num_packer_per_sc; }; +struct gc_info_v2_1 { + struct gpu_info_header header; + + uint32_t gc_num_se; + uint32_t gc_num_cu_per_sh; + uint32_t gc_num_sh_per_se; + uint32_t gc_num_rb_per_se; + uint32_t gc_num_tccs; + uint32_t gc_num_gprs; + uint32_t gc_num_max_gs_thds; + uint32_t gc_gs_table_depth; + uint32_t gc_gsprim_buff_depth; + uint32_t gc_parameter_cache_depth; + uint32_t gc_double_offchip_lds_buffer; + uint32_t gc_wave_size; + uint32_t gc_max_waves_per_simd; + uint32_t gc_max_scratch_slots_per_cu; + uint32_t gc_lds_size; + uint32_t gc_num_sc_per_se; + uint32_t gc_num_packer_per_sc; + /* new for v2_1 */ + uint32_t gc_num_tcp_per_sh; + uint32_t gc_tcp_size_per_cu; + uint32_t gc_num_sdp_interface; + uint32_t gc_num_cu_per_sqc; + uint32_t gc_instruction_cache_size_per_sqc; + uint32_t gc_scalar_data_cache_size_per_sqc; + uint32_t gc_tcc_size; +}; + typedef struct harvest_info_header { uint32_t signature; /* Table Signature */ uint32_t version; /* Table Version */ @@ -312,6 +342,12 @@ struct mall_info_v1_0 { uint32_t reserved[5]; }; +struct mall_info_v2_0 { + struct mall_info_header header; + uint32_t mall_size_per_umc; + uint32_t reserved[8]; +}; + #define VCN_INFO_TABLE_MAX_NUM_INSTANCES 4 struct vcn_info_header { diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 8433f99f66679dbcee25bb7567d993fcb8673294..3b5a56585c4b7283017651aad21ed1c0565ad37f 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -31,12 +31,12 @@ #include #include #include +#include "amdgpu_irq.h" +#include "amdgpu_gfx.h" struct pci_dev; struct amdgpu_device; -#define KGD_MAX_QUEUES 128 - struct kfd_dev; struct kgd_mem; @@ -68,7 +68,7 @@ struct kfd_cu_info { uint32_t wave_front_size; uint32_t max_scratch_slots_per_cu; uint32_t lds_size; - uint32_t cu_bitmap[4][4]; + uint32_t cu_bitmap[AMDGPU_MAX_GC_INSTANCES][4][4]; }; /* For getting GPU local memory information from KGD */ @@ -326,8 +326,7 @@ struct kfd2kgd_calls { uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); void (*get_cu_occupancy)(struct amdgpu_device *adev, int pasid, int *wave_cnt, int *max_waves_per_cu, uint32_t inst); void (*program_trap_handler_settings)(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 5b1d73b00ef7335971474d85249752f2e9d699d2..8bb2da13826f16bac126b7a5c59e0057e30ad8bb 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -2040,6 +2040,7 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ case IP_VERSION(11, 0, 0): case IP_VERSION(11, 0, 1): case IP_VERSION(11, 0, 2): + case IP_VERSION(11, 0, 3): *states = ATTR_STATE_SUPPORTED; break; default: @@ -3311,8 +3312,10 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, (gc_ver != IP_VERSION(9, 4, 3)) && (attr == &sensor_dev_attr_temp2_input.dev_attr.attr || attr == &sensor_dev_attr_temp2_label.dev_attr.attr || + attr == &sensor_dev_attr_temp2_crit.dev_attr.attr || attr == &sensor_dev_attr_temp3_input.dev_attr.attr || - attr == &sensor_dev_attr_temp3_label.dev_attr.attr)) + attr == &sensor_dev_attr_temp3_label.dev_attr.attr || + attr == &sensor_dev_attr_temp3_crit.dev_attr.attr)) return 0; /* hotspot temperature for gc 9,4,3*/ @@ -3324,9 +3327,7 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, /* only SOC15 dGPUs support hotspot and mem temperatures */ if (((adev->flags & AMD_IS_APU) || gc_ver < IP_VERSION(9, 0, 0) || (gc_ver == IP_VERSION(9, 4, 3))) && - (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr || - attr == &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr || - attr == &sensor_dev_attr_temp3_crit.dev_attr.attr || + (attr == &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr || attr == &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr || attr == &sensor_dev_attr_temp1_emergency.dev_attr.attr || attr == &sensor_dev_attr_temp2_emergency.dev_attr.attr || @@ -3471,6 +3472,9 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) seq_printf(m, "\t%u.%u W (average GPU)\n", query >> 8, query & 0xff); + size = sizeof(uint32_t); + if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) + seq_printf(m, "\t%u.%u W (current GPU)\n", query >> 8, query & 0xff); size = sizeof(value); seq_printf(m, "\n"); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index 95eb8a5eb54f6e5fd01ed74a1114c50bc147d86a..5a52098bcf1664c52fb8f9191d18919bb26da55c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -1031,10 +1031,7 @@ struct pptable_funcs { enum smu_feature_mask mask); /** - * @notify_display_change: Enable fast memory clock switching. - * - * Allows for fine grained memory clock switching but has more stringent - * timing requirements. + * @notify_display_change: General interface call to let SMU know about DC change */ int (*notify_display_change)(struct smu_context *smu); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h index 10cff75b44d5cfa98033885f75a0f2ae51ac0e87..e2ee855c7748805381bcf18a7466c555e9c16588 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_0_ppsmc.h @@ -138,7 +138,10 @@ #define PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel 0x4A #define PPSMC_MSG_SetPriorityDeltaGain 0x4B #define PPSMC_MSG_AllowIHHostInterrupt 0x4C -#define PPSMC_Message_Count 0x4D + +#define PPSMC_MSG_DALNotPresent 0x4E + +#define PPSMC_Message_Count 0x4F //Debug Dump Message #define DEBUGSMC_MSG_TestMessage 0x1 diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_pmfw.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_pmfw.h index 252aef190c5c2cf15232accaad6ed94900726de6..9be4051c0865c090482ccde9ca36b249f1272387 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_pmfw.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_pmfw.h @@ -123,7 +123,7 @@ typedef enum { VOLTAGE_GUARDBAND_COUNT } GFX_GUARDBAND_e; -#define SMU_METRICS_TABLE_VERSION 0x5 +#define SMU_METRICS_TABLE_VERSION 0x7 typedef struct __attribute__((packed, aligned(4))) { uint32_t AccumulationCounter; @@ -198,7 +198,7 @@ typedef struct __attribute__((packed, aligned(4))) { uint32_t SocketThmResidencyAcc; uint32_t VrThmResidencyAcc; uint32_t HbmThmResidencyAcc; - uint32_t spare; + uint32_t GfxLockXCDMak; // New Items at end to maintain driver compatibility uint32_t GfxclkFrequency[8]; diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h index ae4f44c4b87714b54c4b738dfa45fed7b43846ce..70a4a717fd3f850bcae7f84f777256e46fc4576b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_6_ppsmc.h @@ -83,13 +83,27 @@ #define PPSMC_MSG_GetMinGfxDpmFreq 0x32 #define PPSMC_MSG_GetMaxGfxDpmFreq 0x33 #define PPSMC_MSG_PrepareForDriverUnload 0x34 -#define PPSMC_Message_Count 0x35 +#define PPSMC_MSG_ReadThrottlerLimit 0x35 +#define PPSMC_MSG_QueryValidMcaCount 0x36 +#define PPSMC_MSG_McaBankDumpDW 0x37 +#define PPSMC_MSG_GetCTFLimit 0x38 +#define PPSMC_Message_Count 0x39 //PPSMC Reset Types for driver msg argument #define PPSMC_RESET_TYPE_DRIVER_MODE_1_RESET 0x1 #define PPSMC_RESET_TYPE_DRIVER_MODE_2_RESET 0x2 #define PPSMC_RESET_TYPE_DRIVER_MODE_3_RESET 0x3 +//PPSMC Reset Types for driver msg argument +#define PPSMC_THROTTLING_LIMIT_TYPE_SOCKET 0x1 +#define PPSMC_THROTTLING_LIMIT_TYPE_HBM 0x2 + +//CTF/Throttle Limit types +#define PPSMC_AID_THM_TYPE 0x1 +#define PPSMC_CCD_THM_TYPE 0x2 +#define PPSMC_XCD_THM_TYPE 0x3 +#define PPSMC_HBM_THM_TYPE 0x4 + typedef uint32_t PPSMC_Result; typedef uint32_t PPSMC_MSG; diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index 297b70b9388f4a22e4d63f5e955309b70bf9181c..e57265cf637c990692b64c96274447d0f87179c9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -84,6 +84,7 @@ __SMU_DUMMY_MAP(SetTjMax), \ __SMU_DUMMY_MAP(SetFanTemperatureTarget), \ __SMU_DUMMY_MAP(PrepareMp1ForUnload), \ + __SMU_DUMMY_MAP(GetCTFLimit), \ __SMU_DUMMY_MAP(DramLogSetDramAddrHigh), \ __SMU_DUMMY_MAP(DramLogSetDramAddrLow), \ __SMU_DUMMY_MAP(DramLogSetDramSize), \ @@ -245,7 +246,8 @@ __SMU_DUMMY_MAP(AllowGpo), \ __SMU_DUMMY_MAP(Mode2Reset), \ __SMU_DUMMY_MAP(RequestI2cTransaction), \ - __SMU_DUMMY_MAP(GetMetricsTable), + __SMU_DUMMY_MAP(GetMetricsTable), \ + __SMU_DUMMY_MAP(DALNotPresent), #undef __SMU_DUMMY_MAP #define __SMU_DUMMY_MAP(type) SMU_MSG_##type diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index 4bb289f9b4b8b82a59a99ad92e18deaa6900b850..da2860da60188e084bbbfafd11403822befc20ab 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -2082,36 +2082,41 @@ static int sienna_cichlid_display_disable_memory_clock_switch(struct smu_context return ret; } +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + static int sienna_cichlid_update_pcie_parameters(struct smu_context *smu, uint32_t pcie_gen_cap, uint32_t pcie_width_cap) { struct smu_11_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context; struct smu_11_0_pcie_table *pcie_table = &dpm_context->dpm_tables.pcie_table; - u32 smu_pcie_arg; + uint8_t *table_member1, *table_member2; + uint32_t min_gen_speed, max_gen_speed; + uint32_t min_lane_width, max_lane_width; + uint32_t smu_pcie_arg; int ret, i; - /* PCIE gen speed and lane width override */ - if (!amdgpu_device_pcie_dynamic_switching_supported()) { - if (pcie_table->pcie_gen[NUM_LINK_LEVELS - 1] < pcie_gen_cap) - pcie_gen_cap = pcie_table->pcie_gen[NUM_LINK_LEVELS - 1]; + GET_PPTABLE_MEMBER(PcieGenSpeed, &table_member1); + GET_PPTABLE_MEMBER(PcieLaneCount, &table_member2); - if (pcie_table->pcie_lane[NUM_LINK_LEVELS - 1] < pcie_width_cap) - pcie_width_cap = pcie_table->pcie_lane[NUM_LINK_LEVELS - 1]; + min_gen_speed = MAX(0, table_member1[0]); + max_gen_speed = MIN(pcie_gen_cap, table_member1[1]); + min_gen_speed = min_gen_speed > max_gen_speed ? + max_gen_speed : min_gen_speed; + min_lane_width = MAX(1, table_member2[0]); + max_lane_width = MIN(pcie_width_cap, table_member2[1]); + min_lane_width = min_lane_width > max_lane_width ? + max_lane_width : min_lane_width; - /* Force all levels to use the same settings */ - for (i = 0; i < NUM_LINK_LEVELS; i++) { - pcie_table->pcie_gen[i] = pcie_gen_cap; - pcie_table->pcie_lane[i] = pcie_width_cap; - } + if (!amdgpu_device_pcie_dynamic_switching_supported()) { + pcie_table->pcie_gen[0] = max_gen_speed; + pcie_table->pcie_lane[0] = max_lane_width; } else { - for (i = 0; i < NUM_LINK_LEVELS; i++) { - if (pcie_table->pcie_gen[i] > pcie_gen_cap) - pcie_table->pcie_gen[i] = pcie_gen_cap; - if (pcie_table->pcie_lane[i] > pcie_width_cap) - pcie_table->pcie_lane[i] = pcie_width_cap; - } + pcie_table->pcie_gen[0] = min_gen_speed; + pcie_table->pcie_lane[0] = min_lane_width; } + pcie_table->pcie_gen[1] = max_gen_speed; + pcie_table->pcie_lane[1] = max_lane_width; for (i = 0; i < NUM_LINK_LEVELS; i++) { smu_pcie_arg = (i << 16 | diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index f1282fc4b90ab5f41b030b3be6756c7bd018be5c..0232adb95df3a898d4a98894996690b61d703d56 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -837,12 +837,8 @@ int smu_v13_0_notify_display_change(struct smu_context *smu) { int ret = 0; - if (!smu->pm_enabled) - return ret; - - if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_UCLK_BIT) && - smu->adev->gmc.vram_type == AMDGPU_VRAM_TYPE_HBM) - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetUclkFastSwitch, 1, NULL); + if (!amdgpu_device_has_dc_support(smu->adev)) + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_DALNotPresent, NULL); return ret; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 8b7403ba89d7c8c0e588158a0a458a2c2090232e..3903a47669e4379a449099da19bec7ce303c651e 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -162,6 +162,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_0_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0), MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0), MSG_MAP(ReenableAcDcInterrupt, PPSMC_MSG_ReenableAcDcInterrupt, 0), + MSG_MAP(DALNotPresent, PPSMC_MSG_DALNotPresent, 0), }; static struct cmn2asic_mapping smu_v13_0_0_clk_map[SMU_CLK_COUNT] = { @@ -2687,6 +2688,7 @@ static const struct pptable_funcs smu_v13_0_0_ppt_funcs = { .send_hbm_bad_channel_flag = smu_v13_0_0_send_bad_mem_channel_flag, .gpo_control = smu_v13_0_gpo_control, .get_ecc_info = smu_v13_0_0_get_ecc_info, + .notify_display_change = smu_v13_0_notify_display_change, }; void smu_v13_0_0_set_ppt_funcs(struct smu_context *smu) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 6ed9cd0a1e4edd2cada5826d2b1ea824b49d21a5..de80e191a92c4946e3571da69268ac874e982f10 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -132,6 +132,7 @@ static const struct cmn2asic_msg_mapping smu_v13_0_6_message_map[SMU_MSG_MAX_COU MSG_MAP(SetSoftMinGfxclk, PPSMC_MSG_SetSoftMinGfxClk, 0), MSG_MAP(SetSoftMaxGfxClk, PPSMC_MSG_SetSoftMaxGfxClk, 0), MSG_MAP(PrepareMp1ForUnload, PPSMC_MSG_PrepareForDriverUnload, 0), + MSG_MAP(GetCTFLimit, PPSMC_MSG_GetCTFLimit, 0), }; static const struct cmn2asic_mapping smu_v13_0_6_clk_map[SMU_CLK_COUNT] = { @@ -335,7 +336,7 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) /* Store one-time values in driver PPTable */ if (!pptable->Init) { - while (retry--) { + while (--retry) { ret = smu_v13_0_6_get_metrics_table(smu, NULL, true); if (ret) return ret; @@ -2081,6 +2082,55 @@ static int smu_v13_0_6_mode2_reset(struct smu_context *smu) return ret; } +static int smu_v13_0_6_get_thermal_temperature_range(struct smu_context *smu, + struct smu_temperature_range *range) +{ + struct amdgpu_device *adev = smu->adev; + u32 aid_temp, xcd_temp, mem_temp; + uint32_t smu_version; + u32 ccd_temp = 0; + int ret; + + if (amdgpu_sriov_vf(smu->adev)) + return 0; + + if (!range) + return -EINVAL; + + /*Check smu version, GetCtfLimit message only supported for smu version 85.69 or higher */ + smu_cmn_get_smc_version(smu, NULL, &smu_version); + if (smu_version < 0x554500) + return 0; + + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetCTFLimit, + PPSMC_AID_THM_TYPE, &aid_temp); + if (ret) + goto failed; + + if (adev->flags & AMD_IS_APU) { + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetCTFLimit, + PPSMC_CCD_THM_TYPE, &ccd_temp); + if (ret) + goto failed; + } + + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetCTFLimit, + PPSMC_XCD_THM_TYPE, &xcd_temp); + if (ret) + goto failed; + + range->hotspot_crit_max = max3(aid_temp, xcd_temp, ccd_temp) * + SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GetCTFLimit, + PPSMC_HBM_THM_TYPE, &mem_temp); + if (ret) + goto failed; + + range->mem_crit_max = mem_temp * SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; +failed: + return ret; +} + static int smu_v13_0_6_mode1_reset(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -2108,8 +2158,7 @@ static int smu_v13_0_6_mode1_reset(struct smu_context *smu) static bool smu_v13_0_6_is_mode1_reset_supported(struct smu_context *smu) { - /* TODO: Enable this when FW support is added */ - return false; + return true; } static bool smu_v13_0_6_is_mode2_reset_supported(struct smu_context *smu) @@ -2177,6 +2226,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = { .get_pp_feature_mask = smu_cmn_get_pp_feature_mask, .set_pp_feature_mask = smu_cmn_set_pp_feature_mask, .get_gpu_metrics = smu_v13_0_6_get_gpu_metrics, + .get_thermal_temperature_range = smu_v13_0_6_get_thermal_temperature_range, .mode1_reset_is_support = smu_v13_0_6_is_mode1_reset_supported, .mode2_reset_is_support = smu_v13_0_6_is_mode2_reset_supported, .mode1_reset = smu_v13_0_6_mode1_reset, diff --git a/drivers/gpu/drm/ci/arm.config b/drivers/gpu/drm/ci/arm.config new file mode 100644 index 0000000000000000000000000000000000000000..871f4de063ad1bb936155d7b319172ada0e0d32a --- /dev/null +++ b/drivers/gpu/drm/ci/arm.config @@ -0,0 +1,69 @@ +CONFIG_LOCALVERSION_AUTO=y +CONFIG_DEBUG_KERNEL=y + +CONFIG_CRYPTO_ZSTD=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM=y +CONFIG_ZSMALLOC_STAT=y + +# abootimg with a 'dummy' rootfs fails with root=/dev/nfs +CONFIG_BLK_DEV_INITRD=n + +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y + +CONFIG_DRM=y +CONFIG_DRM_ETNAVIV=y +CONFIG_DRM_ROCKCHIP=y +CONFIG_DRM_PANFROST=y +CONFIG_DRM_LIMA=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_PWM_CROS_EC=y +CONFIG_BACKLIGHT_PWM=y + +CONFIG_ROCKCHIP_CDN_DP=n + +CONFIG_SPI_ROCKCHIP=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_DP=y +CONFIG_DWMAC_ROCKCHIP=y + +CONFIG_MFD_RK808=y +CONFIG_REGULATOR_RK808=y +CONFIG_RTC_DRV_RK808=y +CONFIG_COMMON_CLK_RK808=y + +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR=y + +CONFIG_REGULATOR_VCTRL=y + +CONFIG_KASAN=n +CONFIG_KASAN_INLINE=n +CONFIG_STACKTRACE=n + +CONFIG_TMPFS=y + +CONFIG_PROVE_LOCKING=n +CONFIG_DEBUG_LOCKDEP=n +CONFIG_SOFTLOCKUP_DETECTOR=n +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=n + +CONFIG_FW_LOADER_COMPRESS=y + +CONFIG_USB_USBNET=y +CONFIG_NETDEVICES=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_RTL8152=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_SMSC95XX=y + +# TK1 +CONFIG_ARM_TEGRA_DEVFREQ=y + +# 32-bit build failure +CONFIG_DRM_MSM=n diff --git a/drivers/gpu/drm/ci/arm64.config b/drivers/gpu/drm/ci/arm64.config new file mode 100644 index 0000000000000000000000000000000000000000..817e18ddfd4f5fd4995252a12a70b689def229a4 --- /dev/null +++ b/drivers/gpu/drm/ci/arm64.config @@ -0,0 +1,199 @@ +CONFIG_LOCALVERSION_AUTO=y +CONFIG_DEBUG_KERNEL=y + +CONFIG_CRYPTO_ZSTD=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM=y +CONFIG_ZSMALLOC_STAT=y + +# abootimg with a 'dummy' rootfs fails with root=/dev/nfs +CONFIG_BLK_DEV_INITRD=n + +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y + +CONFIG_DRM=y +CONFIG_DRM_ROCKCHIP=y +CONFIG_DRM_PANFROST=y +CONFIG_DRM_LIMA=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_DRM_PANEL_EDP=y +CONFIG_DRM_MSM=y +CONFIG_DRM_ETNAVIV=y +CONFIG_DRM_I2C_ADV7511=y +CONFIG_PWM_CROS_EC=y +CONFIG_BACKLIGHT_PWM=y + +CONFIG_ROCKCHIP_CDN_DP=n + +CONFIG_SPI_ROCKCHIP=y +CONFIG_PWM_ROCKCHIP=y +CONFIG_PHY_ROCKCHIP_DP=y +CONFIG_DWMAC_ROCKCHIP=y +CONFIG_STMMAC_ETH=y +CONFIG_TYPEC_FUSB302=y +CONFIG_TYPEC=y +CONFIG_TYPEC_TCPM=y + +# MSM platform bits + +# For CONFIG_QCOM_LMH +CONFIG_OF=y + +CONFIG_ARM_SMMU_QCOM=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_RPMHPD=y +CONFIG_QCOM_RPMPD=y +CONFIG_QCOM_OCMEM=y +CONFIG_SDM_GPUCC_845=y +CONFIG_SDM_VIDEOCC_845=y +CONFIG_SDM_DISPCC_845=y +CONFIG_SDM_LPASSCC_845=y +CONFIG_SDM_CAMCC_845=y +CONFIG_RESET_QCOM_PDC=y +CONFIG_DRM_TI_SN65DSI86=y +CONFIG_I2C_QCOM_GENI=y +CONFIG_SPI_QCOM_GENI=y +CONFIG_PHY_QCOM_QUSB2=y +CONFIG_PHY_QCOM_QMP=y +CONFIG_MSM_GCC_8996=y +CONFIG_QCOM_CLK_APCC_MSM8996=y +CONFIG_QCOM_LLCC=y +CONFIG_QCOM_LMH=y +CONFIG_QCOM_SPMI_TEMP_ALARM=y +CONFIG_QCOM_WDT=y +CONFIG_POWER_RESET_QCOM_PON=y +CONFIG_RTC_DRV_PM8XXX=y +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_QCOM=y +CONFIG_INTERCONNECT_QCOM_MSM8996=y +CONFIG_INTERCONNECT_QCOM_SDM845=y +CONFIG_INTERCONNECT_QCOM_MSM8916=y +CONFIG_INTERCONNECT_QCOM_MSM8996=y +CONFIG_INTERCONNECT_QCOM_OSM_L3=y +CONFIG_INTERCONNECT_QCOM_SC7180=y +CONFIG_INTERCONNECT_QCOM_SM8350=y +CONFIG_CRYPTO_DEV_QCOM_RNG=y +CONFIG_SC_DISPCC_7180=y +CONFIG_SC_GPUCC_7180=y +CONFIG_SM_GPUCC_8350=y +CONFIG_QCOM_SPMI_ADC5=y +CONFIG_DRM_PARADE_PS8640=y +CONFIG_DRM_LONTIUM_LT9611UXC=y +CONFIG_PHY_QCOM_USB_HS=y +CONFIG_QCOM_GPI_DMA=y +CONFIG_USB_ONBOARD_HUB=y +CONFIG_NVMEM_QCOM_QFPROM=y +CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y + + +# db410c ethernet +CONFIG_USB_RTL8152=y +# db820c ethernet +CONFIG_ATL1C=y +# Chromebooks ethernet +CONFIG_USB_ONBOARD_HUB=y +# 888 HDK ethernet +CONFIG_USB_LAN78XX=y + +CONFIG_ARCH_ALPINE=n +CONFIG_ARCH_BCM2835=y +CONFIG_ARCH_BCM_IPROC=n +CONFIG_ARCH_BERLIN=n +CONFIG_ARCH_BRCMSTB=n +CONFIG_ARCH_EXYNOS=n +CONFIG_ARCH_K3=n +CONFIG_ARCH_LAYERSCAPE=n +CONFIG_ARCH_LG1K=n +CONFIG_ARCH_HISI=n +CONFIG_ARCH_MVEBU=n +CONFIG_ARCH_SEATTLE=n +CONFIG_ARCH_SYNQUACER=n +CONFIG_ARCH_RENESAS=n +CONFIG_ARCH_R8A774A1=n +CONFIG_ARCH_R8A774C0=n +CONFIG_ARCH_R8A7795=n +CONFIG_ARCH_R8A7796=n +CONFIG_ARCH_R8A77965=n +CONFIG_ARCH_R8A77970=n +CONFIG_ARCH_R8A77980=n +CONFIG_ARCH_R8A77990=n +CONFIG_ARCH_R8A77995=n +CONFIG_ARCH_STRATIX10=n +CONFIG_ARCH_TEGRA=n +CONFIG_ARCH_SPRD=n +CONFIG_ARCH_THUNDER=n +CONFIG_ARCH_THUNDER2=n +CONFIG_ARCH_UNIPHIER=n +CONFIG_ARCH_VEXPRESS=n +CONFIG_ARCH_XGENE=n +CONFIG_ARCH_ZX=n +CONFIG_ARCH_ZYNQMP=n + +# Strip out some stuff we don't need for graphics testing, to reduce +# the build. +CONFIG_CAN=n +CONFIG_WIRELESS=n +CONFIG_RFKILL=n +CONFIG_WLAN=n + +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR=y + +CONFIG_REGULATOR_VCTRL=y + +CONFIG_KASAN=n +CONFIG_KASAN_INLINE=n +CONFIG_STACKTRACE=n + +CONFIG_TMPFS=y + +CONFIG_PROVE_LOCKING=n +CONFIG_DEBUG_LOCKDEP=n +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y + +CONFIG_DETECT_HUNG_TASK=y + +CONFIG_FW_LOADER_COMPRESS=y +CONFIG_FW_LOADER_USER_HELPER=n + +CONFIG_USB_USBNET=y +CONFIG_NETDEVICES=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_RTL8152=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_SMSC95XX=y + +# For amlogic +CONFIG_MESON_GXL_PHY=y +CONFIG_MDIO_BUS_MUX_MESON_G12A=y +CONFIG_DRM_MESON=y + +# For Mediatek +CONFIG_DRM_MEDIATEK=y +CONFIG_PWM_MEDIATEK=y +CONFIG_DRM_MEDIATEK_HDMI=y +CONFIG_GNSS=y +CONFIG_GNSS_MTK_SERIAL=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_MTK=y +CONFIG_MTK_DEVAPC=y +CONFIG_PWM_MTK_DISP=y +CONFIG_MTK_CMDQ=y + +# For nouveau. Note that DRM must be a module so that it's loaded after NFS is up to provide the firmware. +CONFIG_ARCH_TEGRA=y +CONFIG_DRM_NOUVEAU=m +CONFIG_DRM_TEGRA=m +CONFIG_R8169=y +CONFIG_STAGING=y +CONFIG_DRM_TEGRA_STAGING=y +CONFIG_TEGRA_HOST1X=y +CONFIG_ARM_TEGRA_DEVFREQ=y +CONFIG_TEGRA_SOCTHERM=y +CONFIG_DRM_TEGRA_DEBUG=y +CONFIG_PWM_TEGRA=y diff --git a/drivers/gpu/drm/ci/build-igt.sh b/drivers/gpu/drm/ci/build-igt.sh new file mode 100644 index 0000000000000000000000000000000000000000..500fa4f5c30a2d97526bfd78c71222b6d23eb489 --- /dev/null +++ b/drivers/gpu/drm/ci/build-igt.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT + +set -ex + +git clone https://gitlab.freedesktop.org/drm/igt-gpu-tools.git --single-branch --no-checkout +cd igt-gpu-tools +git checkout $IGT_VERSION + +if [[ "$KERNEL_ARCH" = "arm" ]]; then + . ../.gitlab-ci/container/create-cross-file.sh armhf + EXTRA_MESON_ARGS="--cross-file /cross_file-armhf.txt" +fi + +MESON_OPTIONS="-Doverlay=disabled \ + -Dchamelium=disabled \ + -Dvalgrind=disabled \ + -Dman=enabled \ + -Dtests=enabled \ + -Drunner=enabled \ + -Dlibunwind=enabled \ + -Dprefix=/igt" + +mkdir -p /igt +meson build $MESON_OPTIONS $EXTRA_MESON_ARGS +ninja -C build -j${FDO_CI_CONCURRENT:-4} || ninja -C build -j 1 +ninja -C build install + +mkdir -p artifacts/ +tar -cf artifacts/igt.tar /igt + +# Pass needed files to the test stage +S3_ARTIFACT_NAME="igt.tar.gz" +gzip -c artifacts/igt.tar > ${S3_ARTIFACT_NAME} +ci-fairy s3cp --token-file "${CI_JOB_JWT_FILE}" ${S3_ARTIFACT_NAME} https://${PIPELINE_ARTIFACTS_BASE}/${KERNEL_ARCH}/${S3_ARTIFACT_NAME} diff --git a/drivers/gpu/drm/ci/build.sh b/drivers/gpu/drm/ci/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..7b014287a041b3e59a090212374098fbd1fd2ae9 --- /dev/null +++ b/drivers/gpu/drm/ci/build.sh @@ -0,0 +1,157 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT + +set -ex + +# Clean up stale rebases that GitLab might not have removed when reusing a checkout dir +rm -rf .git/rebase-apply + +. .gitlab-ci/container/container_pre_build.sh + +# libssl-dev was uninstalled because it was considered an ephemeral package +apt-get update +apt-get install -y libssl-dev + +if [[ "$KERNEL_ARCH" = "arm64" ]]; then + GCC_ARCH="aarch64-linux-gnu" + DEBIAN_ARCH="arm64" + DEVICE_TREES="arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/amlogic/meson-gxl-s805x-libretech-ac.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/allwinner/sun50i-h6-pine-h64.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/amlogic/meson-gxm-khadas-vim2.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/qcom/apq8016-sbc.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/qcom/apq8096-db820c.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/amlogic/meson-g12b-a311d-khadas-vim3.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/mediatek/mt8173-elm-hana.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/mediatek/mt8183-kukui-jacuzzi-juniper-sku16.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/mediatek/mt8192-asurada-spherion-r0.dtb" + DEVICE_TREES+=" arch/arm64/boot/dts/qcom/sc7180-trogdor-lazor-limozeen-nots-r5.dtb" +elif [[ "$KERNEL_ARCH" = "arm" ]]; then + GCC_ARCH="arm-linux-gnueabihf" + DEBIAN_ARCH="armhf" + DEVICE_TREES="arch/arm/boot/dts/rockchip/rk3288-veyron-jaq.dtb" + DEVICE_TREES+=" arch/arm/boot/dts/allwinner/sun8i-h3-libretech-all-h3-cc.dtb" + DEVICE_TREES+=" arch/arm/boot/dts/nxp/imx/imx6q-cubox-i.dtb" + apt-get install -y libssl-dev:armhf +else + GCC_ARCH="x86_64-linux-gnu" + DEBIAN_ARCH="x86_64" + DEVICE_TREES="" +fi + +export ARCH=${KERNEL_ARCH} +export CROSS_COMPILE="${GCC_ARCH}-" + +# The kernel doesn't like the gold linker (or the old lld in our debians). +# Sneak in some override symlinks during kernel build until we can update +# debian. +mkdir -p ld-links +for i in /usr/bin/*-ld /usr/bin/ld; do + i=$(basename $i) + ln -sf /usr/bin/$i.bfd ld-links/$i +done + +NEWPATH=$(pwd)/ld-links +export PATH=$NEWPATH:$PATH + +git config --global user.email "fdo@example.com" +git config --global user.name "freedesktop.org CI" +git config --global pull.rebase true + +# Try to merge fixes from target repo +if [ "$(git ls-remote --exit-code --heads ${UPSTREAM_REPO} ${TARGET_BRANCH}-external-fixes)" ]; then + git pull ${UPSTREAM_REPO} ${TARGET_BRANCH}-external-fixes +fi + +# Try to merge fixes from local repo if this isn't a merge request +if [ -z "$CI_MERGE_REQUEST_PROJECT_PATH" ]; then + if [ "$(git ls-remote --exit-code --heads origin ${TARGET_BRANCH}-external-fixes)" ]; then + git pull origin ${TARGET_BRANCH}-external-fixes + fi +fi + +for opt in $ENABLE_KCONFIGS; do + echo CONFIG_$opt=y >> drivers/gpu/drm/ci/${KERNEL_ARCH}.config +done +for opt in $DISABLE_KCONFIGS; do + echo CONFIG_$opt=n >> drivers/gpu/drm/ci/${KERNEL_ARCH}.config +done + +if [[ -n "${MERGE_FRAGMENT}" ]]; then + ./scripts/kconfig/merge_config.sh ${DEFCONFIG} drivers/gpu/drm/ci/${MERGE_FRAGMENT} +else + make `basename ${DEFCONFIG}` +fi + +make ${KERNEL_IMAGE_NAME} + +mkdir -p /lava-files/ +for image in ${KERNEL_IMAGE_NAME}; do + cp arch/${KERNEL_ARCH}/boot/${image} /lava-files/. +done + +if [[ -n ${DEVICE_TREES} ]]; then + make dtbs + cp ${DEVICE_TREES} /lava-files/. +fi + +make modules +mkdir -p install/modules/ +INSTALL_MOD_PATH=install/modules/ make modules_install + +if [[ ${DEBIAN_ARCH} = "arm64" ]]; then + make Image.lzma + mkimage \ + -f auto \ + -A arm \ + -O linux \ + -d arch/arm64/boot/Image.lzma \ + -C lzma\ + -b arch/arm64/boot/dts/qcom/sdm845-cheza-r3.dtb \ + /lava-files/cheza-kernel + KERNEL_IMAGE_NAME+=" cheza-kernel" + + # Make a gzipped copy of the Image for db410c. + gzip -k /lava-files/Image + KERNEL_IMAGE_NAME+=" Image.gz" +fi + +# Pass needed files to the test stage +mkdir -p install +cp -rfv .gitlab-ci/* install/. +cp -rfv install/common install/ci-common +cp -rfv drivers/gpu/drm/ci/* install/. + +. .gitlab-ci/container/container_post_build.sh + +if [[ "$UPLOAD_TO_MINIO" = "1" ]]; then + xz -7 -c -T${FDO_CI_CONCURRENT:-4} vmlinux > /lava-files/vmlinux.xz + FILES_TO_UPLOAD="$KERNEL_IMAGE_NAME vmlinux.xz" + + if [[ -n $DEVICE_TREES ]]; then + FILES_TO_UPLOAD="$FILES_TO_UPLOAD $(basename -a $DEVICE_TREES)" + fi + + for f in $FILES_TO_UPLOAD; do + ci-fairy s3cp --token-file "${CI_JOB_JWT_FILE}" /lava-files/$f \ + https://${PIPELINE_ARTIFACTS_BASE}/${DEBIAN_ARCH}/$f + done + + S3_ARTIFACT_NAME="kernel-files.tar.zst" + tar --zstd -cf $S3_ARTIFACT_NAME install + ci-fairy s3cp --token-file "${CI_JOB_JWT_FILE}" ${S3_ARTIFACT_NAME} https://${PIPELINE_ARTIFACTS_BASE}/${DEBIAN_ARCH}/${S3_ARTIFACT_NAME} + + echo "Download vmlinux.xz from https://${PIPELINE_ARTIFACTS_BASE}/${DEBIAN_ARCH}/vmlinux.xz" +fi + +mkdir -p artifacts/install/lib +mv install/* artifacts/install/. +rm -rf artifacts/install/modules +ln -s common artifacts/install/ci-common + +for image in ${KERNEL_IMAGE_NAME}; do + cp /lava-files/$image artifacts/install/. +done + +tar -C artifacts -cf artifacts/install.tar install +rm -rf artifacts/install diff --git a/drivers/gpu/drm/ci/build.yml b/drivers/gpu/drm/ci/build.yml new file mode 100644 index 0000000000000000000000000000000000000000..e6503f1c5927bbbf187f2d59624ad7f5dbd04d52 --- /dev/null +++ b/drivers/gpu/drm/ci/build.yml @@ -0,0 +1,110 @@ +.build: + extends: + - .build-rules + stage: build + artifacts: + paths: + - artifacts + script: + - FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash drivers/gpu/drm/ci/build.sh + +.build:arm32: + extends: + - .build + - .use-debian/arm64_build + tags: + - aarch64 + variables: + DEFCONFIG: "arch/arm/configs/multi_v7_defconfig" + KERNEL_IMAGE_NAME: "zImage" + KERNEL_ARCH: "arm" + +.build:arm64: + extends: + - .build + - .use-debian/arm64_build + tags: + - aarch64 + variables: + DEFCONFIG: "arch/arm64/configs/defconfig" + KERNEL_IMAGE_NAME: "Image" + KERNEL_ARCH: "arm64" + +.build:x86_64: + extends: + - .build + - .use-debian/x86_64_build + variables: + DEFCONFIG: "arch/x86/configs/x86_64_defconfig" + KERNEL_IMAGE_NAME: "bzImage" + KERNEL_ARCH: "x86_64" + + +# Build IGT for testing on devices + +igt:arm32: + extends: .build:arm32 + script: + - FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash drivers/gpu/drm/ci/build-igt.sh + +igt:arm64: + extends: .build:arm64 + script: + - FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash drivers/gpu/drm/ci/build-igt.sh + +igt:x86_64: + extends: .build:x86_64 + script: + - FDO_CI_CONCURRENT=${FDO_CI_CONCURRENT} bash drivers/gpu/drm/ci/build-igt.sh + +# Build kernels for testing on devices + +testing:arm32: + extends: .build:arm32 + variables: + # Would be good to have DEBUG_KMEMLEAK, but it doesn't work well with any of + # PROVE_LOCKING and KASAN as of 5.17. + # + # db410c and db820c don't boot with KASAN_INLINE, probably due to the kernel + # becoming too big for their bootloaders. + ENABLE_KCONFIGS: "PROVE_LOCKING DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT" + UPLOAD_TO_MINIO: 1 + MERGE_FRAGMENT: arm.config + +testing:arm64: + extends: .build:arm64 + variables: + # Would be good to have DEBUG_KMEMLEAK, but it doesn't work well with any of + # PROVE_LOCKING and KASAN as of 5.17. + # + # db410c and db820c don't boot with KASAN_INLINE, probably due to the kernel + # becoming too big for their bootloaders. + ENABLE_KCONFIGS: "PROVE_LOCKING DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT" + UPLOAD_TO_MINIO: 1 + MERGE_FRAGMENT: arm64.config + +testing:x86_64: + extends: .build:x86_64 + variables: + # Would be good to have DEBUG_KMEMLEAK, but it doesn't work well with any of + # PROVE_LOCKING and KASAN as of 5.17. + # + # db410c and db820c don't boot with KASAN_INLINE, probably due to the kernel + # becoming too big for their bootloaders. + ENABLE_KCONFIGS: "PROVE_LOCKING DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT" + UPLOAD_TO_MINIO: 1 + MERGE_FRAGMENT: x86_64.config + + +# Jobs for build-testing different configurations + +build:arm32: + extends: .build:arm32 + +build-nodebugfs:arm64: + extends: .build:arm64 + variables: + DISABLE_KCONFIGS: "DEBUG_FS" + +build:x86_64: + extends: .build:x86_64 diff --git a/drivers/gpu/drm/ci/check-patch.py b/drivers/gpu/drm/ci/check-patch.py new file mode 100755 index 0000000000000000000000000000000000000000..a5f399a20e25903897500e88ecf7b64644bb70c6 --- /dev/null +++ b/drivers/gpu/drm/ci/check-patch.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# check-patch.py: run checkpatch.pl across all commits in a branch +# +# Based on qemu/.gitlab-ci.d/check-patch.py +# +# Copyright (C) 2020 Red Hat, Inc. +# Copyright (C) 2022 Collabora Ltd. + +import os +import os.path +import sys +import subprocess + +repourl = "https://gitlab.freedesktop.org/%s.git" % os.environ["CI_MERGE_REQUEST_PROJECT_PATH"] + +# GitLab CI environment does not give us any direct info about the +# base for the user's branch. We thus need to figure out a common +# ancestor between the user's branch and current git master. +os.environ["GIT_DEPTH"] = "1000" +subprocess.call(["git", "remote", "remove", "check-patch"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) +subprocess.check_call(["git", "remote", "add", "check-patch", repourl]) +subprocess.check_call(["git", "fetch", "check-patch", os.environ["CI_MERGE_REQUEST_TARGET_BRANCH_NAME"]], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + +ancestor = subprocess.check_output(["git", "merge-base", + "check-patch/%s" % os.environ["CI_MERGE_REQUEST_TARGET_BRANCH_NAME"], "HEAD"], + universal_newlines=True) + +ancestor = ancestor.strip() + +log = subprocess.check_output(["git", "log", "--format=%H %s", + ancestor + "..."], + universal_newlines=True) + +subprocess.check_call(["git", "remote", "rm", "check-patch"]) + +if log == "": + print("\nNo commits since %s, skipping checks\n" % ancestor) + sys.exit(0) + +errors = False + +print("\nChecking all commits since %s...\n" % ancestor, flush=True) + +ret = subprocess.run(["scripts/checkpatch.pl", + "--terse", + "--types", os.environ["CHECKPATCH_TYPES"], + "--git", ancestor + "..."]) + +if ret.returncode != 0: + print(" ❌ FAIL one or more commits failed scripts/checkpatch.pl") + sys.exit(1) + +sys.exit(0) diff --git a/drivers/gpu/drm/ci/container.yml b/drivers/gpu/drm/ci/container.yml new file mode 100644 index 0000000000000000000000000000000000000000..9764e7921a4fe3e33693dce419ee69d149ee4ed8 --- /dev/null +++ b/drivers/gpu/drm/ci/container.yml @@ -0,0 +1,65 @@ +.container: + variables: + CI_REPOSITORY_URL: ${DRM_CI_PROJECT_URL}.git # So ci-templates clones drm-ci instead of the repo to test + CI_COMMIT_SHA: ${DRM_CI_COMMIT_SHA} + +debian/x86_64_build-base: + variables: + EXTRA_LOCAL_PACKAGES: "libcairo-dev libdw-dev libjson-c-dev libkmod2 libkmod-dev libpciaccess-dev libproc2-dev libudev-dev libunwind-dev python3-docutils bc python3-ply libssl-dev bc" + +debian/x86_64_test-gl: + variables: + EXTRA_LOCAL_PACKAGES: "jq libasound2 libcairo2 libdw1 libglib2.0-0 libjson-c5 libkmod-dev libkmod2 libgles2 libproc2-dev" + +debian/arm64_build: + variables: + EXTRA_LOCAL_PACKAGES: "libcairo-dev libdw-dev libjson-c-dev libproc2-dev libkmod2 libkmod-dev libpciaccess-dev libudev-dev libunwind-dev python3-docutils libssl-dev crossbuild-essential-armhf libkmod-dev:armhf libproc2-dev:armhf libunwind-dev:armhf libdw-dev:armhf libpixman-1-dev:armhf libcairo-dev:armhf libudev-dev:armhf libjson-c-dev:armhf" + +.kernel+rootfs: + variables: + EXTRA_LOCAL_PACKAGES: "jq libasound2 libcairo2 libdw1 libglib2.0-0 libjson-c5" + +# Disable container jobs that we won't use +alpine/x86_64_build: + rules: + - when: never + +debian/x86_64_test-vk: + rules: + - when: never + +fedora/x86_64_build: + rules: + - when: never + +debian/android_build: + rules: + - when: never + +debian/x86_64_test-android: + rules: + - when: never + +windows_build_vs2019: + rules: + - when: never + +windows_test_vs2019: + rules: + - when: never + +.debian/x86_64_build-mingw: + rules: + - when: never + +rustfmt: + rules: + - when: never + +windows_vs2019: + rules: + - when: never + +clang-format: + rules: + - when: never \ No newline at end of file diff --git a/drivers/gpu/drm/ci/gitlab-ci.yml b/drivers/gpu/drm/ci/gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..2c4df53f5dfe3a5878af6ec93a84a9ff2770c402 --- /dev/null +++ b/drivers/gpu/drm/ci/gitlab-ci.yml @@ -0,0 +1,251 @@ +variables: + DRM_CI_PROJECT_PATH: &drm-ci-project-path mesa/mesa + DRM_CI_COMMIT_SHA: &drm-ci-commit-sha 0dc961645c4f0241f8512cb0ec3ad59635842072 + + UPSTREAM_REPO: git://anongit.freedesktop.org/drm/drm + TARGET_BRANCH: drm-next + + IGT_VERSION: 471bfababd070e1dac0ebb87470ac4f2ae85e663 + + DEQP_RUNNER_GIT_URL: https://gitlab.freedesktop.org/anholt/deqp-runner.git + DEQP_RUNNER_GIT_TAG: v0.15.0 + + FDO_UPSTREAM_REPO: helen.fornazier/linux # The repo where the git-archive daily runs + MESA_TEMPLATES_COMMIT: &ci-templates-commit d5aa3941aa03c2f716595116354fb81eb8012acb + DRM_CI_PROJECT_URL: https://gitlab.freedesktop.org/${DRM_CI_PROJECT_PATH} + CI_PRE_CLONE_SCRIPT: |- + set -o xtrace + curl -L --retry 4 -f --retry-all-errors --retry-delay 60 -s ${DRM_CI_PROJECT_URL}/-/raw/${DRM_CI_COMMIT_SHA}/.gitlab-ci/download-git-cache.sh -o download-git-cache.sh + bash download-git-cache.sh + rm download-git-cache.sh + set +o xtrace + S3_HOST: s3.freedesktop.org + # per-pipeline artifact storage on MinIO + PIPELINE_ARTIFACTS_BASE: ${S3_HOST}/artifacts/${CI_PROJECT_PATH}/${CI_PIPELINE_ID} + # per-job artifact storage on MinIO + JOB_ARTIFACTS_BASE: ${PIPELINE_ARTIFACTS_BASE}/${CI_JOB_ID} + + LAVA_JOB_PRIORITY: 30 + +default: + before_script: + - export SCRIPTS_DIR=$(mktemp -d) + - curl -L -s --retry 4 -f --retry-all-errors --retry-delay 60 -O --output-dir "${SCRIPTS_DIR}" "${DRM_CI_PROJECT_URL}/-/raw/${DRM_CI_COMMIT_SHA}/.gitlab-ci/setup-test-env.sh" + - source ${SCRIPTS_DIR}/setup-test-env.sh + - echo -e "\e[0Ksection_start:$(date +%s):unset_env_vars_section[collapsed=true]\r\e[0KUnsetting vulnerable environment variables" + - export CI_JOB_JWT_FILE="${CI_JOB_JWT_FILE:-$(mktemp)}" + - echo -n "${CI_JOB_JWT}" > "${CI_JOB_JWT_FILE}" + - unset CI_JOB_JWT + - echo -e "\e[0Ksection_end:$(date +%s):unset_env_vars_section\r\e[0K" + + - echo -e "\e[0Ksection_start:$(date +%s):drm_ci_download_section[collapsed=true]\r\e[0KDownloading mesa from $DRM_CI_PROJECT_URL/-/archive/$DRM_CI_COMMIT_SHA/mesa-$DRM_CI_COMMIT_SHA.tar.gz" + - cd $CI_PROJECT_DIR + - curl --output - $DRM_CI_PROJECT_URL/-/archive/$DRM_CI_COMMIT_SHA/mesa-$DRM_CI_COMMIT_SHA.tar.gz | tar -xz + - mv mesa-$DRM_CI_COMMIT_SHA/.gitlab-ci* . + - rm -rf mesa-$DRM_CI_COMMIT_SHA/ + - echo -e "\e[0Ksection_end:$(date +%s):drm_ci_download_section\r\e[0K" + + after_script: + - > + set +x + + test -e "${CI_JOB_JWT_FILE}" && + export CI_JOB_JWT="$(<${CI_JOB_JWT_FILE})" && + rm "${CI_JOB_JWT_FILE}" + + # Retry when job fails. + retry: + max: 1 + # Ignore runner_unsupported, stale_schedule, archived_failure, or + # unmet_prerequisites + when: + - api_failure + - runner_system_failure + - script_failure + - job_execution_timeout + - scheduler_failure + - data_integrity_failure + - unknown_failure + +include: + - project: 'freedesktop/ci-templates' + ref: 16bc29078de5e0a067ff84a1a199a3760d3b3811 + file: + - '/templates/ci-fairy.yml' + - project: 'freedesktop/ci-templates' + ref: *ci-templates-commit + file: + - '/templates/alpine.yml' + - '/templates/debian.yml' + - '/templates/fedora.yml' + - project: *drm-ci-project-path + ref: *drm-ci-commit-sha + file: + - '/.gitlab-ci/farm-rules.yml' + - '/.gitlab-ci/test-source-dep.yml' + - '/.gitlab-ci/container/gitlab-ci.yml' + - '/.gitlab-ci/test/gitlab-ci.yml' + - '/.gitlab-ci/lava/lava-gitlab-ci.yml' + - drivers/gpu/drm/ci/image-tags.yml + - drivers/gpu/drm/ci/container.yml + - drivers/gpu/drm/ci/static-checks.yml + - drivers/gpu/drm/ci/build.yml + - drivers/gpu/drm/ci/test.yml + - 'https://gitlab.freedesktop.org/gfx-ci/lab-status/-/raw/main/lab-status.yml' + + +stages: + - sanity + - container + - git-archive + - build + - amdgpu + - i915 + - mediatek + - meson + - msm + - rockchip + - virtio-gpu + - lint + +# YAML anchors for rule conditions +# -------------------------------- +.rules-anchors: + rules: + # Pipeline for forked project branch + - if: &is-forked-branch '$CI_COMMIT_BRANCH && $CI_PROJECT_NAMESPACE != "mesa"' + when: manual + # Forked project branch / pre-merge pipeline not for Marge bot + - if: &is-forked-branch-or-pre-merge-not-for-marge '$CI_PROJECT_NAMESPACE != "mesa" || ($GITLAB_USER_LOGIN != "marge-bot" && $CI_PIPELINE_SOURCE == "merge_request_event")' + when: manual + # Pipeline runs for the main branch of the upstream Mesa project + - if: &is-mesa-main '$CI_PROJECT_NAMESPACE == "mesa" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH && $CI_COMMIT_BRANCH' + when: always + # Post-merge pipeline + - if: &is-post-merge '$CI_PROJECT_NAMESPACE == "mesa" && $CI_COMMIT_BRANCH' + when: on_success + # Post-merge pipeline, not for Marge Bot + - if: &is-post-merge-not-for-marge '$CI_PROJECT_NAMESPACE == "mesa" && $GITLAB_USER_LOGIN != "marge-bot" && $CI_COMMIT_BRANCH' + when: on_success + # Pre-merge pipeline + - if: &is-pre-merge '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: on_success + # Pre-merge pipeline for Marge Bot + - if: &is-pre-merge-for-marge '$GITLAB_USER_LOGIN == "marge-bot" && $CI_PIPELINE_SOURCE == "merge_request_event"' + when: on_success + +# Rule to filter for only scheduled pipelines. +.scheduled_pipeline-rules: + rules: + - if: &is-scheduled-pipeline '$CI_PIPELINE_SOURCE == "schedule"' + when: on_success + +# Generic rule to not run the job during scheduled pipelines. Jobs that aren't +# something like a nightly run should include this rule. +.no_scheduled_pipelines-rules: + rules: + - if: *is-scheduled-pipeline + when: never + +# When to automatically run the CI for build jobs +.build-rules: + rules: + - !reference [.no_scheduled_pipelines-rules, rules] + # Run automatically once all dependency jobs have passed + - when: on_success + + +.ci-deqp-artifacts: + artifacts: + name: "mesa_${CI_JOB_NAME}" + when: always + untracked: false + paths: + # Watch out! Artifacts are relative to the build dir. + # https://gitlab.com/gitlab-org/gitlab-ce/commit/8788fb925706cad594adf6917a6c5f6587dd1521 + - artifacts + - _build/meson-logs/*.txt + - _build/meson-logs/strace + + +.container-rules: + rules: + - !reference [.no_scheduled_pipelines-rules, rules] + # Run pipeline by default in the main project if any CI pipeline + # configuration files were changed, to ensure docker images are up to date + - if: *is-post-merge + changes: + - drivers/gpu/drm/ci/**/* + when: on_success + # Run pipeline by default if it was triggered by Marge Bot, is for a + # merge request, and any files affecting the pipeline were changed + - if: *is-pre-merge-for-marge + when: on_success + # Run pipeline by default in the main project if it was not triggered by + # Marge Bot, and any files affecting the pipeline were changed + - if: *is-post-merge-not-for-marge + when: on_success + # Allow triggering jobs manually in other cases + - when: manual + + + +# Git archive + +make git archive: + extends: + - .fdo.ci-fairy + stage: git-archive + rules: + - !reference [.scheduled_pipeline-rules, rules] + # ensure we are running on packet + tags: + - packet.net + script: + # Remove drm-ci files we just added + - rm -rf .gitlab-ci.* + + # Compactify the .git directory + - git gc --aggressive + # compress the current folder + - tar -cvzf ../$CI_PROJECT_NAME.tar.gz . + + # login with the JWT token file + - ci-fairy s3cp --token-file "${CI_JOB_JWT_FILE}" ../$CI_PROJECT_NAME.tar.gz https://$S3_HOST/git-cache/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/$CI_PROJECT_NAME.tar.gz + + +# Sanity checks of MR settings and commit logs +sanity: + extends: + - .fdo.ci-fairy + stage: sanity + rules: + - if: *is-pre-merge + when: on_success + # Other cases default to never + variables: + GIT_STRATEGY: none + script: + # ci-fairy check-commits --junit-xml=check-commits.xml + - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request.xml + artifacts: + when: on_failure + reports: + junit: check-*.xml + +# Rules for tests that should not block merging, but should be available to +# optionally run with the "play" button in the UI in pre-merge non-marge +# pipelines. This should appear in "extends:" after any includes of +# test-source-dep.yml rules, so that these rules replace those. +.test-manual-mr: + rules: + - !reference [.no_scheduled_pipelines-rules, rules] + - if: *is-forked-branch-or-pre-merge-not-for-marge + when: manual + variables: + JOB_TIMEOUT: 80 + + +# Jobs that need to pass before spending hardware resources on further testing +.required-for-hardware-jobs: + needs: [] \ No newline at end of file diff --git a/drivers/gpu/drm/ci/igt_runner.sh b/drivers/gpu/drm/ci/igt_runner.sh new file mode 100755 index 0000000000000000000000000000000000000000..2bb75916506392a63d1857d23202cf4cc7cd37b7 --- /dev/null +++ b/drivers/gpu/drm/ci/igt_runner.sh @@ -0,0 +1,77 @@ +#!/bin/sh +# SPDX-License-Identifier: MIT + +set -ex + +export IGT_FORCE_DRIVER=${DRIVER_NAME} +export PATH=$PATH:/igt/bin/ +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/igt/lib/aarch64-linux-gnu/:/igt/lib/x86_64-linux-gnu:/igt/lib:/igt/lib64 + +# Uncomment the below to debug problems with driver probing +: ' +ls -l /dev/dri/ +cat /sys/kernel/debug/devices_deferred +cat /sys/kernel/debug/device_component/* +' + +# Dump drm state to confirm that kernel was able to find a connected display: +# TODO this path might not exist for all drivers.. maybe run modetest instead? +set +e +cat /sys/kernel/debug/dri/*/state +set -e + +# Cannot use HWCI_KERNEL_MODULES as at that point we don't have the module in /lib +if [ "$IGT_FORCE_DRIVER" = "amdgpu" ]; then + mv /install/modules/lib/modules/* /lib/modules/. + modprobe amdgpu +fi + +if [ -e "/install/xfails/$DRIVER_NAME-$GPU_VERSION-skips.txt" ]; then + IGT_SKIPS="--skips /install/xfails/$DRIVER_NAME-$GPU_VERSION-skips.txt" +fi + +if [ -e "/install/xfails/$DRIVER_NAME-$GPU_VERSION-flakes.txt" ]; then + IGT_FLAKES="--flakes /install/xfails/$DRIVER_NAME-$GPU_VERSION-flakes.txt" +fi + +if [ -e "/install/xfails/$DRIVER_NAME-$GPU_VERSION-fails.txt" ]; then + IGT_FAILS="--baseline /install/xfails/$DRIVER_NAME-$GPU_VERSION-fails.txt" +fi + +if [ "`uname -m`" = "aarch64" ]; then + ARCH="arm64" +elif [ "`uname -m`" = "armv7l" ]; then + ARCH="arm" +else + ARCH="x86_64" +fi + +curl -L --retry 4 -f --retry-all-errors --retry-delay 60 -s ${FDO_HTTP_CACHE_URI:-}$PIPELINE_ARTIFACTS_BASE/$ARCH/igt.tar.gz | tar --zstd -v -x -C / + +set +e +igt-runner \ + run \ + --igt-folder /igt/libexec/igt-gpu-tools \ + --caselist /install/testlist.txt \ + --output /results \ + $IGT_SKIPS \ + $IGT_FLAKES \ + $IGT_FAILS \ + --fraction-start $CI_NODE_INDEX \ + --fraction $CI_NODE_TOTAL \ + --jobs 1 +ret=$? +set -e + +deqp-runner junit \ + --testsuite IGT \ + --results /results/failures.csv \ + --output /results/junit.xml \ + --limit 50 \ + --template "See https://$CI_PROJECT_ROOT_NAMESPACE.pages.freedesktop.org/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/results/{{testcase}}.xml" + +# Store the results also in the simpler format used by the runner in ChromeOS CI +#sed -r 's/(dmesg-warn|pass)/success/g' /results/results.txt > /results/results_simple.txt + +cd $oldpath +exit $ret diff --git a/drivers/gpu/drm/ci/image-tags.yml b/drivers/gpu/drm/ci/image-tags.yml new file mode 100644 index 0000000000000000000000000000000000000000..f051b6c547c531c62af394db6caf02c06a52c531 --- /dev/null +++ b/drivers/gpu/drm/ci/image-tags.yml @@ -0,0 +1,15 @@ +variables: + CONTAINER_TAG: "2023-08-10-mesa-uprev" + DEBIAN_X86_64_BUILD_BASE_IMAGE: "debian/x86_64_build-base" + DEBIAN_BASE_TAG: "${CONTAINER_TAG}" + + DEBIAN_X86_64_BUILD_IMAGE_PATH: "debian/x86_64_build" + DEBIAN_BUILD_TAG: "${CONTAINER_TAG}" + + KERNEL_ROOTFS_TAG: "${CONTAINER_TAG}" + + DEBIAN_X86_64_TEST_BASE_IMAGE: "debian/x86_64_test-base" + DEBIAN_X86_64_TEST_IMAGE_GL_PATH: "debian/x86_64_test-gl" + DEBIAN_X86_64_TEST_GL_TAG: "${CONTAINER_TAG}" + + ALPINE_X86_64_LAVA_SSH_TAG: "${CONTAINER_TAG}" \ No newline at end of file diff --git a/drivers/gpu/drm/ci/lava-submit.sh b/drivers/gpu/drm/ci/lava-submit.sh new file mode 100755 index 0000000000000000000000000000000000000000..0c4456b21b0fc67bdcfd4f45b6e62288bbd8a68e --- /dev/null +++ b/drivers/gpu/drm/ci/lava-submit.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT + +set -e +set -x + +# Try to use the kernel and rootfs built in mainline first, so we're more +# likely to hit cache +if curl -L --retry 4 -f --retry-all-errors --retry-delay 60 -s "https://${BASE_SYSTEM_MAINLINE_HOST_PATH}/done"; then + BASE_SYSTEM_HOST_PATH="${BASE_SYSTEM_MAINLINE_HOST_PATH}" +else + BASE_SYSTEM_HOST_PATH="${BASE_SYSTEM_FORK_HOST_PATH}" +fi + +rm -rf results +mkdir -p results/job-rootfs-overlay/ + +cp artifacts/ci-common/capture-devcoredump.sh results/job-rootfs-overlay/ +cp artifacts/ci-common/init-*.sh results/job-rootfs-overlay/ +cp artifacts/ci-common/intel-gpu-freq.sh results/job-rootfs-overlay/ +cp "$SCRIPTS_DIR"/setup-test-env.sh results/job-rootfs-overlay/ + +# Prepare env vars for upload. +section_start variables "Variables passed through:" +KERNEL_IMAGE_BASE_URL="https://${BASE_SYSTEM_HOST_PATH}" \ + artifacts/ci-common/generate-env.sh | tee results/job-rootfs-overlay/set-job-env-vars.sh +section_end variables + +tar zcf job-rootfs-overlay.tar.gz -C results/job-rootfs-overlay/ . +ci-fairy s3cp --token-file "${CI_JOB_JWT_FILE}" job-rootfs-overlay.tar.gz "https://${JOB_ROOTFS_OVERLAY_PATH}" + +touch results/lava.log +tail -f results/lava.log & + +PYTHONPATH=artifacts/ artifacts/lava/lava_job_submitter.py \ + submit \ + --dump-yaml \ + --pipeline-info "$CI_JOB_NAME: $CI_PIPELINE_URL on $CI_COMMIT_REF_NAME ${CI_NODE_INDEX}/${CI_NODE_TOTAL}" \ + --rootfs-url-prefix "https://${BASE_SYSTEM_HOST_PATH}" \ + --kernel-url-prefix "https://${PIPELINE_ARTIFACTS_BASE}/${ARCH}" \ + --build-url "${FDO_HTTP_CACHE_URI:-}https://${PIPELINE_ARTIFACTS_BASE}/${ARCH}/kernel-files.tar.zst" \ + --job-rootfs-overlay-url "${FDO_HTTP_CACHE_URI:-}https://${JOB_ROOTFS_OVERLAY_PATH}" \ + --job-timeout-min ${JOB_TIMEOUT:-80} \ + --first-stage-init artifacts/ci-common/init-stage1.sh \ + --ci-project-dir "${CI_PROJECT_DIR}" \ + --device-type "${DEVICE_TYPE}" \ + --dtb-filename "${DTB}" \ + --jwt-file "${CI_JOB_JWT_FILE}" \ + --kernel-image-name "${KERNEL_IMAGE_NAME}" \ + --kernel-image-type "${KERNEL_IMAGE_TYPE}" \ + --boot-method "${BOOT_METHOD}" \ + --visibility-group "${VISIBILITY_GROUP}" \ + --lava-tags "${LAVA_TAGS}" \ + --mesa-job-name "$CI_JOB_NAME" \ + --structured-log-file "results/lava_job_detail.json" \ + --ssh-client-image "${LAVA_SSH_CLIENT_IMAGE}" \ + >> results/lava.log diff --git a/drivers/gpu/drm/ci/static-checks.yml b/drivers/gpu/drm/ci/static-checks.yml new file mode 100644 index 0000000000000000000000000000000000000000..13ffa827b7fa3981174e95c0f197d2fe0887e8b5 --- /dev/null +++ b/drivers/gpu/drm/ci/static-checks.yml @@ -0,0 +1,12 @@ +check-patch: + extends: + - .build + - .use-debian/x86_64_build + script: + - drivers/gpu/drm/ci/check-patch.py + variables: + CHECKPATCH_TYPES: "BAD_SIGN_OFF,BAD_STABLE_ADDRESS_STYLE,COMMIT_COMMENT_SYMBOL,COMMIT_MESSAGE,EMAIL_SUBJECT,FROM_SIGN_OFF_MISMATCH,MISSING_SIGN_OFF,NO_AUTHOR_SIGN_OFF,DIFF_IN_COMMIT_MSG,GERRIT_CHANGE_ID,GIT_COMMIT_ID,UNKNOWN_COMMIT_ID,CODE_INDENT,BIT_MACRO,DOS_LINE_ENDINGS" + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: on_success + # Other cases default to never diff --git a/drivers/gpu/drm/ci/test.yml b/drivers/gpu/drm/ci/test.yml new file mode 100644 index 0000000000000000000000000000000000000000..6473cddaa7a962ef8302358f531363aa5e4f69b1 --- /dev/null +++ b/drivers/gpu/drm/ci/test.yml @@ -0,0 +1,335 @@ +.test-rules: + rules: + - if: '$FD_FARM == "offline" && $RUNNER_TAG =~ /^google-freedreno-/' + when: never + - if: '$COLLABORA_FARM == "offline" && $RUNNER_TAG =~ /^mesa-ci-x86-64-lava-/' + when: never + - !reference [.no_scheduled_pipelines-rules, rules] + - when: on_success + +.lava-test: + extends: + - .test-rules + script: + # Note: Build dir (and thus install) may be dirty due to GIT_STRATEGY + - rm -rf install + - tar -xf artifacts/install.tar + - mv install/* artifacts/. + # Override it with our lava-submit.sh script + - ./artifacts/lava-submit.sh + +.lava-igt:arm32: + extends: + - .lava-test:arm32 + variables: + HWCI_TEST_SCRIPT: "/install/igt_runner.sh" + ARCH: "armhf" + dependencies: + - testing:arm32 + needs: + - alpine/x86_64_lava_ssh_client + - kernel+rootfs_arm32 + - debian/x86_64_build + - testing:arm32 + - igt:arm32 + +.lava-igt:arm64: + extends: + - .lava-test:arm64 + variables: + HWCI_TEST_SCRIPT: "/install/igt_runner.sh" + ARCH: "arm64" + dependencies: + - testing:arm64 + needs: + - alpine/x86_64_lava_ssh_client + - kernel+rootfs_arm64 + - debian/x86_64_build + - testing:arm64 + - igt:arm64 + +.lava-igt:x86_64: + extends: + - .lava-test:x86_64 + variables: + HWCI_TEST_SCRIPT: "/install/igt_runner.sh" + ARCH: "x86_64" + dependencies: + - testing:x86_64 + needs: + - alpine/x86_64_lava_ssh_client + - kernel+rootfs_x86_64 + - debian/x86_64_build + - testing:x86_64 + - igt:x86_64 + +.baremetal-igt-arm64: + extends: + - .baremetal-test-arm64 + - .use-debian/arm64_test + - .test-rules + variables: + FDO_CI_CONCURRENT: 10 + HWCI_TEST_SCRIPT: "/install/igt_runner.sh" + S3_ARTIFACT_NAME: "arm64/kernel-files" + BM_KERNEL: https://${PIPELINE_ARTIFACTS_BASE}/arm64/Image.gz + BM_CMDLINE: "ip=dhcp console=ttyMSM0,115200n8 $BM_KERNEL_EXTRA_ARGS root=/dev/nfs rw nfsrootdebug nfsroot=,tcp,nfsvers=4.2 init=/init $BM_KERNELARGS" + needs: + - debian/arm64_test + - job: testing:arm64 + artifacts: false + - igt:arm64 + tags: + - $RUNNER_TAG + +msm:sc7180: + extends: + - .lava-igt:arm64 + stage: msm + parallel: 2 + variables: + DRIVER_NAME: msm + DEVICE_TYPE: sc7180-trogdor-lazor-limozeen + DTB: sc7180-trogdor-lazor-limozeen-nots-r5 + BOOT_METHOD: depthcharge + KERNEL_IMAGE_TYPE: "" + GPU_VERSION: sc7180 + RUNNER_TAG: mesa-ci-x86-64-lava-sc7180-trogdor-lazor-limozeen + +msm:apq8016: + extends: + - .baremetal-igt-arm64 + stage: msm + variables: + DRIVER_NAME: msm + BM_DTB: https://${PIPELINE_ARTIFACTS_BASE}/arm64/apq8016-sbc.dtb + GPU_VERSION: apq8016 + BM_CMDLINE: "ip=dhcp console=ttyMSM0,115200n8 $BM_KERNEL_EXTRA_ARGS root=/dev/nfs rw nfsrootdebug nfsroot=,tcp,nfsvers=4.2 init=/init $BM_KERNELARGS" + RUNNER_TAG: google-freedreno-db410c + script: + - ./install/bare-metal/fastboot.sh + rules: + # TODO: current issue: it is not fiding the NFS root. Fix and remove this rule. + - when: never + +msm:apq8096: + extends: + - .baremetal-igt-arm64 + stage: msm + variables: + DRIVER_NAME: msm + BM_KERNEL_EXTRA_ARGS: maxcpus=2 + BM_DTB: https://${PIPELINE_ARTIFACTS_BASE}/arm64/apq8096-db820c.dtb + GPU_VERSION: apq8096 + RUNNER_TAG: google-freedreno-db820c + script: + - ./install/bare-metal/fastboot.sh + +msm:sdm845: + extends: + - .baremetal-igt-arm64 + stage: msm + parallel: 6 + variables: + DRIVER_NAME: msm + BM_KERNEL: https://${PIPELINE_ARTIFACTS_BASE}/arm64/cheza-kernel + GPU_VERSION: sdm845 + RUNNER_TAG: google-freedreno-cheza + script: + - ./install/bare-metal/cros-servo.sh + +rockchip:rk3288: + extends: + - .lava-igt:arm32 + stage: rockchip + variables: + DRIVER_NAME: rockchip + DEVICE_TYPE: rk3288-veyron-jaq + DTB: ${DEVICE_TYPE} + BOOT_METHOD: depthcharge + KERNEL_IMAGE_TYPE: "zimage" + GPU_VERSION: rk3288 + RUNNER_TAG: mesa-ci-x86-64-lava-rk3288-veyron-jaq + +rockchip:rk3399: + extends: + - .lava-igt:arm64 + stage: rockchip + parallel: 3 + variables: + DRIVER_NAME: rockchip + DEVICE_TYPE: rk3399-gru-kevin + DTB: ${DEVICE_TYPE} + BOOT_METHOD: depthcharge + KERNEL_IMAGE_TYPE: "" + GPU_VERSION: rk3399 + RUNNER_TAG: mesa-ci-x86-64-lava-rk3399-gru-kevin + +.i915: + extends: + - .lava-igt:x86_64 + stage: i915 + variables: + DRIVER_NAME: i915 + DTB: "" + BOOT_METHOD: depthcharge + KERNEL_IMAGE_TYPE: "" + +i915:apl: + extends: + - .i915 + parallel: 12 + variables: + DEVICE_TYPE: asus-C523NA-A20057-coral + GPU_VERSION: apl + RUNNER_TAG: mesa-ci-x86-64-lava-asus-C523NA-A20057-coral + +i915:glk: + extends: + - .i915 + parallel: 5 + variables: + DEVICE_TYPE: hp-x360-12b-ca0010nr-n4020-octopus + GPU_VERSION: glk + RUNNER_TAG: mesa-ci-x86-64-lava-hp-x360-12b-ca0010nr-n4020-octopus + +i915:amly: + extends: + - .i915 + parallel: 8 + variables: + DEVICE_TYPE: asus-C433TA-AJ0005-rammus + GPU_VERSION: amly + RUNNER_TAG: mesa-ci-x86-64-lava-asus-C433TA-AJ0005-rammus + +i915:kbl: + extends: + - .i915 + parallel: 5 + variables: + DEVICE_TYPE: hp-x360-14-G1-sona + GPU_VERSION: kbl + RUNNER_TAG: mesa-ci-x86-64-lava-hp-x360-14-G1-sona + +i915:whl: + extends: + - .i915 + parallel: 8 + variables: + DEVICE_TYPE: dell-latitude-5400-8665U-sarien + GPU_VERSION: whl + RUNNER_TAG: mesa-ci-x86-64-lava-dell-latitude-5400-8665U-sarien + +i915:cml: + extends: + - .i915 + parallel: 6 + variables: + DEVICE_TYPE: asus-C436FA-Flip-hatch + GPU_VERSION: cml + RUNNER_TAG: mesa-ci-x86-64-lava-asus-C436FA-flip-hatch + +i915:tgl: + extends: + - .i915 + parallel: 6 + variables: + DEVICE_TYPE: asus-cx9400-volteer + GPU_VERSION: tgl + RUNNER_TAG: mesa-ci-x86-64-lava-asus-cx9400-volteer + +.amdgpu: + extends: + - .lava-igt:x86_64 + stage: amdgpu + variables: + DRIVER_NAME: amdgpu + DTB: "" + BOOT_METHOD: depthcharge + KERNEL_IMAGE_TYPE: "" + +amdgpu:stoney: + extends: + - .amdgpu + variables: + DEVICE_TYPE: hp-11A-G6-EE-grunt + GPU_VERSION: stoney + RUNNER_TAG: mesa-ci-x86-64-lava-hp-11A-G6-EE-grunt + +.mediatek: + extends: + - .lava-igt:arm64 + stage: mediatek + variables: + DRIVER_NAME: mediatek + DTB: ${DEVICE_TYPE} + BOOT_METHOD: depthcharge + KERNEL_IMAGE_TYPE: "" + +mediatek:mt8173: + extends: + - .mediatek + variables: + DEVICE_TYPE: mt8173-elm-hana + GPU_VERSION: mt8173 + RUNNER_TAG: mesa-ci-x86-64-lava-mt8173-elm-hana + rules: + # TODO: current issue: device is hanging. Fix and remove this rule. + - when: never + +mediatek:mt8183: + extends: + - .mediatek + variables: + DEVICE_TYPE: mt8183-kukui-jacuzzi-juniper-sku16 + GPU_VERSION: mt8183 + RUNNER_TAG: mesa-ci-x86-64-lava-mt8183-kukui-jacuzzi-juniper-sku16 + +# drm-mtk doesn't even probe yet in mainline for mt8192 +.mediatek:mt8192: + extends: + - .mediatek + variables: + DEVICE_TYPE: mt8192-asurada-spherion-r0 + GPU_VERSION: mt8192 + RUNNER_TAG: mesa-ci-x86-64-lava-mt8192-asurada-spherion-r0 + +.meson: + extends: + - .lava-igt:arm64 + stage: meson + variables: + DRIVER_NAME: meson + DTB: ${DEVICE_TYPE} + BOOT_METHOD: u-boot + KERNEL_IMAGE_TYPE: "image" + +meson:g12b: + extends: + - .meson + variables: + DEVICE_TYPE: meson-g12b-a311d-khadas-vim3 + GPU_VERSION: g12b + RUNNER_TAG: mesa-ci-x86-64-lava-meson-g12b-a311d-khadas-vim3 + +virtio_gpu:none: + stage: virtio-gpu + variables: + CROSVM_GALLIUM_DRIVER: llvmpipe + DRIVER_NAME: virtio_gpu + GPU_VERSION: none + extends: + - .test-gl + tags: + - kvm + script: + - ln -sf $CI_PROJECT_DIR/install /install + - mv install/bzImage /lava-files/bzImage + - install/crosvm-runner.sh install/igt_runner.sh + needs: + - debian/x86_64_test-gl + - testing:x86_64 + - igt:x86_64 + rules: + # TODO: current issue: malloc(): corrupted top size. Fix and remove this rule. + - when: never \ No newline at end of file diff --git a/drivers/gpu/drm/ci/testlist.txt b/drivers/gpu/drm/ci/testlist.txt new file mode 100644 index 0000000000000000000000000000000000000000..f82cd90372f4a4e6f82bd18f5e64a42e4585bad4 --- /dev/null +++ b/drivers/gpu/drm/ci/testlist.txt @@ -0,0 +1,2912 @@ +core_auth@getclient-simple +core_auth@getclient-master-drop +core_auth@basic-auth +core_auth@many-magics +core_getclient +core_getstats +core_getversion +core_setmaster_vs_auth +drm_read@invalid-buffer +drm_read@fault-buffer +drm_read@empty-block +drm_read@empty-nonblock +drm_read@short-buffer-block +drm_read@short-buffer-nonblock +drm_read@short-buffer-wakeup +gem_eio@throttle +gem_eio@create +gem_eio@create-ext +gem_eio@context-create +gem_eio@execbuf +gem_eio@banned +gem_eio@suspend +gem_eio@hibernate +gem_eio@in-flight-external +gem_eio@in-flight-suspend +gem_eio@reset-stress +gem_eio@unwedge-stress +gem_eio@wait-immediate +gem_eio@wait-wedge-immediate +gem_eio@in-flight-immediate +gem_eio@in-flight-contexts-immediate +gem_eio@in-flight-internal-immediate +gem_eio@wait-1us +gem_eio@wait-wedge-1us +gem_eio@in-flight-1us +gem_eio@in-flight-contexts-1us +gem_eio@in-flight-internal-1us +gem_eio@wait-10ms +gem_eio@wait-wedge-10ms +gem_eio@in-flight-10ms +gem_eio@in-flight-contexts-10ms +gem_eio@in-flight-internal-10ms +gem_eio@kms +kms_3d +kms_addfb_basic@unused-handle +kms_addfb_basic@unused-pitches +kms_addfb_basic@unused-offsets +kms_addfb_basic@unused-modifier +kms_addfb_basic@clobberred-modifier +kms_addfb_basic@invalid-smem-bo-on-discrete +kms_addfb_basic@legacy-format +kms_addfb_basic@no-handle +kms_addfb_basic@basic +kms_addfb_basic@bad-pitch-0 +kms_addfb_basic@bad-pitch-32 +kms_addfb_basic@bad-pitch-63 +kms_addfb_basic@bad-pitch-128 +kms_addfb_basic@bad-pitch-256 +kms_addfb_basic@bad-pitch-1024 +kms_addfb_basic@bad-pitch-999 +kms_addfb_basic@bad-pitch-65536 +kms_addfb_basic@invalid-get-prop-any +kms_addfb_basic@invalid-get-prop +kms_addfb_basic@invalid-set-prop-any +kms_addfb_basic@invalid-set-prop +kms_addfb_basic@master-rmfb +kms_addfb_basic@addfb25-modifier-no-flag +kms_addfb_basic@addfb25-bad-modifier +kms_addfb_basic@addfb25-x-tiled-mismatch-legacy +kms_addfb_basic@addfb25-x-tiled-legacy +kms_addfb_basic@addfb25-framebuffer-vs-set-tiling +kms_addfb_basic@basic-x-tiled-legacy +kms_addfb_basic@framebuffer-vs-set-tiling +kms_addfb_basic@tile-pitch-mismatch +kms_addfb_basic@basic-y-tiled-legacy +kms_addfb_basic@size-max +kms_addfb_basic@too-wide +kms_addfb_basic@too-high +kms_addfb_basic@bo-too-small +kms_addfb_basic@small-bo +kms_addfb_basic@bo-too-small-due-to-tiling +kms_addfb_basic@addfb25-y-tiled-legacy +kms_addfb_basic@addfb25-yf-tiled-legacy +kms_addfb_basic@addfb25-y-tiled-small-legacy +kms_addfb_basic@addfb25-4-tiled +kms_async_flips@async-flip-with-page-flip-events +kms_async_flips@alternate-sync-async-flip +kms_async_flips@test-time-stamp +kms_async_flips@test-cursor +kms_async_flips@invalid-async-flip +kms_async_flips@crc +kms_atomic@plane-overlay-legacy +kms_atomic@plane-primary-legacy +kms_atomic@plane-primary-overlay-mutable-zpos +kms_atomic@plane-immutable-zpos +kms_atomic@test-only +kms_atomic@plane-cursor-legacy +kms_atomic@plane-invalid-params +kms_atomic@plane-invalid-params-fence +kms_atomic@crtc-invalid-params +kms_atomic@crtc-invalid-params-fence +kms_atomic@atomic-invalid-params +kms_atomic@atomic_plane_damage +kms_atomic_interruptible@legacy-setmode +kms_atomic_interruptible@atomic-setmode +kms_atomic_interruptible@legacy-dpms +kms_atomic_interruptible@legacy-pageflip +kms_atomic_interruptible@legacy-cursor +kms_atomic_interruptible@universal-setplane-primary +kms_atomic_interruptible@universal-setplane-cursor +kms_atomic_transition@plane-primary-toggle-with-vblank-wait +kms_atomic_transition@plane-all-transition +kms_atomic_transition@plane-all-transition-fencing +kms_atomic_transition@plane-all-transition-nonblocking +kms_atomic_transition@plane-all-transition-nonblocking-fencing +kms_atomic_transition@plane-use-after-nonblocking-unbind +kms_atomic_transition@plane-use-after-nonblocking-unbind-fencing +kms_atomic_transition@plane-all-modeset-transition +kms_atomic_transition@plane-all-modeset-transition-fencing +kms_atomic_transition@plane-all-modeset-transition-internal-panels +kms_atomic_transition@plane-all-modeset-transition-fencing-internal-panels +kms_atomic_transition@plane-toggle-modeset-transition +kms_atomic_transition@modeset-transition +kms_atomic_transition@modeset-transition-fencing +kms_atomic_transition@modeset-transition-nonblocking +kms_atomic_transition@modeset-transition-nonblocking-fencing +kms_big_fb@x-tiled-addfb-size-overflow +kms_big_fb@y-tiled-addfb-size-overflow +kms_big_fb@yf-tiled-addfb-size-overflow +kms_big_fb@4-tiled-addfb-size-overflow +kms_big_fb@x-tiled-addfb-size-offset-overflow +kms_big_fb@y-tiled-addfb-size-offset-overflow +kms_big_fb@yf-tiled-addfb-size-offset-overflow +kms_big_fb@4-tiled-addfb-size-offset-overflow +kms_big_fb@linear-addfb +kms_big_fb@x-tiled-addfb +kms_big_fb@y-tiled-addfb +kms_big_fb@yf-tiled-addfb +kms_big_fb@4-tiled-addfb +kms_big_fb@linear-8bpp-rotate-0 +kms_big_fb@linear-8bpp-rotate-90 +kms_big_fb@linear-8bpp-rotate-180 +kms_big_fb@linear-8bpp-rotate-270 +kms_big_fb@linear-16bpp-rotate-0 +kms_big_fb@linear-16bpp-rotate-90 +kms_big_fb@linear-16bpp-rotate-180 +kms_big_fb@linear-16bpp-rotate-270 +kms_big_fb@linear-32bpp-rotate-0 +kms_big_fb@linear-32bpp-rotate-90 +kms_big_fb@linear-32bpp-rotate-180 +kms_big_fb@linear-32bpp-rotate-270 +kms_big_fb@linear-64bpp-rotate-0 +kms_big_fb@linear-64bpp-rotate-90 +kms_big_fb@linear-64bpp-rotate-180 +kms_big_fb@linear-64bpp-rotate-270 +kms_big_fb@x-tiled-8bpp-rotate-0 +kms_big_fb@x-tiled-8bpp-rotate-90 +kms_big_fb@x-tiled-8bpp-rotate-180 +kms_big_fb@x-tiled-8bpp-rotate-270 +kms_big_fb@x-tiled-16bpp-rotate-0 +kms_big_fb@x-tiled-16bpp-rotate-90 +kms_big_fb@x-tiled-16bpp-rotate-180 +kms_big_fb@x-tiled-16bpp-rotate-270 +kms_big_fb@x-tiled-32bpp-rotate-0 +kms_big_fb@x-tiled-32bpp-rotate-90 +kms_big_fb@x-tiled-32bpp-rotate-180 +kms_big_fb@x-tiled-32bpp-rotate-270 +kms_big_fb@x-tiled-64bpp-rotate-0 +kms_big_fb@x-tiled-64bpp-rotate-90 +kms_big_fb@x-tiled-64bpp-rotate-180 +kms_big_fb@x-tiled-64bpp-rotate-270 +kms_big_fb@y-tiled-8bpp-rotate-0 +kms_big_fb@y-tiled-8bpp-rotate-90 +kms_big_fb@y-tiled-8bpp-rotate-180 +kms_big_fb@y-tiled-8bpp-rotate-270 +kms_big_fb@y-tiled-16bpp-rotate-0 +kms_big_fb@y-tiled-16bpp-rotate-90 +kms_big_fb@y-tiled-16bpp-rotate-180 +kms_big_fb@y-tiled-16bpp-rotate-270 +kms_big_fb@y-tiled-32bpp-rotate-0 +kms_big_fb@y-tiled-32bpp-rotate-90 +kms_big_fb@y-tiled-32bpp-rotate-180 +kms_big_fb@y-tiled-32bpp-rotate-270 +kms_big_fb@y-tiled-64bpp-rotate-0 +kms_big_fb@y-tiled-64bpp-rotate-90 +kms_big_fb@y-tiled-64bpp-rotate-180 +kms_big_fb@y-tiled-64bpp-rotate-270 +kms_big_fb@yf-tiled-8bpp-rotate-0 +kms_big_fb@yf-tiled-8bpp-rotate-90 +kms_big_fb@yf-tiled-8bpp-rotate-180 +kms_big_fb@yf-tiled-8bpp-rotate-270 +kms_big_fb@yf-tiled-16bpp-rotate-0 +kms_big_fb@yf-tiled-16bpp-rotate-90 +kms_big_fb@yf-tiled-16bpp-rotate-180 +kms_big_fb@yf-tiled-16bpp-rotate-270 +kms_big_fb@yf-tiled-32bpp-rotate-0 +kms_big_fb@yf-tiled-32bpp-rotate-90 +kms_big_fb@yf-tiled-32bpp-rotate-180 +kms_big_fb@yf-tiled-32bpp-rotate-270 +kms_big_fb@yf-tiled-64bpp-rotate-0 +kms_big_fb@yf-tiled-64bpp-rotate-90 +kms_big_fb@yf-tiled-64bpp-rotate-180 +kms_big_fb@yf-tiled-64bpp-rotate-270 +kms_big_fb@4-tiled-8bpp-rotate-0 +kms_big_fb@4-tiled-8bpp-rotate-90 +kms_big_fb@4-tiled-8bpp-rotate-180 +kms_big_fb@4-tiled-8bpp-rotate-270 +kms_big_fb@4-tiled-16bpp-rotate-0 +kms_big_fb@4-tiled-16bpp-rotate-90 +kms_big_fb@4-tiled-16bpp-rotate-180 +kms_big_fb@4-tiled-16bpp-rotate-270 +kms_big_fb@4-tiled-32bpp-rotate-0 +kms_big_fb@4-tiled-32bpp-rotate-90 +kms_big_fb@4-tiled-32bpp-rotate-180 +kms_big_fb@4-tiled-32bpp-rotate-270 +kms_big_fb@4-tiled-64bpp-rotate-0 +kms_big_fb@4-tiled-64bpp-rotate-90 +kms_big_fb@4-tiled-64bpp-rotate-180 +kms_big_fb@4-tiled-64bpp-rotate-270 +kms_big_fb@linear-max-hw-stride-32bpp-rotate-0 +kms_big_fb@linear-max-hw-stride-32bpp-rotate-180 +kms_big_fb@linear-max-hw-stride-64bpp-rotate-0 +kms_big_fb@linear-max-hw-stride-64bpp-rotate-180 +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-0 +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-0-async-flip +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180 +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-async-flip +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0 +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0-async-flip +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180 +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180-async-flip +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-0-hflip +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-0-hflip-async-flip +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip +kms_big_fb@x-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0-hflip +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-0-hflip-async-flip +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180-hflip +kms_big_fb@x-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0 +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0-async-flip +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180 +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-async-flip +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0 +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-async-flip +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-180 +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-180-async-flip +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0-hflip +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-0-hflip-async-flip +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip +kms_big_fb@y-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-hflip +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-hflip-async-flip +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-180-hflip +kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-0 +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-0-async-flip +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-180 +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-180-async-flip +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0 +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0-async-flip +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180 +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-async-flip +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-0-hflip +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-0-hflip-async-flip +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-180-hflip +kms_big_fb@yf-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0-hflip +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-0-hflip-async-flip +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-hflip +kms_big_fb@yf-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-0 +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-0-async-flip +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-180 +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-180-async-flip +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0 +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-async-flip +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-180 +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-180-async-flip +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-0-hflip +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-0-hflip-async-flip +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-180-hflip +kms_big_fb@4-tiled-max-hw-stride-32bpp-rotate-180-hflip-async-flip +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-0-hflip-async-flip +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-180-hflip +kms_big_fb@4-tiled-max-hw-stride-64bpp-rotate-180-hflip-async-flip +kms_big_joiner@basic +kms_big_joiner@invalid-modeset +kms_big_joiner@2x-modeset +kms_busy@basic +kms_busy@basic-hang +kms_busy@extended-pageflip-modeset-hang-oldfb +kms_busy@extended-pageflip-hang-oldfb +kms_busy@extended-pageflip-hang-newfb +kms_busy@extended-modeset-hang-oldfb +kms_busy@extended-modeset-hang-newfb +kms_busy@extended-modeset-hang-oldfb-with-reset +kms_busy@extended-modeset-hang-newfb-with-reset +kms_bw@linear-tiling-1-displays-1920x1080p +kms_bw@linear-tiling-1-displays-2560x1440p +kms_bw@linear-tiling-1-displays-3840x2160p +kms_bw@linear-tiling-2-displays-1920x1080p +kms_bw@linear-tiling-2-displays-2560x1440p +kms_bw@linear-tiling-2-displays-3840x2160p +kms_bw@linear-tiling-3-displays-1920x1080p +kms_bw@linear-tiling-3-displays-2560x1440p +kms_bw@linear-tiling-3-displays-3840x2160p +kms_bw@linear-tiling-4-displays-1920x1080p +kms_bw@linear-tiling-4-displays-2560x1440p +kms_bw@linear-tiling-4-displays-3840x2160p +kms_bw@linear-tiling-5-displays-1920x1080p +kms_bw@linear-tiling-5-displays-2560x1440p +kms_bw@linear-tiling-5-displays-3840x2160p +kms_bw@linear-tiling-6-displays-1920x1080p +kms_bw@linear-tiling-6-displays-2560x1440p +kms_bw@linear-tiling-6-displays-3840x2160p +kms_bw@linear-tiling-7-displays-1920x1080p +kms_bw@linear-tiling-7-displays-2560x1440p +kms_bw@linear-tiling-7-displays-3840x2160p +kms_bw@linear-tiling-8-displays-1920x1080p +kms_bw@linear-tiling-8-displays-2560x1440p +kms_bw@linear-tiling-8-displays-3840x2160p +kms_ccs@pipe-A-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-A-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-A-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-A-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-A-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-A-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-A-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-A-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-A-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-A-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-A-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-A-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-A-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-A-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-A-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-A-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-A-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-A-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-A-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-A-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-A-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-A-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-A-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-A-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-A-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-A-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-A-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-A-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-A-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-A-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-A-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-A-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-B-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-B-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-B-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-B-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-B-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-B-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-B-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-B-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-B-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-B-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-B-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-B-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-B-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-B-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-B-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-B-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-B-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-B-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-B-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-B-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-B-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-B-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-B-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-B-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-B-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-B-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-B-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-B-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-B-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-B-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-B-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-C-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-C-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-C-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-C-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-C-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-C-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-C-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-C-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-C-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-C-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-C-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-C-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-C-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-C-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-C-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-C-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-C-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-C-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-C-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-C-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-C-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-C-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-C-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-C-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-C-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-C-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-C-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-C-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-C-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-C-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-C-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-D-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-D-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-D-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-D-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-D-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-D-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-D-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-D-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-D-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-D-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-D-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-D-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-D-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-D-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-D-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-D-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-D-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-D-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-D-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-D-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-D-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-D-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-D-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-D-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-D-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-D-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-D-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-D-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-D-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-D-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-D-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-E-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-E-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-E-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-E-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-E-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-E-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-E-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-E-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-E-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-E-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-E-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-E-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-E-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-E-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-E-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-E-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-E-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-E-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-E-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-E-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-E-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-E-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-E-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-E-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-E-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-E-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-E-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-E-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-E-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-E-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-E-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-F-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-F-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-F-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-F-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-F-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-F-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-F-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-F-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-F-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-F-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-F-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-F-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-F-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-F-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-F-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-F-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-F-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-F-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-F-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-F-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-F-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-F-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-F-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-F-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-F-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-F-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-F-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-F-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-F-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-F-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-F-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-G-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-G-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-G-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-G-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-G-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-G-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-G-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-G-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-G-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-G-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-G-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-G-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-G-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-G-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-G-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-G-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-G-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-G-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-G-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-G-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-G-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-G-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-G-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-G-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-G-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-G-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-G-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-G-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-G-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-G-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-G-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-bad-pixel-format-y_tiled_ccs +kms_ccs@pipe-H-bad-pixel-format-yf_tiled_ccs +kms_ccs@pipe-H-bad-pixel-format-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-bad-pixel-format-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-bad-pixel-format-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-bad-pixel-format-4_tiled_dg2_rc_ccs +kms_ccs@pipe-H-bad-pixel-format-4_tiled_dg2_mc_ccs +kms_ccs@pipe-H-bad-pixel-format-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-H-bad-pixel-format-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-bad-pixel-format-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-bad-pixel-format-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-bad-rotation-90-y_tiled_ccs +kms_ccs@pipe-H-bad-rotation-90-yf_tiled_ccs +kms_ccs@pipe-H-bad-rotation-90-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-bad-rotation-90-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-bad-rotation-90-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-bad-rotation-90-4_tiled_dg2_rc_ccs +kms_ccs@pipe-H-bad-rotation-90-4_tiled_dg2_mc_ccs +kms_ccs@pipe-H-bad-rotation-90-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-H-bad-rotation-90-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-bad-rotation-90-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-bad-rotation-90-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-crc-primary-basic-y_tiled_ccs +kms_ccs@pipe-H-crc-primary-basic-yf_tiled_ccs +kms_ccs@pipe-H-crc-primary-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-crc-primary-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-crc-primary-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-crc-primary-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-H-crc-primary-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-H-crc-primary-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-H-crc-primary-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-crc-primary-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-crc-primary-basic-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-crc-primary-rotation-180-y_tiled_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-yf_tiled_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-crc-primary-rotation-180-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-4_tiled_dg2_rc_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-4_tiled_dg2_mc_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-H-crc-primary-rotation-180-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-crc-primary-rotation-180-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-random-ccs-data-y_tiled_ccs +kms_ccs@pipe-H-random-ccs-data-yf_tiled_ccs +kms_ccs@pipe-H-random-ccs-data-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-random-ccs-data-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-random-ccs-data-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-random-ccs-data-4_tiled_dg2_rc_ccs +kms_ccs@pipe-H-random-ccs-data-4_tiled_dg2_mc_ccs +kms_ccs@pipe-H-random-ccs-data-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-H-random-ccs-data-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-random-ccs-data-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-random-ccs-data-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-missing-ccs-buffer-y_tiled_ccs +kms_ccs@pipe-H-missing-ccs-buffer-yf_tiled_ccs +kms_ccs@pipe-H-missing-ccs-buffer-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-missing-ccs-buffer-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-missing-ccs-buffer-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-missing-ccs-buffer-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-missing-ccs-buffer-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-missing-ccs-buffer-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-ccs-on-another-bo-y_tiled_ccs +kms_ccs@pipe-H-ccs-on-another-bo-yf_tiled_ccs +kms_ccs@pipe-H-ccs-on-another-bo-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-ccs-on-another-bo-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-ccs-on-another-bo-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-ccs-on-another-bo-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-ccs-on-another-bo-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-ccs-on-another-bo-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-bad-aux-stride-y_tiled_ccs +kms_ccs@pipe-H-bad-aux-stride-yf_tiled_ccs +kms_ccs@pipe-H-bad-aux-stride-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-bad-aux-stride-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-bad-aux-stride-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-bad-aux-stride-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-bad-aux-stride-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-bad-aux-stride-4_tiled_mtl_rc_ccs_cc +kms_ccs@pipe-H-crc-sprite-planes-basic-y_tiled_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-yf_tiled_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-y_tiled_gen12_rc_ccs_cc +kms_ccs@pipe-H-crc-sprite-planes-basic-y_tiled_gen12_mc_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-4_tiled_dg2_mc_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-4_tiled_dg2_rc_ccs_cc +kms_ccs@pipe-H-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-4_tiled_mtl_mc_ccs +kms_ccs@pipe-H-crc-sprite-planes-basic-4_tiled_mtl_rc_ccs_cc +kms_cdclk@plane-scaling +kms_cdclk@mode-transition +kms_cdclk@mode-transition-all-outputs +kms_color@degamma +kms_color@gamma +kms_color@legacy-gamma +kms_color@legacy-gamma-reset +kms_color@ctm-red-to-blue +kms_color@ctm-green-to-red +kms_color@ctm-blue-to-red +kms_color@ctm-max +kms_color@ctm-negative +kms_color@ctm-0-25 +kms_color@ctm-0-50 +kms_color@ctm-0-75 +kms_color@ctm-signed +kms_color@deep-color +kms_color@invalid-gamma-lut-sizes +kms_color@invalid-degamma-lut-sizes +kms_color@invalid-ctm-matrix-sizes +kms_concurrent@pipe-A +kms_concurrent@pipe-B +kms_concurrent@pipe-C +kms_concurrent@pipe-D +kms_concurrent@pipe-E +kms_concurrent@pipe-F +kms_concurrent@pipe-G +kms_concurrent@pipe-H +kms_content_protection@legacy +kms_content_protection@atomic +kms_content_protection@atomic-dpms +kms_content_protection@LIC +kms_content_protection@type1 +kms_content_protection@mei_interface +kms_content_protection@content_type_change +kms_content_protection@uevent +kms_content_protection@srm +kms_content_protection@dp-mst-type-0 +kms_content_protection@dp-mst-lic-type-0 +kms_content_protection@dp-mst-type-1 +kms_content_protection@dp-mst-lic-type-1 +kms_cursor_crc@cursor-size-change +kms_cursor_crc@cursor-alpha-opaque +kms_cursor_crc@cursor-alpha-transparent +kms_cursor_crc@cursor-dpms +kms_cursor_crc@cursor-suspend +kms_cursor_crc@cursor-onscreen-32x32 +kms_cursor_crc@cursor-offscreen-32x32 +kms_cursor_crc@cursor-sliding-32x32 +kms_cursor_crc@cursor-random-32x32 +kms_cursor_crc@cursor-rapid-movement-32x32 +kms_cursor_crc@cursor-onscreen-32x10 +kms_cursor_crc@cursor-offscreen-32x10 +kms_cursor_crc@cursor-sliding-32x10 +kms_cursor_crc@cursor-random-32x10 +kms_cursor_crc@cursor-rapid-movement-32x10 +kms_cursor_crc@cursor-onscreen-64x64 +kms_cursor_crc@cursor-offscreen-64x64 +kms_cursor_crc@cursor-sliding-64x64 +kms_cursor_crc@cursor-random-64x64 +kms_cursor_crc@cursor-rapid-movement-64x64 +kms_cursor_crc@cursor-onscreen-64x21 +kms_cursor_crc@cursor-offscreen-64x21 +kms_cursor_crc@cursor-sliding-64x21 +kms_cursor_crc@cursor-random-64x21 +kms_cursor_crc@cursor-rapid-movement-64x21 +kms_cursor_crc@cursor-onscreen-128x128 +kms_cursor_crc@cursor-offscreen-128x128 +kms_cursor_crc@cursor-sliding-128x128 +kms_cursor_crc@cursor-random-128x128 +kms_cursor_crc@cursor-rapid-movement-128x128 +kms_cursor_crc@cursor-onscreen-128x42 +kms_cursor_crc@cursor-offscreen-128x42 +kms_cursor_crc@cursor-sliding-128x42 +kms_cursor_crc@cursor-random-128x42 +kms_cursor_crc@cursor-rapid-movement-128x42 +kms_cursor_crc@cursor-onscreen-256x256 +kms_cursor_crc@cursor-offscreen-256x256 +kms_cursor_crc@cursor-sliding-256x256 +kms_cursor_crc@cursor-random-256x256 +kms_cursor_crc@cursor-rapid-movement-256x256 +kms_cursor_crc@cursor-onscreen-256x85 +kms_cursor_crc@cursor-offscreen-256x85 +kms_cursor_crc@cursor-sliding-256x85 +kms_cursor_crc@cursor-random-256x85 +kms_cursor_crc@cursor-rapid-movement-256x85 +kms_cursor_crc@cursor-onscreen-512x512 +kms_cursor_crc@cursor-offscreen-512x512 +kms_cursor_crc@cursor-sliding-512x512 +kms_cursor_crc@cursor-random-512x512 +kms_cursor_crc@cursor-rapid-movement-512x512 +kms_cursor_crc@cursor-onscreen-512x170 +kms_cursor_crc@cursor-offscreen-512x170 +kms_cursor_crc@cursor-sliding-512x170 +kms_cursor_crc@cursor-random-512x170 +kms_cursor_crc@cursor-rapid-movement-512x170 +kms_cursor_crc@cursor-onscreen-max-size +kms_cursor_crc@cursor-offscreen-max-size +kms_cursor_crc@cursor-sliding-max-size +kms_cursor_crc@cursor-random-max-size +kms_cursor_crc@cursor-rapid-movement-max-size +kms_cursor_legacy@single-bo +kms_cursor_legacy@single-move +kms_cursor_legacy@forked-bo +kms_cursor_legacy@forked-move +kms_cursor_legacy@torture-bo +kms_cursor_legacy@torture-move +kms_cursor_legacy@nonblocking-modeset-vs-cursor-atomic +kms_cursor_legacy@long-nonblocking-modeset-vs-cursor-atomic +kms_cursor_legacy@2x-flip-vs-cursor-legacy +kms_cursor_legacy@2x-flip-vs-cursor-atomic +kms_cursor_legacy@2x-long-flip-vs-cursor-legacy +kms_cursor_legacy@2x-long-flip-vs-cursor-atomic +kms_cursor_legacy@2x-nonblocking-modeset-vs-cursor-atomic +kms_cursor_legacy@2x-long-nonblocking-modeset-vs-cursor-atomic +kms_cursor_legacy@2x-cursor-vs-flip-legacy +kms_cursor_legacy@2x-long-cursor-vs-flip-legacy +kms_cursor_legacy@2x-cursor-vs-flip-atomic +kms_cursor_legacy@2x-long-cursor-vs-flip-atomic +kms_cursor_legacy@flip-vs-cursor-crc-legacy +kms_cursor_legacy@flip-vs-cursor-crc-atomic +kms_cursor_legacy@flip-vs-cursor-busy-crc-legacy +kms_cursor_legacy@flip-vs-cursor-busy-crc-atomic +kms_cursor_legacy@basic-flip-before-cursor-legacy +kms_cursor_legacy@basic-busy-flip-before-cursor-legacy +kms_cursor_legacy@basic-flip-after-cursor-legacy +kms_cursor_legacy@basic-flip-before-cursor-varying-size +kms_cursor_legacy@basic-busy-flip-before-cursor-varying-size +kms_cursor_legacy@basic-flip-after-cursor-varying-size +kms_cursor_legacy@short-flip-before-cursor-toggle +kms_cursor_legacy@short-busy-flip-before-cursor-toggle +kms_cursor_legacy@short-flip-after-cursor-toggle +kms_cursor_legacy@basic-flip-before-cursor-atomic +kms_cursor_legacy@basic-busy-flip-before-cursor-atomic +kms_cursor_legacy@basic-flip-after-cursor-atomic +kms_cursor_legacy@short-flip-before-cursor-atomic-transitions +kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions +kms_cursor_legacy@short-flip-after-cursor-atomic-transitions +kms_cursor_legacy@short-flip-before-cursor-atomic-transitions-varying-size +kms_cursor_legacy@short-busy-flip-before-cursor-atomic-transitions-varying-size +kms_cursor_legacy@short-flip-after-cursor-atomic-transitions-varying-size +kms_cursor_legacy@cursor-vs-flip-legacy +kms_cursor_legacy@flip-vs-cursor-legacy +kms_cursor_legacy@cursorA-vs-flipA-legacy +kms_cursor_legacy@cursorA-vs-flipB-legacy +kms_cursor_legacy@cursorB-vs-flipA-legacy +kms_cursor_legacy@cursorB-vs-flipB-legacy +kms_cursor_legacy@cursor-vs-flip-varying-size +kms_cursor_legacy@flip-vs-cursor-varying-size +kms_cursor_legacy@cursorA-vs-flipA-varying-size +kms_cursor_legacy@cursorA-vs-flipB-varying-size +kms_cursor_legacy@cursorB-vs-flipA-varying-size +kms_cursor_legacy@cursorB-vs-flipB-varying-size +kms_cursor_legacy@cursor-vs-flip-toggle +kms_cursor_legacy@flip-vs-cursor-toggle +kms_cursor_legacy@cursorA-vs-flipA-toggle +kms_cursor_legacy@cursorA-vs-flipB-toggle +kms_cursor_legacy@cursorB-vs-flipA-toggle +kms_cursor_legacy@cursorB-vs-flipB-toggle +kms_cursor_legacy@cursor-vs-flip-atomic +kms_cursor_legacy@flip-vs-cursor-atomic +kms_cursor_legacy@cursorA-vs-flipA-atomic +kms_cursor_legacy@cursorA-vs-flipB-atomic +kms_cursor_legacy@cursorB-vs-flipA-atomic +kms_cursor_legacy@cursorB-vs-flipB-atomic +kms_cursor_legacy@cursor-vs-flip-atomic-transitions +kms_cursor_legacy@flip-vs-cursor-atomic-transitions +kms_cursor_legacy@cursorA-vs-flipA-atomic-transitions +kms_cursor_legacy@cursorA-vs-flipB-atomic-transitions +kms_cursor_legacy@cursorB-vs-flipA-atomic-transitions +kms_cursor_legacy@cursorB-vs-flipB-atomic-transitions +kms_cursor_legacy@cursor-vs-flip-atomic-transitions-varying-size +kms_cursor_legacy@flip-vs-cursor-atomic-transitions-varying-size +kms_cursor_legacy@cursorA-vs-flipA-atomic-transitions-varying-size +kms_cursor_legacy@cursorA-vs-flipB-atomic-transitions-varying-size +kms_cursor_legacy@cursorB-vs-flipA-atomic-transitions-varying-size +kms_cursor_legacy@cursorB-vs-flipB-atomic-transitions-varying-size +kms_dither@FB-8BPC-Vs-Panel-6BPC +kms_dither@FB-8BPC-Vs-Panel-8BPC +kms_dp_aux_dev +kms_tiled_display@basic-test-pattern +kms_tiled_display@basic-test-pattern-with-chamelium +kms_draw_crc@draw-method-mmap-cpu +kms_draw_crc@draw-method-mmap-gtt +kms_draw_crc@draw-method-mmap-wc +kms_draw_crc@draw-method-pwrite +kms_draw_crc@draw-method-blt +kms_draw_crc@draw-method-render +kms_draw_crc@fill-fb +kms_dsc@dsc-basic +kms_dsc@dsc-with-formats +kms_dsc@dsc-with-bpc +kms_dsc@dsc-with-bpc-formats +kms_dsc@dsc-with-output-formats +kms_fbcon_fbt@fbc +kms_fbcon_fbt@psr +kms_fbcon_fbt@fbc-suspend +kms_fbcon_fbt@psr-suspend +kms_fence_pin_leak +kms_flip@nonblocking-read +kms_flip@wf_vblank-ts-check +kms_flip@2x-wf_vblank-ts-check +kms_flip@blocking-wf_vblank +kms_flip@2x-blocking-wf_vblank +kms_flip@absolute-wf_vblank +kms_flip@2x-absolute-wf_vblank +kms_flip@blocking-absolute-wf_vblank +kms_flip@2x-blocking-absolute-wf_vblank +kms_flip@basic-plain-flip +kms_flip@2x-plain-flip +kms_flip@busy-flip +kms_flip@2x-busy-flip +kms_flip@flip-vs-fences +kms_flip@2x-flip-vs-fences +kms_flip@plain-flip-ts-check +kms_flip@2x-plain-flip-ts-check +kms_flip@plain-flip-fb-recreate +kms_flip@2x-plain-flip-fb-recreate +kms_flip@flip-vs-rmfb +kms_flip@2x-flip-vs-rmfb +kms_flip@basic-flip-vs-dpms +kms_flip@2x-flip-vs-dpms +kms_flip@flip-vs-panning +kms_flip@2x-flip-vs-panning +kms_flip@basic-flip-vs-modeset +kms_flip@2x-flip-vs-modeset +kms_flip@flip-vs-expired-vblank +kms_flip@2x-flip-vs-expired-vblank +kms_flip@flip-vs-absolute-wf_vblank +kms_flip@2x-flip-vs-absolute-wf_vblank +kms_flip@basic-flip-vs-wf_vblank +kms_flip@2x-flip-vs-wf_vblank +kms_flip@flip-vs-blocking-wf-vblank +kms_flip@2x-flip-vs-blocking-wf-vblank +kms_flip@flip-vs-modeset-vs-hang +kms_flip@2x-flip-vs-modeset-vs-hang +kms_flip@flip-vs-panning-vs-hang +kms_flip@2x-flip-vs-panning-vs-hang +kms_flip@flip-vs-dpms-off-vs-modeset +kms_flip@2x-flip-vs-dpms-off-vs-modeset +kms_flip@single-buffer-flip-vs-dpms-off-vs-modeset +kms_flip@2x-single-buffer-flip-vs-dpms-off-vs-modeset +kms_flip@dpms-off-confusion +kms_flip@nonexisting-fb +kms_flip@2x-nonexisting-fb +kms_flip@dpms-vs-vblank-race +kms_flip@2x-dpms-vs-vblank-race +kms_flip@modeset-vs-vblank-race +kms_flip@2x-modeset-vs-vblank-race +kms_flip@bo-too-big +kms_flip@flip-vs-suspend +kms_flip@2x-flip-vs-suspend +kms_flip@wf_vblank-ts-check-interruptible +kms_flip@2x-wf_vblank-ts-check-interruptible +kms_flip@absolute-wf_vblank-interruptible +kms_flip@2x-absolute-wf_vblank-interruptible +kms_flip@blocking-absolute-wf_vblank-interruptible +kms_flip@2x-blocking-absolute-wf_vblank-interruptible +kms_flip@plain-flip-interruptible +kms_flip@2x-plain-flip-interruptible +kms_flip@flip-vs-fences-interruptible +kms_flip@2x-flip-vs-fences-interruptible +kms_flip@plain-flip-ts-check-interruptible +kms_flip@2x-plain-flip-ts-check-interruptible +kms_flip@plain-flip-fb-recreate-interruptible +kms_flip@2x-plain-flip-fb-recreate-interruptible +kms_flip@flip-vs-rmfb-interruptible +kms_flip@2x-flip-vs-rmfb-interruptible +kms_flip@flip-vs-panning-interruptible +kms_flip@2x-flip-vs-panning-interruptible +kms_flip@flip-vs-expired-vblank-interruptible +kms_flip@2x-flip-vs-expired-vblank-interruptible +kms_flip@flip-vs-absolute-wf_vblank-interruptible +kms_flip@2x-flip-vs-absolute-wf_vblank-interruptible +kms_flip@flip-vs-wf_vblank-interruptible +kms_flip@2x-flip-vs-wf_vblank-interruptible +kms_flip@flip-vs-dpms-off-vs-modeset-interruptible +kms_flip@2x-flip-vs-dpms-off-vs-modeset-interruptible +kms_flip@single-buffer-flip-vs-dpms-off-vs-modeset-interruptible +kms_flip@2x-single-buffer-flip-vs-dpms-off-vs-modeset-interruptible +kms_flip@dpms-off-confusion-interruptible +kms_flip@nonexisting-fb-interruptible +kms_flip@2x-nonexisting-fb-interruptible +kms_flip@dpms-vs-vblank-race-interruptible +kms_flip@2x-dpms-vs-vblank-race-interruptible +kms_flip@modeset-vs-vblank-race-interruptible +kms_flip@2x-modeset-vs-vblank-race-interruptible +kms_flip@bo-too-big-interruptible +kms_flip@flip-vs-suspend-interruptible +kms_flip@2x-flip-vs-suspend-interruptible +kms_flip_event_leak@basic +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-yftile-to-64bpp-yftile-downscaling +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling +kms_flip_scaled_crc@flip-32bpp-4tile-to-64bpp-4tile-downscaling +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-yftile-to-32bpp-yftile-downscaling +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling +kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tile-downscaling +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-yftile-to-16bpp-yftile-downscaling +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling +kms_flip_scaled_crc@flip-64bpp-4tile-to-16bpp-4tile-downscaling +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-yftileccs-to-64bpp-yftile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytilegen12rcccs-downscaling +kms_flip_scaled_crc@flip-32bpp-4tile-to-32bpp-4tiledg2rcccs-downscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytileccs-downscaling +kms_flip_scaled_crc@flip-32bpp-yftile-to-32bpp-yftileccs-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling +kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-downscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-32bpp-yftile-to-64bpp-yftile-upscaling +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling +kms_flip_scaled_crc@flip-32bpp-4tile-to-64bpp-4tile-upscaling +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-yftile-to-32bpp-yftile-upscaling +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling +kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tile-upscaling +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-yftile-to-16bpp-yftile-upscaling +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling +kms_flip_scaled_crc@flip-64bpp-4tile-to-16bpp-4tile-upscaling +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-32bpp-yftileccs-to-64bpp-yftile-upscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling +kms_flip_scaled_crc@flip-32bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-32bpp-ytileccs-upscaling +kms_flip_scaled_crc@flip-32bpp-yftile-to-32bpp-yftileccs-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling +kms_flip_scaled_crc@flip-64bpp-4tile-to-32bpp-4tiledg2rcccs-upscaling +kms_force_connector_basic@force-load-detect +kms_force_connector_basic@force-connector-state +kms_force_connector_basic@force-edid +kms_force_connector_basic@prune-stale-modes +kms_frontbuffer_tracking@fbc-1p-rte +kms_frontbuffer_tracking@fbc-2p-rte +kms_frontbuffer_tracking@psr-1p-rte +kms_frontbuffer_tracking@psr-2p-rte +kms_frontbuffer_tracking@fbcpsr-1p-rte +kms_frontbuffer_tracking@fbcpsr-2p-rte +kms_frontbuffer_tracking@drrs-1p-rte +kms_frontbuffer_tracking@drrs-2p-rte +kms_frontbuffer_tracking@fbcdrrs-1p-rte +kms_frontbuffer_tracking@fbcdrrs-2p-rte +kms_frontbuffer_tracking@psrdrrs-1p-rte +kms_frontbuffer_tracking@psrdrrs-2p-rte +kms_frontbuffer_tracking@fbcpsrdrrs-1p-rte +kms_frontbuffer_tracking@fbcpsrdrrs-2p-rte +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbc-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbc-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@psr-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psr-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@psr-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@psr-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@psr-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psr-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psr-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@psr-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@psr-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psr-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@drrs-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@drrs-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@drrs-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@drrs-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@drrs-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-offscren-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-shrfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-shrfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-shrfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-shrfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-shrfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-pri-shrfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-draw-render +kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbc-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbc-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbc-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@psr-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@psr-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@psr-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@psr-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@psr-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@psr-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@psr-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@psr-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@psr-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@psr-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@psr-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@drrs-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@drrs-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@drrs-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-indfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-indfb-msflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-indfb-plflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-shrfb-pgflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-shrfb-msflip-blt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-shrfb-plflip-blt +kms_frontbuffer_tracking@fbc-1p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbc-2p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-1p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-2p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-1p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-2p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-1p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-indfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-2p-shrfb-fliptrack-mmap-gtt +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbc-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbc-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@fbc-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@psr-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@psr-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@psr-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@drrs-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@drrs-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@drrs-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-move +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-cur-indfb-onoff +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-move +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-onoff +kms_frontbuffer_tracking@fbc-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbc-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@psr-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@psr-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcpsr-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcpsr-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcpsr-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@drrs-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@drrs-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@drrs-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcdrrs-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcdrrs-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@psrdrrs-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@psrdrrs-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@psrdrrs-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcpsrdrrs-1p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcpsrdrrs-2p-primscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbcpsrdrrs-2p-scndscrn-spr-indfb-fullscreen +kms_frontbuffer_tracking@fbc-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbc-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@psr-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@psr-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbcpsr-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbcpsr-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@drrs-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@drrs-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbcdrrs-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbcdrrs-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@psrdrrs-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@psrdrrs-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbcpsrdrrs-1p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbcpsrdrrs-2p-pri-indfb-multidraw +kms_frontbuffer_tracking@fbc-farfromfence-mmap-gtt +kms_frontbuffer_tracking@psr-farfromfence-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-farfromfence-mmap-gtt +kms_frontbuffer_tracking@drrs-farfromfence-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-farfromfence-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-farfromfence-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-farfromfence-mmap-gtt +kms_frontbuffer_tracking@fbc-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@fbc-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@fbc-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@fbc-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@fbc-rgb565-draw-pwrite +kms_frontbuffer_tracking@fbc-rgb101010-draw-pwrite +kms_frontbuffer_tracking@fbc-rgb565-draw-blt +kms_frontbuffer_tracking@fbc-rgb101010-draw-blt +kms_frontbuffer_tracking@fbc-rgb565-draw-render +kms_frontbuffer_tracking@fbc-rgb101010-draw-render +kms_frontbuffer_tracking@psr-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@psr-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@psr-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@psr-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@psr-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@psr-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@psr-rgb565-draw-pwrite +kms_frontbuffer_tracking@psr-rgb101010-draw-pwrite +kms_frontbuffer_tracking@psr-rgb565-draw-blt +kms_frontbuffer_tracking@psr-rgb101010-draw-blt +kms_frontbuffer_tracking@psr-rgb565-draw-render +kms_frontbuffer_tracking@psr-rgb101010-draw-render +kms_frontbuffer_tracking@fbcpsr-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsr-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsr-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsr-rgb565-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-rgb101010-draw-pwrite +kms_frontbuffer_tracking@fbcpsr-rgb565-draw-blt +kms_frontbuffer_tracking@fbcpsr-rgb101010-draw-blt +kms_frontbuffer_tracking@fbcpsr-rgb565-draw-render +kms_frontbuffer_tracking@fbcpsr-rgb101010-draw-render +kms_frontbuffer_tracking@drrs-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@drrs-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@drrs-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@drrs-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@drrs-rgb565-draw-pwrite +kms_frontbuffer_tracking@drrs-rgb101010-draw-pwrite +kms_frontbuffer_tracking@drrs-rgb565-draw-blt +kms_frontbuffer_tracking@drrs-rgb101010-draw-blt +kms_frontbuffer_tracking@drrs-rgb565-draw-render +kms_frontbuffer_tracking@drrs-rgb101010-draw-render +kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-rgb101010-draw-pwrite +kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-blt +kms_frontbuffer_tracking@fbcdrrs-rgb101010-draw-blt +kms_frontbuffer_tracking@fbcdrrs-rgb565-draw-render +kms_frontbuffer_tracking@fbcdrrs-rgb101010-draw-render +kms_frontbuffer_tracking@psrdrrs-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@psrdrrs-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@psrdrrs-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@psrdrrs-rgb565-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-rgb101010-draw-pwrite +kms_frontbuffer_tracking@psrdrrs-rgb565-draw-blt +kms_frontbuffer_tracking@psrdrrs-rgb101010-draw-blt +kms_frontbuffer_tracking@psrdrrs-rgb565-draw-render +kms_frontbuffer_tracking@psrdrrs-rgb101010-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-rgb565-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-rgb101010-draw-mmap-cpu +kms_frontbuffer_tracking@fbcpsrdrrs-rgb565-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-rgb101010-draw-mmap-gtt +kms_frontbuffer_tracking@fbcpsrdrrs-rgb565-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-rgb101010-draw-mmap-wc +kms_frontbuffer_tracking@fbcpsrdrrs-rgb565-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-rgb101010-draw-pwrite +kms_frontbuffer_tracking@fbcpsrdrrs-rgb565-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-rgb101010-draw-blt +kms_frontbuffer_tracking@fbcpsrdrrs-rgb565-draw-render +kms_frontbuffer_tracking@fbcpsrdrrs-rgb101010-draw-render +kms_frontbuffer_tracking@fbc-indfb-scaledprimary +kms_frontbuffer_tracking@fbc-shrfb-scaledprimary +kms_frontbuffer_tracking@psr-indfb-scaledprimary +kms_frontbuffer_tracking@psr-shrfb-scaledprimary +kms_frontbuffer_tracking@fbcpsr-indfb-scaledprimary +kms_frontbuffer_tracking@fbcpsr-shrfb-scaledprimary +kms_frontbuffer_tracking@drrs-indfb-scaledprimary +kms_frontbuffer_tracking@drrs-shrfb-scaledprimary +kms_frontbuffer_tracking@fbcdrrs-indfb-scaledprimary +kms_frontbuffer_tracking@fbcdrrs-shrfb-scaledprimary +kms_frontbuffer_tracking@psrdrrs-indfb-scaledprimary +kms_frontbuffer_tracking@psrdrrs-shrfb-scaledprimary +kms_frontbuffer_tracking@fbcpsrdrrs-indfb-scaledprimary +kms_frontbuffer_tracking@fbcpsrdrrs-shrfb-scaledprimary +kms_frontbuffer_tracking@fbc-modesetfrombusy +kms_frontbuffer_tracking@fbc-badstride +kms_frontbuffer_tracking@fbc-stridechange +kms_frontbuffer_tracking@fbc-tiling-linear +kms_frontbuffer_tracking@fbc-tiling-y +kms_frontbuffer_tracking@fbc-tiling-4 +kms_frontbuffer_tracking@fbc-suspend +kms_frontbuffer_tracking@psr-modesetfrombusy +kms_frontbuffer_tracking@psr-slowdraw +kms_frontbuffer_tracking@psr-suspend +kms_frontbuffer_tracking@fbcpsr-modesetfrombusy +kms_frontbuffer_tracking@fbcpsr-badstride +kms_frontbuffer_tracking@fbcpsr-stridechange +kms_frontbuffer_tracking@fbcpsr-tiling-linear +kms_frontbuffer_tracking@fbcpsr-tiling-y +kms_frontbuffer_tracking@fbcpsr-tiling-4 +kms_frontbuffer_tracking@fbcpsr-slowdraw +kms_frontbuffer_tracking@fbcpsr-suspend +kms_frontbuffer_tracking@drrs-modesetfrombusy +kms_frontbuffer_tracking@drrs-slowdraw +kms_frontbuffer_tracking@drrs-suspend +kms_frontbuffer_tracking@fbcdrrs-modesetfrombusy +kms_frontbuffer_tracking@fbcdrrs-badstride +kms_frontbuffer_tracking@fbcdrrs-stridechange +kms_frontbuffer_tracking@fbcdrrs-tiling-linear +kms_frontbuffer_tracking@fbcdrrs-tiling-y +kms_frontbuffer_tracking@fbcdrrs-tiling-4 +kms_frontbuffer_tracking@fbcdrrs-slowdraw +kms_frontbuffer_tracking@fbcdrrs-suspend +kms_frontbuffer_tracking@psrdrrs-modesetfrombusy +kms_frontbuffer_tracking@psrdrrs-slowdraw +kms_frontbuffer_tracking@psrdrrs-suspend +kms_frontbuffer_tracking@fbcpsrdrrs-modesetfrombusy +kms_frontbuffer_tracking@fbcpsrdrrs-badstride +kms_frontbuffer_tracking@fbcpsrdrrs-stridechange +kms_frontbuffer_tracking@fbcpsrdrrs-tiling-linear +kms_frontbuffer_tracking@fbcpsrdrrs-tiling-y +kms_frontbuffer_tracking@fbcpsrdrrs-tiling-4 +kms_frontbuffer_tracking@fbcpsrdrrs-slowdraw +kms_frontbuffer_tracking@fbcpsrdrrs-suspend +kms_frontbuffer_tracking@basic +kms_getfb@getfb-handle-zero +kms_getfb@getfb-handle-valid +kms_getfb@getfb-handle-closed +kms_getfb@getfb-handle-not-fb +kms_getfb@getfb-addfb-different-handles +kms_getfb@getfb-repeated-different-handles +kms_getfb@getfb-reject-ccs +kms_getfb@getfb2-handle-zero +kms_getfb@getfb2-handle-closed +kms_getfb@getfb2-handle-not-fb +kms_getfb@getfb2-accept-ccs +kms_getfb@getfb2-into-addfb2 +kms_getfb@getfb-handle-protection +kms_getfb@getfb2-handle-protection +kms_hdmi_inject@inject-4k +kms_hdmi_inject@inject-audio +kms_hdr@bpc-switch +kms_hdr@bpc-switch-dpms +kms_hdr@bpc-switch-suspend +kms_hdr@static-toggle +kms_hdr@static-toggle-dpms +kms_hdr@static-toggle-suspend +kms_hdr@static-swap +kms_hdr@invalid-metadata-sizes +kms_hdr@invalid-hdr +kms_invalid_mode@clock-too-high +kms_invalid_mode@zero-clock +kms_invalid_mode@int-max-clock +kms_invalid_mode@uint-max-clock +kms_invalid_mode@zero-hdisplay +kms_invalid_mode@zero-vdisplay +kms_invalid_mode@bad-hsync-start +kms_invalid_mode@bad-vsync-start +kms_invalid_mode@bad-hsync-end +kms_invalid_mode@bad-vsync-end +kms_invalid_mode@bad-htotal +kms_invalid_mode@bad-vtotal +kms_legacy_colorkey@basic +kms_legacy_colorkey@invalid-plane +kms_multipipe_modeset@basic-max-pipe-crc-check +kms_panel_fitting@legacy +kms_panel_fitting@atomic-fastset +kms_pipe_b_c_ivb@pipe-B-dpms-off-modeset-pipe-C +kms_pipe_b_c_ivb@pipe-B-double-modeset-then-modeset-pipe-C +kms_pipe_b_c_ivb@disable-pipe-B-enable-pipe-C +kms_pipe_b_c_ivb@from-pipe-C-to-B-with-3-lanes +kms_pipe_b_c_ivb@enable-pipe-C-while-B-has-3-lanes +kms_pipe_crc_basic@bad-source +kms_pipe_crc_basic@read-crc +kms_pipe_crc_basic@read-crc-frame-sequence +kms_pipe_crc_basic@nonblocking-crc +kms_pipe_crc_basic@nonblocking-crc-frame-sequence +kms_pipe_crc_basic@suspend-read-crc +kms_pipe_crc_basic@hang-read-crc +kms_pipe_crc_basic@disable-crc-after-crtc +kms_pipe_crc_basic@compare-crc-sanitycheck-xr24 +kms_pipe_crc_basic@compare-crc-sanitycheck-nv12 +kms_plane@pixel-format +kms_plane@pixel-format-source-clamping +kms_plane@plane-position-covered +kms_plane@plane-position-hole +kms_plane@plane-position-hole-dpms +kms_plane@plane-panning-top-left +kms_plane@plane-panning-bottom-right +kms_plane@plane-panning-bottom-right-suspend +kms_plane@invalid-pixel-format-settings +kms_plane_alpha_blend@alpha-basic +kms_plane_alpha_blend@alpha-7efc +kms_plane_alpha_blend@coverage-7efc +kms_plane_alpha_blend@coverage-vs-premult-vs-constant +kms_plane_alpha_blend@alpha-transparent-fb +kms_plane_alpha_blend@alpha-opaque-fb +kms_plane_alpha_blend@constant-alpha-min +kms_plane_alpha_blend@constant-alpha-mid +kms_plane_alpha_blend@constant-alpha-max +kms_plane_cursor@primary +kms_plane_cursor@overlay +kms_plane_cursor@viewport +kms_plane_lowres@tiling-none +kms_plane_lowres@tiling-x +kms_plane_lowres@tiling-y +kms_plane_lowres@tiling-yf +kms_plane_lowres@tiling-4 +kms_plane_multiple@tiling-none +kms_plane_multiple@tiling-x +kms_plane_multiple@tiling-y +kms_plane_multiple@tiling-yf +kms_plane_multiple@tiling-4 +kms_plane_scaling@plane-upscale-with-pixel-format-20x20 +kms_plane_scaling@plane-upscale-with-pixel-format-factor-0-25 +kms_plane_scaling@plane-downscale-with-pixel-format-factor-0-25 +kms_plane_scaling@plane-downscale-with-pixel-format-factor-0-5 +kms_plane_scaling@plane-downscale-with-pixel-format-factor-0-75 +kms_plane_scaling@plane-scaler-with-pixel-format-unity-scaling +kms_plane_scaling@plane-upscale-with-rotation-20x20 +kms_plane_scaling@plane-upscale-with-rotation-factor-0-25 +kms_plane_scaling@plane-downscale-with-rotation-factor-0-25 +kms_plane_scaling@plane-downscale-with-rotation-factor-0-5 +kms_plane_scaling@plane-downscale-with-rotation-factor-0-75 +kms_plane_scaling@plane-scaler-with-rotation-unity-scaling +kms_plane_scaling@plane-upscale-with-modifiers-20x20 +kms_plane_scaling@plane-upscale-with-modifiers-factor-0-25 +kms_plane_scaling@plane-downscale-with-modifiers-factor-0-25 +kms_plane_scaling@plane-downscale-with-modifiers-factor-0-5 +kms_plane_scaling@plane-downscale-with-modifiers-factor-0-75 +kms_plane_scaling@plane-scaler-with-modifiers-unity-scaling +kms_plane_scaling@plane-scaler-with-clipping-clamping-pixel-formats +kms_plane_scaling@plane-scaler-with-clipping-clamping-rotation +kms_plane_scaling@plane-scaler-with-clipping-clamping-modifiers +kms_plane_scaling@planes-upscale-20x20 +kms_plane_scaling@planes-upscale-factor-0-25 +kms_plane_scaling@planes-scaler-unity-scaling +kms_plane_scaling@planes-downscale-factor-0-25 +kms_plane_scaling@planes-downscale-factor-0-5 +kms_plane_scaling@planes-downscale-factor-0-75 +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-25 +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-5 +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-75 +kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-25 +kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-5 +kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-75 +kms_plane_scaling@planes-unity-scaling-downscale-factor-0-25 +kms_plane_scaling@planes-unity-scaling-downscale-factor-0-5 +kms_plane_scaling@planes-unity-scaling-downscale-factor-0-75 +kms_plane_scaling@planes-downscale-factor-0-25-upscale-20x20 +kms_plane_scaling@planes-downscale-factor-0-25-upscale-factor-0-25 +kms_plane_scaling@planes-downscale-factor-0-25-unity-scaling +kms_plane_scaling@planes-downscale-factor-0-5-upscale-20x20 +kms_plane_scaling@planes-downscale-factor-0-5-upscale-factor-0-25 +kms_plane_scaling@planes-downscale-factor-0-5-unity-scaling +kms_plane_scaling@planes-downscale-factor-0-75-upscale-20x20 +kms_plane_scaling@planes-downscale-factor-0-75-upscale-factor-0-25 +kms_plane_scaling@planes-downscale-factor-0-75-unity-scaling +kms_plane_scaling@intel-max-src-size +kms_plane_scaling@invalid-num-scalers +kms_plane_scaling@invalid-parameters +kms_plane_scaling@2x-scaler-multi-pipe +kms_prime@basic-crc-hybrid +kms_prime@basic-modeset-hybrid +kms_prime@D3hot +kms_prime@basic-crc-vgem +kms_prop_blob@basic +kms_prop_blob@blob-prop-core +kms_prop_blob@blob-prop-validate +kms_prop_blob@blob-prop-lifetime +kms_prop_blob@blob-multiple +kms_prop_blob@invalid-get-prop-any +kms_prop_blob@invalid-get-prop +kms_prop_blob@invalid-set-prop-any +kms_prop_blob@invalid-set-prop +kms_properties@plane-properties-legacy +kms_properties@plane-properties-atomic +kms_properties@crtc-properties-legacy +kms_properties@crtc-properties-atomic +kms_properties@connector-properties-legacy +kms_properties@connector-properties-atomic +kms_properties@invalid-properties-legacy +kms_properties@invalid-properties-atomic +kms_properties@get_properties-sanity-atomic +kms_properties@get_properties-sanity-non-atomic +kms_psr@basic +kms_psr@no_drrs +kms_psr@primary_page_flip +kms_psr@primary_mmap_gtt +kms_psr@primary_mmap_cpu +kms_psr@primary_blt +kms_psr@primary_render +kms_psr@sprite_mmap_gtt +kms_psr@cursor_mmap_gtt +kms_psr@sprite_mmap_cpu +kms_psr@cursor_mmap_cpu +kms_psr@sprite_blt +kms_psr@cursor_blt +kms_psr@sprite_render +kms_psr@cursor_render +kms_psr@sprite_plane_move +kms_psr@cursor_plane_move +kms_psr@sprite_plane_onoff +kms_psr@cursor_plane_onoff +kms_psr@dpms +kms_psr@suspend +kms_psr@psr2_basic +kms_psr@psr2_no_drrs +kms_psr@psr2_primary_page_flip +kms_psr@psr2_primary_mmap_gtt +kms_psr@psr2_primary_mmap_cpu +kms_psr@psr2_primary_blt +kms_psr@psr2_primary_render +kms_psr@psr2_sprite_mmap_gtt +kms_psr@psr2_cursor_mmap_gtt +kms_psr@psr2_sprite_mmap_cpu +kms_psr@psr2_cursor_mmap_cpu +kms_psr@psr2_sprite_blt +kms_psr@psr2_cursor_blt +kms_psr@psr2_sprite_render +kms_psr@psr2_cursor_render +kms_psr@psr2_sprite_plane_move +kms_psr@psr2_cursor_plane_move +kms_psr@psr2_sprite_plane_onoff +kms_psr@psr2_cursor_plane_onoff +kms_psr@psr2_dpms +kms_psr@psr2_suspend +kms_psr2_sf@primary-plane-update-sf-dmg-area +kms_psr2_sf@primary-plane-update-sf-dmg-area-big-fb +kms_psr2_sf@overlay-plane-update-sf-dmg-area +kms_psr2_sf@cursor-plane-update-sf +kms_psr2_sf@cursor-plane-move-continuous-sf +kms_psr2_sf@cursor-plane-move-continuous-exceed-sf +kms_psr2_sf@cursor-plane-move-continuous-exceed-fully-sf +kms_psr2_sf@plane-move-sf-dmg-area +kms_psr2_sf@overlay-plane-move-continuous-sf +kms_psr2_sf@overlay-plane-move-continuous-exceed-sf +kms_psr2_sf@overlay-plane-move-continuous-exceed-fully-sf +kms_psr2_sf@overlay-primary-update-sf-dmg-area +kms_psr2_sf@overlay-plane-update-continuous-sf +kms_psr2_su@page_flip-XRGB8888 +kms_psr2_su@page_flip-NV12 +kms_psr2_su@page_flip-P010 +kms_psr2_su@frontbuffer-XRGB8888 +kms_pwrite_crc +kms_rmfb@rmfb-ioctl +kms_rmfb@close-fd +kms_rotation_crc@primary-rotation-90 +kms_rotation_crc@primary-rotation-180 +kms_rotation_crc@primary-rotation-270 +kms_rotation_crc@sprite-rotation-90 +kms_rotation_crc@sprite-rotation-180 +kms_rotation_crc@sprite-rotation-270 +kms_rotation_crc@cursor-rotation-180 +kms_rotation_crc@sprite-rotation-90-pos-100-0 +kms_rotation_crc@bad-pixel-format +kms_rotation_crc@bad-tiling +kms_rotation_crc@primary-x-tiled-reflect-x-0 +kms_rotation_crc@primary-x-tiled-reflect-x-180 +kms_rotation_crc@primary-y-tiled-reflect-x-0 +kms_rotation_crc@primary-y-tiled-reflect-x-90 +kms_rotation_crc@primary-y-tiled-reflect-x-180 +kms_rotation_crc@primary-y-tiled-reflect-x-270 +kms_rotation_crc@primary-yf-tiled-reflect-x-0 +kms_rotation_crc@primary-yf-tiled-reflect-x-90 +kms_rotation_crc@primary-yf-tiled-reflect-x-180 +kms_rotation_crc@primary-yf-tiled-reflect-x-270 +kms_rotation_crc@primary-4-tiled-reflect-x-0 +kms_rotation_crc@primary-4-tiled-reflect-x-180 +kms_rotation_crc@multiplane-rotation +kms_rotation_crc@multiplane-rotation-cropping-top +kms_rotation_crc@multiplane-rotation-cropping-bottom +kms_rotation_crc@exhaust-fences +kms_scaling_modes@scaling-mode-full +kms_scaling_modes@scaling-mode-center +kms_scaling_modes@scaling-mode-full-aspect +kms_scaling_modes@scaling-mode-none +kms_selftest@drm_cmdline +kms_selftest@drm_damage +kms_selftest@drm_dp_mst +kms_selftest@drm_format_helper +kms_selftest@drm_format +kms_selftest@framebuffer +kms_selftest@drm_plane +kms_setmode@basic +kms_setmode@basic-clone-single-crtc +kms_setmode@invalid-clone-single-crtc +kms_setmode@invalid-clone-exclusive-crtc +kms_setmode@clone-exclusive-crtc +kms_setmode@invalid-clone-single-crtc-stealing +kms_sysfs_edid_timing +kms_tv_load_detect@load-detect +kms_universal_plane@universal-plane-pipe-A-functional +kms_universal_plane@universal-plane-pipe-A-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-A +kms_universal_plane@cursor-fb-leak-pipe-A +kms_universal_plane@universal-plane-pageflip-windowed-pipe-A +kms_universal_plane@universal-plane-pipe-B-functional +kms_universal_plane@universal-plane-pipe-B-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-B +kms_universal_plane@cursor-fb-leak-pipe-B +kms_universal_plane@universal-plane-pageflip-windowed-pipe-B +kms_universal_plane@universal-plane-pipe-C-functional +kms_universal_plane@universal-plane-pipe-C-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-C +kms_universal_plane@cursor-fb-leak-pipe-C +kms_universal_plane@universal-plane-pageflip-windowed-pipe-C +kms_universal_plane@universal-plane-pipe-D-functional +kms_universal_plane@universal-plane-pipe-D-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-D +kms_universal_plane@cursor-fb-leak-pipe-D +kms_universal_plane@universal-plane-pageflip-windowed-pipe-D +kms_universal_plane@universal-plane-pipe-E-functional +kms_universal_plane@universal-plane-pipe-E-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-E +kms_universal_plane@cursor-fb-leak-pipe-E +kms_universal_plane@universal-plane-pageflip-windowed-pipe-E +kms_universal_plane@universal-plane-pipe-F-functional +kms_universal_plane@universal-plane-pipe-F-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-F +kms_universal_plane@cursor-fb-leak-pipe-F +kms_universal_plane@universal-plane-pageflip-windowed-pipe-F +kms_universal_plane@universal-plane-pipe-G-functional +kms_universal_plane@universal-plane-pipe-G-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-G +kms_universal_plane@cursor-fb-leak-pipe-G +kms_universal_plane@universal-plane-pageflip-windowed-pipe-G +kms_universal_plane@universal-plane-pipe-H-functional +kms_universal_plane@universal-plane-pipe-H-sanity +kms_universal_plane@disable-primary-vs-flip-pipe-H +kms_universal_plane@cursor-fb-leak-pipe-H +kms_universal_plane@universal-plane-pageflip-windowed-pipe-H +kms_vblank@invalid +kms_vblank@crtc-id +kms_vblank@pipe-A-accuracy-idle +kms_vblank@pipe-A-query-idle +kms_vblank@pipe-A-query-idle-hang +kms_vblank@pipe-A-query-forked +kms_vblank@pipe-A-query-forked-hang +kms_vblank@pipe-A-query-busy +kms_vblank@pipe-A-query-busy-hang +kms_vblank@pipe-A-query-forked-busy +kms_vblank@pipe-A-query-forked-busy-hang +kms_vblank@pipe-A-wait-idle +kms_vblank@pipe-A-wait-idle-hang +kms_vblank@pipe-A-wait-forked +kms_vblank@pipe-A-wait-forked-hang +kms_vblank@pipe-A-wait-busy +kms_vblank@pipe-A-wait-busy-hang +kms_vblank@pipe-A-wait-forked-busy +kms_vblank@pipe-A-wait-forked-busy-hang +kms_vblank@pipe-A-ts-continuation-idle +kms_vblank@pipe-A-ts-continuation-idle-hang +kms_vblank@pipe-A-ts-continuation-dpms-rpm +kms_vblank@pipe-A-ts-continuation-dpms-suspend +kms_vblank@pipe-A-ts-continuation-suspend +kms_vblank@pipe-A-ts-continuation-modeset +kms_vblank@pipe-A-ts-continuation-modeset-hang +kms_vblank@pipe-A-ts-continuation-modeset-rpm +kms_vblank@pipe-B-accuracy-idle +kms_vblank@pipe-B-query-idle +kms_vblank@pipe-B-query-idle-hang +kms_vblank@pipe-B-query-forked +kms_vblank@pipe-B-query-forked-hang +kms_vblank@pipe-B-query-busy +kms_vblank@pipe-B-query-busy-hang +kms_vblank@pipe-B-query-forked-busy +kms_vblank@pipe-B-query-forked-busy-hang +kms_vblank@pipe-B-wait-idle +kms_vblank@pipe-B-wait-idle-hang +kms_vblank@pipe-B-wait-forked +kms_vblank@pipe-B-wait-forked-hang +kms_vblank@pipe-B-wait-busy +kms_vblank@pipe-B-wait-busy-hang +kms_vblank@pipe-B-wait-forked-busy +kms_vblank@pipe-B-wait-forked-busy-hang +kms_vblank@pipe-B-ts-continuation-idle +kms_vblank@pipe-B-ts-continuation-idle-hang +kms_vblank@pipe-B-ts-continuation-dpms-rpm +kms_vblank@pipe-B-ts-continuation-dpms-suspend +kms_vblank@pipe-B-ts-continuation-suspend +kms_vblank@pipe-B-ts-continuation-modeset +kms_vblank@pipe-B-ts-continuation-modeset-hang +kms_vblank@pipe-B-ts-continuation-modeset-rpm +kms_vblank@pipe-C-accuracy-idle +kms_vblank@pipe-C-query-idle +kms_vblank@pipe-C-query-idle-hang +kms_vblank@pipe-C-query-forked +kms_vblank@pipe-C-query-forked-hang +kms_vblank@pipe-C-query-busy +kms_vblank@pipe-C-query-busy-hang +kms_vblank@pipe-C-query-forked-busy +kms_vblank@pipe-C-query-forked-busy-hang +kms_vblank@pipe-C-wait-idle +kms_vblank@pipe-C-wait-idle-hang +kms_vblank@pipe-C-wait-forked +kms_vblank@pipe-C-wait-forked-hang +kms_vblank@pipe-C-wait-busy +kms_vblank@pipe-C-wait-busy-hang +kms_vblank@pipe-C-wait-forked-busy +kms_vblank@pipe-C-wait-forked-busy-hang +kms_vblank@pipe-C-ts-continuation-idle +kms_vblank@pipe-C-ts-continuation-idle-hang +kms_vblank@pipe-C-ts-continuation-dpms-rpm +kms_vblank@pipe-C-ts-continuation-dpms-suspend +kms_vblank@pipe-C-ts-continuation-suspend +kms_vblank@pipe-C-ts-continuation-modeset +kms_vblank@pipe-C-ts-continuation-modeset-hang +kms_vblank@pipe-C-ts-continuation-modeset-rpm +kms_vblank@pipe-D-accuracy-idle +kms_vblank@pipe-D-query-idle +kms_vblank@pipe-D-query-idle-hang +kms_vblank@pipe-D-query-forked +kms_vblank@pipe-D-query-forked-hang +kms_vblank@pipe-D-query-busy +kms_vblank@pipe-D-query-busy-hang +kms_vblank@pipe-D-query-forked-busy +kms_vblank@pipe-D-query-forked-busy-hang +kms_vblank@pipe-D-wait-idle +kms_vblank@pipe-D-wait-idle-hang +kms_vblank@pipe-D-wait-forked +kms_vblank@pipe-D-wait-forked-hang +kms_vblank@pipe-D-wait-busy +kms_vblank@pipe-D-wait-busy-hang +kms_vblank@pipe-D-wait-forked-busy +kms_vblank@pipe-D-wait-forked-busy-hang +kms_vblank@pipe-D-ts-continuation-idle +kms_vblank@pipe-D-ts-continuation-idle-hang +kms_vblank@pipe-D-ts-continuation-dpms-rpm +kms_vblank@pipe-D-ts-continuation-dpms-suspend +kms_vblank@pipe-D-ts-continuation-suspend +kms_vblank@pipe-D-ts-continuation-modeset +kms_vblank@pipe-D-ts-continuation-modeset-hang +kms_vblank@pipe-D-ts-continuation-modeset-rpm +kms_vblank@pipe-E-accuracy-idle +kms_vblank@pipe-E-query-idle +kms_vblank@pipe-E-query-idle-hang +kms_vblank@pipe-E-query-forked +kms_vblank@pipe-E-query-forked-hang +kms_vblank@pipe-E-query-busy +kms_vblank@pipe-E-query-busy-hang +kms_vblank@pipe-E-query-forked-busy +kms_vblank@pipe-E-query-forked-busy-hang +kms_vblank@pipe-E-wait-idle +kms_vblank@pipe-E-wait-idle-hang +kms_vblank@pipe-E-wait-forked +kms_vblank@pipe-E-wait-forked-hang +kms_vblank@pipe-E-wait-busy +kms_vblank@pipe-E-wait-busy-hang +kms_vblank@pipe-E-wait-forked-busy +kms_vblank@pipe-E-wait-forked-busy-hang +kms_vblank@pipe-E-ts-continuation-idle +kms_vblank@pipe-E-ts-continuation-idle-hang +kms_vblank@pipe-E-ts-continuation-dpms-rpm +kms_vblank@pipe-E-ts-continuation-dpms-suspend +kms_vblank@pipe-E-ts-continuation-suspend +kms_vblank@pipe-E-ts-continuation-modeset +kms_vblank@pipe-E-ts-continuation-modeset-hang +kms_vblank@pipe-E-ts-continuation-modeset-rpm +kms_vblank@pipe-F-accuracy-idle +kms_vblank@pipe-F-query-idle +kms_vblank@pipe-F-query-idle-hang +kms_vblank@pipe-F-query-forked +kms_vblank@pipe-F-query-forked-hang +kms_vblank@pipe-F-query-busy +kms_vblank@pipe-F-query-busy-hang +kms_vblank@pipe-F-query-forked-busy +kms_vblank@pipe-F-query-forked-busy-hang +kms_vblank@pipe-F-wait-idle +kms_vblank@pipe-F-wait-idle-hang +kms_vblank@pipe-F-wait-forked +kms_vblank@pipe-F-wait-forked-hang +kms_vblank@pipe-F-wait-busy +kms_vblank@pipe-F-wait-busy-hang +kms_vblank@pipe-F-wait-forked-busy +kms_vblank@pipe-F-wait-forked-busy-hang +kms_vblank@pipe-F-ts-continuation-idle +kms_vblank@pipe-F-ts-continuation-idle-hang +kms_vblank@pipe-F-ts-continuation-dpms-rpm +kms_vblank@pipe-F-ts-continuation-dpms-suspend +kms_vblank@pipe-F-ts-continuation-suspend +kms_vblank@pipe-F-ts-continuation-modeset +kms_vblank@pipe-F-ts-continuation-modeset-hang +kms_vblank@pipe-F-ts-continuation-modeset-rpm +kms_vblank@pipe-G-accuracy-idle +kms_vblank@pipe-G-query-idle +kms_vblank@pipe-G-query-idle-hang +kms_vblank@pipe-G-query-forked +kms_vblank@pipe-G-query-forked-hang +kms_vblank@pipe-G-query-busy +kms_vblank@pipe-G-query-busy-hang +kms_vblank@pipe-G-query-forked-busy +kms_vblank@pipe-G-query-forked-busy-hang +kms_vblank@pipe-G-wait-idle +kms_vblank@pipe-G-wait-idle-hang +kms_vblank@pipe-G-wait-forked +kms_vblank@pipe-G-wait-forked-hang +kms_vblank@pipe-G-wait-busy +kms_vblank@pipe-G-wait-busy-hang +kms_vblank@pipe-G-wait-forked-busy +kms_vblank@pipe-G-wait-forked-busy-hang +kms_vblank@pipe-G-ts-continuation-idle +kms_vblank@pipe-G-ts-continuation-idle-hang +kms_vblank@pipe-G-ts-continuation-dpms-rpm +kms_vblank@pipe-G-ts-continuation-dpms-suspend +kms_vblank@pipe-G-ts-continuation-suspend +kms_vblank@pipe-G-ts-continuation-modeset +kms_vblank@pipe-G-ts-continuation-modeset-hang +kms_vblank@pipe-G-ts-continuation-modeset-rpm +kms_vblank@pipe-H-accuracy-idle +kms_vblank@pipe-H-query-idle +kms_vblank@pipe-H-query-idle-hang +kms_vblank@pipe-H-query-forked +kms_vblank@pipe-H-query-forked-hang +kms_vblank@pipe-H-query-busy +kms_vblank@pipe-H-query-busy-hang +kms_vblank@pipe-H-query-forked-busy +kms_vblank@pipe-H-query-forked-busy-hang +kms_vblank@pipe-H-wait-idle +kms_vblank@pipe-H-wait-idle-hang +kms_vblank@pipe-H-wait-forked +kms_vblank@pipe-H-wait-forked-hang +kms_vblank@pipe-H-wait-busy +kms_vblank@pipe-H-wait-busy-hang +kms_vblank@pipe-H-wait-forked-busy +kms_vblank@pipe-H-wait-forked-busy-hang +kms_vblank@pipe-H-ts-continuation-idle +kms_vblank@pipe-H-ts-continuation-idle-hang +kms_vblank@pipe-H-ts-continuation-dpms-rpm +kms_vblank@pipe-H-ts-continuation-dpms-suspend +kms_vblank@pipe-H-ts-continuation-suspend +kms_vblank@pipe-H-ts-continuation-modeset +kms_vblank@pipe-H-ts-continuation-modeset-hang +kms_vblank@pipe-H-ts-continuation-modeset-rpm +kms_vrr@flip-basic +kms_vrr@flip-dpms +kms_vrr@flip-suspend +kms_vrr@flipline +kms_vrr@negative-basic +kms_writeback@writeback-pixel-formats +kms_writeback@writeback-invalid-parameters +kms_writeback@writeback-fb-id +kms_writeback@writeback-check-output +prime_mmap_kms@buffer-sharing diff --git a/drivers/gpu/drm/ci/x86_64.config b/drivers/gpu/drm/ci/x86_64.config new file mode 100644 index 0000000000000000000000000000000000000000..1cbd49a5b23af7fae3f79f03a19cede26788a6f7 --- /dev/null +++ b/drivers/gpu/drm/ci/x86_64.config @@ -0,0 +1,111 @@ +CONFIG_LOCALVERSION_AUTO=y +CONFIG_DEBUG_KERNEL=y + +CONFIG_CRYPTO_ZSTD=y +CONFIG_ZRAM_MEMORY_TRACKING=y +CONFIG_ZRAM_WRITEBACK=y +CONFIG_ZRAM=y +CONFIG_ZSMALLOC_STAT=y + +CONFIG_PWM=y +CONFIG_PM_DEVFREQ=y +CONFIG_OF=y +CONFIG_CROS_EC=y + +# abootimg with a 'dummy' rootfs fails with root=/dev/nfs +CONFIG_BLK_DEV_INITRD=n + +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y + +CONFIG_DRM=y +CONFIG_DRM_PANEL_SIMPLE=y +CONFIG_PWM_CROS_EC=y +CONFIG_BACKLIGHT_PWM=y + +# Strip out some stuff we don't need for graphics testing, to reduce +# the build. +CONFIG_CAN=n +CONFIG_WIRELESS=n +CONFIG_RFKILL=n +CONFIG_WLAN=n + +CONFIG_REGULATOR_FAN53555=y +CONFIG_REGULATOR=y + +CONFIG_REGULATOR_VCTRL=y + +CONFIG_KASAN=n +CONFIG_KASAN_INLINE=n +CONFIG_STACKTRACE=n + +CONFIG_TMPFS=y + +CONFIG_PROVE_LOCKING=n +CONFIG_DEBUG_LOCKDEP=n +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y + +CONFIG_DETECT_HUNG_TASK=y + +CONFIG_USB_USBNET=y +CONFIG_NETDEVICES=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_RTL8152=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_SMSC95XX=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=y + +CONFIG_FW_LOADER_COMPRESS=y + +# options for AMD devices +CONFIG_X86_AMD_PLATFORM_DEVICE=y +CONFIG_ACPI_VIDEO=y +CONFIG_X86_AMD_FREQ_SENSITIVITY=y +CONFIG_PINCTRL=y +CONFIG_PINCTRL_AMD=y +CONFIG_DRM_AMDGPU=m +CONFIG_DRM_AMDGPU_SI=y +CONFIG_DRM_AMDGPU_USERPTR=y +CONFIG_DRM_AMD_ACP=n +CONFIG_ACPI_WMI=y +CONFIG_MXM_WMI=y +CONFIG_PARPORT=y +CONFIG_PARPORT_PC=y +CONFIG_PARPORT_SERIAL=y +CONFIG_SERIAL_8250_DW=y +CONFIG_CHROME_PLATFORMS=y +CONFIG_KVM_AMD=m + +#options for Intel devices +CONFIG_MFD_INTEL_LPSS_PCI=y +CONFIG_KVM_INTEL=m + +#options for KVM guests +CONFIG_FUSE_FS=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_KVM=y +CONFIG_KVM_GUEST=y +CONFIG_VIRT_DRIVERS=y +CONFIG_VIRTIO_FS=y +CONFIG_DRM_VIRTIO_GPU=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_PARAVIRT=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTUALIZATION=y +CONFIG_VIRTIO=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y +CONFIG_CRYPTO_DEV_VIRTIO=y +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_BLK_MQ_VIRTIO=y +CONFIG_TUN=y +CONFIG_VSOCKETS=y +CONFIG_VIRTIO_VSOCKETS=y +CONFIG_VHOST_VSOCK=m diff --git a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd9392536e7ce8d29e4644d647469ea9ac29afc2 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-fails.txt @@ -0,0 +1,19 @@ +kms_addfb_basic@bad-pitch-65536,Fail +kms_addfb_basic@bo-too-small,Fail +kms_async_flips@invalid-async-flip,Fail +kms_atomic@plane-immutable-zpos,Fail +kms_atomic_transition@plane-toggle-modeset-transition,Fail +kms_bw@linear-tiling-1-displays-2560x1440p,Fail +kms_bw@linear-tiling-1-displays-3840x2160p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-1920x1080p,Fail +kms_color@degamma,Fail +kms_cursor_crc@cursor-size-change,Fail +kms_cursor_crc@pipe-A-cursor-size-change,Fail +kms_cursor_crc@pipe-B-cursor-size-change,Fail +kms_cursor_legacy@forked-move,Fail +kms_hdr@bpc-switch,Fail +kms_hdr@bpc-switch-dpms,Fail +kms_plane_multiple@atomic-pipe-A-tiling-none,Fail +kms_rmfb@close-fd,Fail +kms_rotation_crc@primary-rotation-180,Fail diff --git a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..f8defa0f9e6733414758f0050ba8be5521cceefb --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-flakes.txt @@ -0,0 +1,21 @@ +kms_addfb_basic@too-high +kms_async_flips@alternate-sync-async-flip +kms_async_flips@async-flip-with-page-flip-events +kms_async_flips@crc +kms_async_flips@test-cursor +kms_async_flips@test-time-stamp +kms_atomic_transition@plane-all-modeset-transition-internal-panels +kms_atomic_transition@plane-all-transition +kms_atomic_transition@plane-use-after-nonblocking-unbind +kms_bw@linear-tiling-1-displays-1920x1080p +kms_bw@linear-tiling-2-displays-1920x1080p +kms_bw@linear-tiling-2-displays-2560x1440p +kms_bw@linear-tiling-3-displays-2560x1440p +kms_bw@linear-tiling-3-displays-3840x2160p +kms_cursor_crc@pipe-A-cursor-alpha-opaque +kms_cursor_crc@pipe-B-cursor-alpha-opaque +kms_plane@pixel-format +kms_plane_multiple@atomic-pipe-B-tiling-none +kms_plane_scaling@downscale-with-rotation-factor-0-5 +kms_universal_plane@disable-primary-vs-flip-pipe-A +kms_universal_plane@disable-primary-vs-flip-pipe-B diff --git a/drivers/gpu/drm/ci/xfails/amdgpu-stoney-skips.txt b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..e2c538a0f954dc0d6cd9399cbaaf4c80f9731a17 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/amdgpu-stoney-skips.txt @@ -0,0 +1,2 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt b/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f513c638beb2d3a0ea10166961c3badccba39fa --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-amly-fails.txt @@ -0,0 +1,17 @@ +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-amly-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-amly-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..d5000515a3157840a6a3c8917cf133cf30ed8f43 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-amly-flakes.txt @@ -0,0 +1,32 @@ +kms_bw@linear-tiling-2-displays-1920x1080p +kms_bw@linear-tiling-2-displays-2560x1440p +kms_bw@linear-tiling-2-displays-3840x2160p +kms_bw@linear-tiling-3-displays-1920x1080p +kms_bw@linear-tiling-3-displays-2560x1440p +kms_bw@linear-tiling-3-displays-3840x2160p +kms_bw@linear-tiling-4-displays-1920x1080p +kms_bw@linear-tiling-4-displays-2560x1440p +kms_bw@linear-tiling-4-displays-3840x2160p +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling +kms_plane_alpha_blend@pipe-A-alpha-basic +kms_plane_alpha_blend@pipe-A-alpha-opaque-fb +kms_plane_alpha_blend@pipe-A-alpha-transparent-fb +kms_plane_alpha_blend@pipe-A-constant-alpha-max +kms_plane_alpha_blend@pipe-B-alpha-basic +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb +kms_plane_alpha_blend@pipe-B-alpha-transparent-fb +kms_plane_alpha_blend@pipe-B-constant-alpha-max +kms_plane_alpha_blend@pipe-C-alpha-basic +kms_plane_alpha_blend@pipe-C-alpha-opaque-fb +kms_plane_alpha_blend@pipe-C-alpha-transparent-fb +kms_plane_alpha_blend@pipe-C-constant-alpha-max +kms_sysfs_edid_timing diff --git a/drivers/gpu/drm/ci/xfails/i915-amly-skips.txt b/drivers/gpu/drm/ci/xfails/i915-amly-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..fe55540a3f9a767dce5d0ecdfdc1e18d525ddd58 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-amly-skips.txt @@ -0,0 +1,4 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..46397ce38d5aa1c0bab6373d933431a76fa6321c --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-apl-fails.txt @@ -0,0 +1,58 @@ +kms_3d,Timeout +kms_bw@linear-tiling-2-displays-1920x1080p,Fail +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-1920x1080p,Fail +kms_bw@linear-tiling-3-displays-2560x1440p,Fail +kms_bw@linear-tiling-3-displays-3840x2160p,Fail +kms_bw@linear-tiling-4-displays-1920x1080p,Fail +kms_bw@linear-tiling-4-displays-2560x1440p,Fail +kms_bw@linear-tiling-4-displays-3840x2160p,Fail +kms_color@ctm-0-25,Fail +kms_color@ctm-0-50,Fail +kms_color@ctm-0-75,Fail +kms_color@ctm-max,Fail +kms_color@ctm-negative,Fail +kms_color@ctm-red-to-blue,Fail +kms_color@ctm-signed,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling,Fail +kms_hdmi_inject@inject-4k,Timeout +kms_plane@plane-position-hole,Timeout +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-A-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-A-alpha-transparent-fb,Fail +kms_plane_alpha_blend@pipe-A-constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-B-alpha-transparent-fb,Fail +kms_plane_alpha_blend@pipe-B-constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-C-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-C-alpha-transparent-fb,Fail +kms_plane_alpha_blend@pipe-C-constant-alpha-max,Fail +kms_plane_multiple@tiling-y,Timeout +kms_pwrite_crc,Timeout +kms_sysfs_edid_timing,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-apl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-apl-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..331c5841bb4115f5031afbb37869fcec49164a04 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-apl-flakes.txt @@ -0,0 +1 @@ +kms_frontbuffer_tracking@fbc-tiling-linear diff --git a/drivers/gpu/drm/ci/xfails/i915-apl-skips.txt b/drivers/gpu/drm/ci/xfails/i915-apl-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..3430b215c06e04cbbec3de5ab0a665b17b4fd7da --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-apl-skips.txt @@ -0,0 +1,6 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters +# This is cascading issues +kms_3d \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt b/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..6139b410e7674a440562623e047c8b6364a0adf9 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-cml-fails.txt @@ -0,0 +1,18 @@ +kms_color@ctm-0-25,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..0514a7b3fdb04414d06f9f70305d902ea05665da --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-cml-flakes.txt @@ -0,0 +1,38 @@ +kms_bw@linear-tiling-2-displays-1920x1080p +kms_bw@linear-tiling-2-displays-2560x1440p +kms_bw@linear-tiling-2-displays-3840x2160p +kms_bw@linear-tiling-3-displays-1920x1080p +kms_bw@linear-tiling-3-displays-2560x1440p +kms_bw@linear-tiling-3-displays-3840x2160p +kms_bw@linear-tiling-4-displays-1920x1080p +kms_bw@linear-tiling-4-displays-2560x1440p +kms_bw@linear-tiling-4-displays-3840x2160p +kms_draw_crc@draw-method-xrgb8888-render-xtiled +kms_flip@flip-vs-suspend +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling +kms_hdr@bpc-switch-suspend +kms_plane_alpha_blend@constant-alpha-min +kms_plane_alpha_blend@pipe-A-alpha-basic +kms_plane_alpha_blend@pipe-A-alpha-opaque-fb +kms_plane_alpha_blend@pipe-A-alpha-transparent-fb +kms_plane_alpha_blend@pipe-A-constant-alpha-max +kms_plane_alpha_blend@pipe-B-alpha-basic +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb +kms_plane_alpha_blend@pipe-B-alpha-transparent-fb +kms_plane_alpha_blend@pipe-B-constant-alpha-max +kms_plane_alpha_blend@pipe-C-alpha-basic +kms_plane_alpha_blend@pipe-C-alpha-opaque-fb +kms_plane_alpha_blend@pipe-C-alpha-transparent-fb +kms_plane_alpha_blend@pipe-C-constant-alpha-max +kms_psr2_su@page_flip-NV12 +kms_psr2_su@page_flip-P010 +kms_setmode@basic diff --git a/drivers/gpu/drm/ci/xfails/i915-cml-skips.txt b/drivers/gpu/drm/ci/xfails/i915-cml-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d3d7ddc377f8318c0b3876e13e989e011c91776 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-cml-skips.txt @@ -0,0 +1,2 @@ +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters diff --git a/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt b/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..5bd432e78129d27ce760ff220427d66ec538341a --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-glk-fails.txt @@ -0,0 +1,19 @@ +kms_fbcon_fbt@fbc,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_frontbuffer_tracking@fbcdrrs-tiling-linear,Fail +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-glk-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-glk-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc41d13a2d566945ee3da6b7059e05db2e60d15b --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-glk-flakes.txt @@ -0,0 +1,41 @@ +kms_bw@linear-tiling-1-displays-3840x2160p +kms_bw@linear-tiling-2-displays-1920x1080p +kms_bw@linear-tiling-2-displays-2560x1440p +kms_bw@linear-tiling-2-displays-3840x2160p +kms_bw@linear-tiling-3-displays-1920x1080p +kms_bw@linear-tiling-3-displays-2560x1440p +kms_bw@linear-tiling-3-displays-3840x2160p +kms_bw@linear-tiling-4-displays-1920x1080p +kms_bw@linear-tiling-4-displays-2560x1440p +kms_bw@linear-tiling-4-displays-3840x2160p +kms_flip@blocking-wf_vblank +kms_flip@wf_vblank-ts-check +kms_flip@wf_vblank-ts-check-interruptible +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling +kms_frontbuffer_tracking@fbc-tiling-linear +kms_plane_alpha_blend@pipe-A-alpha-basic +kms_plane_alpha_blend@pipe-A-alpha-opaque-fb +kms_plane_alpha_blend@pipe-A-alpha-transparent-fb +kms_plane_alpha_blend@pipe-A-constant-alpha-max +kms_plane_alpha_blend@pipe-B-alpha-basic +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb +kms_plane_alpha_blend@pipe-B-alpha-transparent-fb +kms_plane_alpha_blend@pipe-B-constant-alpha-max +kms_plane_alpha_blend@pipe-C-alpha-basic +kms_plane_alpha_blend@pipe-C-alpha-opaque-fb +kms_plane_alpha_blend@pipe-C-alpha-transparent-fb +kms_plane_alpha_blend@pipe-C-constant-alpha-max +kms_prop_blob@invalid-set-prop-any +kms_rotation_crc@multiplane-rotation +kms_rotation_crc@multiplane-rotation-cropping-bottom +kms_rotation_crc@multiplane-rotation-cropping-top +kms_setmode@basic diff --git a/drivers/gpu/drm/ci/xfails/i915-glk-skips.txt b/drivers/gpu/drm/ci/xfails/i915-glk-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c7d00ce14bc0d14a054f6d15b9bac0fce763159 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-glk-skips.txt @@ -0,0 +1,5 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..56ec021a7679fff721aabbfe73028b760a7d7703 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-kbl-fails.txt @@ -0,0 +1,25 @@ +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-4-displays-2560x1440p,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling,Fail +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-A-constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-C-constant-alpha-max,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-kbl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-kbl-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..f3ba1c4c5d460b7a853ba0147756cf41722778e7 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-kbl-flakes.txt @@ -0,0 +1,26 @@ +kms_async_flips@crc +kms_bw@linear-tiling-2-displays-1920x1080p +kms_bw@linear-tiling-2-displays-3840x2160p +kms_bw@linear-tiling-3-displays-1920x1080p +kms_bw@linear-tiling-3-displays-2560x1440p +kms_bw@linear-tiling-3-displays-3840x2160p +kms_bw@linear-tiling-4-displays-1920x1080p +kms_bw@linear-tiling-4-displays-3840x2160p +kms_color@ctm-0-25 +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling +kms_plane_alpha_blend@pipe-A-alpha-basic +kms_plane_alpha_blend@pipe-A-alpha-opaque-fb +kms_plane_alpha_blend@pipe-A-alpha-transparent-fb +kms_plane_alpha_blend@pipe-B-alpha-basic +kms_plane_alpha_blend@pipe-B-alpha-transparent-fb +kms_plane_alpha_blend@pipe-B-constant-alpha-max +kms_plane_alpha_blend@pipe-C-alpha-basic +kms_plane_alpha_blend@pipe-C-alpha-opaque-fb +kms_plane_alpha_blend@pipe-C-alpha-transparent-fb +kms_sysfs_edid_timing diff --git a/drivers/gpu/drm/ci/xfails/i915-kbl-skips.txt b/drivers/gpu/drm/ci/xfails/i915-kbl-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c7d00ce14bc0d14a054f6d15b9bac0fce763159 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-kbl-skips.txt @@ -0,0 +1,5 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..a6da5544e1985348e98bc9ab9e7c79346afd644f --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-tgl-fails.txt @@ -0,0 +1,37 @@ +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-1920x1080p,Fail +kms_bw@linear-tiling-3-displays-2560x1440p,Fail +kms_bw@linear-tiling-3-displays-3840x2160p,Fail +kms_bw@linear-tiling-4-displays-1920x1080p,Fail +kms_bw@linear-tiling-4-displays-2560x1440p,Fail +kms_bw@linear-tiling-4-displays-3840x2160p,Fail +kms_bw@linear-tiling-5-displays-1920x1080p,Fail +kms_bw@linear-tiling-5-displays-2560x1440p,Fail +kms_bw@linear-tiling-5-displays-3840x2160p,Fail +kms_color@ctm-0-25,Fail +kms_flip@flip-vs-panning-vs-hang,Timeout +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling,Fail +kms_rotation_crc@bad-pixel-format,Fail +kms_rotation_crc@multiplane-rotation,Fail +kms_rotation_crc@multiplane-rotation-cropping-bottom,Fail +kms_rotation_crc@multiplane-rotation-cropping-top,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..1cd910ee06df6dc45d519aaa8d516d2655f9a47c --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-tgl-flakes.txt @@ -0,0 +1,5 @@ +kms_draw_crc@.* +kms_flip@blocking-absolute-wf_vblank +kms_flip@bo-too-big-interruptible +kms_flip@busy-flip +kms_flip@flip-vs-rmfb-interruptible diff --git a/drivers/gpu/drm/ci/xfails/i915-tgl-skips.txt b/drivers/gpu/drm/ci/xfails/i915-tgl-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d0621750b14e2e67e081b62228a327f5c5f9544 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-tgl-skips.txt @@ -0,0 +1,11 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# GPU hangs, then the whole machine +gem_eio.* + +# Whole machine hangs +kms_flip@absolute-wf_vblank@a-edp1 + +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt b/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..967327ddc1ac4cf8c53a643d9e8e31ba4b480387 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-whl-fails.txt @@ -0,0 +1,48 @@ +kms_bw@linear-tiling-2-displays-1920x1080p,Fail +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-1920x1080p,Fail +kms_bw@linear-tiling-3-displays-2560x1440p,Fail +kms_bw@linear-tiling-3-displays-3840x2160p,Fail +kms_bw@linear-tiling-4-displays-1920x1080p,Fail +kms_bw@linear-tiling-4-displays-2560x1440p,Fail +kms_bw@linear-tiling-4-displays-3840x2160p,Fail +kms_fbcon_fbt@fbc,Fail +kms_fbcon_fbt@fbc-suspend,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-linear-to-64bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-xtile-to-64bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytile-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-32bpp-ytileccs-to-64bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-16bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-linear-to-32bpp-linear-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-16bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-xtile-to-32bpp-xtile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-16bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytile-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilegen12rcccs-upscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-downscaling,Fail +kms_flip_scaled_crc@flip-64bpp-ytile-to-32bpp-ytilercccs-upscaling,Fail +kms_frontbuffer_tracking@fbc-tiling-linear,Fail +kms_plane_alpha_blend@alpha-basic,Fail +kms_plane_alpha_blend@alpha-opaque-fb,Fail +kms_plane_alpha_blend@alpha-transparent-fb,Fail +kms_plane_alpha_blend@constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-A-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-A-alpha-transparent-fb,Fail +kms_plane_alpha_blend@pipe-A-constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-B-alpha-transparent-fb,Fail +kms_plane_alpha_blend@pipe-B-constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-C-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-C-alpha-transparent-fb,Fail +kms_plane_alpha_blend@pipe-C-constant-alpha-max,Fail diff --git a/drivers/gpu/drm/ci/xfails/i915-whl-flakes.txt b/drivers/gpu/drm/ci/xfails/i915-whl-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..c33202e7e2a1dd6f79326e278890b566470c1f2e --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-whl-flakes.txt @@ -0,0 +1 @@ +kms_flip@flip-vs-suspend diff --git a/drivers/gpu/drm/ci/xfails/i915-whl-skips.txt b/drivers/gpu/drm/ci/xfails/i915-whl-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..f3be0888a2142f8d7f80d01c3b46f1c2f95f2743 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/i915-whl-skips.txt @@ -0,0 +1,2 @@ +# This is generating kernel oops with divide error +kms_plane_scaling@invalid-parameters \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..671916067dbaf55936c0e5e6688b6529cc7570b3 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-fails.txt @@ -0,0 +1,29 @@ +kms_3d,Fail +kms_addfb_basic@addfb25-bad-modifier,Fail +kms_bw@linear-tiling-1-displays-1920x1080p,Fail +kms_bw@linear-tiling-1-displays-2560x1440p,Fail +kms_bw@linear-tiling-1-displays-3840x2160p,Fail +kms_bw@linear-tiling-2-displays-1920x1080p,Fail +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-1920x1080p,Fail +kms_bw@linear-tiling-3-displays-2560x1440p,Fail +kms_bw@linear-tiling-3-displays-3840x2160p,Fail +kms_color@pipe-A-invalid-gamma-lut-sizes,Fail +kms_color@pipe-B-invalid-gamma-lut-sizes,Fail +kms_force_connector_basic@force-connector-state,Fail +kms_force_connector_basic@force-edid,Fail +kms_force_connector_basic@force-load-detect,Fail +kms_force_connector_basic@prune-stale-modes,Fail +kms_invalid_mode@int-max-clock,Fail +kms_plane_scaling@planes-upscale-20x20,Fail +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-25,Fail +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-5,Fail +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-75,Fail +kms_plane_scaling@upscale-with-modifier-20x20,Fail +kms_plane_scaling@upscale-with-pixel-format-20x20,Fail +kms_plane_scaling@upscale-with-rotation-20x20,Fail +kms_properties@get_properties-sanity-atomic,Fail +kms_properties@plane-properties-atomic,Fail +kms_properties@plane-properties-legacy,Fail +kms_rmfb@close-fd,Fail diff --git a/drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt b/drivers/gpu/drm/ci/xfails/mediatek-mt8173-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/drivers/gpu/drm/ci/xfails/mediatek-mt8183-fails.txt b/drivers/gpu/drm/ci/xfails/mediatek-mt8183-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..6ff81d00e84e042109cad6d8d29c7d4a578515e1 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/mediatek-mt8183-fails.txt @@ -0,0 +1,10 @@ +kms_addfb_basic@addfb25-bad-modifier,Fail +kms_bw@linear-tiling-1-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-1920x1080p,Fail +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-2560x1440p,Fail +kms_bw@linear-tiling-3-displays-3840x2160p,Fail +kms_color@pipe-A-invalid-gamma-lut-sizes,Fail +kms_plane_scaling@upscale-with-rotation-20x20,Fail +kms_rmfb@close-fd,Fail \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/mediatek-mt8183-flakes.txt b/drivers/gpu/drm/ci/xfails/mediatek-mt8183-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..208890b79eb0e362e7d991502d5c034a1ff54729 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/mediatek-mt8183-flakes.txt @@ -0,0 +1,14 @@ +core_setmaster_vs_auth +kms_bw@linear-tiling-1-displays-1920x1080p +kms_bw@linear-tiling-1-displays-3840x2160p +kms_bw@linear-tiling-3-displays-1920x1080p +kms_cursor_legacy@cursor-vs-flip-atomic +kms_plane_scaling@invalid-num-scalers +kms_plane_scaling@planes-upscale-20x20 +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-5 +kms_plane_scaling@upscale-with-modifier-20x20 +kms_plane_scaling@upscale-with-pixel-format-20x20 +kms_prop_blob@invalid-set-prop-any +kms_properties@get_properties-sanity-atomic +kms_properties@plane-properties-atomic +kms_properties@plane-properties-legacy \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/meson-g12b-fails.txt b/drivers/gpu/drm/ci/xfails/meson-g12b-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..860e702091e2036bba8d30bbda3cac0614084041 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/meson-g12b-fails.txt @@ -0,0 +1,12 @@ +kms_3d,Fail +kms_properties@connector-properties-atomic,Fail +kms_properties@get_properties-sanity-atomic,Fail +kms_properties@get_properties-sanity-non-atomic,Fail +kms_properties@connector-properties-legacy,Fail +kms_cursor_legacy@forked-bo,Fail +kms_cursor_legacy@forked-move,Fail +kms_cursor_legacy@single-bo,Fail +kms_cursor_legacy@single-move,Fail +kms_cursor_legacy@torture-bo,Fail +kms_cursor_legacy@torture-move,Fail +kms_hdmi_inject@inject-4k,Fail \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/meson-g12b-flakes.txt b/drivers/gpu/drm/ci/xfails/meson-g12b-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..b63329d0676781a9abe6b916db54ea74e15ea9af --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/meson-g12b-flakes.txt @@ -0,0 +1,4 @@ +kms_force_connector_basic@force-connector-state +kms_force_connector_basic@force-edid +kms_force_connector_basic@force-load-detect +kms_force_connector_basic@prune-stale-modes \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt b/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..9981682feab281eb07a70d702dec029a339063ef --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-apq8016-fails.txt @@ -0,0 +1,15 @@ +kms_3d,Fail +kms_addfb_basic@addfb25-bad-modifier,Fail +kms_cursor_legacy@all-pipes-forked-bo,Fail +kms_cursor_legacy@all-pipes-forked-move,Fail +kms_cursor_legacy@all-pipes-single-bo,Fail +kms_cursor_legacy@all-pipes-single-move,Fail +kms_cursor_legacy@all-pipes-torture-bo,Fail +kms_cursor_legacy@all-pipes-torture-move,Fail +kms_cursor_legacy@pipe-A-forked-bo,Fail +kms_cursor_legacy@pipe-A-forked-move,Fail +kms_cursor_legacy@pipe-A-single-bo,Fail +kms_cursor_legacy@pipe-A-single-move,Fail +kms_cursor_legacy@pipe-A-torture-bo,Fail +kms_cursor_legacy@pipe-A-torture-move,Fail +kms_hdmi_inject@inject-4k,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8016-flakes.txt b/drivers/gpu/drm/ci/xfails/msm-apq8016-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e3b60d3fadef4fe889f845b58e5f9bec8bc9017 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-apq8016-flakes.txt @@ -0,0 +1,4 @@ +kms_force_connector_basic@force-connector-state +kms_force_connector_basic@force-edid +kms_force_connector_basic@force-load-detect +kms_force_connector_basic@prune-stale-modes diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt b/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..88a1fc0a3b0de2f0d7583ec0fd0741006c4849c6 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-apq8096-fails.txt @@ -0,0 +1,2 @@ +kms_3d,Fail +kms_addfb_basic@addfb25-bad-modifier,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8096-flakes.txt b/drivers/gpu/drm/ci/xfails/msm-apq8096-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e3b60d3fadef4fe889f845b58e5f9bec8bc9017 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-apq8096-flakes.txt @@ -0,0 +1,4 @@ +kms_force_connector_basic@force-connector-state +kms_force_connector_basic@force-edid +kms_force_connector_basic@force-load-detect +kms_force_connector_basic@prune-stale-modes diff --git a/drivers/gpu/drm/ci/xfails/msm-apq8096-skips.txt b/drivers/gpu/drm/ci/xfails/msm-apq8096-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd49c8ce205921521dd6c890a98ba052b61a795f --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-apq8096-skips.txt @@ -0,0 +1,2 @@ +# Whole machine hangs +kms_cursor_legacy@all-pipes-torture-move \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/msm-sc7180-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sc7180-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..14adeba3b62dc9864666c94ca3030f86b6c55226 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sc7180-fails.txt @@ -0,0 +1,25 @@ +kms_cursor_legacy@cursor-vs-flip-toggle,Fail +kms_cursor_legacy@cursor-vs-flip-varying-size,Fail +kms_cursor_legacy@cursorA-vs-flipA-atomic-transitions,Crash +kms_pipe_crc_basic@compare-crc-sanitycheck-nv12,Fail +kms_plane@pixel-format,Fail +kms_plane@pixel-format-source-clamping,Fail +kms_plane@plane-position-covered,Fail +kms_plane@plane-position-hole,Fail +kms_plane@plane-position-hole-dpms,Fail +kms_plane_alpha_blend@alpha-7efc,Fail +kms_plane_alpha_blend@coverage-7efc,Fail +kms_plane_alpha_blend@coverage-vs-premult-vs-constant,Fail +kms_plane_alpha_blend@pipe-A-alpha-7efc,Fail +kms_plane_alpha_blend@pipe-A-coverage-7efc,Fail +kms_plane_alpha_blend@pipe-A-coverage-vs-premult-vs-constant,Fail +kms_plane_alpha_blend@pipe-B-alpha-7efc,Fail +kms_plane_alpha_blend@pipe-B-alpha-basic,Fail +kms_plane_alpha_blend@pipe-B-alpha-opaque-fb,Fail +kms_plane_alpha_blend@pipe-B-constant-alpha-max,Fail +kms_plane_alpha_blend@pipe-B-constant-alpha-mid,Fail +kms_plane_alpha_blend@pipe-B-coverage-7efc,Fail +kms_plane_alpha_blend@pipe-B-coverage-vs-premult-vs-constant,Fail +kms_rmfb@close-fd,Fail +kms_universal_plane@disable-primary-vs-flip-pipe-b,Fail +kms_universal_plane@universal-plane-pipe-B-sanity,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-sc7180-flakes.txt b/drivers/gpu/drm/ci/xfails/msm-sc7180-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..636563d3e59aa888c2c757876390bd8675e5995a --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sc7180-flakes.txt @@ -0,0 +1,7 @@ + +# Test ends up reading CRC from frame before cursor update +# bug +# sometimes.. tbd if this is a kernel CRC bug or a test +kms_cursor_crc@.* +kms_plane_multiple@atomic-pipe-A-tiling-none +kms_atomic_transition@modeset-transition-nonblocking-fencing,Fail \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/msm-sc7180-skips.txt b/drivers/gpu/drm/ci/xfails/msm-sc7180-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..410e0eeb3161f505d4031d6075baee83d5c91712 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sc7180-skips.txt @@ -0,0 +1,23 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# Test incorrectly assumes that CTM support implies gamma/degamma +# LUT support. None of the subtests handle the case of only having +# CTM support +kms_color.* + +# 4k@60 is not supported on this hw, but driver doesn't handle it +# too gracefully.. https://gitlab.freedesktop.org/drm/msm/-/issues/15 +kms_bw@linear-tiling-.*-displays-3840x2160p + +# Until igt fix lands: https://patchwork.freedesktop.org/patch/493175/ +kms_bw@linear-tiling-2.* +kms_bw@linear-tiling-3.* +kms_bw@linear-tiling-4.* +kms_bw@linear-tiling-5.* +kms_bw@linear-tiling-6.* + +# igt fix posted: https://patchwork.freedesktop.org/patch/499926/ +# failure mode is flakey due to randomization but fails frequently +# enough to be detected as a Crash or occasionally UnexpectedPass. +kms_plane_multiple@atomic-pipe-A-tiling-none diff --git a/drivers/gpu/drm/ci/xfails/msm-sdm845-fails.txt b/drivers/gpu/drm/ci/xfails/msm-sdm845-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..09c0c623cd758ea66ba44cb18410647ba12822ac --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sdm845-fails.txt @@ -0,0 +1,68 @@ +kms_color@ctm-0-25,Fail +kms_color@ctm-0-50,Fail +kms_color@ctm-0-75,Fail +kms_color@ctm-blue-to-red,Fail +kms_color@ctm-green-to-red,Fail +kms_color@ctm-negative,Fail +kms_color@ctm-red-to-blue,Fail +kms_color@ctm-signed,Fail +kms_color@pipe-A-ctm-0-25,Fail +kms_color@pipe-A-ctm-0-5,Fail +kms_color@pipe-A-ctm-0-75,Fail +kms_color@pipe-A-ctm-blue-to-red,Fail +kms_color@pipe-A-ctm-green-to-red,Fail +kms_color@pipe-A-ctm-max,Fail +kms_color@pipe-A-ctm-negative,Fail +kms_color@pipe-A-ctm-red-to-blue,Fail +kms_color@pipe-A-legacy-gamma,Fail +kms_cursor_legacy@basic-flip-after-cursor-atomic,Fail +kms_cursor_legacy@basic-flip-after-cursor-legacy,Fail +kms_cursor_legacy@basic-flip-after-cursor-varying-size,Fail +kms_cursor_legacy@basic-flip-before-cursor-atomic,Fail +kms_cursor_legacy@basic-flip-before-cursor-legacy,Fail +kms_cursor_legacy@basic-flip-before-cursor-varying-size,Fail +kms_cursor_legacy@cursor-vs-flip-atomic,Fail +kms_cursor_legacy@cursor-vs-flip-atomic-transitions,Fail +kms_cursor_legacy@cursor-vs-flip-atomic-transitions-varying-size,Fail +kms_cursor_legacy@cursor-vs-flip-legacy,Fail +kms_cursor_legacy@cursor-vs-flip-toggle,Fail +kms_cursor_legacy@cursor-vs-flip-varying-size,Fail +kms_cursor_legacy@cursorA-vs-flipA-toggle,Fail +kms_cursor_legacy@flip-vs-cursor-atomic,Fail +kms_cursor_legacy@flip-vs-cursor-crc-atomic,Fail +kms_cursor_legacy@flip-vs-cursor-crc-legacy,Fail +kms_cursor_legacy@flip-vs-cursor-legacy,Fail +kms_cursor_legacy@short-flip-after-cursor-atomic-transitions,Fail +kms_cursor_legacy@short-flip-after-cursor-atomic-transitions-varying-size,Fail +kms_cursor_legacy@short-flip-after-cursor-toggle,Fail +kms_cursor_legacy@short-flip-before-cursor-atomic-transitions,Fail +kms_cursor_legacy@short-flip-before-cursor-atomic-transitions-varying-size,Fail +kms_pipe_crc_basic@compare-crc-sanitycheck-nv12,Fail +kms_plane@pixel-format,Fail +kms_plane@pixel-format-source-clamping,Fail +kms_plane_alpha_blend@alpha-7efc,Fail +kms_plane_alpha_blend@coverage-7efc,Fail +kms_plane_alpha_blend@coverage-vs-premult-vs-constant,Fail +kms_plane_alpha_blend@pipe-A-alpha-7efc,Fail +kms_plane_alpha_blend@pipe-A-coverage-7efc,Fail +kms_plane_alpha_blend@pipe-A-coverage-vs-premult-vs-constant,Fail +kms_plane_cursor@overlay,Fail +kms_plane_cursor@pipe-A-overlay-size-128,Fail +kms_plane_cursor@pipe-A-overlay-size-256,Fail +kms_plane_cursor@pipe-A-overlay-size-64,Fail +kms_plane_cursor@pipe-A-viewport-size-128,Fail +kms_plane_cursor@pipe-A-viewport-size-256,Fail +kms_plane_cursor@pipe-A-viewport-size-64,Fail +kms_plane_cursor@viewport,Fail +kms_plane_scaling@downscale-with-pixel-format-factor-0-25,Timeout +kms_plane_scaling@downscale-with-pixel-format-factor-0-5,Timeout +kms_plane_scaling@downscale-with-pixel-format-factor-0-75,Timeout +kms_plane_scaling@plane-downscale-with-pixel-format-factor-0-25,Timeout +kms_plane_scaling@plane-downscale-with-pixel-format-factor-0-5,Timeout +kms_plane_scaling@plane-downscale-with-pixel-format-factor-0-75,Timeout +kms_plane_scaling@plane-scaler-with-clipping-clamping-pixel-formats,Timeout +kms_plane_scaling@plane-scaler-with-pixel-format-unity-scaling,Timeout +kms_plane_scaling@planes-downscale-factor-0-25,Fail +kms_plane_scaling@scaler-with-clipping-clamping,Timeout +kms_plane_scaling@scaler-with-pixel-format-unity-scaling,Timeout +kms_rmfb@close-fd,Fail diff --git a/drivers/gpu/drm/ci/xfails/msm-sdm845-flakes.txt b/drivers/gpu/drm/ci/xfails/msm-sdm845-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..5b3aaab7ac3f6ca3a3b8fd472d75811eb1764f72 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sdm845-flakes.txt @@ -0,0 +1,11 @@ + + +# Test ends up reading CRC from frame before cursor update +# bug +# sometimes.. tbd if this is a kernel CRC bug or a test +kms_cursor_crc@.* +kms_cursor_legacy@flip-vs-cursor-toggle +kms_cursor_legacy@pipe-A-forked-bo +kms_cursor_legacy@pipe-A-forked-move +kms_cursor_legacy@short-flip-before-cursor-toggle +kms_flip@dpms-vs-vblank-race-interruptible diff --git a/drivers/gpu/drm/ci/xfails/msm-sdm845-skips.txt b/drivers/gpu/drm/ci/xfails/msm-sdm845-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..42675f1c6d762c819390744c9c0dfec92cb3e203 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/msm-sdm845-skips.txt @@ -0,0 +1,2 @@ +# Hangs machine +kms_bw.* \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..2a1baa948e12b46450d728d746ee53b291ea2176 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-fails.txt @@ -0,0 +1,48 @@ +kms_3d,Crash +kms_bw@linear-tiling-2-displays-1920x1080p,Fail +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_bw@linear-tiling-3-displays-1920x1080p,Fail +kms_bw@linear-tiling-3-displays-2560x1440p,Fail +kms_bw@linear-tiling-3-displays-3840x2160p,Fail +kms_force_connector_basic@force-load-detect,Fail +kms_invalid_mode@int-max-clock,Crash +kms_plane@pixel-format,Crash +kms_plane@pixel-format-source-clamping,Crash +kms_plane@plane-position-hole,Crash +kms_plane@plane-position-hole-dpms,Crash +kms_plane_cursor@overlay,Crash +kms_plane_cursor@pipe-A-overlay-size-128,Fail +kms_plane_cursor@pipe-A-overlay-size-256,Fail +kms_plane_cursor@pipe-A-overlay-size-64,Fail +kms_plane_cursor@pipe-A-primary-size-128,Fail +kms_plane_cursor@pipe-A-primary-size-256,Fail +kms_plane_cursor@pipe-A-primary-size-64,Fail +kms_plane_cursor@pipe-A-viewport-size-128,Fail +kms_plane_cursor@pipe-A-viewport-size-256,Fail +kms_plane_cursor@pipe-A-viewport-size-64,Fail +kms_plane_cursor@pipe-B-overlay-size-128,Fail +kms_plane_cursor@pipe-B-overlay-size-256,Fail +kms_plane_cursor@pipe-B-overlay-size-64,Fail +kms_plane_cursor@pipe-B-primary-size-128,Fail +kms_plane_cursor@pipe-B-primary-size-256,Fail +kms_plane_cursor@pipe-B-primary-size-64,Fail +kms_plane_cursor@pipe-B-viewport-size-128,Fail +kms_plane_cursor@pipe-B-viewport-size-256,Fail +kms_plane_cursor@pipe-B-viewport-size-64,Fail +kms_plane_cursor@primary,Crash +kms_plane_cursor@viewport,Crash +kms_plane_lowres@tiling-none,Fail +kms_plane_scaling@downscale-with-modifier-factor-0-25,Fail +kms_plane_scaling@downscale-with-rotation-factor-0-25,Fail +kms_plane_scaling@upscale-with-modifier-20x20,Fail +kms_plane_scaling@upscale-with-modifier-factor-0-25,Fail +kms_plane_scaling@upscale-with-pixel-format-20x20,Fail +kms_plane_scaling@upscale-with-pixel-format-factor-0-25,Fail +kms_plane_scaling@upscale-with-rotation-20x20,Fail +kms_prime@basic-crc,Fail +kms_properties@connector-properties-atomic,Crash +kms_properties@connector-properties-legacy,Crash +kms_properties@get_properties-sanity-atomic,Crash +kms_properties@get_properties-sanity-non-atomic,Crash +kms_setmode@invalid-clone-single-crtc,Crash diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..45c54c75c89951a9f2c31d79407572dfff8f823b --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-flakes.txt @@ -0,0 +1,9 @@ +kms_addfb_basic@addfb25-bad-modifier +kms_cursor_crc@.* +kms_flip@basic-flip-vs-wf_vblank +kms_invalid_mode@int-max-clock,Crash +kms_pipe_crc_basic@.* +kms_properties@connector-properties-atomic,Crash +kms_properties@get_properties-sanity-atomic,Crash +kms_properties@get_properties-sanity-non-atomic,Crash +kms_rmfb@close-fd diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3288-skips.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..f20c3574b75ae1b257822e9814a7d227bbe0851e --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3288-skips.txt @@ -0,0 +1,52 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# Too unstable, machine ends up hanging after lots of Oopses +kms_cursor_legacy.* + +# Started hanging the machine on Linux 5.19-rc2: +# +# [IGT] kms_plane_lowres: executing +# [IGT] kms_plane_lowres: starting subtest pipe-F-tiling-y +# [IGT] kms_plane_lowres: exiting, ret=77 +# Console: switching to colour frame buffer device 170x48 +# rockchip-drm display-subsystem: [drm] *ERROR* flip_done timed out +# rockchip-drm display-subsystem: [drm] *ERROR* [CRTC:35:crtc-0] commit wait timed out +# BUG: spinlock bad magic on CPU#3, kms_plane_lowre/482 +# 8<--- cut here --- +# Unable to handle kernel paging request at virtual address 7812078e +# [7812078e] *pgd=00000000 +# Internal error: Oops: 5 [#1] SMP ARM +# Modules linked in: +# CPU: 3 PID: 482 Comm: kms_plane_lowre Tainted: G W 5.19.0-rc2-323596-g00535de92171 #1 +# Hardware name: Rockchip (Device Tree) +# Process kms_plane_lowre (pid: 482, stack limit = 0x1193ac2b) +# spin_dump from do_raw_spin_lock+0xa4/0xe8 +# do_raw_spin_lock from wait_for_completion_timeout+0x2c/0x120 +# wait_for_completion_timeout from drm_crtc_commit_wait+0x18/0x7c +# drm_crtc_commit_wait from drm_atomic_helper_wait_for_dependencies+0x44/0x168 +# drm_atomic_helper_wait_for_dependencies from commit_tail+0x34/0x180 +# commit_tail from drm_atomic_helper_commit+0x164/0x18c +# drm_atomic_helper_commit from drm_atomic_commit+0xac/0xe4 +# drm_atomic_commit from drm_client_modeset_commit_atomic+0x23c/0x284 +# drm_client_modeset_commit_atomic from drm_client_modeset_commit_locked+0x60/0x1c8 +# drm_client_modeset_commit_locked from drm_client_modeset_commit+0x24/0x40 +# drm_client_modeset_commit from drm_fbdev_client_restore+0x58/0x94 +# drm_fbdev_client_restore from drm_client_dev_restore+0x70/0xbc +# drm_client_dev_restore from drm_release+0xf4/0x114 +# drm_release from __fput+0x74/0x240 +# __fput from task_work_run+0x84/0xb4 +# task_work_run from do_exit+0x34c/0xa20 +# do_exit from do_group_exit+0x34/0x98 +# do_group_exit from __wake_up_parent+0x0/0x18 +# Code: e595c008 12843d19 03e00000 03093168 (15940508) +# ---[ end trace 0000000000000000 ]--- +# note: kms_plane_lowre[482] exited with preempt_count 1 +# Fixing recursive fault but reboot is needed! +kms_plane_lowres@pipe-F-tiling-y + +# Take too long, we have only two machines, and these are very flaky +kms_cursor_crc.* + +# Machine is hanging in this test, so skip it +kms_pipe_crc_basic@disable-crc-after-crtc \ No newline at end of file diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..6db08ba6b00894ed74be2e8278849de2c46dedd0 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-fails.txt @@ -0,0 +1,37 @@ +kms_color@legacy-gamma,Fail +kms_color@pipe-A-legacy-gamma,Fail +kms_color@pipe-B-legacy-gamma,Fail +kms_flip@basic-flip-vs-wf_vblank,Fail +kms_flip@blocking-wf_vblank,Fail +kms_flip@dpms-vs-vblank-race,Fail +kms_flip@flip-vs-absolute-wf_vblank,Fail +kms_flip@flip-vs-absolute-wf_vblank-interruptible,Fail +kms_flip@flip-vs-blocking-wf-vblank,Fail +kms_flip@flip-vs-panning,Fail +kms_flip@flip-vs-panning-interruptible,Fail +kms_flip@flip-vs-wf_vblank-interruptible,Fail +kms_flip@plain-flip-fb-recreate,Fail +kms_flip@plain-flip-fb-recreate-interruptible,Fail +kms_flip@plain-flip-ts-check,Fail +kms_flip@plain-flip-ts-check-interruptible,Fail +kms_flip@wf_vblank-ts-check,Fail +kms_flip@wf_vblank-ts-check-interruptible,Fail +kms_invalid_mode@int-max-clock,Fail +kms_plane@pixel-format,Fail +kms_plane@pixel-format-source-clamping,Fail +kms_plane@plane-panning-bottom-right,Fail +kms_plane@plane-panning-top-left,Fail +kms_plane@plane-position-covered,Fail +kms_plane_cursor@pipe-B-overlay-size-128,Fail +kms_plane_cursor@pipe-B-overlay-size-256,Fail +kms_plane_cursor@pipe-B-overlay-size-64,Fail +kms_plane_cursor@pipe-B-primary-size-128,Fail +kms_plane_cursor@pipe-B-primary-size-256,Fail +kms_plane_cursor@pipe-B-primary-size-64,Fail +kms_plane_cursor@pipe-B-viewport-size-128,Fail +kms_plane_cursor@pipe-B-viewport-size-256,Fail +kms_plane_cursor@pipe-B-viewport-size-64,Fail +kms_plane_multiple@atomic-pipe-B-tiling-none,Fail +kms_plane_multiple@tiling-none,Fail +kms_prime@basic-crc,Fail +kms_rmfb@close-fd,Fail diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..4c0539b4beafab156816d78835199cba5d5be6fa --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-flakes.txt @@ -0,0 +1,23 @@ + +kms_cursor_crc@.* +kms_flip@dpms-vs-vblank-race-interruptible +kms_flip@flip-vs-expired-vblank +kms_flip@modeset-vs-vblank-race-interruptible +kms_pipe_crc_basic@.* +kms_pipe_crc_basic@compare-crc-sanitycheck-pipe-A +kms_pipe_crc_basic@compare-crc-sanitycheck-pipe-B +kms_plane@plane-position-hole +kms_plane_multiple@atomic-pipe-A-tiling-none +kms_plane_multiple@atomic-pipe-B-tiling-none +kms_sequence@get-forked +kms_sequence@get-forked-busy +kms_setmode@basic +kms_universal_plane@universal-plane-pipe-B-functional,UnexpectedPass +kms_vblank@pipe-A-accuracy-idle +kms_vblank@pipe-A-query-busy +kms_vblank@pipe-A-query-forked-busy +kms_vblank@pipe-A-wait-idle +kms_vblank@pipe-B-accuracy-idle +kms_vblank@pipe-B-query-busy +kms_vblank@pipe-B-query-forked-busy +kms_vblank@pipe-B-wait-idle diff --git a/drivers/gpu/drm/ci/xfails/rockchip-rk3399-skips.txt b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..10c3d81a919ad8a1b7893c04fc93a193aa5711b4 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/rockchip-rk3399-skips.txt @@ -0,0 +1,5 @@ +# Suspend to RAM seems to be broken on this machine +.*suspend.* + +# Too unstable, machine ends up hanging after lots of Oopses +kms_cursor_legacy.* diff --git a/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt new file mode 100644 index 0000000000000000000000000000000000000000..9586b2339f6f69b3c8e8c9d0e10f6b86fce3d501 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-fails.txt @@ -0,0 +1,38 @@ +kms_addfb_basic@addfb25-bad-modifier,Fail +kms_addfb_basic@bad-pitch-65536,Fail +kms_addfb_basic@bo-too-small,Fail +kms_addfb_basic@size-max,Fail +kms_addfb_basic@too-high,Fail +kms_atomic_transition@plane-primary-toggle-with-vblank-wait,Fail +kms_bw@linear-tiling-1-displays-1920x1080p,Fail +kms_bw@linear-tiling-1-displays-2560x1440p,Fail +kms_bw@linear-tiling-1-displays-3840x2160p,Fail +kms_bw@linear-tiling-2-displays-1920x1080p,Fail +kms_bw@linear-tiling-2-displays-2560x1440p,Fail +kms_bw@linear-tiling-2-displays-3840x2160p,Fail +kms_invalid_mode@int-max-clock,Fail +kms_plane_scaling@downscale-with-modifier-factor-0-25,Fail +kms_plane_scaling@downscale-with-rotation-factor-0-25,Fail +kms_plane_scaling@planes-upscale-20x20,Fail +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-25,Fail +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-5,Fail +kms_plane_scaling@planes-upscale-20x20-downscale-factor-0-75,Fail +kms_plane_scaling@upscale-with-modifier-20x20,Fail +kms_plane_scaling@upscale-with-modifier-factor-0-25,Fail +kms_plane_scaling@upscale-with-pixel-format-20x20,Fail +kms_plane_scaling@upscale-with-pixel-format-factor-0-25,Fail +kms_plane_scaling@upscale-with-rotation-20x20,Fail +kms_vblank@crtc-id,Fail +kms_vblank@invalid,Fail +kms_vblank@pipe-A-accuracy-idle,Fail +kms_vblank@pipe-A-query-busy,Fail +kms_vblank@pipe-A-query-forked,Fail +kms_vblank@pipe-A-query-forked-busy,Fail +kms_vblank@pipe-A-query-idle,Fail +kms_vblank@pipe-A-ts-continuation-idle,Fail +kms_vblank@pipe-A-ts-continuation-modeset,Fail +kms_vblank@pipe-A-ts-continuation-suspend,Fail +kms_vblank@pipe-A-wait-busy,Fail +kms_vblank@pipe-A-wait-forked,Fail +kms_vblank@pipe-A-wait-forked-busy,Fail +kms_vblank@pipe-A-wait-idle,Fail diff --git a/drivers/gpu/drm/ci/xfails/virtio_gpu-none-flakes.txt b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-flakes.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/drivers/gpu/drm/ci/xfails/virtio_gpu-none-skips.txt b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-skips.txt new file mode 100644 index 0000000000000000000000000000000000000000..78be18174012ec08f94bfaa0e70c12851e875d59 --- /dev/null +++ b/drivers/gpu/drm/ci/xfails/virtio_gpu-none-skips.txt @@ -0,0 +1,6 @@ +# Hits a "refcount_t: underflow; use-after-free" in virtio_gpu_fence_event_process +# When run in a particular order with other tests +kms_cursor_legacy.* + +# Job just hangs without any output +kms_flip@flip-vs-suspend.* \ No newline at end of file diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index bf8371dc2a612111a9b2aaa40e06d3f063ba6120..c44d5bcf12847b7e130fc2ed4adf708dd55deebd 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -2203,6 +2203,7 @@ static int drm_mode_create_colorspace_property(struct drm_connector *connector, /** * drm_mode_create_hdmi_colorspace_property - create hdmi colorspace property * @connector: connector to create the Colorspace property on. + * @supported_colorspaces: bitmap of supported color spaces * * Called by a driver the first time it's needed, must be attached to desired * HDMI connectors. @@ -2227,6 +2228,7 @@ EXPORT_SYMBOL(drm_mode_create_hdmi_colorspace_property); /** * drm_mode_create_dp_colorspace_property - create dp colorspace property * @connector: connector to create the Colorspace property on. + * @supported_colorspaces: bitmap of supported color spaces * * Called by a driver the first time it's needed, must be attached to desired * DP connectors. diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index ff69cf0fb42aab8e23ea128c81feef2883d95947..5d2809de4517cadca632e60834b52852d67a7dcd 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec) struct drm_gem_object *obj; unsigned long index; - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object_reverse(exec, index, obj) { dma_resv_unlock(obj->resv); drm_gem_object_put(obj); } diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 0cb646cb04ee1ae5028aa03439df18f06925760a..d5c15292ae93781335a8e8a441be94f9bb235f04 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -38,6 +38,14 @@ static const struct drm_dmi_panel_orientation_data gpd_micropc = { .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +static const struct drm_dmi_panel_orientation_data gpd_onemix2s = { + .width = 1200, + .height = 1920, + .bios_dates = (const char * const []){ "05/21/2018", "10/26/2018", + "03/04/2019", NULL }, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + static const struct drm_dmi_panel_orientation_data gpd_pocket = { .width = 1200, .height = 1920, @@ -401,6 +409,14 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "LTH17"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* One Mix 2S (generic strings, also match on bios date) */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"), + }, + .driver_data = (void *)&gpd_onemix2s, }, {} }; diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 858c959f7babfffbfae06f10057f6f463a9e4ae1..f735b035436c02f0aa5a89916f6d3731752ba0a1 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -3540,6 +3540,27 @@ enum aux_ch intel_bios_dp_aux_ch(const struct intel_bios_encoder_data *devdata) return map_aux_ch(devdata->i915, devdata->child.aux_channel); } +bool intel_bios_dp_has_shared_aux_ch(const struct intel_bios_encoder_data *devdata) +{ + struct drm_i915_private *i915; + u8 aux_channel; + int count = 0; + + if (!devdata || !devdata->child.aux_channel) + return false; + + i915 = devdata->i915; + aux_channel = devdata->child.aux_channel; + + list_for_each_entry(devdata, &i915->display.vbt.display_devices, node) { + if (intel_bios_encoder_supports_dp(devdata) && + aux_channel == devdata->child.aux_channel) + count++; + } + + return count > 1; +} + int intel_bios_dp_boost_level(const struct intel_bios_encoder_data *devdata) { if (!devdata || devdata->i915->display.vbt.version < 196 || !devdata->child.iboost) diff --git a/drivers/gpu/drm/i915/display/intel_bios.h b/drivers/gpu/drm/i915/display/intel_bios.h index 9680e3e92bb516df9d42f9b5d1469010dfa1c65e..49e24b7cf6753ad2dc409ab83fc6567743170832 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.h +++ b/drivers/gpu/drm/i915/display/intel_bios.h @@ -273,6 +273,7 @@ enum aux_ch intel_bios_dp_aux_ch(const struct intel_bios_encoder_data *devdata); int intel_bios_dp_boost_level(const struct intel_bios_encoder_data *devdata); int intel_bios_dp_max_lane_count(const struct intel_bios_encoder_data *devdata); int intel_bios_dp_max_link_rate(const struct intel_bios_encoder_data *devdata); +bool intel_bios_dp_has_shared_aux_ch(const struct intel_bios_encoder_data *devdata); int intel_bios_hdmi_boost_level(const struct intel_bios_encoder_data *devdata); int intel_bios_hdmi_ddc_pin(const struct intel_bios_encoder_data *devdata); int intel_bios_hdmi_level_shift(const struct intel_bios_encoder_data *devdata); diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 12bd2f322e6279a7b85f2ef29a8474903f18ec9d..e0e4cb529284611483afb713454bb08f617a6d40 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5512,8 +5512,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* * VBT and straps are liars. Also check HPD as that seems * to be the most reliable piece of information available. + * + * ... expect on devices that forgot to hook HPD up for eDP + * (eg. Acer Chromebook C710), so we'll check it only if multiple + * ports are attempting to use the same AUX CH, according to VBT. */ - if (!intel_digital_port_connected(encoder)) { + if (intel_bios_dp_has_shared_aux_ch(encoder->devdata) && + !intel_digital_port_connected(encoder)) { /* * If this fails, presume the DPCD answer came * from some other port using the same AUX CH. diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c index 6b6d22c194110c5c04797493db44154b8426d0d1..0ba955611dfb543b1c87b6de1d11faeecbf988f8 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c @@ -198,7 +198,7 @@ static void flush_tlb_invalidate(struct drm_i915_gem_object *obj) for_each_gt(gt, i915, id) { if (!obj->mm.tlb[id]) - return; + continue; intel_gt_invalidate_tlb_full(gt, obj->mm.tlb[id]); obj->mm.tlb[id] = 0; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c index 8f1633c3fb9352cc0678ff80329736187cef39b8..73a4a4eb29e08689fdac30acd3a88739f3205a12 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c @@ -100,6 +100,7 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st, st->nents = 0; for (i = 0; i < page_count; i++) { struct folio *folio; + unsigned long nr_pages; const unsigned int shrink[] = { I915_SHRINK_BOUND | I915_SHRINK_UNBOUND, 0, @@ -150,6 +151,8 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st, } } while (1); + nr_pages = min_t(unsigned long, + folio_nr_pages(folio), page_count - i); if (!i || sg->length >= max_segment || folio_pfn(folio) != next_pfn) { @@ -157,13 +160,13 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st, sg = sg_next(sg); st->nents++; - sg_set_folio(sg, folio, folio_size(folio), 0); + sg_set_folio(sg, folio, nr_pages * PAGE_SIZE, 0); } else { /* XXX: could overflow? */ - sg->length += folio_size(folio); + sg->length += nr_pages * PAGE_SIZE; } - next_pfn = folio_pfn(folio) + folio_nr_pages(folio); - i += folio_nr_pages(folio) - 1; + next_pfn = folio_pfn(folio) + nr_pages; + i += nr_pages - 1; /* Check that the i965g/gm workaround works. */ GEM_BUG_ON(gfp & __GFP_DMA32 && next_pfn >= 0x00100000UL); diff --git a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c index a4ff55aa5e55b91835e0bbde3617601d56d9a6ef..7ad36198aab2a4301e509614b0d3fb2300ef11f7 100644 --- a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c @@ -271,8 +271,17 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) if (GRAPHICS_VER_FULL(rq->i915) >= IP_VER(12, 70)) bit_group_0 |= PIPE_CONTROL_CCS_FLUSH; + /* + * L3 fabric flush is needed for AUX CCS invalidation + * which happens as part of pipe-control so we can + * ignore PIPE_CONTROL_FLUSH_L3. Also PIPE_CONTROL_FLUSH_L3 + * deals with Protected Memory which is not needed for + * AUX CCS invalidation and lead to unwanted side effects. + */ + if (mode & EMIT_FLUSH) + bit_group_1 |= PIPE_CONTROL_FLUSH_L3; + bit_group_1 |= PIPE_CONTROL_TILE_CACHE_FLUSH; - bit_group_1 |= PIPE_CONTROL_FLUSH_L3; bit_group_1 |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; bit_group_1 |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; /* Wa_1409600907:tgl,adl-p */ diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index ee15486fed0daa57479b07da01e2b7c3ea78391b..e85d70a62123f9bbd824c9df1542dd7ecf9c7b62 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -558,7 +558,6 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, DRIVER_CAPS(i915)->has_logical_contexts = true; ewma__engine_latency_init(&engine->latency); - seqcount_init(&engine->stats.execlists.lock); ATOMIC_INIT_NOTIFIER_HEAD(&engine->context_status_notifier); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_types.h b/drivers/gpu/drm/i915/gt/intel_engine_types.h index e99a6fa03d45398cf1e81b2d7ee2b864cf477286..a7e6775980043c9ce25fee01368d67378663bf44 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_types.h +++ b/drivers/gpu/drm/i915/gt/intel_engine_types.h @@ -58,6 +58,7 @@ struct i915_perf_group; typedef u32 intel_engine_mask_t; #define ALL_ENGINES ((intel_engine_mask_t)~0ul) +#define VIRTUAL_ENGINES BIT(BITS_PER_TYPE(intel_engine_mask_t) - 1) struct intel_hw_status_page { struct list_head timelines; diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c index 8a641bcf777cb4da2c211911a470272d759314a4..3292524469d509655db5a7130a07c744af440bb3 100644 --- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c @@ -3550,6 +3550,8 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine) logical_ring_default_vfuncs(engine); logical_ring_default_irqs(engine); + seqcount_init(&engine->stats.execlists.lock); + if (engine->flags & I915_ENGINE_HAS_RCS_REG_STATE) rcs_submission_override(engine); diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c index dd0ed941441aab84be728ee5f333f44d682a8c66..da21f2786b5d7d2601024be7630e56e289bf61dc 100644 --- a/drivers/gpu/drm/i915/gt/intel_ggtt.c +++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c @@ -511,20 +511,31 @@ void intel_ggtt_unbind_vma(struct i915_address_space *vm, vm->clear_range(vm, vma_res->start, vma_res->vma_size); } +/* + * Reserve the top of the GuC address space for firmware images. Addresses + * beyond GUC_GGTT_TOP in the GuC address space are inaccessible by GuC, + * which makes for a suitable range to hold GuC/HuC firmware images if the + * size of the GGTT is 4G. However, on a 32-bit platform the size of the GGTT + * is limited to 2G, which is less than GUC_GGTT_TOP, but we reserve a chunk + * of the same size anyway, which is far more than needed, to keep the logic + * in uc_fw_ggtt_offset() simple. + */ +#define GUC_TOP_RESERVE_SIZE (SZ_4G - GUC_GGTT_TOP) + static int ggtt_reserve_guc_top(struct i915_ggtt *ggtt) { - u64 size; + u64 offset; int ret; if (!intel_uc_uses_guc(&ggtt->vm.gt->uc)) return 0; - GEM_BUG_ON(ggtt->vm.total <= GUC_GGTT_TOP); - size = ggtt->vm.total - GUC_GGTT_TOP; + GEM_BUG_ON(ggtt->vm.total <= GUC_TOP_RESERVE_SIZE); + offset = ggtt->vm.total - GUC_TOP_RESERVE_SIZE; - ret = i915_gem_gtt_reserve(&ggtt->vm, NULL, &ggtt->uc_fw, size, - GUC_GGTT_TOP, I915_COLOR_UNEVICTABLE, - PIN_NOEVICT); + ret = i915_gem_gtt_reserve(&ggtt->vm, NULL, &ggtt->uc_fw, + GUC_TOP_RESERVE_SIZE, offset, + I915_COLOR_UNEVICTABLE, PIN_NOEVICT); if (ret) drm_dbg(&ggtt->vm.i915->drm, "Failed to reserve top of GGTT for GuC\n"); diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index 957d0aeb0c022f464720f1b456192b849ebff875..c378cc7c953c47c3af190a62bcff2b36f57f9b7a 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -1094,6 +1094,9 @@ __lrc_alloc_state(struct intel_context *ce, struct intel_engine_cs *engine) I915_BO_ALLOC_PM_VOLATILE); if (IS_ERR(obj)) { obj = i915_gem_object_create_shmem(engine->i915, context_size); + if (IS_ERR(obj)) + return ERR_CAST(obj); + /* * Wa_22016122933: For Media version 13.0, all Media GT shared * memory needs to be mapped as WC on CPU side and UC (PAT @@ -1102,8 +1105,6 @@ __lrc_alloc_state(struct intel_context *ce, struct intel_engine_cs *engine) if (intel_gt_needs_wa_22016122933(engine->gt)) i915_gem_object_set_cache_coherency(obj, I915_CACHE_NONE); } - if (IS_ERR(obj)) - return ERR_CAST(obj); vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL); if (IS_ERR(vma)) { diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c index a0e3ef1c65d246b80696a5503b7c284b0e205b44..dc7b40e06e38afc2e88f6f6e9c6c564d6a7b8b30 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c @@ -1432,6 +1432,36 @@ static void guc_timestamp_ping(struct work_struct *wrk) unsigned long index; int srcu, ret; + /* + * Ideally the busyness worker should take a gt pm wakeref because the + * worker only needs to be active while gt is awake. However, the + * gt_park path cancels the worker synchronously and this complicates + * the flow if the worker is also running at the same time. The cancel + * waits for the worker and when the worker releases the wakeref, that + * would call gt_park and would lead to a deadlock. + * + * The resolution is to take the global pm wakeref if runtime pm is + * already active. If not, we don't need to update the busyness stats as + * the stats would already be updated when the gt was parked. + * + * Note: + * - We do not requeue the worker if we cannot take a reference to runtime + * pm since intel_guc_busyness_unpark would requeue the worker in the + * resume path. + * + * - If the gt was parked longer than time taken for GT timestamp to roll + * over, we ignore those rollovers since we don't care about tracking + * the exact GT time. We only care about roll overs when the gt is + * active and running workloads. + * + * - There is a window of time between gt_park and runtime suspend, + * where the worker may run. This is acceptable since the worker will + * not find any new data to update busyness. + */ + wakeref = intel_runtime_pm_get_if_active(>->i915->runtime_pm); + if (!wakeref) + return; + /* * Synchronize with gt reset to make sure the worker does not * corrupt the engine/guc stats. NB: can't actually block waiting @@ -1440,10 +1470,9 @@ static void guc_timestamp_ping(struct work_struct *wrk) */ ret = intel_gt_reset_trylock(gt, &srcu); if (ret) - return; + goto err_trylock; - with_intel_runtime_pm(>->i915->runtime_pm, wakeref) - __update_guc_busyness_stats(guc); + __update_guc_busyness_stats(guc); /* adjust context stats for overflow */ xa_for_each(&guc->context_lookup, index, ce) @@ -1452,6 +1481,9 @@ static void guc_timestamp_ping(struct work_struct *wrk) intel_gt_reset_unlock(gt, srcu); guc_enable_busyness_worker(guc); + +err_trylock: + intel_runtime_pm_put(>->i915->runtime_pm, wakeref); } static int guc_action_enable_usage_stats(struct intel_guc *guc) @@ -5470,6 +5502,9 @@ guc_create_virtual(struct intel_engine_cs **siblings, unsigned int count, ve->base.flags = I915_ENGINE_IS_VIRTUAL; + BUILD_BUG_ON(ilog2(VIRTUAL_ENGINES) < I915_NUM_ENGINES); + ve->base.mask = VIRTUAL_ENGINES; + intel_context_init(&ve->context, &ve->base); for (n = 0; n < count; n++) { diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c index 4ec85308379a4feb76b44ed57155449c4e4623b6..094fca9b0e73d1da202dd738c52256bc736739ee 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.c +++ b/drivers/gpu/drm/i915/gvt/gtt.c @@ -49,22 +49,6 @@ static bool enable_out_of_sync = false; static int preallocated_oos_pages = 8192; -static bool intel_gvt_is_valid_gfn(struct intel_vgpu *vgpu, unsigned long gfn) -{ - struct kvm *kvm = vgpu->vfio_device.kvm; - int idx; - bool ret; - - if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status)) - return false; - - idx = srcu_read_lock(&kvm->srcu); - ret = kvm_is_visible_gfn(kvm, gfn); - srcu_read_unlock(&kvm->srcu, idx); - - return ret; -} - /* * validate a gm address and related range size, * translate it to host gm address @@ -1161,31 +1145,6 @@ static inline void ppgtt_generate_shadow_entry(struct intel_gvt_gtt_entry *se, ops->set_pfn(se, s->shadow_page.mfn); } -/* - * Check if can do 2M page - * @vgpu: target vgpu - * @entry: target pfn's gtt entry - * - * Return 1 if 2MB huge gtt shadowing is possible, 0 if miscondition, - * negative if found err. - */ -static int is_2MB_gtt_possible(struct intel_vgpu *vgpu, - struct intel_gvt_gtt_entry *entry) -{ - const struct intel_gvt_gtt_pte_ops *ops = vgpu->gvt->gtt.pte_ops; - kvm_pfn_t pfn; - - if (!HAS_PAGE_SIZES(vgpu->gvt->gt->i915, I915_GTT_PAGE_SIZE_2M)) - return 0; - - if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status)) - return -EINVAL; - pfn = gfn_to_pfn(vgpu->vfio_device.kvm, ops->get_pfn(entry)); - if (is_error_noslot_pfn(pfn)) - return -EINVAL; - return PageTransHuge(pfn_to_page(pfn)); -} - static int split_2MB_gtt_entry(struct intel_vgpu *vgpu, struct intel_vgpu_ppgtt_spt *spt, unsigned long index, struct intel_gvt_gtt_entry *se) @@ -1279,7 +1238,7 @@ static int ppgtt_populate_shadow_entry(struct intel_vgpu *vgpu, { const struct intel_gvt_gtt_pte_ops *pte_ops = vgpu->gvt->gtt.pte_ops; struct intel_gvt_gtt_entry se = *ge; - unsigned long gfn, page_size = PAGE_SIZE; + unsigned long gfn; dma_addr_t dma_addr; int ret; @@ -1291,6 +1250,9 @@ static int ppgtt_populate_shadow_entry(struct intel_vgpu *vgpu, switch (ge->type) { case GTT_TYPE_PPGTT_PTE_4K_ENTRY: gvt_vdbg_mm("shadow 4K gtt entry\n"); + ret = intel_gvt_dma_map_guest_page(vgpu, gfn, PAGE_SIZE, &dma_addr); + if (ret) + return -ENXIO; break; case GTT_TYPE_PPGTT_PTE_64K_ENTRY: gvt_vdbg_mm("shadow 64K gtt entry\n"); @@ -1302,25 +1264,20 @@ static int ppgtt_populate_shadow_entry(struct intel_vgpu *vgpu, return split_64KB_gtt_entry(vgpu, spt, index, &se); case GTT_TYPE_PPGTT_PTE_2M_ENTRY: gvt_vdbg_mm("shadow 2M gtt entry\n"); - ret = is_2MB_gtt_possible(vgpu, ge); - if (ret == 0) + if (!HAS_PAGE_SIZES(vgpu->gvt->gt->i915, I915_GTT_PAGE_SIZE_2M) || + intel_gvt_dma_map_guest_page(vgpu, gfn, + I915_GTT_PAGE_SIZE_2M, &dma_addr)) return split_2MB_gtt_entry(vgpu, spt, index, &se); - else if (ret < 0) - return ret; - page_size = I915_GTT_PAGE_SIZE_2M; break; case GTT_TYPE_PPGTT_PTE_1G_ENTRY: gvt_vgpu_err("GVT doesn't support 1GB entry\n"); return -EINVAL; default: GEM_BUG_ON(1); + return -EINVAL; } - /* direct shadow */ - ret = intel_gvt_dma_map_guest_page(vgpu, gfn, page_size, &dma_addr); - if (ret) - return -ENXIO; - + /* Successfully shadowed a 4K or 2M page (without splitting). */ pte_ops->set_pfn(&se, dma_addr >> PAGE_SHIFT); ppgtt_set_shadow_entry(spt, &se, index); return 0; @@ -1329,11 +1286,9 @@ static int ppgtt_populate_shadow_entry(struct intel_vgpu *vgpu, static int ppgtt_populate_spt(struct intel_vgpu_ppgtt_spt *spt) { struct intel_vgpu *vgpu = spt->vgpu; - struct intel_gvt *gvt = vgpu->gvt; - const struct intel_gvt_gtt_pte_ops *ops = gvt->gtt.pte_ops; struct intel_vgpu_ppgtt_spt *s; struct intel_gvt_gtt_entry se, ge; - unsigned long gfn, i; + unsigned long i; int ret; trace_spt_change(spt->vgpu->id, "born", spt, @@ -1350,13 +1305,6 @@ static int ppgtt_populate_spt(struct intel_vgpu_ppgtt_spt *spt) ppgtt_generate_shadow_entry(&se, s, &ge); ppgtt_set_shadow_entry(spt, &se, i); } else { - gfn = ops->get_pfn(&ge); - if (!intel_gvt_is_valid_gfn(vgpu, gfn)) { - ops->set_pfn(&se, gvt->gtt.scratch_mfn); - ppgtt_set_shadow_entry(spt, &se, i); - continue; - } - ret = ppgtt_populate_shadow_entry(vgpu, spt, i, &ge); if (ret) goto fail; @@ -1845,6 +1793,9 @@ static int shadow_ppgtt_mm(struct intel_vgpu_mm *mm) if (mm->ppgtt_mm.shadowed) return 0; + if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status)) + return -EINVAL; + mm->ppgtt_mm.shadowed = true; for (index = 0; index < ARRAY_SIZE(mm->ppgtt_mm.guest_pdps); index++) { @@ -2331,14 +2282,6 @@ static int emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off, m.val64 = e.val64; m.type = e.type; - /* one PTE update may be issued in multiple writes and the - * first write may not construct a valid gfn - */ - if (!intel_gvt_is_valid_gfn(vgpu, gfn)) { - ops->set_pfn(&m, gvt->gtt.scratch_mfn); - goto out; - } - ret = intel_gvt_dma_map_guest_page(vgpu, gfn, PAGE_SIZE, &dma_addr); if (ret) { @@ -2355,7 +2298,6 @@ static int emulate_ggtt_mmio_write(struct intel_vgpu *vgpu, unsigned int off, ops->clear_present(&m); } -out: ggtt_set_guest_entry(ggtt_mm, &e, g_gtt_index); ggtt_get_host_entry(ggtt_mm, &e, g_gtt_index); @@ -2875,24 +2817,6 @@ void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu, bool invalidate_old) ggtt_invalidate(gvt->gt); } -/** - * intel_vgpu_reset_gtt - reset the all GTT related status - * @vgpu: a vGPU - * - * This function is called from vfio core to reset reset all - * GTT related status, including GGTT, PPGTT, scratch page. - * - */ -void intel_vgpu_reset_gtt(struct intel_vgpu *vgpu) -{ - /* Shadow pages are only created when there is no page - * table tracking data, so remove page tracking data after - * removing the shadow pages. - */ - intel_vgpu_destroy_all_ppgtt_mm(vgpu); - intel_vgpu_reset_ggtt(vgpu, true); -} - /** * intel_gvt_restore_ggtt - restore all vGPU's ggtt entries * @gvt: intel gvt device diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index a3b0f59ec8bd91efefd51ee443a131592f6df0fa..4cb183e06e95a1ba408a06099a4049671bea6789 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -224,7 +224,6 @@ void intel_vgpu_reset_ggtt(struct intel_vgpu *vgpu, bool invalidate_old); void intel_vgpu_invalidate_ppgtt(struct intel_vgpu *vgpu); int intel_gvt_init_gtt(struct intel_gvt *gvt); -void intel_vgpu_reset_gtt(struct intel_vgpu *vgpu); void intel_gvt_clean_gtt(struct intel_gvt *gvt); struct intel_vgpu_mm *intel_gvt_find_ppgtt_mm(struct intel_vgpu *vgpu, diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 2d65800d8e93b9b288fda886c35448394f99506a..53a0a42a50db6a4c350dd6f7cff8e7e6c15908f7 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -34,10 +34,11 @@ #define _GVT_H_ #include -#include #include #include +#include + #include "i915_drv.h" #include "intel_gvt.h" diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index 9cd9e9da60ddb28bf02b4449480aabcdee95375c..42ce20e72db767a4bcc6a19ddf1888b5677ea923 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -106,12 +106,10 @@ struct gvt_dma { #define vfio_dev_to_vgpu(vfio_dev) \ container_of((vfio_dev), struct intel_vgpu, vfio_device) -static void kvmgt_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, - const u8 *val, int len, - struct kvm_page_track_notifier_node *node); -static void kvmgt_page_track_flush_slot(struct kvm *kvm, - struct kvm_memory_slot *slot, - struct kvm_page_track_notifier_node *node); +static void kvmgt_page_track_write(gpa_t gpa, const u8 *val, int len, + struct kvm_page_track_notifier_node *node); +static void kvmgt_page_track_remove_region(gfn_t gfn, unsigned long nr_pages, + struct kvm_page_track_notifier_node *node); static ssize_t intel_vgpu_show_description(struct mdev_type *mtype, char *buf) { @@ -161,8 +159,7 @@ static int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn, if (npage == 0) base_page = cur_page; - else if (base_page + npage != cur_page) { - gvt_vgpu_err("The pages are not continuous\n"); + else if (page_to_pfn(base_page) + npage != page_to_pfn(cur_page)) { ret = -EINVAL; npage++; goto err; @@ -172,7 +169,8 @@ static int gvt_pin_guest_page(struct intel_vgpu *vgpu, unsigned long gfn, *page = base_page; return 0; err: - gvt_unpin_guest_page(vgpu, gfn, npage * PAGE_SIZE); + if (npage) + gvt_unpin_guest_page(vgpu, gfn, npage * PAGE_SIZE); return ret; } @@ -352,6 +350,8 @@ __kvmgt_protect_table_find(struct intel_vgpu *info, gfn_t gfn) { struct kvmgt_pgfn *p, *res = NULL; + lockdep_assert_held(&info->vgpu_lock); + hash_for_each_possible(info->ptable, p, hnode, gfn) { if (gfn == p->gfn) { res = p; @@ -654,21 +654,19 @@ static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu) static int intel_vgpu_open_device(struct vfio_device *vfio_dev) { struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev); - - if (!vgpu->vfio_device.kvm || - vgpu->vfio_device.kvm->mm != current->mm) { - gvt_vgpu_err("KVM is required to use Intel vGPU\n"); - return -ESRCH; - } + int ret; if (__kvmgt_vgpu_exist(vgpu)) return -EEXIST; vgpu->track_node.track_write = kvmgt_page_track_write; - vgpu->track_node.track_flush_slot = kvmgt_page_track_flush_slot; - kvm_get_kvm(vgpu->vfio_device.kvm); - kvm_page_track_register_notifier(vgpu->vfio_device.kvm, - &vgpu->track_node); + vgpu->track_node.track_remove_region = kvmgt_page_track_remove_region; + ret = kvm_page_track_register_notifier(vgpu->vfio_device.kvm, + &vgpu->track_node); + if (ret) { + gvt_vgpu_err("KVM is required to use Intel vGPU\n"); + return ret; + } set_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status); @@ -703,7 +701,6 @@ static void intel_vgpu_close_device(struct vfio_device *vfio_dev) kvm_page_track_unregister_notifier(vgpu->vfio_device.kvm, &vgpu->track_node); - kvm_put_kvm(vgpu->vfio_device.kvm); kvmgt_protect_table_destroy(vgpu); gvt_cache_destroy(vgpu); @@ -1547,95 +1544,70 @@ static struct mdev_driver intel_vgpu_mdev_driver = { int intel_gvt_page_track_add(struct intel_vgpu *info, u64 gfn) { - struct kvm *kvm = info->vfio_device.kvm; - struct kvm_memory_slot *slot; - int idx; + int r; if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status)) return -ESRCH; - idx = srcu_read_lock(&kvm->srcu); - slot = gfn_to_memslot(kvm, gfn); - if (!slot) { - srcu_read_unlock(&kvm->srcu, idx); - return -EINVAL; - } - - write_lock(&kvm->mmu_lock); - if (kvmgt_gfn_is_write_protected(info, gfn)) - goto out; + return 0; - kvm_slot_page_track_add_page(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE); - kvmgt_protect_table_add(info, gfn); + r = kvm_write_track_add_gfn(info->vfio_device.kvm, gfn); + if (r) + return r; -out: - write_unlock(&kvm->mmu_lock); - srcu_read_unlock(&kvm->srcu, idx); + kvmgt_protect_table_add(info, gfn); return 0; } int intel_gvt_page_track_remove(struct intel_vgpu *info, u64 gfn) { - struct kvm *kvm = info->vfio_device.kvm; - struct kvm_memory_slot *slot; - int idx; + int r; if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status)) return -ESRCH; - idx = srcu_read_lock(&kvm->srcu); - slot = gfn_to_memslot(kvm, gfn); - if (!slot) { - srcu_read_unlock(&kvm->srcu, idx); - return -EINVAL; - } - - write_lock(&kvm->mmu_lock); - if (!kvmgt_gfn_is_write_protected(info, gfn)) - goto out; + return 0; - kvm_slot_page_track_remove_page(kvm, slot, gfn, KVM_PAGE_TRACK_WRITE); - kvmgt_protect_table_del(info, gfn); + r = kvm_write_track_remove_gfn(info->vfio_device.kvm, gfn); + if (r) + return r; -out: - write_unlock(&kvm->mmu_lock); - srcu_read_unlock(&kvm->srcu, idx); + kvmgt_protect_table_del(info, gfn); return 0; } -static void kvmgt_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, - const u8 *val, int len, - struct kvm_page_track_notifier_node *node) +static void kvmgt_page_track_write(gpa_t gpa, const u8 *val, int len, + struct kvm_page_track_notifier_node *node) { struct intel_vgpu *info = container_of(node, struct intel_vgpu, track_node); - if (kvmgt_gfn_is_write_protected(info, gpa_to_gfn(gpa))) + mutex_lock(&info->vgpu_lock); + + if (kvmgt_gfn_is_write_protected(info, gpa >> PAGE_SHIFT)) intel_vgpu_page_track_handler(info, gpa, (void *)val, len); + + mutex_unlock(&info->vgpu_lock); } -static void kvmgt_page_track_flush_slot(struct kvm *kvm, - struct kvm_memory_slot *slot, - struct kvm_page_track_notifier_node *node) +static void kvmgt_page_track_remove_region(gfn_t gfn, unsigned long nr_pages, + struct kvm_page_track_notifier_node *node) { - int i; - gfn_t gfn; + unsigned long i; struct intel_vgpu *info = container_of(node, struct intel_vgpu, track_node); - write_lock(&kvm->mmu_lock); - for (i = 0; i < slot->npages; i++) { - gfn = slot->base_gfn + i; - if (kvmgt_gfn_is_write_protected(info, gfn)) { - kvm_slot_page_track_remove_page(kvm, slot, gfn, - KVM_PAGE_TRACK_WRITE); - kvmgt_protect_table_del(info, gfn); - } + mutex_lock(&info->vgpu_lock); + + for (i = 0; i < nr_pages; i++) { + if (kvmgt_gfn_is_write_protected(info, gfn + i)) + kvmgt_protect_table_del(info, gfn + i); } - write_unlock(&kvm->mmu_lock); + + mutex_unlock(&info->vgpu_lock); } void intel_vgpu_detach_regions(struct intel_vgpu *vgpu) diff --git a/drivers/gpu/drm/i915/gvt/page_track.c b/drivers/gpu/drm/i915/gvt/page_track.c index df34e73cba41a4d492761403dfac54fbbf478563..60a65435556dc9e6d7e2cd1812b8ca16dd9ab2f1 100644 --- a/drivers/gpu/drm/i915/gvt/page_track.c +++ b/drivers/gpu/drm/i915/gvt/page_track.c @@ -162,13 +162,9 @@ int intel_vgpu_page_track_handler(struct intel_vgpu *vgpu, u64 gpa, struct intel_vgpu_page_track *page_track; int ret = 0; - mutex_lock(&vgpu->vgpu_lock); - page_track = intel_vgpu_find_page_track(vgpu, gpa >> PAGE_SHIFT); - if (!page_track) { - ret = -ENXIO; - goto out; - } + if (!page_track) + return -ENXIO; if (unlikely(vgpu->failsafe)) { /* Remove write protection to prevent furture traps. */ @@ -179,7 +175,5 @@ int intel_vgpu_page_track_handler(struct intel_vgpu *vgpu, u64 gpa, gvt_err("guest page write error, gpa %llx\n", gpa); } -out: - mutex_unlock(&vgpu->vgpu_lock); return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1f65bb33dd212f2c44895f2317c8738a2d05b64c..a8551ce322de21bc2183f64029a0782340b77ad6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1199,6 +1199,13 @@ int i915_gem_init(struct drm_i915_private *dev_priv) goto err_unlock; } + /* + * Register engines early to ensure the engine list is in its final + * rb-tree form, lowering the amount of code that has to deal with + * the intermediate llist state. + */ + intel_engines_driver_register(dev_priv); + return 0; /* @@ -1246,8 +1253,6 @@ int i915_gem_init(struct drm_i915_private *dev_priv) void i915_gem_driver_register(struct drm_i915_private *i915) { i915_gem_driver_register__shrinker(i915); - - intel_engines_driver_register(i915); } void i915_gem_driver_unregister(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 7c7da284990df7cbde55ea63119a5691399b4623..f59081066a197a9ae0ccf0a1d2dd9eca01e04700 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -134,9 +134,7 @@ static void i915_fence_release(struct dma_fence *fence) i915_sw_fence_fini(&rq->semaphore); /* - * Keep one request on each engine for reserved use under mempressure - * do not use with virtual engines as this really is only needed for - * kernel contexts. + * Keep one request on each engine for reserved use under mempressure. * * We do not hold a reference to the engine here and so have to be * very careful in what rq->engine we poke. The virtual engine is @@ -166,8 +164,7 @@ static void i915_fence_release(struct dma_fence *fence) * know that if the rq->execution_mask is a single bit, rq->engine * can be a physical engine with the exact corresponding mask. */ - if (!intel_engine_is_virtual(rq->engine) && - is_power_of_2(rq->execution_mask) && + if (is_power_of_2(rq->execution_mask) && !cmpxchg(&rq->engine->request_pool, NULL, rq)) return; diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index 9913971fa5d20bc9d192695d8b2773d0244615ce..25ea765586908f14d08715f45ca9def85a6a07f3 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -334,6 +334,8 @@ static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge, return; cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid); + + kfree(edid); } else cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier); } diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index a34924523133c78f864c829abe3f9d8dcc9a22c4..a34917b048f96f4c755c6fe605ab7e92c674e28a 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -1122,18 +1122,11 @@ nv04_page_flip_emit(struct nouveau_channel *chan, PUSH_NVSQ(push, NV_SW, NV_SW_PAGE_FLIP, 0x00000000); PUSH_KICK(push); - ret = nouveau_fence_new(pfence); + ret = nouveau_fence_new(pfence, chan); if (ret) goto fail; - ret = nouveau_fence_emit(*pfence, chan); - if (ret) - goto fail_fence_unref; - return 0; - -fail_fence_unref: - nouveau_fence_unref(pfence); fail: spin_lock_irqsave(&dev->event_lock, flags); list_del(&s->head); diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 30afbec9e3b1bdbf87fd05c1cb4b58739f2941c6..2edd7bb13faea5ebb15985867cab304d12d2da96 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -31,6 +31,7 @@ #include "nouveau_drv.h" #include "nouveau_dma.h" +#include "nouveau_exec.h" #include "nouveau_gem.h" #include "nouveau_chan.h" #include "nouveau_abi16.h" @@ -183,6 +184,20 @@ nouveau_abi16_fini(struct nouveau_abi16 *abi16) cli->abi16 = NULL; } +static inline int +getparam_dma_ib_max(struct nvif_device *device) +{ + const struct nvif_mclass dmas[] = { + { NV03_CHANNEL_DMA, 0 }, + { NV10_CHANNEL_DMA, 0 }, + { NV17_CHANNEL_DMA, 0 }, + { NV40_CHANNEL_DMA, 0 }, + {} + }; + + return nvif_mclass(&device->object, dmas) < 0 ? NV50_DMA_IB_MAX : 0; +} + int nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) { @@ -247,6 +262,12 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) case NOUVEAU_GETPARAM_GRAPH_UNITS: getparam->value = nvkm_gr_units(gr); break; + case NOUVEAU_GETPARAM_EXEC_PUSH_MAX: { + int ib_max = getparam_dma_ib_max(device); + + getparam->value = nouveau_exec_push_max_from_ib_max(ib_max); + break; + } default: NV_PRINTK(dbg, cli, "unknown parameter %lld\n", getparam->param); return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 19cab37ac69c9931ea7af1dc659701af98000e58..0f3bd187ede67d0a8bb6be040956c70436a6aebf 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -875,16 +875,10 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, if (ret) goto out_unlock; - ret = nouveau_fence_new(&fence); + ret = nouveau_fence_new(&fence, chan); if (ret) goto out_unlock; - ret = nouveau_fence_emit(fence, chan); - if (ret) { - nouveau_fence_unref(&fence); - goto out_unlock; - } - /* TODO: figure out a better solution here * * wait on the fence here explicitly as going through diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index 1fd5ccf41128adf9904ef5c8652aebd4fd26016b..7c97b288680760819971a7823221da5e7179c7ab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -70,11 +70,9 @@ nouveau_channel_idle(struct nouveau_channel *chan) struct nouveau_fence *fence = NULL; int ret; - ret = nouveau_fence_new(&fence); + ret = nouveau_fence_new(&fence, chan); if (!ret) { - ret = nouveau_fence_emit(fence, chan); - if (!ret) - ret = nouveau_fence_wait(fence, false, false); + ret = nouveau_fence_wait(fence, false, false); nouveau_fence_unref(&fence); } @@ -259,10 +257,7 @@ static int nouveau_channel_ctor(struct nouveau_drm *drm, struct nvif_device *device, bool priv, u64 runm, struct nouveau_channel **pchan) { - static const struct { - s32 oclass; - int version; - } hosts[] = { + const struct nvif_mclass hosts[] = { { AMPERE_CHANNEL_GPFIFO_B, 0 }, { AMPERE_CHANNEL_GPFIFO_A, 0 }, { TURING_CHANNEL_GPFIFO_A, 0 }, @@ -445,9 +440,11 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) } /* initialise dma tracking parameters */ - switch (chan->user.oclass & 0x00ff) { - case 0x006b: - case 0x006e: + switch (chan->user.oclass) { + case NV03_CHANNEL_DMA: + case NV10_CHANNEL_DMA: + case NV17_CHANNEL_DMA: + case NV40_CHANNEL_DMA: chan->user_put = 0x40; chan->user_get = 0x44; chan->dma.max = (0x10000 / 4) - 2; @@ -457,7 +454,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) chan->user_get = 0x44; chan->user_get_hi = 0x60; chan->dma.ib_base = 0x10000 / 4; - chan->dma.ib_max = (0x02000 / 8) - 1; + chan->dma.ib_max = NV50_DMA_IB_MAX; chan->dma.ib_put = 0; chan->dma.ib_free = chan->dma.ib_max - chan->dma.ib_put; chan->dma.max = chan->dma.ib_base; diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h index 1744d95b233e11a1e51339ea96f15627f36a20b4..c52cda82353e337e07ec51227333a4dba25c8b9c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.h +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h @@ -49,6 +49,9 @@ void nv50_dma_push(struct nouveau_channel *, u64 addr, u32 length, /* Maximum push buffer size. */ #define NV50_DMA_PUSH_MAX_LENGTH 0x7fffff +/* Maximum IBs per ring. */ +#define NV50_DMA_IB_MAX ((0x02000 / 8) - 1) + /* Object handles - for stuff that's doesn't use handle == oclass. */ enum { NvDmaFB = 0x80000002, diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c index 61e84562094a218dcf9790f35bc4dec196dc6556..12feecf71e752de075eaa94d7d3de9ec5a9e082b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dmem.c +++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c @@ -209,8 +209,7 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf) goto done; } - if (!nouveau_fence_new(&fence)) - nouveau_fence_emit(fence, dmem->migrate.chan); + nouveau_fence_new(&fence, dmem->migrate.chan); migrate_vma_pages(&args); nouveau_dmem_fence_done(&fence); dma_unmap_page(drm->dev->dev, dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL); @@ -403,8 +402,7 @@ nouveau_dmem_evict_chunk(struct nouveau_dmem_chunk *chunk) } } - if (!nouveau_fence_new(&fence)) - nouveau_fence_emit(fence, chunk->drm->dmem->migrate.chan); + nouveau_fence_new(&fence, chunk->drm->dmem->migrate.chan); migrate_device_pages(src_pfns, dst_pfns, npages); nouveau_dmem_fence_done(&fence); migrate_device_finalize(src_pfns, dst_pfns, npages); @@ -677,8 +675,7 @@ static void nouveau_dmem_migrate_chunk(struct nouveau_drm *drm, addr += PAGE_SIZE; } - if (!nouveau_fence_new(&fence)) - nouveau_fence_emit(fence, drm->dmem->migrate.chan); + nouveau_fence_new(&fence, drm->dmem->migrate.chan); migrate_vma_pages(args); nouveau_dmem_fence_done(&fence); nouveau_pfns_map(svmm, args->vma->vm_mm, args->start, pfns, i); diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 1fe17ff95f5eec4c49e73a7123c91112c2a3fd22..e73a233c6572322dbee898b8f6962cf6b386b23f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -189,21 +189,12 @@ u_free(void *addr) static inline void * u_memcpya(uint64_t user, unsigned int nmemb, unsigned int size) { - void *mem; - void __user *userptr = (void __force __user *)(uintptr_t)user; + void __user *userptr = u64_to_user_ptr(user); + size_t bytes; - size *= nmemb; - - mem = kvmalloc(size, GFP_KERNEL); - if (!mem) - return ERR_PTR(-ENOMEM); - - if (copy_from_user(mem, userptr, size)) { - u_free(mem); - return ERR_PTR(-EFAULT); - } - - return mem; + if (unlikely(check_mul_overflow(nmemb, size, &bytes))) + return ERR_PTR(-EOVERFLOW); + return vmemdup_user(userptr, bytes); } #include diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index a90c4cd8cbb25424d119997d4f6e555d262c9c80..c1837ba95fb5802cf589e8ee5aaa14ca2de1c083 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -96,7 +96,8 @@ nouveau_exec_job_submit(struct nouveau_job *job) unsigned long index; int ret; - ret = nouveau_fence_new(&exec_job->fence); + /* Create a new fence, but do not emit yet. */ + ret = nouveau_fence_create(&exec_job->fence, exec_job->chan); if (ret) return ret; @@ -170,13 +171,17 @@ nouveau_exec_job_run(struct nouveau_job *job) nv50_dma_push(chan, p->va, p->va_len, no_prefetch); } - ret = nouveau_fence_emit(fence, chan); + ret = nouveau_fence_emit(fence); if (ret) { + nouveau_fence_unref(&exec_job->fence); NV_PRINTK(err, job->cli, "error fencing pushbuf: %d\n", ret); WIND_RING(chan); return ERR_PTR(ret); } + /* The fence was emitted successfully, set the job's fence pointer to + * NULL in order to avoid freeing it up when the job is cleaned up. + */ exec_job->fence = NULL; return &fence->base; @@ -189,7 +194,7 @@ nouveau_exec_job_free(struct nouveau_job *job) nouveau_job_free(job); - nouveau_fence_unref(&exec_job->fence); + kfree(exec_job->fence); kfree(exec_job->push.s); kfree(exec_job); } @@ -208,7 +213,7 @@ nouveau_exec_job_timeout(struct nouveau_job *job) nouveau_sched_entity_fini(job->entity); - return DRM_GPU_SCHED_STAT_ENODEV; + return DRM_GPU_SCHED_STAT_NOMINAL; } static struct nouveau_job_ops nouveau_exec_job_ops = { @@ -374,7 +379,7 @@ nouveau_exec_ioctl_exec(struct drm_device *dev, struct nouveau_channel *chan = NULL; struct nouveau_exec_job_args args = {}; struct drm_nouveau_exec *req = data; - int ret = 0; + int push_max, ret = 0; if (unlikely(!abi16)) return -ENOMEM; @@ -399,9 +404,10 @@ nouveau_exec_ioctl_exec(struct drm_device *dev, if (!chan->dma.ib_max) return nouveau_abi16_put(abi16, -ENOSYS); - if (unlikely(req->push_count > NOUVEAU_GEM_MAX_PUSH)) { + push_max = nouveau_exec_push_max_from_ib_max(chan->dma.ib_max); + if (unlikely(req->push_count > push_max)) { NV_PRINTK(err, cli, "pushbuf push count exceeds limit: %d max %d\n", - req->push_count, NOUVEAU_GEM_MAX_PUSH); + req->push_count, push_max); return nouveau_abi16_put(abi16, -EINVAL); } diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.h b/drivers/gpu/drm/nouveau/nouveau_exec.h index 778cacd90f656361aeac7d6774b32b40f7162bbd..5488d337bcc0e29852a20e2e2c6af986cc60f9fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.h +++ b/drivers/gpu/drm/nouveau/nouveau_exec.h @@ -51,4 +51,14 @@ int nouveau_exec_job_init(struct nouveau_exec_job **job, int nouveau_exec_ioctl_exec(struct drm_device *dev, void *data, struct drm_file *file_priv); +static inline unsigned int +nouveau_exec_push_max_from_ib_max(int ib_max) +{ + /* Limit the number of IBs per job to half the size of the ring in order + * to avoid the ring running dry between submissions and preserve one + * more slot for the job's HW fence. + */ + return ib_max > 1 ? ib_max / 2 - 1 : 0; +} + #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 77c739a55b1959a85bec13252375fe6e94744df7..ca762ea5541361bb023e8b0288470502797f1a15 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -205,16 +205,13 @@ nouveau_fence_context_new(struct nouveau_channel *chan, struct nouveau_fence_cha } int -nouveau_fence_emit(struct nouveau_fence *fence, struct nouveau_channel *chan) +nouveau_fence_emit(struct nouveau_fence *fence) { + struct nouveau_channel *chan = unrcu_pointer(fence->channel); struct nouveau_fence_chan *fctx = chan->fence; struct nouveau_fence_priv *priv = (void*)chan->drm->fence; int ret; - if (unlikely(!chan->fence)) - return -ENODEV; - - fence->channel = chan; fence->timeout = jiffies + (15 * HZ); if (priv->uevent) @@ -406,18 +403,41 @@ nouveau_fence_unref(struct nouveau_fence **pfence) } int -nouveau_fence_new(struct nouveau_fence **pfence) +nouveau_fence_create(struct nouveau_fence **pfence, + struct nouveau_channel *chan) { struct nouveau_fence *fence; + if (unlikely(!chan->fence)) + return -ENODEV; + fence = kzalloc(sizeof(*fence), GFP_KERNEL); if (!fence) return -ENOMEM; + fence->channel = chan; + *pfence = fence; return 0; } +int +nouveau_fence_new(struct nouveau_fence **pfence, + struct nouveau_channel *chan) +{ + int ret = 0; + + ret = nouveau_fence_create(pfence, chan); + if (ret) + return ret; + + ret = nouveau_fence_emit(*pfence); + if (ret) + nouveau_fence_unref(pfence); + + return ret; +} + static const char *nouveau_fence_get_get_driver_name(struct dma_fence *fence) { return "nouveau"; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 2c72d96ef17d12a92ae3001293b12388c4d9d0b3..64d33ae7f35610fe763cd34f717335ab7b24ea4e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -17,10 +17,11 @@ struct nouveau_fence { unsigned long timeout; }; -int nouveau_fence_new(struct nouveau_fence **); +int nouveau_fence_create(struct nouveau_fence **, struct nouveau_channel *); +int nouveau_fence_new(struct nouveau_fence **, struct nouveau_channel *); void nouveau_fence_unref(struct nouveau_fence **); -int nouveau_fence_emit(struct nouveau_fence *, struct nouveau_channel *); +int nouveau_fence_emit(struct nouveau_fence *); bool nouveau_fence_done(struct nouveau_fence *); int nouveau_fence_wait(struct nouveau_fence *, bool lazy, bool intr); int nouveau_fence_sync(struct nouveau_bo *, struct nouveau_channel *, bool exclusive, bool intr); diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index c0b10d8d3d0305bb8ef16351a2ad21d9452a0839..a0d303e5ce3d8dfdeb3c7b9a885d2e9992c752dd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -914,11 +914,8 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, } } - ret = nouveau_fence_new(&fence); - if (!ret) - ret = nouveau_fence_emit(fence, chan); + ret = nouveau_fence_new(&fence, chan); if (ret) { - nouveau_fence_unref(&fence); NV_PRINTK(err, cli, "error fencing pushbuf: %d\n", ret); WIND_RING(chan); goto out; diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c index 88217185e0f388353a247a7bb033c59300f73d2f..3b7ea52212260583ffbdfae77ddd92936d1d00ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sched.c +++ b/drivers/gpu/drm/nouveau/nouveau_sched.c @@ -375,14 +375,20 @@ nouveau_sched_run_job(struct drm_sched_job *sched_job) static enum drm_gpu_sched_stat nouveau_sched_timedout_job(struct drm_sched_job *sched_job) { + struct drm_gpu_scheduler *sched = sched_job->sched; struct nouveau_job *job = to_nouveau_job(sched_job); + enum drm_gpu_sched_stat stat = DRM_GPU_SCHED_STAT_NOMINAL; - NV_PRINTK(warn, job->cli, "Job timed out.\n"); + drm_sched_stop(sched, sched_job); if (job->ops->timeout) - return job->ops->timeout(job); + stat = job->ops->timeout(job); + else + NV_PRINTK(warn, job->cli, "Generic job timeout.\n"); + + drm_sched_start(sched, true); - return DRM_GPU_SCHED_STAT_ENODEV; + return stat; } static void diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index c87a57c9c592b41cc7a1a44b23bbc8da23817862..22dd8b4456855f9b78fd3c3d32f9978be4a4ed2d 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -123,7 +123,7 @@ int radeon_sa_bo_new(struct radeon_sa_manager *sa_manager, unsigned int size, unsigned int align) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, true, align); + GFP_KERNEL, false, align); if (IS_ERR(sa)) { *sa_bo = NULL; diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index c1dfbfcaa0001f405a84725f53e2739b1ccc80d3..bccb33b900f390ff070b7e45241d57603f890cab 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -118,7 +118,7 @@ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) kunit_release_action(test, kunit_action_platform_driver_unregister, - pdev); + &fake_platform_driver); } EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c index 186b28dc70380f1477d5eabb49ece2e9ed37e848..05d5e7af6d250881b50b29d26e1069ca6c41273e 100644 --- a/drivers/gpu/drm/tests/drm_mm_test.c +++ b/drivers/gpu/drm/tests/drm_mm_test.c @@ -939,7 +939,7 @@ static void drm_test_mm_insert_range(struct kunit *test) KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max - 1)); KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max / 2)); KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, - max / 2, max / 2)); + max / 2, max)); KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, max / 4 + 1, 3 * max / 4 - 1)); diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index c5bb683e440c5f5d8ee1ea71cf301de20e3f28c8..0187539ff5eaaa5f5c59096ed427de7f268f29e5 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -70,10 +70,10 @@ MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)"); #define READ_STATUS_SIZE 13 #define MISC_VALUE_SIZE 4 -#define CMD_TIMEOUT msecs_to_jiffies(200) -#define DATA_TIMEOUT msecs_to_jiffies(1000) -#define IDLE_TIMEOUT msecs_to_jiffies(2000) -#define FIRST_FRAME_TIMEOUT msecs_to_jiffies(2000) +#define CMD_TIMEOUT 200 +#define DATA_TIMEOUT 1000 +#define IDLE_TIMEOUT 2000 +#define FIRST_FRAME_TIMEOUT 2000 #define MISC_REQ_GET_SET_ECO_A 0xff #define MISC_REQ_GET_SET_ECO_B 0x35 @@ -389,7 +389,7 @@ static void gm12u320_fb_update_work(struct work_struct *work) * switches back to showing its logo. */ queue_delayed_work(system_long_wq, &gm12u320->fb_update.work, - IDLE_TIMEOUT); + msecs_to_jiffies(IDLE_TIMEOUT)); return; err: diff --git a/drivers/gpu/drm/virtio/virtgpu_submit.c b/drivers/gpu/drm/virtio/virtgpu_submit.c index 3c00135ead45af7bd5f9b9c64236750a0cdd4ad7..5c514946bbad974cdb05ee8ee6f456066b741255 100644 --- a/drivers/gpu/drm/virtio/virtgpu_submit.c +++ b/drivers/gpu/drm/virtio/virtgpu_submit.c @@ -361,7 +361,6 @@ static void virtio_gpu_complete_submit(struct virtio_gpu_submit *submit) submit->buf = NULL; submit->buflist = NULL; submit->sync_file = NULL; - submit->out_fence = NULL; submit->out_fence_fd = -1; } diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index d5d4f642d3678077c7053dad9c5026e04b9f2055..3c99fb8b54e2df54ab26f8b6b15b4fd30d41183f 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -408,15 +408,10 @@ void vkms_set_composer(struct vkms_output *out, bool enabled) if (enabled) drm_crtc_vblank_get(&out->crtc); - mutex_lock(&out->enabled_lock); + spin_lock_irq(&out->lock); old_enabled = out->composer_enabled; out->composer_enabled = enabled; - - /* the composition wasn't enabled, so unlock the lock to make sure the lock - * will be balanced even if we have a failed commit - */ - if (!out->composer_enabled) - mutex_unlock(&out->enabled_lock); + spin_unlock_irq(&out->lock); if (old_enabled) drm_crtc_vblank_put(&out->crtc); diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 3c5ebf106b66de958d78518ffecb7e6a343d9fba..61e500b8c9da2397b17fe07a98d469e25bc6d846 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -16,7 +16,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) struct drm_crtc *crtc = &output->crtc; struct vkms_crtc_state *state; u64 ret_overrun; - bool ret, fence_cookie, composer_enabled; + bool ret, fence_cookie; fence_cookie = dma_fence_begin_signalling(); @@ -25,15 +25,15 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) if (ret_overrun != 1) pr_warn("%s: vblank timer overrun\n", __func__); + spin_lock(&output->lock); ret = drm_crtc_handle_vblank(crtc); if (!ret) DRM_ERROR("vkms failure on handling vblank"); state = output->composer_state; - composer_enabled = output->composer_enabled; - mutex_unlock(&output->enabled_lock); + spin_unlock(&output->lock); - if (state && composer_enabled) { + if (state && output->composer_enabled) { u64 frame = drm_crtc_accurate_vblank_count(crtc); /* update frame_start only if a queued vkms_composer_worker() @@ -295,7 +295,6 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, spin_lock_init(&vkms_out->lock); spin_lock_init(&vkms_out->composer_lock); - mutex_init(&vkms_out->enabled_lock); vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0); if (!vkms_out->composer_workq) diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index c7ae6c2ba1df08fe903a8ff8be3782918e70efbb..8f5710debb1eb708fb9edfaf594c10382a790e66 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -108,10 +108,8 @@ struct vkms_output { struct workqueue_struct *composer_workq; /* protects concurrent access to composer */ spinlock_t lock; - /* guarantees that if the composer is enabled, a job will be queued */ - struct mutex enabled_lock; - /* protected by @enabled_lock */ + /* protected by @lock */ bool composer_enabled; struct vkms_crtc_state *composer_state; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 8db4ae05febc8f069528cb79095b44d97e83159b..e098cc7b3944375387bb8003ca6ab4807bfb3eb9 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1802,7 +1802,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) return 0; } -#ifdef CONFIG_PM static int mt_suspend(struct hid_device *hdev, pm_message_t state) { struct mt_device *td = hid_get_drvdata(hdev); @@ -1836,7 +1835,6 @@ static int mt_resume(struct hid_device *hdev) return 0; } -#endif static void mt_remove(struct hid_device *hdev) { @@ -2259,10 +2257,8 @@ static struct hid_driver mt_driver = { .usage_table = mt_grabbed_usages, .event = mt_event, .report = mt_report, -#ifdef CONFIG_PM - .suspend = mt_suspend, - .reset_resume = mt_reset_resume, - .resume = mt_resume, -#endif + .suspend = pm_ptr(mt_suspend), + .reset_resume = pm_ptr(mt_reset_resume), + .resume = pm_ptr(mt_resume), }; module_hid_driver(mt_driver); diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 84e7ba5314d3f8d6c6c5578597bc4a42c72e8379..d4af17fdba4670ae9f26db1fb8609a180472dc20 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -436,7 +436,6 @@ static void rmi_report(struct hid_device *hid, struct hid_report *report) input_sync(field->hidinput->input); } -#ifdef CONFIG_PM static int rmi_suspend(struct hid_device *hdev, pm_message_t message) { struct rmi_data *data = hid_get_drvdata(hdev); @@ -483,7 +482,6 @@ static int rmi_post_resume(struct hid_device *hdev) hid_hw_close(hdev); return ret; } -#endif /* CONFIG_PM */ static int rmi_hid_reset(struct rmi_transport_dev *xport, u16 reset_addr) { @@ -774,11 +772,9 @@ static struct hid_driver rmi_driver = { .report = rmi_report, .input_mapping = rmi_input_mapping, .input_configured = rmi_input_configured, -#ifdef CONFIG_PM - .suspend = rmi_suspend, - .resume = rmi_post_resume, - .reset_resume = rmi_post_resume, -#endif + .suspend = pm_ptr(rmi_suspend), + .resume = pm_ptr(rmi_post_resume), + .reset_resume = pm_ptr(rmi_post_resume), }; module_hid_driver(rmi_driver); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 257dd73e37bf5756d18e05ad46c9476c2baa1554..a90ed2ceae841871c742e38258d6944910abf77d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1562,7 +1562,6 @@ static int hid_post_reset(struct usb_interface *intf) return 0; } -#ifdef CONFIG_PM static int hid_resume_common(struct hid_device *hid, bool driver_suspended) { int status = 0; @@ -1654,8 +1653,6 @@ static int hid_reset_resume(struct usb_interface *intf) return status; } -#endif /* CONFIG_PM */ - static const struct usb_device_id hid_usb_ids[] = { { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, .bInterfaceClass = USB_INTERFACE_CLASS_HID }, @@ -1668,11 +1665,9 @@ static struct usb_driver hid_driver = { .name = "usbhid", .probe = usbhid_probe, .disconnect = usbhid_disconnect, -#ifdef CONFIG_PM - .suspend = hid_suspend, - .resume = hid_resume, - .reset_resume = hid_reset_resume, -#endif + .suspend = pm_ptr(hid_suspend), + .resume = pm_ptr(hid_resume), + .reset_resume = pm_ptr(hid_reset_resume), .pre_reset = hid_pre_reset, .post_reset = hid_post_reset, .id_table = hid_usb_ids, diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index ebf15f31d97e3018475416121d21ec1c923c27f3..3cabeeabb1cacf0627b02110d6f4fc17abc7e4a0 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -98,6 +98,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) */ if (version >= VERSION_WIN10_V5) { msg->msg_sint = VMBUS_MESSAGE_SINT; + msg->msg_vtl = ms_hyperv.vtl; vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4; } else { msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); @@ -482,10 +483,17 @@ void vmbus_set_event(struct vmbus_channel *channel) ++channel->sig_events; - if (hv_isolation_type_snp()) - hv_ghcb_hypercall(HVCALL_SIGNAL_EVENT, &channel->sig_event, - NULL, sizeof(channel->sig_event)); - else + if (ms_hyperv.paravisor_present) { + if (hv_isolation_type_snp()) + hv_ghcb_hypercall(HVCALL_SIGNAL_EVENT, &channel->sig_event, + NULL, sizeof(channel->sig_event)); + else if (hv_isolation_type_tdx()) + hv_tdx_hypercall(HVCALL_SIGNAL_EVENT | HV_HYPERCALL_FAST_BIT, + channel->sig_event, 0); + else + WARN_ON_ONCE(1); + } else { hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, channel->sig_event); + } } EXPORT_SYMBOL_GPL(vmbus_set_event); diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index de6708dbe0df48d1e54407257aa083e1af9de7b8..51e5018ac9b26cb682ff21cc3cd23461a632658d 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "hyperv_vmbus.h" /* The one and only */ @@ -56,20 +57,37 @@ int hv_post_message(union hv_connection_id connection_id, local_irq_save(flags); - aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg); + /* + * A TDX VM with the paravisor must use the decrypted post_msg_page: see + * the comment in struct hv_per_cpu_context. A SNP VM with the paravisor + * can use the encrypted hyperv_pcpu_input_arg because it copies the + * input into the GHCB page, which has been decrypted by the paravisor. + */ + if (hv_isolation_type_tdx() && ms_hyperv.paravisor_present) + aligned_msg = this_cpu_ptr(hv_context.cpu_context)->post_msg_page; + else + aligned_msg = *this_cpu_ptr(hyperv_pcpu_input_arg); + aligned_msg->connectionid = connection_id; aligned_msg->reserved = 0; aligned_msg->message_type = message_type; aligned_msg->payload_size = payload_size; memcpy((void *)aligned_msg->payload, payload, payload_size); - if (hv_isolation_type_snp()) - status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE, - (void *)aligned_msg, NULL, - sizeof(*aligned_msg)); - else + if (ms_hyperv.paravisor_present) { + if (hv_isolation_type_tdx()) + status = hv_tdx_hypercall(HVCALL_POST_MESSAGE, + virt_to_phys(aligned_msg), 0); + else if (hv_isolation_type_snp()) + status = hv_ghcb_hypercall(HVCALL_POST_MESSAGE, + aligned_msg, NULL, + sizeof(*aligned_msg)); + else + status = HV_STATUS_INVALID_PARAMETER; + } else { status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL); + } local_irq_restore(flags); @@ -78,7 +96,7 @@ int hv_post_message(union hv_connection_id connection_id, int hv_synic_alloc(void) { - int cpu; + int cpu, ret = -ENOMEM; struct hv_per_cpu_context *hv_cpu; /* @@ -104,11 +122,29 @@ int hv_synic_alloc(void) tasklet_init(&hv_cpu->msg_dpc, vmbus_on_msg_dpc, (unsigned long) hv_cpu); + if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) { + hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC); + if (hv_cpu->post_msg_page == NULL) { + pr_err("Unable to allocate post msg page\n"); + goto err; + } + + ret = set_memory_decrypted((unsigned long)hv_cpu->post_msg_page, 1); + if (ret) { + pr_err("Failed to decrypt post msg page: %d\n", ret); + /* Just leak the page, as it's unsafe to free the page. */ + hv_cpu->post_msg_page = NULL; + goto err; + } + + memset(hv_cpu->post_msg_page, 0, PAGE_SIZE); + } + /* * Synic message and event pages are allocated by paravisor. * Skip these pages allocation here. */ - if (!hv_isolation_type_snp() && !hv_root_partition) { + if (!ms_hyperv.paravisor_present && !hv_root_partition) { hv_cpu->synic_message_page = (void *)get_zeroed_page(GFP_ATOMIC); if (hv_cpu->synic_message_page == NULL) { @@ -120,29 +156,96 @@ int hv_synic_alloc(void) (void *)get_zeroed_page(GFP_ATOMIC); if (hv_cpu->synic_event_page == NULL) { pr_err("Unable to allocate SYNIC event page\n"); + + free_page((unsigned long)hv_cpu->synic_message_page); + hv_cpu->synic_message_page = NULL; goto err; } } + + if (!ms_hyperv.paravisor_present && + (hv_isolation_type_snp() || hv_isolation_type_tdx())) { + ret = set_memory_decrypted((unsigned long) + hv_cpu->synic_message_page, 1); + if (ret) { + pr_err("Failed to decrypt SYNIC msg page: %d\n", ret); + hv_cpu->synic_message_page = NULL; + + /* + * Free the event page here so that hv_synic_free() + * won't later try to re-encrypt it. + */ + free_page((unsigned long)hv_cpu->synic_event_page); + hv_cpu->synic_event_page = NULL; + goto err; + } + + ret = set_memory_decrypted((unsigned long) + hv_cpu->synic_event_page, 1); + if (ret) { + pr_err("Failed to decrypt SYNIC event page: %d\n", ret); + hv_cpu->synic_event_page = NULL; + goto err; + } + + memset(hv_cpu->synic_message_page, 0, PAGE_SIZE); + memset(hv_cpu->synic_event_page, 0, PAGE_SIZE); + } } return 0; + err: /* * Any memory allocations that succeeded will be freed when * the caller cleans up by calling hv_synic_free() */ - return -ENOMEM; + return ret; } void hv_synic_free(void) { - int cpu; + int cpu, ret; for_each_present_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); + /* It's better to leak the page if the encryption fails. */ + if (ms_hyperv.paravisor_present && hv_isolation_type_tdx()) { + if (hv_cpu->post_msg_page) { + ret = set_memory_encrypted((unsigned long) + hv_cpu->post_msg_page, 1); + if (ret) { + pr_err("Failed to encrypt post msg page: %d\n", ret); + hv_cpu->post_msg_page = NULL; + } + } + } + + if (!ms_hyperv.paravisor_present && + (hv_isolation_type_snp() || hv_isolation_type_tdx())) { + if (hv_cpu->synic_message_page) { + ret = set_memory_encrypted((unsigned long) + hv_cpu->synic_message_page, 1); + if (ret) { + pr_err("Failed to encrypt SYNIC msg page: %d\n", ret); + hv_cpu->synic_message_page = NULL; + } + } + + if (hv_cpu->synic_event_page) { + ret = set_memory_encrypted((unsigned long) + hv_cpu->synic_event_page, 1); + if (ret) { + pr_err("Failed to encrypt SYNIC event page: %d\n", ret); + hv_cpu->synic_event_page = NULL; + } + } + } + + free_page((unsigned long)hv_cpu->post_msg_page); free_page((unsigned long)hv_cpu->synic_event_page); free_page((unsigned long)hv_cpu->synic_message_page); } @@ -170,7 +273,7 @@ void hv_synic_enable_regs(unsigned int cpu) simp.as_uint64 = hv_get_register(HV_REGISTER_SIMP); simp.simp_enabled = 1; - if (hv_isolation_type_snp() || hv_root_partition) { + if (ms_hyperv.paravisor_present || hv_root_partition) { /* Mask out vTOM bit. ioremap_cache() maps decrypted */ u64 base = (simp.base_simp_gpa << HV_HYP_PAGE_SHIFT) & ~ms_hyperv.shared_gpa_boundary; @@ -189,7 +292,7 @@ void hv_synic_enable_regs(unsigned int cpu) siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP); siefp.siefp_enabled = 1; - if (hv_isolation_type_snp() || hv_root_partition) { + if (ms_hyperv.paravisor_present || hv_root_partition) { /* Mask out vTOM bit. ioremap_cache() maps decrypted */ u64 base = (siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT) & ~ms_hyperv.shared_gpa_boundary; @@ -272,7 +375,7 @@ void hv_synic_disable_regs(unsigned int cpu) * addresses. */ simp.simp_enabled = 0; - if (hv_isolation_type_snp() || hv_root_partition) { + if (ms_hyperv.paravisor_present || hv_root_partition) { iounmap(hv_cpu->synic_message_page); hv_cpu->synic_message_page = NULL; } else { @@ -284,7 +387,7 @@ void hv_synic_disable_regs(unsigned int cpu) siefp.as_uint64 = hv_get_register(HV_REGISTER_SIEFP); siefp.siefp_enabled = 0; - if (hv_isolation_type_snp() || hv_root_partition) { + if (ms_hyperv.paravisor_present || hv_root_partition) { iounmap(hv_cpu->synic_event_page); hv_cpu->synic_event_page = NULL; } else { diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 0d7a3ba66396491fb68f94d68fad4780940b7511..e000fa3b9f978cfc195babd258b1d17f8cb83995 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -646,7 +647,7 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, void *v) { struct memory_notify *mem = (struct memory_notify *)v; - unsigned long flags, pfn_count; + unsigned long pfn_count; switch (val) { case MEM_ONLINE: @@ -655,21 +656,22 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, break; case MEM_OFFLINE: - spin_lock_irqsave(&dm_device.ha_lock, flags); - pfn_count = hv_page_offline_check(mem->start_pfn, - mem->nr_pages); - if (pfn_count <= dm_device.num_pages_onlined) { - dm_device.num_pages_onlined -= pfn_count; - } else { - /* - * We're offlining more pages than we managed to online. - * This is unexpected. In any case don't let - * num_pages_onlined wrap around zero. - */ - WARN_ON_ONCE(1); - dm_device.num_pages_onlined = 0; + scoped_guard(spinlock_irqsave, &dm_device.ha_lock) { + pfn_count = hv_page_offline_check(mem->start_pfn, + mem->nr_pages); + if (pfn_count <= dm_device.num_pages_onlined) { + dm_device.num_pages_onlined -= pfn_count; + } else { + /* + * We're offlining more pages than we + * managed to online. This is + * unexpected. In any case don't let + * num_pages_onlined wrap around zero. + */ + WARN_ON_ONCE(1); + dm_device.num_pages_onlined = 0; + } } - spin_unlock_irqrestore(&dm_device.ha_lock, flags); break; case MEM_GOING_ONLINE: case MEM_GOING_OFFLINE: @@ -721,24 +723,23 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, unsigned long start_pfn; unsigned long processed_pfn; unsigned long total_pfn = pfn_count; - unsigned long flags; for (i = 0; i < (size/HA_CHUNK); i++) { start_pfn = start + (i * HA_CHUNK); - spin_lock_irqsave(&dm_device.ha_lock, flags); - has->ha_end_pfn += HA_CHUNK; + scoped_guard(spinlock_irqsave, &dm_device.ha_lock) { + has->ha_end_pfn += HA_CHUNK; - if (total_pfn > HA_CHUNK) { - processed_pfn = HA_CHUNK; - total_pfn -= HA_CHUNK; - } else { - processed_pfn = total_pfn; - total_pfn = 0; - } + if (total_pfn > HA_CHUNK) { + processed_pfn = HA_CHUNK; + total_pfn -= HA_CHUNK; + } else { + processed_pfn = total_pfn; + total_pfn = 0; + } - has->covered_end_pfn += processed_pfn; - spin_unlock_irqrestore(&dm_device.ha_lock, flags); + has->covered_end_pfn += processed_pfn; + } reinit_completion(&dm_device.ol_waitevent); @@ -758,10 +759,10 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, */ do_hot_add = false; } - spin_lock_irqsave(&dm_device.ha_lock, flags); - has->ha_end_pfn -= HA_CHUNK; - has->covered_end_pfn -= processed_pfn; - spin_unlock_irqrestore(&dm_device.ha_lock, flags); + scoped_guard(spinlock_irqsave, &dm_device.ha_lock) { + has->ha_end_pfn -= HA_CHUNK; + has->covered_end_pfn -= processed_pfn; + } break; } @@ -781,10 +782,9 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size, static void hv_online_page(struct page *pg, unsigned int order) { struct hv_hotadd_state *has; - unsigned long flags; unsigned long pfn = page_to_pfn(pg); - spin_lock_irqsave(&dm_device.ha_lock, flags); + guard(spinlock_irqsave)(&dm_device.ha_lock); list_for_each_entry(has, &dm_device.ha_region_list, list) { /* The page belongs to a different HAS. */ if ((pfn < has->start_pfn) || @@ -794,7 +794,6 @@ static void hv_online_page(struct page *pg, unsigned int order) hv_bring_pgs_online(has, pfn, 1UL << order); break; } - spin_unlock_irqrestore(&dm_device.ha_lock, flags); } static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) @@ -803,9 +802,8 @@ static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) struct hv_hotadd_gap *gap; unsigned long residual, new_inc; int ret = 0; - unsigned long flags; - spin_lock_irqsave(&dm_device.ha_lock, flags); + guard(spinlock_irqsave)(&dm_device.ha_lock); list_for_each_entry(has, &dm_device.ha_region_list, list) { /* * If the pfn range we are dealing with is not in the current @@ -852,7 +850,6 @@ static int pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt) ret = 1; break; } - spin_unlock_irqrestore(&dm_device.ha_lock, flags); return ret; } @@ -947,7 +944,6 @@ static unsigned long process_hot_add(unsigned long pg_start, { struct hv_hotadd_state *ha_region = NULL; int covered; - unsigned long flags; if (pfn_cnt == 0) return 0; @@ -979,9 +975,9 @@ static unsigned long process_hot_add(unsigned long pg_start, ha_region->covered_end_pfn = pg_start; ha_region->end_pfn = rg_start + rg_size; - spin_lock_irqsave(&dm_device.ha_lock, flags); - list_add_tail(&ha_region->list, &dm_device.ha_region_list); - spin_unlock_irqrestore(&dm_device.ha_lock, flags); + scoped_guard(spinlock_irqsave, &dm_device.ha_lock) { + list_add_tail(&ha_region->list, &dm_device.ha_region_list); + } } do_pg_range: @@ -2047,7 +2043,6 @@ static void balloon_remove(struct hv_device *dev) struct hv_dynmem_device *dm = hv_get_drvdata(dev); struct hv_hotadd_state *has, *tmp; struct hv_hotadd_gap *gap, *tmp_gap; - unsigned long flags; if (dm->num_pages_ballooned != 0) pr_warn("Ballooned pages: %d\n", dm->num_pages_ballooned); @@ -2073,7 +2068,7 @@ static void balloon_remove(struct hv_device *dev) #endif } - spin_lock_irqsave(&dm_device.ha_lock, flags); + guard(spinlock_irqsave)(&dm_device.ha_lock); list_for_each_entry_safe(has, tmp, &dm->ha_region_list, list) { list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) { list_del(&gap->list); @@ -2082,7 +2077,6 @@ static void balloon_remove(struct hv_device *dev) list_del(&has->list); kfree(has); } - spin_unlock_irqrestore(&dm_device.ha_lock, flags); } static int balloon_suspend(struct hv_device *hv_dev) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index 6a2258fef1fe90acc68842ddad6efdd8c2d7621a..ccad7bca3fd3dadc4c162ba6c1b6d95e9a3a991d 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -359,6 +360,8 @@ int hv_common_cpu_init(unsigned int cpu) u64 msr_vp_index; gfp_t flags; int pgcount = hv_root_partition ? 2 : 1; + void *mem; + int ret; /* hv_cpu_init() can be called with IRQs disabled from hv_resume() */ flags = irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL; @@ -370,14 +373,41 @@ int hv_common_cpu_init(unsigned int cpu) * allocated if this CPU was previously online and then taken offline */ if (!*inputarg) { - *inputarg = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags); - if (!(*inputarg)) + mem = kmalloc(pgcount * HV_HYP_PAGE_SIZE, flags); + if (!mem) return -ENOMEM; if (hv_root_partition) { outputarg = (void **)this_cpu_ptr(hyperv_pcpu_output_arg); - *outputarg = (char *)(*inputarg) + HV_HYP_PAGE_SIZE; + *outputarg = (char *)mem + HV_HYP_PAGE_SIZE; + } + + if (!ms_hyperv.paravisor_present && + (hv_isolation_type_snp() || hv_isolation_type_tdx())) { + ret = set_memory_decrypted((unsigned long)mem, pgcount); + if (ret) { + /* It may be unsafe to free 'mem' */ + return ret; + } + + memset(mem, 0x00, pgcount * HV_HYP_PAGE_SIZE); } + + /* + * In a fully enlightened TDX/SNP VM with more than 64 VPs, if + * hyperv_pcpu_input_arg is not NULL, set_memory_decrypted() -> + * ... -> cpa_flush()-> ... -> __send_ipi_mask_ex() tries to + * use hyperv_pcpu_input_arg as the hypercall input page, which + * must be a decrypted page in such a VM, but the page is still + * encrypted before set_memory_decrypted() returns. Fix this by + * setting *inputarg after the above set_memory_decrypted(): if + * hyperv_pcpu_input_arg is NULL, __send_ipi_mask_ex() returns + * HV_STATUS_INVALID_PARAMETER immediately, and the function + * hv_send_ipi_mask() falls back to orig_apic.send_IPI_mask(), + * which may be slightly slower than the hypercall, but still + * works correctly in such a VM. + */ + *inputarg = mem; } msr_vp_index = hv_get_register(HV_REGISTER_VP_INDEX); @@ -502,6 +532,12 @@ bool __weak hv_isolation_type_snp(void) } EXPORT_SYMBOL_GPL(hv_isolation_type_snp); +bool __weak hv_isolation_type_tdx(void) +{ + return false; +} +EXPORT_SYMBOL_GPL(hv_isolation_type_tdx); + void __weak hv_setup_vmbus_handler(void (*handler)(void)) { } @@ -542,3 +578,9 @@ u64 __weak hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_s return HV_STATUS_INVALID_PARAMETER; } EXPORT_SYMBOL_GPL(hv_ghcb_hypercall); + +u64 __weak hv_tdx_hypercall(u64 control, u64 param1, u64 param2) +{ + return HV_STATUS_INVALID_PARAMETER; +} +EXPORT_SYMBOL_GPL(hv_tdx_hypercall); diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 55f2086841ae41a1493a235aaee4f297601f496c..f6b1e710f805511229258bea51a722a0dcb898f6 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -123,6 +123,17 @@ struct hv_per_cpu_context { void *synic_message_page; void *synic_event_page; + /* + * The page is only used in hv_post_message() for a TDX VM (with the + * paravisor) to post a messages to Hyper-V: when such a VM calls + * HVCALL_POST_MESSAGE, it can't use the hyperv_pcpu_input_arg (which + * is encrypted in such a VM) as the hypercall input page, because + * the input page for HVCALL_POST_MESSAGE must be decrypted in such a + * VM, so post_msg_page (which is decrypted in hv_synic_alloc()) is + * introduced for this purpose. See hyperv_init() for more comments. + */ + void *post_msg_page; + /* * Starting with win8, we can take channel interrupts on any CPU; * we will manage the tasklet that handles events messages on a per CPU diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 67f95a29aeca5b8e0b7f3755448724f4c7e726fb..edbb38f6956b94514bcf93188df1bc0a6185f25f 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2287,7 +2287,8 @@ static int vmbus_acpi_add(struct platform_device *pdev) * Some ancestor of the vmbus acpi device (Gen1 or Gen2 * firmware) is the VMOD that has the mmio ranges. Get that. */ - for (ancestor = acpi_dev_parent(device); ancestor; + for (ancestor = acpi_dev_parent(device); + ancestor && ancestor->handle != ACPI_ROOT_OBJECT; ancestor = acpi_dev_parent(ancestor)) { result = acpi_walk_resources(ancestor->handle, METHOD_NAME__CRS, vmbus_walk_resources, NULL); diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 02a71244fc3b765d77efedcc56cbc876afdfc864..b5b81bd83bb1573d0434eeb2a6cdf54898e4f227 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -1910,6 +1910,10 @@ static umode_t nct6775_in_is_visible(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct nct6775_data *data = dev_get_drvdata(dev); int in = index / 5; /* voltage index */ + int nr = index % 5; /* attribute index */ + + if (nr == 1 && data->ALARM_BITS[in] == -1) + return 0; if (!(data->have_in & BIT(in))) return 0; diff --git a/drivers/hwspinlock/omap_hwspinlock.c b/drivers/hwspinlock/omap_hwspinlock.c index dfe82952671b1584ade4c48c6864352fd211e468..a9fd9ca45f2a893a37386f6c3256c67e0e4525c1 100644 --- a/drivers/hwspinlock/omap_hwspinlock.c +++ b/drivers/hwspinlock/omap_hwspinlock.c @@ -145,7 +145,7 @@ static int omap_hwspinlock_probe(struct platform_device *pdev) return ret; } -static int omap_hwspinlock_remove(struct platform_device *pdev) +static void omap_hwspinlock_remove(struct platform_device *pdev) { struct hwspinlock_device *bank = platform_get_drvdata(pdev); int ret; @@ -153,12 +153,10 @@ static int omap_hwspinlock_remove(struct platform_device *pdev) ret = hwspin_lock_unregister(bank); if (ret) { dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); - return ret; + return; } pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id omap_hwspinlock_of_match[] = { @@ -171,7 +169,7 @@ MODULE_DEVICE_TABLE(of, omap_hwspinlock_of_match); static struct platform_driver omap_hwspinlock_driver = { .probe = omap_hwspinlock_probe, - .remove = omap_hwspinlock_remove, + .remove_new = omap_hwspinlock_remove, .driver = { .name = "omap_hwspinlock", .of_match_table = omap_hwspinlock_of_match, diff --git a/drivers/hwspinlock/qcom_hwspinlock.c b/drivers/hwspinlock/qcom_hwspinlock.c index 9cf186362ae2f0f76565e5758ac778618e0baaac..a0fd67fd2934448c9879374b48ebee8f6103c0a6 100644 --- a/drivers/hwspinlock/qcom_hwspinlock.c +++ b/drivers/hwspinlock/qcom_hwspinlock.c @@ -69,9 +69,18 @@ static const struct hwspinlock_ops qcom_hwspinlock_ops = { .unlock = qcom_hwspinlock_unlock, }; +static const struct regmap_config sfpb_mutex_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x100, + .fast_io = true, +}; + static const struct qcom_hwspinlock_of_data of_sfpb_mutex = { .offset = 0x4, .stride = 0x4, + .regmap_config = &sfpb_mutex_config, }; static const struct regmap_config tcsr_msm8226_mutex_config = { @@ -197,6 +206,8 @@ static int qcom_hwspinlock_probe(struct platform_device *pdev) bank->lock[i].priv = devm_regmap_field_alloc(&pdev->dev, regmap, field); + if (IS_ERR(bank->lock[i].priv)) + return PTR_ERR(bank->lock[i].priv); } return devm_hwspin_lock_register(&pdev->dev, bank, &qcom_hwspinlock_ops, diff --git a/drivers/hwspinlock/u8500_hsem.c b/drivers/hwspinlock/u8500_hsem.c index 67845c0c970126309cda5132782eceb1fe80807e..1edca1092f29b0077b4b5ff7f606b763719e5b2c 100644 --- a/drivers/hwspinlock/u8500_hsem.c +++ b/drivers/hwspinlock/u8500_hsem.c @@ -120,20 +120,18 @@ static int u8500_hsem_probe(struct platform_device *pdev) pdata->base_id, num_locks); } -static int u8500_hsem_remove(struct platform_device *pdev) +static void u8500_hsem_remove(struct platform_device *pdev) { struct hwspinlock_device *bank = platform_get_drvdata(pdev); void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET; /* clear all interrupts */ writel(0xFFFF, io_base + HSEM_ICRALL); - - return 0; } static struct platform_driver u8500_hsem_driver = { .probe = u8500_hsem_probe, - .remove = u8500_hsem_remove, + .remove_new = u8500_hsem_remove, .driver = { .name = "u8500_hsem", }, diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index c6d1a345ea6d8aee2eadc886d94287783f5c668c..9388823bb0bb960c52f0534bfad732a60778a170 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -72,7 +72,7 @@ config I2C_MUX source "drivers/i2c/muxes/Kconfig" config I2C_ATR - tristate "I2C Address Translator (ATR) support" + tristate "I2C Address Translator (ATR) support" if COMPILE_TEST help Enable support for I2C Address Translator (ATR) chips. diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 9cfe8fc509d7d6618cdad2bd74c30d9b4f3d672e..6644eebedaf3b7f4727fd88da340bd3a72314ac3 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1384,10 +1384,10 @@ config I2C_ICY config I2C_MLXCPLD tristate "Mellanox I2C driver" - depends on X86_64 || COMPILE_TEST + depends on X86_64 || (ARM64 && ACPI) || COMPILE_TEST help This exposes the Mellanox platform I2C busses to the linux I2C layer - for X86 based systems. + for X86 and ARM64/ACPI based systems. Controller is implemented as CPLD logic. This driver can also be built as a module. If so, the module will be diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index cc58feacd08212b792d486f4d455a764d6e9bb81..0231c5be6354f4ff40b2dc0c8ab57bd191511e3c 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -165,14 +165,15 @@ static int ali15x3_setup(struct pci_dev *ALI15X3_dev) } if(force_addr) { + int ret; + dev_info(&ALI15X3_dev->dev, "forcing ISA address 0x%04X\n", ali15x3_smba); - if (PCIBIOS_SUCCESSFUL != pci_write_config_word(ALI15X3_dev, - SMBBA, - ali15x3_smba)) + ret = pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba); + if (ret != PCIBIOS_SUCCESSFUL) goto error; - if (PCIBIOS_SUCCESSFUL != pci_read_config_word(ALI15X3_dev, - SMBBA, &a)) + ret = pci_read_config_word(ALI15X3_dev, SMBBA, &a); + if (ret != PCIBIOS_SUCCESSFUL) goto error; if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { /* make sure it works */ diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 2e5acfeb76c818a3058ffbd75d59930b35e17df8..5a416b39b8183628bb71890a847b1ed3d799e806 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -698,13 +698,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, if (time_left == 0) { /* - * If timed out and bus is still busy in a multi master - * environment, attempt recovery at here. + * In a multi-master setup, if a timeout occurs, attempt + * recovery. But if the bus is idle, we still need to reset the + * i2c controller to clear the remaining interrupts. */ if (bus->multi_master && (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS)) aspeed_i2c_recover_bus(bus); + else + aspeed_i2c_reset(bus); /* * If timed out and the state is still pending, drop the pending diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c index 05ad3bc3578ac67dcb02adc483c4fdad4c53f993..db45554327aedcb7c92d76d774fe624ab9608f57 100644 --- a/drivers/i2c/busses/i2c-at91-core.c +++ b/drivers/i2c/busses/i2c-at91-core.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -207,19 +206,15 @@ static int at91_twi_probe(struct platform_device *pdev) dev->dev = &pdev->dev; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) - return -ENODEV; + dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); phy_addr = mem->start; dev->pdata = at91_twi_get_driver_data(pdev); if (!dev->pdata) return -ENODEV; - dev->base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(dev->base)) - return PTR_ERR(dev->base); - dev->irq = platform_get_irq(pdev, 0); if (dev->irq < 0) return dev->irq; @@ -227,10 +222,9 @@ static int at91_twi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); dev->clk = devm_clk_get(dev->dev, NULL); - if (IS_ERR(dev->clk)) { - dev_err(dev->dev, "no clock defined\n"); - return -ENODEV; - } + if (IS_ERR(dev->clk)) + return dev_err_probe(dev->dev, PTR_ERR(dev->clk), "no clock defined\n"); + clk_prepare_enable(dev->clk); snprintf(dev->adapter.name, sizeof(dev->adapter.name), "AT91"); diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c index c0c35785a0dc45f04633601c0b33963bfd284db5..d311981d3e6088a0af09bc1465617503dcc71530 100644 --- a/drivers/i2c/busses/i2c-at91-master.c +++ b/drivers/i2c/busses/i2c-at91-master.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -832,7 +831,11 @@ static int at91_init_twi_recovery_gpio(struct platform_device *pdev, struct i2c_bus_recovery_info *rinfo = &dev->rinfo; rinfo->pinctrl = devm_pinctrl_get(&pdev->dev); - if (!rinfo->pinctrl || IS_ERR(rinfo->pinctrl)) { + if (!rinfo->pinctrl) { + dev_info(dev->dev, "pinctrl unavailable, bus recovery not supported\n"); + return 0; + } + if (IS_ERR(rinfo->pinctrl)) { dev_info(dev->dev, "can't get pinctrl, bus recovery not supported\n"); return PTR_ERR(rinfo->pinctrl); } diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c index e66c12ecf27063627535367fc801188de3501f64..8e43f25c117e6f4471a761e4cf6b3cb014fa5b13 100644 --- a/drivers/i2c/busses/i2c-au1550.c +++ b/drivers/i2c/busses/i2c-au1550.c @@ -342,7 +342,6 @@ static void i2c_au1550_remove(struct platform_device *pdev) i2c_au1550_disable(priv); } -#ifdef CONFIG_PM static int i2c_au1550_suspend(struct device *dev) { struct i2c_au1550_data *priv = dev_get_drvdata(dev); @@ -361,21 +360,13 @@ static int i2c_au1550_resume(struct device *dev) return 0; } -static const struct dev_pm_ops i2c_au1550_pmops = { - .suspend = i2c_au1550_suspend, - .resume = i2c_au1550_resume, -}; - -#define AU1XPSC_SMBUS_PMOPS (&i2c_au1550_pmops) - -#else -#define AU1XPSC_SMBUS_PMOPS NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(i2c_au1550_pmops, + i2c_au1550_suspend, i2c_au1550_resume); static struct platform_driver au1xpsc_smbus_driver = { .driver = { .name = "au1xpsc_smbus", - .pm = AU1XPSC_SMBUS_PMOPS, + .pm = pm_sleep_ptr(&i2c_au1550_pmops), }, .probe = i2c_au1550_probe, .remove_new = i2c_au1550_remove, diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 05c80680dff47a125fc6b495e59131c198c49a76..51aab662050b1fc795dbaf578d27b36fdfb75c92 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -1029,7 +1029,6 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) int irq, ret = 0; struct bcm_iproc_i2c_dev *iproc_i2c; struct i2c_adapter *adap; - struct resource *res; iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), GFP_KERNEL); @@ -1042,15 +1041,12 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) (enum bcm_iproc_i2c_type)of_device_get_match_data(&pdev->dev); init_completion(&iproc_i2c->done); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + iproc_i2c->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(iproc_i2c->base)) return PTR_ERR(iproc_i2c->base); if (iproc_i2c->type == IPROC_I2C_NIC) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - iproc_i2c->idm_base = devm_ioremap_resource(iproc_i2c->device, - res); + iproc_i2c->idm_base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(iproc_i2c->idm_base)) return PTR_ERR(iproc_i2c->idm_base); @@ -1128,8 +1124,6 @@ static void bcm_iproc_i2c_remove(struct platform_device *pdev) bcm_iproc_i2c_enable_disable(iproc_i2c, false); } -#ifdef CONFIG_PM_SLEEP - static int bcm_iproc_i2c_suspend(struct device *dev) { struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); @@ -1180,12 +1174,6 @@ static const struct dev_pm_ops bcm_iproc_i2c_pm_ops = { .resume_early = &bcm_iproc_i2c_resume }; -#define BCM_IPROC_I2C_PM_OPS (&bcm_iproc_i2c_pm_ops) -#else -#define BCM_IPROC_I2C_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - - static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave) { struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(slave->adapter); @@ -1258,7 +1246,7 @@ static struct platform_driver bcm_iproc_i2c_driver = { .driver = { .name = "bcm-iproc-i2c", .of_match_table = bcm_iproc_i2c_of_match, - .pm = BCM_IPROC_I2C_PM_OPS, + .pm = pm_sleep_ptr(&bcm_iproc_i2c_pm_ops), }, .probe = bcm_iproc_i2c_probe, .remove_new = bcm_iproc_i2c_remove, diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index 8ce6d3f4955160a834800d9ff9108dc227ed1d11..b92de1944221691e91412f9c33ad3d4274917e48 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include @@ -430,10 +430,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) i2c_dev->bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk, i2c_dev); - if (IS_ERR(i2c_dev->bus_clk)) { - dev_err(&pdev->dev, "Could not register clock\n"); - return PTR_ERR(i2c_dev->bus_clk); - } + if (IS_ERR(i2c_dev->bus_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->bus_clk), + "Could not register clock\n"); ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &bus_clk_rate); @@ -444,10 +443,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) } ret = clk_set_rate_exclusive(i2c_dev->bus_clk, bus_clk_rate); - if (ret < 0) { - dev_err(&pdev->dev, "Could not set clock frequency\n"); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Could not set clock frequency\n"); ret = clk_prepare_enable(i2c_dev->bus_clk); if (ret) { diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index cf92cbcb8c86bb8b23ffe46609cd5837b76349e0..acee7673254417c914652cecfb539b23d61440aa 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -594,11 +594,10 @@ static int bcm2711_release_bsc(struct brcmstb_i2c_dev *dev) static int brcmstb_i2c_probe(struct platform_device *pdev) { - int rc = 0; struct brcmstb_i2c_dev *dev; struct i2c_adapter *adap; - struct resource *iomem; const char *int_name; + int rc; /* Allocate memory for private data structure */ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); @@ -614,18 +613,15 @@ static int brcmstb_i2c_probe(struct platform_device *pdev) init_completion(&dev->done); /* Map hardware registers */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->base = devm_ioremap_resource(dev->device, iomem); - if (IS_ERR(dev->base)) { - rc = -ENOMEM; - goto probe_errorout; - } + dev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->base)) + return PTR_ERR(dev->base); if (of_device_is_compatible(dev->device->of_node, "brcm,bcm2711-hdmi-i2c")) { rc = bcm2711_release_bsc(dev); if (rc) - goto probe_errorout; + return rc; } rc = of_property_read_string(dev->device->of_node, "interrupt-names", @@ -678,16 +674,13 @@ static int brcmstb_i2c_probe(struct platform_device *pdev) adap->dev.of_node = pdev->dev.of_node; rc = i2c_add_adapter(adap); if (rc) - goto probe_errorout; + return rc; dev_info(dev->device, "%s@%dhz registered in %s mode\n", int_name ? int_name : " ", dev->clk_freq_hz, (dev->irq >= 0) ? "interrupt" : "polling"); return 0; - -probe_errorout: - return rc; } static void brcmstb_i2c_remove(struct platform_device *pdev) @@ -697,7 +690,6 @@ static void brcmstb_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&dev->adapter); } -#ifdef CONFIG_PM_SLEEP static int brcmstb_i2c_suspend(struct device *dev) { struct brcmstb_i2c_dev *i2c_dev = dev_get_drvdata(dev); @@ -715,10 +707,9 @@ static int brcmstb_i2c_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend, - brcmstb_i2c_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend, + brcmstb_i2c_resume); static const struct of_device_id brcmstb_i2c_of_match[] = { {.compatible = "brcm,brcmstb-i2c"}, @@ -732,7 +723,7 @@ static struct platform_driver brcmstb_i2c_driver = { .driver = { .name = "brcmstb-i2c", .of_match_table = brcmstb_i2c_of_match, - .pm = &brcmstb_i2c_pm, + .pm = pm_sleep_ptr(&brcmstb_i2c_pm), }, .probe = brcmstb_i2c_probe, .remove_new = brcmstb_i2c_remove, diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 9849f450257002e3ce41d5bbac6432646377c4ba..de3f58b60dce5d465f75e3bc509ea449f0c927d0 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -182,6 +182,7 @@ enum cdns_i2c_slave_state { * @reset: Reset control for the device * @quirks: flag for broken hold bit usage in r1p10 * @ctrl_reg: Cached value of the control register. + * @rinfo: I2C GPIO recovery information * @ctrl_reg_diva_divb: value of fields DIV_A and DIV_B from CR register * @slave: Registered slave instance. * @dev_mode: I2C operating role(master/slave). diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index 732daf6a932b3263493f4c7a4abfed2873ef22d0..9a664abf734d6627889a33f7f0729c384a8322e0 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -26,10 +26,10 @@ #include #include #include +#include #include -#include #include -#include +#include #include #include diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index 71b60778c643ddb0d471ca96b0ef2346ec52bf7c..02b3b1160fb062a9f64b2af166cdce225702a523 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -765,7 +765,7 @@ static int davinci_i2c_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return dev_err_probe(&pdev->dev, irq, "can't get irq resource\n"); + return irq; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) @@ -902,7 +902,6 @@ static void davinci_i2c_remove(struct platform_device *pdev) pm_runtime_disable(dev->dev); } -#ifdef CONFIG_PM static int davinci_i2c_suspend(struct device *dev) { struct davinci_i2c_dev *i2c_dev = dev_get_drvdata(dev); @@ -926,15 +925,10 @@ static int davinci_i2c_resume(struct device *dev) static const struct dev_pm_ops davinci_i2c_pm = { .suspend = davinci_i2c_suspend, .resume = davinci_i2c_resume, - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; -#define davinci_i2c_pm_ops (&davinci_i2c_pm) -#else -#define davinci_i2c_pm_ops NULL -#endif - static const struct platform_device_id davinci_i2c_driver_ids[] = { { .name = "i2c_davinci", }, { /* sentinel */ } @@ -947,7 +941,7 @@ static struct platform_driver davinci_i2c_driver = { .id_table = davinci_i2c_driver_ids, .driver = { .name = "i2c_davinci", - .pm = davinci_i2c_pm_ops, + .pm = pm_sleep_ptr(&davinci_i2c_pm), .of_match_table = davinci_i2c_of_match, }, }; diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index cdd8c67d91298681f5243f307fc77cd7a5536f15..affcfb243f0f52a5848a48016eac8e78ef709f7e 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -441,8 +441,25 @@ int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev) void __i2c_dw_disable(struct dw_i2c_dev *dev) { + unsigned int raw_intr_stats; + unsigned int enable; int timeout = 100; + bool abort_needed; unsigned int status; + int ret; + + regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &raw_intr_stats); + regmap_read(dev->map, DW_IC_ENABLE, &enable); + + abort_needed = raw_intr_stats & DW_IC_INTR_MST_ON_HOLD; + if (abort_needed) { + regmap_write(dev->map, DW_IC_ENABLE, enable | DW_IC_ENABLE_ABORT); + ret = regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, enable, + !(enable & DW_IC_ENABLE_ABORT), 10, + 100); + if (ret) + dev_err(dev->dev, "timeout while trying to abort current transfer\n"); + } do { __i2c_dw_disable_nowait(dev); diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index cf4f684f535664ff59275d604cdbbbe7dc60939c..a7f6f3eafad7dd72241f57a2a356219d36bfaf56 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -98,6 +98,7 @@ #define DW_IC_INTR_START_DET BIT(10) #define DW_IC_INTR_GEN_CALL BIT(11) #define DW_IC_INTR_RESTART_DET BIT(12) +#define DW_IC_INTR_MST_ON_HOLD BIT(13) #define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ DW_IC_INTR_TX_ABRT | \ @@ -108,6 +109,8 @@ DW_IC_INTR_RX_UNDER | \ DW_IC_INTR_RD_REQ) +#define DW_IC_ENABLE_ABORT BIT(1) + #define DW_IC_STATUS_ACTIVITY BIT(0) #define DW_IC_STATUS_TFE BIT(2) #define DW_IC_STATUS_RFNE BIT(3) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 24bef0025c988b16213b2a34ce5cfe9b93a8f140..ca1035e010c7224ca38dbf6172773be29d22b66f 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -917,6 +918,17 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev) return PTR_ERR(gpio); rinfo->sda_gpiod = gpio; + rinfo->pinctrl = devm_pinctrl_get(dev->dev); + if (IS_ERR(rinfo->pinctrl)) { + if (PTR_ERR(rinfo->pinctrl) == -EPROBE_DEFER) + return PTR_ERR(rinfo->pinctrl); + + rinfo->pinctrl = NULL; + dev_err(dev->dev, "getting pinctrl info failed: bus recovery might not work\n"); + } else if (!rinfo->pinctrl) { + dev_dbg(dev->dev, "pinctrl is disabled, bus recovery might not work\n"); + } + rinfo->recover_bus = i2c_generic_scl_recovery; rinfo->prepare_recovery = i2c_dw_prepare_recovery; rinfo->unprepare_recovery = i2c_dw_unprepare_recovery; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 970c1c3b0402753511e5619476547c184e40b60a..855b698e99c08004df29a5da485722974de63335 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -418,7 +418,6 @@ static void dw_i2c_plat_remove(struct platform_device *pdev) reset_control_assert(dev->rst); } -#ifdef CONFIG_PM_SLEEP static int dw_i2c_plat_prepare(struct device *dev) { /* @@ -429,11 +428,7 @@ static int dw_i2c_plat_prepare(struct device *dev) */ return !has_acpi_companion(dev); } -#else -#define dw_i2c_plat_prepare NULL -#endif -#ifdef CONFIG_PM static int dw_i2c_plat_runtime_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -447,7 +442,7 @@ static int dw_i2c_plat_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused dw_i2c_plat_suspend(struct device *dev) +static int dw_i2c_plat_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -468,7 +463,7 @@ static int dw_i2c_plat_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused dw_i2c_plat_resume(struct device *dev) +static int dw_i2c_plat_resume(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -479,16 +474,11 @@ static int __maybe_unused dw_i2c_plat_resume(struct device *dev) } static const struct dev_pm_ops dw_i2c_dev_pm_ops = { - .prepare = dw_i2c_plat_prepare, - SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) - SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL) + .prepare = pm_sleep_ptr(dw_i2c_plat_prepare), + LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) + RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL) }; -#define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) -#else -#define DW_I2C_DEV_PMOPS NULL -#endif - /* Work with hotplug and coldplug */ MODULE_ALIAS("platform:i2c_designware"); @@ -499,7 +489,7 @@ static struct platform_driver dw_i2c_driver = { .name = "i2c_designware", .of_match_table = of_match_ptr(dw_i2c_of_match), .acpi_match_table = ACPI_PTR(dw_i2c_acpi_match), - .pm = DW_I2C_DEV_PMOPS, + .pm = pm_ptr(&dw_i2c_dev_pm_ops), }, }; diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c index 4f02cc2fb567559984d0469fa73a4eebde80cfdb..631109c7a09882c6cd6b376c2d1f7204e11d2cf2 100644 --- a/drivers/i2c/busses/i2c-dln2.c +++ b/drivers/i2c/busses/i2c-dln2.c @@ -218,10 +218,8 @@ static int dln2_i2c_probe(struct platform_device *pdev) /* initialize the i2c interface */ ret = dln2_i2c_enable(dln2, true); - if (ret < 0) { - dev_err(dev, "failed to initialize adapter: %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to initialize adapter\n"); /* and finally attach to i2c layer */ ret = i2c_add_adapter(&dln2->adapter); diff --git a/drivers/i2c/busses/i2c-emev2.c b/drivers/i2c/busses/i2c-emev2.c index 4ba93cd91c0f01431595c1bb557b431fa57a611c..5574094104457ad2a8e1faa730653056326b459b 100644 --- a/drivers/i2c/busses/i2c-emev2.c +++ b/drivers/i2c/busses/i2c-emev2.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index f378cd479e558429b860e2b06e67609b3dcd8ce0..2b0b9cdffa861bc123f240d9401df62048167f5f 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -18,9 +18,7 @@ #include #include #include -#include -#include -#include +#include #include /* @@ -892,7 +890,6 @@ static void exynos5_i2c_remove(struct platform_device *pdev) clk_unprepare(i2c->pclk); } -#ifdef CONFIG_PM_SLEEP static int exynos5_i2c_suspend_noirq(struct device *dev) { struct exynos5_i2c *i2c = dev_get_drvdata(dev); @@ -934,11 +931,10 @@ static int exynos5_i2c_resume_noirq(struct device *dev) clk_disable_unprepare(i2c->pclk); return ret; } -#endif static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq, - exynos5_i2c_resume_noirq) + NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq, + exynos5_i2c_resume_noirq) }; static struct platform_driver exynos5_i2c_driver = { @@ -946,7 +942,7 @@ static struct platform_driver exynos5_i2c_driver = { .remove_new = exynos5_i2c_remove, .driver = { .name = "exynos5-hsi2c", - .pm = &exynos5_i2c_dev_pm_ops, + .pm = pm_sleep_ptr(&exynos5_i2c_dev_pm_ops), .of_match_table = exynos5_i2c_match, }, }; diff --git a/drivers/i2c/busses/i2c-gxp.c b/drivers/i2c/busses/i2c-gxp.c index 70b0de07ed99a1156821c12fa44c0186c87a07d7..efafc0528c44da12155bd1c0d18fa1a667550135 100644 --- a/drivers/i2c/busses/i2c-gxp.c +++ b/drivers/i2c/busses/i2c-gxp.c @@ -4,8 +4,9 @@ #include #include #include +#include #include -#include +#include #include #include diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index 0980c773cb5b1aa790b8ed39a8132bdccc892ac7..dfad5bad507550d00efa0ac282e18c4e4de44310 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -470,18 +470,14 @@ static int hisi_i2c_probe(struct platform_device *pdev) hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL); ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr); - if (ret) { - dev_err(dev, "failed to request irq handler, ret = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to request irq handler\n"); ctlr->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR_OR_NULL(ctlr->clk)) { ret = device_property_read_u64(dev, "clk_rate", &clk_rate_hz); - if (ret) { - dev_err(dev, "failed to get clock frequency, ret = %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to get clock frequency\n"); } else { clk_rate_hz = clk_get_rate(ctlr->clk); } diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c index 784a5f56eb76507c1e883c72c4b9f8f552a6223c..8e75515c3ca4784cf94d3245b0fdb1c2fd81401e 100644 --- a/drivers/i2c/busses/i2c-hix5hd2.c +++ b/drivers/i2c/busses/i2c-hix5hd2.c @@ -475,7 +475,6 @@ static void hix5hd2_i2c_remove(struct platform_device *pdev) pm_runtime_set_suspended(priv->dev); } -#ifdef CONFIG_PM static int hix5hd2_i2c_runtime_suspend(struct device *dev) { struct hix5hd2_i2c_priv *priv = dev_get_drvdata(dev); @@ -494,12 +493,11 @@ static int hix5hd2_i2c_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops hix5hd2_i2c_pm_ops = { - SET_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend, - hix5hd2_i2c_runtime_resume, - NULL) + RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend, + hix5hd2_i2c_runtime_resume, + NULL) }; static const struct of_device_id hix5hd2_i2c_match[] = { @@ -513,7 +511,7 @@ static struct platform_driver hix5hd2_i2c_driver = { .remove_new = hix5hd2_i2c_remove, .driver = { .name = "hix5hd2-i2c", - .pm = &hix5hd2_i2c_pm_ops, + .pm = pm_ptr(&hix5hd2_i2c_pm_ops), .of_match_table = hix5hd2_i2c_match, }, }; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 943b8e6d026dab31aa254e14099f818eca36378b..1d855258a45dc3c68c71280cb791f77383f38457 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1754,6 +1754,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) "SMBus I801 adapter at %04lx", priv->smba); err = i2c_add_adapter(&priv->adapter); if (err) { + platform_device_unregister(priv->tco_pdev); i801_acpi_remove(priv); return err; } @@ -1808,7 +1809,6 @@ static void i801_shutdown(struct pci_dev *dev) pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); } -#ifdef CONFIG_PM_SLEEP static int i801_suspend(struct device *dev) { struct i801_priv *priv = dev_get_drvdata(dev); @@ -1827,9 +1827,8 @@ static int i801_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume); static struct pci_driver i801_driver = { .name = DRV_NAME, @@ -1838,7 +1837,7 @@ static struct pci_driver i801_driver = { .remove = i801_remove, .shutdown = i801_shutdown, .driver = { - .pm = &i801_pm_ops, + .pm = pm_sleep_ptr(&i801_pm_ops), .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 1ad9d3b26dd3f6da1bef39ebde308df97ee96069..408820319ec48e6aa9258aab21996ec0e09c8656 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -37,9 +37,10 @@ #include #include #include +#include #include #include -#include +#include #include "i2c-ibm_iic.h" diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 66ba36949ab59c0414fd3df004aec219aa67013c..f9d4bfef511c3067dd0359b42152719f6c353f0f 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -1454,7 +1454,6 @@ static int img_i2c_runtime_resume(struct device *dev) return 0; } -#ifdef CONFIG_PM_SLEEP static int img_i2c_suspend(struct device *dev) { struct img_i2c *i2c = dev_get_drvdata(dev); @@ -1482,13 +1481,10 @@ static int img_i2c_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops img_i2c_pm = { - SET_RUNTIME_PM_OPS(img_i2c_runtime_suspend, - img_i2c_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(img_i2c_suspend, img_i2c_resume) + RUNTIME_PM_OPS(img_i2c_runtime_suspend, img_i2c_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(img_i2c_suspend, img_i2c_resume) }; static const struct of_device_id img_scb_i2c_match[] = { @@ -1501,7 +1497,7 @@ static struct platform_driver img_scb_i2c_driver = { .driver = { .name = "img-i2c-scb", .of_match_table = img_scb_i2c_match, - .pm = &img_i2c_pm, + .pm = pm_ptr(&img_i2c_pm), }, .probe = img_i2c_probe, .remove_new = img_i2c_remove, diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c b/drivers/i2c/busses/i2c-imx-lpi2c.c index 150d923ca7f1436b18200cefa528b9c123a89c65..678b30e90492ad17294453a5e6c482007946e8f5 100644 --- a/drivers/i2c/busses/i2c-imx-lpi2c.c +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -517,14 +516,12 @@ static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id) temp = readl(lpi2c_imx->base + LPI2C_MSR); temp &= enabled; - if (temp & MSR_RDF) - lpi2c_imx_read_rxfifo(lpi2c_imx); - - if (temp & MSR_TDF) - lpi2c_imx_write_txfifo(lpi2c_imx); - if (temp & MSR_NDF) complete(&lpi2c_imx->complete); + else if (temp & MSR_RDF) + lpi2c_imx_read_rxfifo(lpi2c_imx); + else if (temp & MSR_TDF) + lpi2c_imx_write_txfifo(lpi2c_imx); return IRQ_HANDLED; } @@ -572,10 +569,8 @@ static int lpi2c_imx_probe(struct platform_device *pdev) sizeof(lpi2c_imx->adapter.name)); ret = devm_clk_bulk_get_all(&pdev->dev, &lpi2c_imx->clks); - if (ret < 0) { - dev_err(&pdev->dev, "can't get I2C peripheral clock, ret=%d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "can't get I2C peripheral clock\n"); lpi2c_imx->num_clks = ret; ret = of_property_read_u32(pdev->dev.of_node, @@ -585,10 +580,8 @@ static int lpi2c_imx_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0, pdev->name, lpi2c_imx); - if (ret) { - dev_err(&pdev->dev, "can't claim irq %d\n", irq); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "can't claim irq %d\n", irq); i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx); platform_set_drvdata(pdev, lpi2c_imx); diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 65128a73e8a325eda8ed8ff4eddc554a8695e617..1775a79aeba2afa64b1ad3e0e22ac823deaeb48e 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -1389,7 +1388,11 @@ static int i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx, struct i2c_bus_recovery_info *rinfo = &i2c_imx->rinfo; i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev); - if (!i2c_imx->pinctrl || IS_ERR(i2c_imx->pinctrl)) { + if (!i2c_imx->pinctrl) { + dev_info(&pdev->dev, "pinctrl unavailable, bus recovery not supported\n"); + return 0; + } + if (IS_ERR(i2c_imx->pinctrl)) { dev_info(&pdev->dev, "can't get pinctrl, bus recovery not supported\n"); return PTR_ERR(i2c_imx->pinctrl); } @@ -1506,8 +1509,7 @@ static int i2c_imx_probe(struct platform_device *pdev) goto rpm_disable; /* Request IRQ */ - ret = request_threaded_irq(irq, i2c_imx_isr, NULL, IRQF_SHARED, - pdev->name, i2c_imx); + ret = request_irq(irq, i2c_imx_isr, IRQF_SHARED, pdev->name, i2c_imx); if (ret) { dev_err(&pdev->dev, "can't claim irq %d\n", irq); goto rpm_disable; diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 0dfe6039952147c7d3ef96ae79c54625f7f47fe1..55035cca0ae531a326446d4b8923b4e6ef264e6f 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c index 281058e3ea4632703e0d4d7b9093dd6c52bfab5a..e01d7530828840950273a3e332bb99ced6bf2e6b 100644 --- a/drivers/i2c/busses/i2c-kempld.c +++ b/drivers/i2c/busses/i2c-kempld.c @@ -350,10 +350,9 @@ static void kempld_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); } -#ifdef CONFIG_PM -static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state) +static int kempld_i2c_suspend(struct device *dev) { - struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_i2c_data *i2c = dev_get_drvdata(dev); struct kempld_device_data *pld = i2c->pld; u8 ctrl; @@ -366,9 +365,9 @@ static int kempld_i2c_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int kempld_i2c_resume(struct platform_device *pdev) +static int kempld_i2c_resume(struct device *dev) { - struct kempld_i2c_data *i2c = platform_get_drvdata(pdev); + struct kempld_i2c_data *i2c = dev_get_drvdata(dev); struct kempld_device_data *pld = i2c->pld; kempld_get_mutex(pld); @@ -377,19 +376,17 @@ static int kempld_i2c_resume(struct platform_device *pdev) return 0; } -#else -#define kempld_i2c_suspend NULL -#define kempld_i2c_resume NULL -#endif + +static DEFINE_SIMPLE_DEV_PM_OPS(kempld_i2c_pm_ops, + kempld_i2c_suspend, kempld_i2c_resume); static struct platform_driver kempld_i2c_driver = { .driver = { .name = "kempld-i2c", + .pm = pm_sleep_ptr(&kempld_i2c_pm_ops), }, .probe = kempld_i2c_probe, .remove_new = kempld_i2c_remove, - .suspend = kempld_i2c_suspend, - .resume = kempld_i2c_resume, }; module_platform_driver(kempld_i2c_driver); diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c index 5c6d965547539134dc34899751e821bc10b3735e..e3660333e91cca2ce3d3d65477b8b89272c9081f 100644 --- a/drivers/i2c/busses/i2c-lpc2k.c +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -431,7 +430,6 @@ static void i2c_lpc2k_remove(struct platform_device *dev) i2c_del_adapter(&i2c->adap); } -#ifdef CONFIG_PM static int i2c_lpc2k_suspend(struct device *dev) { struct lpc2k_i2c *i2c = dev_get_drvdata(dev); @@ -456,11 +454,6 @@ static const struct dev_pm_ops i2c_lpc2k_dev_pm_ops = { .resume_noirq = i2c_lpc2k_resume, }; -#define I2C_LPC2K_DEV_PM_OPS (&i2c_lpc2k_dev_pm_ops) -#else -#define I2C_LPC2K_DEV_PM_OPS NULL -#endif - static const struct of_device_id lpc2k_i2c_match[] = { { .compatible = "nxp,lpc1788-i2c" }, {}, @@ -472,7 +465,7 @@ static struct platform_driver i2c_lpc2k_driver = { .remove_new = i2c_lpc2k_remove, .driver = { .name = "lpc2k-i2c", - .pm = I2C_LPC2K_DEV_PM_OPS, + .pm = pm_sleep_ptr(&i2c_lpc2k_dev_pm_ops), .of_match_table = lpc2k_i2c_match, }, }; diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c index 16026c895bb65ae27634dde9ca5fb34719490a1b..c7b203cc4434518616d3535b1cb1e6c56aae853a 100644 --- a/drivers/i2c/busses/i2c-meson.c +++ b/drivers/i2c/busses/i2c-meson.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/i2c/busses/i2c-microchip-corei2c.c b/drivers/i2c/busses/i2c-microchip-corei2c.c index 7f58f7eaabb63f8d543941b9a5aebbabed71bab0..0b0a1c4d17caef75b4b7da236ec4c383d68a8ae0 100644 --- a/drivers/i2c/busses/i2c-microchip-corei2c.c +++ b/drivers/i2c/busses/i2c-microchip-corei2c.c @@ -378,9 +378,8 @@ static int mchp_corei2c_probe(struct platform_device *pdev) return PTR_ERR(idev->base); irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return dev_err_probe(&pdev->dev, -ENXIO, - "invalid IRQ %d for I2C controller\n", irq); + if (irq < 0) + return irq; idev->i2c_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(idev->i2c_clk)) diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index ae66bdd1b7379e47c2ac6d33a677ace36e715012..b3a73921ab699cbe3b9bdc8dd847452aeeeb4703 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -1080,13 +1080,7 @@ static int mlxbf_i2c_init_resource(struct platform_device *pdev, if (!tmp_res) return -ENOMEM; - tmp_res->params = platform_get_resource(pdev, IORESOURCE_MEM, type); - if (!tmp_res->params) { - devm_kfree(dev, tmp_res); - return -EIO; - } - - tmp_res->io = devm_ioremap_resource(dev, tmp_res->params); + tmp_res->io = devm_platform_get_and_ioremap_resource(pdev, type, &tmp_res->params); if (IS_ERR(tmp_res->io)) { devm_kfree(dev, tmp_res); return PTR_ERR(tmp_res->io); @@ -2323,10 +2317,8 @@ static int mlxbf_i2c_probe(struct platform_device *pdev) ret = mlxbf_i2c_init_resource(pdev, &priv->smbus, MLXBF_I2C_SMBUS_RES); - if (ret < 0) { - dev_err(dev, "Cannot fetch smbus resource info"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot fetch smbus resource info"); priv->timer->io = priv->smbus->io; priv->mst->io = priv->smbus->io + MLXBF_I2C_MST_ADDR_OFFSET; @@ -2334,39 +2326,29 @@ static int mlxbf_i2c_probe(struct platform_device *pdev) } else { ret = mlxbf_i2c_init_resource(pdev, &priv->timer, MLXBF_I2C_SMBUS_TIMER_RES); - if (ret < 0) { - dev_err(dev, "Cannot fetch timer resource info"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot fetch timer resource info"); ret = mlxbf_i2c_init_resource(pdev, &priv->mst, MLXBF_I2C_SMBUS_MST_RES); - if (ret < 0) { - dev_err(dev, "Cannot fetch master resource info"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot fetch master resource info"); ret = mlxbf_i2c_init_resource(pdev, &priv->slv, MLXBF_I2C_SMBUS_SLV_RES); - if (ret < 0) { - dev_err(dev, "Cannot fetch slave resource info"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot fetch slave resource info"); } ret = mlxbf_i2c_init_resource(pdev, &priv->mst_cause, MLXBF_I2C_MST_CAUSE_RES); - if (ret < 0) { - dev_err(dev, "Cannot fetch cause master resource info"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot fetch cause master resource info"); ret = mlxbf_i2c_init_resource(pdev, &priv->slv_cause, MLXBF_I2C_SLV_CAUSE_RES); - if (ret < 0) { - dev_err(dev, "Cannot fetch cause slave resource info"); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot fetch cause slave resource info"); adap = &priv->adap; adap->owner = THIS_MODULE; @@ -2397,11 +2379,9 @@ static int mlxbf_i2c_probe(struct platform_device *pdev) * does not really hurt, then keep the code as is. */ ret = mlxbf_i2c_init_master(pdev, priv); - if (ret < 0) { - dev_err(dev, "failed to initialize smbus master %d", - priv->bus); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to initialize smbus master %d", + priv->bus); mlxbf_i2c_init_timings(pdev, priv); @@ -2413,10 +2393,8 @@ static int mlxbf_i2c_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, mlxbf_i2c_irq, IRQF_SHARED | IRQF_PROBE_SHARED, dev_name(dev), priv); - if (ret < 0) { - dev_err(dev, "Cannot get irq %d\n", irq); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "Cannot get irq %d\n", irq); priv->irq = irq; diff --git a/drivers/i2c/busses/i2c-mlxcpld.c b/drivers/i2c/busses/i2c-mlxcpld.c index c42fd4b329e4b1e1b4e9b97b2a96b4180f8c4921..6fec64ea67fbc11bffcf2c0a8c1604ea804bca33 100644 --- a/drivers/i2c/busses/i2c-mlxcpld.c +++ b/drivers/i2c/busses/i2c-mlxcpld.c @@ -22,6 +22,7 @@ #define MLXCPLD_I2C_BUS_NUM 1 #define MLXCPLD_I2C_DATA_REG_SZ 36 #define MLXCPLD_I2C_DATA_SZ_BIT BIT(5) +#define MLXCPLD_I2C_DATA_EXT2_SZ_BIT BIT(6) #define MLXCPLD_I2C_DATA_SZ_MASK GENMASK(6, 5) #define MLXCPLD_I2C_SMBUS_BLK_BIT BIT(7) #define MLXCPLD_I2C_MAX_ADDR_LEN 4 @@ -466,6 +467,13 @@ static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext = { .max_comb_1st_msg_len = 4, }; +static const struct i2c_adapter_quirks mlxcpld_i2c_quirks_ext2 = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_read_len = (MLXCPLD_I2C_DATA_REG_SZ - 4) * 4, + .max_write_len = (MLXCPLD_I2C_DATA_REG_SZ - 4) * 4 + MLXCPLD_I2C_MAX_ADDR_LEN, + .max_comb_1st_msg_len = 4, +}; + static struct i2c_adapter mlxcpld_i2c_adapter = { .owner = THIS_MODULE, .name = "i2c-mlxcpld", @@ -547,6 +555,8 @@ static int mlxcpld_i2c_probe(struct platform_device *pdev) /* Check support for extended transaction length */ if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_SZ_BIT) mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext; + else if ((val & MLXCPLD_I2C_DATA_SZ_MASK) == MLXCPLD_I2C_DATA_EXT2_SZ_BIT) + mlxcpld_i2c_adapter.quirks = &mlxcpld_i2c_quirks_ext2; /* Check support for smbus block transaction */ if (val & MLXCPLD_I2C_SMBUS_BLK_BIT) priv->smbus_block = true; diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index f460a7fb4eae6ff03f07ffe3c95bb3d732fe1d6c..e4e4995ab22430e0cdb50b19167feb3d638bb92a 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -11,9 +11,10 @@ #include #include #include +#include #include #include -#include +#include #include #include diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 7ca3f2221ba69ed2c0093fd372f1f9813410cd8d..1a9b5a068ef1b2e45c6d3e4d995563969a7561d4 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -19,9 +19,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include @@ -1514,7 +1512,6 @@ static void mtk_i2c_remove(struct platform_device *pdev) clk_bulk_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); } -#ifdef CONFIG_PM_SLEEP static int mtk_i2c_suspend_noirq(struct device *dev) { struct mtk_i2c *i2c = dev_get_drvdata(dev); @@ -1544,11 +1541,10 @@ static int mtk_i2c_resume_noirq(struct device *dev) return 0; } -#endif static const struct dev_pm_ops mtk_i2c_pm = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_i2c_suspend_noirq, - mtk_i2c_resume_noirq) + NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_i2c_suspend_noirq, + mtk_i2c_resume_noirq) }; static struct platform_driver mtk_i2c_driver = { @@ -1556,7 +1552,7 @@ static struct platform_driver mtk_i2c_driver = { .remove_new = mtk_i2c_remove, .driver = { .name = I2C_DRV_NAME, - .pm = &mtk_i2c_pm, + .pm = pm_sleep_ptr(&mtk_i2c_pm), .of_match_table = mtk_i2c_of_match, }, }; diff --git a/drivers/i2c/busses/i2c-mt7621.c b/drivers/i2c/busses/i2c-mt7621.c index 104bb194e9906d68c6a59019acb1ca27a77139f8..81d46169bc1f9da1b1f53c2a766d6dae0d254e7a 100644 --- a/drivers/i2c/busses/i2c-mt7621.c +++ b/drivers/i2c/busses/i2c-mt7621.c @@ -16,7 +16,8 @@ #include #include #include -#include +#include +#include #include #define REG_SM0CFG2_REG 0x28 diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 1d76f1c4dc06a3d2cad9c772332291276f568bf7..36def0a9c95c4fbc6fcc025a534c44e1692c9d06 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 777278386f58eb1904a042c3f896ce1e7a6ba32b..38d203d93eeec4bde521ddb034a1e4bd1fc88da0 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -327,8 +327,8 @@ static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg, /* Older incarnations of the device used non-standard BARs */ u16 iobase; - if (pci_read_config_word(dev, alt_reg, &iobase) - != PCIBIOS_SUCCESSFUL) { + error = pci_read_config_word(dev, alt_reg, &iobase); + if (error != PCIBIOS_SUCCESSFUL) { dev_err(&dev->dev, "Error reading PCI config for %s\n", name); return -EIO; diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 212f412f1c74959e4b5b5f2f141a2917b7c551c5..b10574d42b7ac998bd73eb3ff9b38aaa68ca4b53 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -873,7 +873,6 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg) return IRQ_HANDLED; } -#ifdef CONFIG_PM_SLEEP static int nmk_i2c_suspend_late(struct device *dev) { int ret; @@ -890,9 +889,7 @@ static int nmk_i2c_resume_early(struct device *dev) { return pm_runtime_force_resume(dev); } -#endif -#ifdef CONFIG_PM static int nmk_i2c_runtime_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); @@ -925,13 +922,10 @@ static int nmk_i2c_runtime_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops nmk_i2c_pm = { - SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early) - SET_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend, - nmk_i2c_runtime_resume, - NULL) + LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early) + RUNTIME_PM_OPS(nmk_i2c_runtime_suspend, nmk_i2c_runtime_resume, NULL) }; static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap) @@ -1078,7 +1072,7 @@ static struct amba_driver nmk_i2c_driver = { .drv = { .owner = THIS_MODULE, .name = DRIVER_NAME, - .pm = &nmk_i2c_pm, + .pm = pm_ptr(&nmk_i2c_pm), }, .id_table = nmk_i2c_ids, .probe = nmk_i2c_probe, diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 53b65ffb6a6477bf10fdb2130827859e8810428c..ae4bae63ad4f3c3086635928acdd9b46deb10c28 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -695,6 +694,7 @@ static void npcm_i2c_callback(struct npcm_i2c *bus, { struct i2c_msg *msgs; int msgs_num; + bool do_complete = false; msgs = bus->msgs; msgs_num = bus->msgs_num; @@ -723,23 +723,17 @@ static void npcm_i2c_callback(struct npcm_i2c *bus, msgs[1].flags & I2C_M_RD) msgs[1].len = info; } - if (completion_done(&bus->cmd_complete) == false) - complete(&bus->cmd_complete); - break; - + do_complete = true; + break; case I2C_NACK_IND: /* MASTER transmit got a NACK before tx all bytes */ bus->cmd_err = -ENXIO; - if (bus->master_or_slave == I2C_MASTER) - complete(&bus->cmd_complete); - + do_complete = true; break; case I2C_BUS_ERR_IND: /* Bus error */ bus->cmd_err = -EAGAIN; - if (bus->master_or_slave == I2C_MASTER) - complete(&bus->cmd_complete); - + do_complete = true; break; case I2C_WAKE_UP_IND: /* I2C wake up */ @@ -753,6 +747,8 @@ static void npcm_i2c_callback(struct npcm_i2c *bus, if (bus->slave) bus->master_or_slave = I2C_SLAVE; #endif + if (do_complete) + complete(&bus->cmd_complete); } static u8 npcm_i2c_fifo_usage(struct npcm_i2c *bus) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index 4ac77e57bbbfe19726c3a79fd4059bb7eb5ad685..041a76f71a49cc5db369dcb4bc1340ca4c04d154 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -743,7 +743,6 @@ static void ocores_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); } -#ifdef CONFIG_PM_SLEEP static int ocores_i2c_suspend(struct device *dev) { struct ocores_i2c *i2c = dev_get_drvdata(dev); @@ -772,11 +771,8 @@ static int ocores_i2c_resume(struct device *dev) return ocores_init(dev, i2c); } -static SIMPLE_DEV_PM_OPS(ocores_i2c_pm, ocores_i2c_suspend, ocores_i2c_resume); -#define OCORES_I2C_PM (&ocores_i2c_pm) -#else -#define OCORES_I2C_PM NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(ocores_i2c_pm, + ocores_i2c_suspend, ocores_i2c_resume); static struct platform_driver ocores_i2c_driver = { .probe = ocores_i2c_probe, @@ -784,7 +780,7 @@ static struct platform_driver ocores_i2c_driver = { .driver = { .name = "ocores-i2c", .of_match_table = ocores_i2c_match, - .pm = OCORES_I2C_PM, + .pm = pm_sleep_ptr(&ocores_i2c_pm), }, }; diff --git a/drivers/i2c/busses/i2c-owl.c b/drivers/i2c/busses/i2c-owl.c index 5f0ef8c351418544dcfd050cc7792f643f70939a..777f1a0278c747b59a7e64d249a0b232822d4fb1 100644 --- a/drivers/i2c/busses/i2c-owl.c +++ b/drivers/i2c/busses/i2c-owl.c @@ -16,7 +16,8 @@ #include #include #include -#include +#include +#include /* I2C registers */ #define OWL_I2C_REG_CTL 0x0000 diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c index d2a9e7b61c1abdc49f5791e293cfde34c8aa4588..b8d5480c54f65a1615b8a5963ef005cb09eac5bd 100644 --- a/drivers/i2c/busses/i2c-pca-platform.c +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -22,7 +22,6 @@ #include #include #include -#include #include diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index 82400057f810a5feaa0e05b58e1d9efaa06cfbd1..a12525b3186bcf487b26d05946111a3d66d242b2 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -613,7 +613,6 @@ static const struct i2c_algorithm pnx_algorithm = { .functionality = i2c_pnx_func, }; -#ifdef CONFIG_PM_SLEEP static int i2c_pnx_controller_suspend(struct device *dev) { struct i2c_pnx_algo_data *alg_data = dev_get_drvdata(dev); @@ -630,12 +629,9 @@ static int i2c_pnx_controller_resume(struct device *dev) return clk_prepare_enable(alg_data->clk); } -static SIMPLE_DEV_PM_OPS(i2c_pnx_pm, - i2c_pnx_controller_suspend, i2c_pnx_controller_resume); -#define PNX_I2C_PM (&i2c_pnx_pm) -#else -#define PNX_I2C_PM NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(i2c_pnx_pm, + i2c_pnx_controller_suspend, + i2c_pnx_controller_resume); static int i2c_pnx_probe(struct platform_device *pdev) { @@ -683,8 +679,7 @@ static int i2c_pnx_probe(struct platform_device *pdev) "%s", pdev->name); /* Register I/O resource */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - alg_data->ioaddr = devm_ioremap_resource(&pdev->dev, res); + alg_data->ioaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(alg_data->ioaddr)) return PTR_ERR(alg_data->ioaddr); @@ -763,7 +758,7 @@ static struct platform_driver i2c_pnx_driver = { .driver = { .name = "pnx-i2c", .of_match_table = of_match_ptr(i2c_pnx_of_match), - .pm = PNX_I2C_PM, + .pm = pm_sleep_ptr(&i2c_pnx_pm), }, .probe = i2c_pnx_probe, .remove_new = i2c_pnx_remove, diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c index 30e38bc8b6db81fb56f4ee33e18662d8993adb7a..08b3229c443df794df59a172f8a3afdc7997338b 100644 --- a/drivers/i2c/busses/i2c-pxa-pci.c +++ b/drivers/i2c/busses/i2c-pxa-pci.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #define CE4100_PCI_I2C_DEVS 3 diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 937f7eebe90675f72b43dd0f89a8152b649e41dd..29be05af826b05a3a283d75db7ca82068978c42f 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1362,7 +1362,7 @@ static int i2c_pxa_probe(struct platform_device *dev) struct i2c_pxa_platform_data *plat = dev_get_platdata(&dev->dev); enum pxa_i2c_types i2c_type; struct pxa_i2c *i2c; - struct resource *res = NULL; + struct resource *res; int ret, irq; i2c = devm_kzalloc(&dev->dev, sizeof(struct pxa_i2c), GFP_KERNEL); @@ -1379,8 +1379,7 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->adap.dev.of_node = dev->dev.of_node; #endif - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - i2c->reg_base = devm_ioremap_resource(&dev->dev, res); + i2c->reg_base = devm_platform_get_and_ioremap_resource(dev, 0, &res); if (IS_ERR(i2c->reg_base)) return PTR_ERR(i2c->reg_base); @@ -1404,10 +1403,9 @@ static int i2c_pxa_probe(struct platform_device *dev) strscpy(i2c->adap.name, "pxa_i2c-i2c", sizeof(i2c->adap.name)); i2c->clk = devm_clk_get(&dev->dev, NULL); - if (IS_ERR(i2c->clk)) { - dev_err(&dev->dev, "failed to get the clk: %ld\n", PTR_ERR(i2c->clk)); - return PTR_ERR(i2c->clk); - } + if (IS_ERR(i2c->clk)) + return dev_err_probe(&dev->dev, PTR_ERR(i2c->clk), + "failed to get the clk\n"); i2c->reg_ibmr = i2c->reg_base + pxa_reg_layout[i2c_type].ibmr; i2c->reg_idbr = i2c->reg_base + pxa_reg_layout[i2c_type].idbr; @@ -1491,7 +1489,6 @@ static void i2c_pxa_remove(struct platform_device *dev) clk_disable_unprepare(i2c->clk); } -#ifdef CONFIG_PM static int i2c_pxa_suspend_noirq(struct device *dev) { struct pxa_i2c *i2c = dev_get_drvdata(dev); @@ -1516,17 +1513,12 @@ static const struct dev_pm_ops i2c_pxa_dev_pm_ops = { .resume_noirq = i2c_pxa_resume_noirq, }; -#define I2C_PXA_DEV_PM_OPS (&i2c_pxa_dev_pm_ops) -#else -#define I2C_PXA_DEV_PM_OPS NULL -#endif - static struct platform_driver i2c_pxa_driver = { .probe = i2c_pxa_probe, .remove_new = i2c_pxa_remove, .driver = { .name = "pxa2xx-i2c", - .pm = I2C_PXA_DEV_PM_OPS, + .pm = pm_sleep_ptr(&i2c_pxa_dev_pm_ops), .of_match_table = i2c_pxa_dt_ids, }, .id_table = i2c_pxa_id_table, diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 622dc14add9d7a92ff237eb12092b87af7201e78..414882c57d7f40c7de51ebd121f0b0af996e3dd5 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -588,10 +588,10 @@ static int cci_probe(struct platform_device *pdev) /* Clocks */ ret = devm_clk_bulk_get_all(dev, &cci->clocks); - if (ret < 1) { - dev_err(dev, "failed to get clocks %d\n", ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get clocks\n"); + else if (!ret) + return dev_err_probe(dev, -EINVAL, "not enough clocks in DT\n"); cci->nclocks = ret; /* Retrieve CCI clock rate */ diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index b670a67c4fdd00e068010718dbfd6a7ed970c2bd..229353e96e095486e87feb436f865bb09d7c0d8c 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -767,7 +767,6 @@ static int setup_gpi_dma(struct geni_i2c_dev *gi2c) static int geni_i2c_probe(struct platform_device *pdev) { struct geni_i2c_dev *gi2c; - struct resource *res; u32 proto, tx_depth, fifo_disable; int ret; struct device *dev = &pdev->dev; @@ -779,8 +778,7 @@ static int geni_i2c_probe(struct platform_device *pdev) gi2c->se.dev = dev; gi2c->se.wrapper = dev_get_drvdata(dev->parent); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - gi2c->se.base = devm_ioremap_resource(dev, res); + gi2c->se.base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(gi2c->se.base)) return PTR_ERR(gi2c->se.base); diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index ae90170023b0075b10808364783acd9da5c97482..598102d16677a1e20f264ca26fe4066320851c18 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -1927,7 +1927,6 @@ static void qup_i2c_remove(struct platform_device *pdev) pm_runtime_set_suspended(qup->dev); } -#ifdef CONFIG_PM static int qup_i2c_pm_suspend_runtime(struct device *device) { struct qup_i2c_dev *qup = dev_get_drvdata(device); @@ -1945,9 +1944,7 @@ static int qup_i2c_pm_resume_runtime(struct device *device) qup_i2c_enable_clocks(qup); return 0; } -#endif -#ifdef CONFIG_PM_SLEEP static int qup_i2c_suspend(struct device *device) { if (!pm_runtime_suspended(device)) @@ -1962,16 +1959,11 @@ static int qup_i2c_resume(struct device *device) pm_request_autosuspend(device); return 0; } -#endif static const struct dev_pm_ops qup_i2c_qup_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS( - qup_i2c_suspend, - qup_i2c_resume) - SET_RUNTIME_PM_OPS( - qup_i2c_pm_suspend_runtime, - qup_i2c_pm_resume_runtime, - NULL) + SYSTEM_SLEEP_PM_OPS(qup_i2c_suspend, qup_i2c_resume) + RUNTIME_PM_OPS(qup_i2c_pm_suspend_runtime, + qup_i2c_pm_resume_runtime, NULL) }; static const struct of_device_id qup_i2c_dt_match[] = { @@ -1987,7 +1979,7 @@ static struct platform_driver qup_i2c_driver = { .remove_new = qup_i2c_remove, .driver = { .name = "i2c_qup", - .pm = &qup_i2c_qup_pm_ops, + .pm = pm_ptr(&qup_i2c_qup_pm_ops), .of_match_table = qup_i2c_dt_match, .acpi_match_table = ACPI_PTR(qup_i2c_acpi_match), }, diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 2d9c37410ebd0f1f397d91b3589b28b2bcdcf954..a32a93f9a60d03db0a9cb92ba3c04ffbd43984dd 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -1169,7 +1169,6 @@ static void rcar_i2c_remove(struct platform_device *pdev) pm_runtime_disable(dev); } -#ifdef CONFIG_PM_SLEEP static int rcar_i2c_suspend(struct device *dev) { struct rcar_i2c_priv *priv = dev_get_drvdata(dev); @@ -1187,19 +1186,14 @@ static int rcar_i2c_resume(struct device *dev) } static const struct dev_pm_ops rcar_i2c_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(rcar_i2c_suspend, rcar_i2c_resume) }; -#define DEV_PM_OPS (&rcar_i2c_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - static struct platform_driver rcar_i2c_driver = { .driver = { .name = "i2c-rcar", .of_match_table = rcar_i2c_dt_ids, - .pm = DEV_PM_OPS, + .pm = pm_sleep_ptr(&rcar_i2c_pm_ops), }, .probe = rcar_i2c_probe, .remove_new = rcar_i2c_remove, diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 5f8c0bd508d2f3edb28e2d12b0de33769686465c..f0ee8871d5ae1b2c1b2f127de0a7e03221c93812 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 28f0e5c64f32ec0b4c9b3cfa9e9b493189a2ea1e..127eb3805facb55a9f2e6fa0badb4885c709195d 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -1034,9 +1033,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); /* map the registers */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2c->regs = devm_ioremap_resource(&pdev->dev, res); - + i2c->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2c->regs)) return PTR_ERR(i2c->regs); @@ -1076,7 +1073,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) if (!(i2c->quirks & QUIRK_POLL)) { i2c->irq = ret = platform_get_irq(pdev, 0); if (ret < 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); clk_unprepare(i2c->clk); return ret; } @@ -1125,7 +1121,6 @@ static void s3c24xx_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); } -#ifdef CONFIG_PM_SLEEP static int s3c24xx_i2c_suspend_noirq(struct device *dev) { struct s3c24xx_i2c *i2c = dev_get_drvdata(dev); @@ -1155,26 +1150,19 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq, - s3c24xx_i2c_resume_noirq) + NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq, + s3c24xx_i2c_resume_noirq) }; -#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops) -#else -#define S3C24XX_DEV_PM_OPS NULL -#endif - static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove_new = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { .name = "s3c-i2c", - .pm = S3C24XX_DEV_PM_OPS, + .pm = pm_sleep_ptr(&s3c24xx_i2c_dev_pm_ops), .of_match_table = of_match_ptr(s3c24xx_i2c_match), }, }; diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 21717b943a9e0044d049dc3d881eb32bc40a3829..5adbe62cf6212865aa6df5bb532f9def35196e4a 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -871,7 +871,6 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) { struct sh_mobile_i2c_data *pd; struct i2c_adapter *adap; - struct resource *res; const struct sh_mobile_dt_config *config; int ret; u32 bus_speed; @@ -893,10 +892,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) pd->dev = &dev->dev; platform_set_drvdata(dev, pd); - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - - pd->res = res; - pd->reg = devm_ioremap_resource(&dev->dev, res); + pd->reg = devm_platform_get_and_ioremap_resource(dev, 0, &pd->res); if (IS_ERR(pd->reg)) return PTR_ERR(pd->reg); @@ -905,7 +901,7 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) pd->clks_per_count = 1; /* Newer variants come with two new bits in ICIC */ - if (resource_size(res) > 0x17) + if (resource_size(pd->res) > 0x17) pd->flags |= IIC_FLAG_HAS_ICIC67; pm_runtime_enable(&dev->dev); @@ -965,7 +961,6 @@ static void sh_mobile_i2c_remove(struct platform_device *dev) pm_runtime_disable(&dev->dev); } -#ifdef CONFIG_PM_SLEEP static int sh_mobile_i2c_suspend(struct device *dev) { struct sh_mobile_i2c_data *pd = dev_get_drvdata(dev); @@ -983,20 +978,15 @@ static int sh_mobile_i2c_resume(struct device *dev) } static const struct dev_pm_ops sh_mobile_i2c_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sh_mobile_i2c_suspend, - sh_mobile_i2c_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(sh_mobile_i2c_suspend, + sh_mobile_i2c_resume) }; -#define DEV_PM_OPS (&sh_mobile_i2c_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ - static struct platform_driver sh_mobile_i2c_driver = { .driver = { .name = "i2c-sh_mobile", .of_match_table = sh_mobile_i2c_dt_ids, - .pm = DEV_PM_OPS, + .pm = pm_sleep_ptr(&sh_mobile_i2c_pm_ops), }, .probe = sh_mobile_i2c_probe, .remove_new = sh_mobile_i2c_remove, diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index c793a5c14cda1bdbc8ee402e1efaf3d06eb0a700..486f1e9dfb74aabff9b29ae39f9b37d16b0444a9 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -175,11 +175,11 @@ static int sis5595_setup(struct pci_dev *SIS5595_dev) if (force_addr) { dev_info(&SIS5595_dev->dev, "forcing ISA address 0x%04X\n", sis5595_base); - if (pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base) - != PCIBIOS_SUCCESSFUL) + retval = pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base); + if (retval != PCIBIOS_SUCCESSFUL) goto error; - if (pci_read_config_word(SIS5595_dev, ACPI_BASE, &a) - != PCIBIOS_SUCCESSFUL) + retval = pci_read_config_word(SIS5595_dev, ACPI_BASE, &a); + if (retval != PCIBIOS_SUCCESSFUL) goto error; if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { /* doesn't work for some chips! */ @@ -188,16 +188,16 @@ static int sis5595_setup(struct pci_dev *SIS5595_dev) } } - if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) - != PCIBIOS_SUCCESSFUL) + retval = pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val); + if (retval != PCIBIOS_SUCCESSFUL) goto error; if ((val & 0x80) == 0) { dev_info(&SIS5595_dev->dev, "enabling ACPI\n"); - if (pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80) - != PCIBIOS_SUCCESSFUL) + retval = pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, val | 0x80); + if (retval != PCIBIOS_SUCCESSFUL) goto error; - if (pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val) - != PCIBIOS_SUCCESSFUL) + retval = pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val); + if (retval != PCIBIOS_SUCCESSFUL) goto error; if ((val & 0x80) == 0) { /* doesn't work for some chips? */ diff --git a/drivers/i2c/busses/i2c-sprd.c b/drivers/i2c/busses/i2c-sprd.c index ffc54fbf814dde0a8a9ada780d811ea6138edbe6..c52d1bec60b4cd4090c5acf11783e4cbc88ec308 100644 --- a/drivers/i2c/busses/i2c-sprd.c +++ b/drivers/i2c/busses/i2c-sprd.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 25c3521cae0e35a433e52d9d91047bc197e1a2ca..ce2333408904bdceccf55877c2b572481194e11d 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -812,8 +812,7 @@ static int st_i2c_probe(struct platform_device *pdev) if (!i2c_dev) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2c_dev->base = devm_ioremap_resource(&pdev->dev, res); + i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2c_dev->base)) return PTR_ERR(i2c_dev->base); diff --git a/drivers/i2c/busses/i2c-stm32f4.c b/drivers/i2c/busses/i2c-stm32f4.c index 6ad06a5a22b43b22cf14a9d95064a8d69f192a09..ecc54792a66f1dc6b9b010cab9751234e5a6d293 100644 --- a/drivers/i2c/busses/i2c-stm32f4.c +++ b/drivers/i2c/busses/i2c-stm32f4.c @@ -767,8 +767,7 @@ static int stm32f4_i2c_probe(struct platform_device *pdev) if (!i2c_dev) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2c_dev->base = devm_ioremap_resource(&pdev->dev, res); + i2c_dev->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(i2c_dev->base)) return PTR_ERR(i2c_dev->base); diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index e897d9101434dccb15ab32f54bc0cf8396c1b08d..579b30581725f29fe69d0e2d309896cd7070efb0 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -2121,12 +2121,12 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) phy_addr = (dma_addr_t)res->start; irq_event = platform_get_irq(pdev, 0); - if (irq_event <= 0) - return irq_event ? : -ENOENT; + if (irq_event < 0) + return irq_event; irq_error = platform_get_irq(pdev, 1); - if (irq_error <= 0) - return irq_error ? : -ENOENT; + if (irq_error < 0) + return irq_error; i2c_dev->wakeup_src = of_property_read_bool(pdev->dev.of_node, "wakeup-source"); diff --git a/drivers/i2c/busses/i2c-synquacer.c b/drivers/i2c/busses/i2c-synquacer.c index 4cc196ca8f6dcca5a4d425b393e9c68a5ecb2cc3..bbea521b05dda7461736487ad3837aede5589ea4 100644 --- a/drivers/i2c/busses/i2c-synquacer.c +++ b/drivers/i2c/busses/i2c-synquacer.c @@ -557,20 +557,16 @@ static int synquacer_i2c_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "clock source %p\n", i2c->pclk); ret = clk_prepare_enable(i2c->pclk); - if (ret) { - dev_err(&pdev->dev, "failed to enable clock (%d)\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to enable clock\n"); i2c->pclkrate = clk_get_rate(i2c->pclk); } if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE || - i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) { - dev_err(&pdev->dev, "PCLK missing or out of range (%d)\n", - i2c->pclkrate); - return -EINVAL; - } + i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) + return dev_err_probe(&pdev->dev, -EINVAL, + "PCLK missing or out of range (%d)\n", + i2c->pclkrate); i2c->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(i2c->base)) @@ -582,10 +578,8 @@ static int synquacer_i2c_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, i2c->irq, synquacer_i2c_isr, 0, dev_name(&pdev->dev), i2c); - if (ret < 0) { - dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); - return ret; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "cannot claim IRQ %d\n", i2c->irq); i2c->state = STATE_IDLE; i2c->dev = &pdev->dev; @@ -605,10 +599,8 @@ static int synquacer_i2c_probe(struct platform_device *pdev) synquacer_i2c_hw_init(i2c); ret = i2c_add_numbered_adapter(&i2c->adapter); - if (ret) { - dev_err(&pdev->dev, "failed to add bus to i2c core\n"); - return ret; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to add bus to i2c core\n"); platform_set_drvdata(pdev, i2c); diff --git a/drivers/i2c/busses/i2c-tegra-bpmp.c b/drivers/i2c/busses/i2c-tegra-bpmp.c index bc3f94561746e9b7a3e48cc04bfc44dc9f4f0b68..b0840fa0f53e09ea37c174059490a53e4f3f9998 100644 --- a/drivers/i2c/busses/i2c-tegra-bpmp.c +++ b/drivers/i2c/busses/i2c-tegra-bpmp.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 03fc10b45bd6e9af5a2981b431250116d828b34d..920d5a8cbf4c753b316d2256caeadc319246581e 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index d1fa9ff5aeab48f809eb8a928222a5bfb1587a0a..1bffe36c40ad890d68f7a482b1a8dbbed94f3ef4 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -222,6 +222,10 @@ static int i2c_tiny_usb_probe(struct usb_interface *interface, int retval = -ENOMEM; u16 version; + if (interface->intf_assoc && + interface->intf_assoc->bFunctionClass != USB_CLASS_VENDOR_SPEC) + return -ENODEV; + dev_dbg(&interface->dev, "probing usb device\n"); /* allocate memory for our device state and initialize it */ diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c index 4b9536f508006dce032e752f0dccaae9b8919182..c60ae531ba57c7f0a5f830267e0d0bbb044da2b7 100644 --- a/drivers/i2c/busses/i2c-virtio.c +++ b/drivers/i2c/busses/i2c-virtio.c @@ -243,7 +243,6 @@ static struct virtio_device_id id_table[] = { }; MODULE_DEVICE_TABLE(virtio, id_table); -#ifdef CONFIG_PM_SLEEP static int virtio_i2c_freeze(struct virtio_device *vdev) { virtio_i2c_del_vqs(vdev); @@ -254,7 +253,6 @@ static int virtio_i2c_restore(struct virtio_device *vdev) { return virtio_i2c_setup_vqs(vdev->priv); } -#endif static const unsigned int features[] = { VIRTIO_I2C_F_ZERO_LENGTH_REQUEST, @@ -269,10 +267,8 @@ static struct virtio_driver virtio_i2c_driver = { .driver = { .name = "i2c_virtio", }, -#ifdef CONFIG_PM_SLEEP - .freeze = virtio_i2c_freeze, - .restore = virtio_i2c_restore, -#endif + .freeze = pm_sleep_ptr(virtio_i2c_freeze), + .restore = pm_sleep_ptr(virtio_i2c_restore), }; module_virtio_driver(virtio_i2c_driver); diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index b3bb97762c859fcdc04c3b21491055d9f105ce4e..71391b590adaeba9d69044dd5b66afcfa8c32aea 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -710,7 +710,7 @@ static irqreturn_t xiic_process(int irq, void *dev_id) * reset the IP instead of just flush fifos */ ret = xiic_reinit(i2c); - if (!ret) + if (ret < 0) dev_dbg(i2c->adap.dev.parent, "reinit failed\n"); if (i2c->rx_msg) { diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index f59e8c544f36655b87cc23d466cf393b78f2c7cd..08a59a920929f38023f388afec96dfab425180fc 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -529,10 +529,8 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev) err = devm_request_irq(&pdev->dev, priv->irq, xlp9xx_i2c_isr, 0, pdev->name, priv); - if (err) { - dev_err(&pdev->dev, "IRQ request failed!\n"); - return err; - } + if (err) + return dev_err_probe(&pdev->dev, err, "IRQ request failed!\n"); init_completion(&priv->msg_complete); priv->adapter.dev.parent = &pdev->dev; diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index 313904be5f3bde25990ffb631c248e60aa94bb09..57ff09f18c371dd28cdbe989d144f546e24ec8a3 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -341,7 +341,7 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, priv->adap.lock_ops = &i2c_parent_lock_ops; /* Sanity check on class */ - if (i2c_mux_parent_classes(parent) & class) + if (i2c_mux_parent_classes(parent) & class & ~I2C_CLASS_DEPRECATED) dev_err(&parent->dev, "Segment %d behind mux can't share classes with ancestors\n", chan_id); diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index ea838dbae32e53c4ebcb505a70572e714a28ca2f..db1b9057612a54dc2d5b6e8106316773a21648e1 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -65,11 +65,11 @@ config I2C_MUX_PCA9541 will be called i2c-mux-pca9541. config I2C_MUX_PCA954x - tristate "NXP PCA954x and PCA984x I2C Mux/switches" + tristate "NXP PCA954x/PCA984x and Maxim MAX735x/MAX736x I2C Mux/switches" depends on GPIOLIB || COMPILE_TEST help - If you say yes here you get support for the NXP PCA954x - and PCA984x I2C mux/switch devices. + If you say yes here you get support for NXP PCA954x/PCA984x + and Maxim MAX735x/MAX736x I2C mux/switch devices. This driver can also be built as a module. If so, the module will be called i2c-mux-pca954x. diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index a3a122fae71e09853200069e668b326e139eb90f..22f2280eab7f75101a8233911f6c7dd8be3bef6c 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -243,6 +243,10 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev) props[i].name = devm_kstrdup(&pdev->dev, "status", GFP_KERNEL); props[i].value = devm_kstrdup(&pdev->dev, "ok", GFP_KERNEL); + if (!props[i].name || !props[i].value) { + err = -ENOMEM; + goto err_rollback; + } props[i].length = 3; of_changeset_init(&priv->chan[i].chgset); diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c index 5d5cbe0130cdf99de1917d5c013b8894c622a727..5ca03bd34c8d115190327c5d8e35d3b09d554ef9 100644 --- a/drivers/i2c/muxes/i2c-mux-gpio.c +++ b/drivers/i2c/muxes/i2c-mux-gpio.c @@ -105,8 +105,10 @@ static int i2c_mux_gpio_probe_fw(struct gpiomux *mux, } else if (is_acpi_node(child)) { rc = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), values + i); - if (rc) + if (rc) { + fwnode_handle_put(child); return dev_err_probe(dev, rc, "Cannot get address\n"); + } } i++; diff --git a/drivers/i2c/muxes/i2c-mux-gpmux.c b/drivers/i2c/muxes/i2c-mux-gpmux.c index 0405af0e15104993d0e186cbc98c097044ba9ff8..baccf4bfaf0284be021aeb744d84e1cfaefa8cea 100644 --- a/drivers/i2c/muxes/i2c-mux-gpmux.c +++ b/drivers/i2c/muxes/i2c-mux-gpmux.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include struct mux { diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c index 5a03031519bee0e44eae93352142965c1f58291f..23766d853e7674c2f78364c981573b7a6c49b386 100644 --- a/drivers/i2c/muxes/i2c-mux-ltc4306.c +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -62,7 +61,7 @@ static const struct chip_desc chips[] = { static bool ltc4306_is_volatile_reg(struct device *dev, unsigned int reg) { - return (reg == LTC_REG_CONFIG) ? true : false; + return reg == LTC_REG_CONFIG; } static const struct regmap_config ltc4306_regmap_config = { diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 0ccee2ae57204aa7af9979f7dbd8d6495ad91981..2219062104fbca4fcd23b89012090c7c30602790 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -11,6 +11,12 @@ * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547, * PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849. * + * It's also compatible to Maxims MAX735x I2C switch chips, which are controlled + * as the NXP PCA9548 and the MAX736x chips that act like the PCA9544. + * + * This includes the: + * MAX7356, MAX7357, MAX7358, MAX7367, MAX7368 and MAX7369 + * * These chips are all controlled via the I2C bus itself, and all have a * single 8-bit register. The upstream "parent" bus fans out to two, * four, or eight downstream busses or channels; which of these @@ -42,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +58,12 @@ #define PCA954X_IRQ_OFFSET 4 enum pca_type { + max_7356, + max_7357, + max_7358, + max_7367, + max_7368, + max_7369, pca_9540, pca_9542, pca_9543, @@ -88,10 +101,52 @@ struct pca954x { struct irq_domain *irq; unsigned int irq_mask; raw_spinlock_t lock; + struct regulator *supply; }; -/* Provide specs for the PCA954x types we know about */ +/* Provide specs for the MAX735x, PCA954x and PCA984x types we know about */ static const struct chip_desc chips[] = { + [max_7356] = { + .nchans = 8, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [max_7357] = { + .nchans = 8, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + /* + * No interrupt controller support. The interrupt + * provides information about stuck channels. + */ + }, + [max_7358] = { + .nchans = 8, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + /* + * No interrupt controller support. The interrupt + * provides information about stuck channels. + */ + }, + [max_7367] = { + .nchans = 4, + .muxtype = pca954x_isswi, + .has_irq = 1, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [max_7368] = { + .nchans = 4, + .muxtype = pca954x_isswi, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, + [max_7369] = { + .nchans = 4, + .enable = 0x4, + .muxtype = pca954x_ismux, + .has_irq = 1, + .id = { .manufacturer_id = I2C_DEVICE_ID_NONE }, + }, [pca_9540] = { .nchans = 2, .enable = 0x4, @@ -177,6 +232,12 @@ static const struct chip_desc chips[] = { }; static const struct i2c_device_id pca954x_id[] = { + { "max7356", max_7356 }, + { "max7357", max_7357 }, + { "max7358", max_7358 }, + { "max7367", max_7367 }, + { "max7368", max_7368 }, + { "max7369", max_7369 }, { "pca9540", pca_9540 }, { "pca9542", pca_9542 }, { "pca9543", pca_9543 }, @@ -194,6 +255,12 @@ static const struct i2c_device_id pca954x_id[] = { MODULE_DEVICE_TABLE(i2c, pca954x_id); static const struct of_device_id pca954x_of_match[] = { + { .compatible = "maxim,max7356", .data = &chips[max_7356] }, + { .compatible = "maxim,max7357", .data = &chips[max_7357] }, + { .compatible = "maxim,max7358", .data = &chips[max_7358] }, + { .compatible = "maxim,max7367", .data = &chips[max_7367] }, + { .compatible = "maxim,max7368", .data = &chips[max_7368] }, + { .compatible = "maxim,max7369", .data = &chips[max_7369] }, { .compatible = "nxp,pca9540", .data = &chips[pca_9540] }, { .compatible = "nxp,pca9542", .data = &chips[pca_9542] }, { .compatible = "nxp,pca9543", .data = &chips[pca_9543] }, @@ -382,6 +449,8 @@ static void pca954x_cleanup(struct i2c_mux_core *muxc) struct pca954x *data = i2c_mux_priv(muxc); int c, irq; + regulator_disable(data->supply); + if (data->irq) { for (c = 0; c < data->chip->nchans; c++) { irq = irq_find_mapping(data->irq, c); @@ -434,10 +503,22 @@ static int pca954x_probe(struct i2c_client *client) i2c_set_clientdata(client, muxc); data->client = client; + data->supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->supply)) + return dev_err_probe(dev, PTR_ERR(data->supply), + "Failed to request regulator\n"); + + ret = regulator_enable(data->supply); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable vdd supply\n"); + /* Reset the mux if a reset GPIO is specified. */ gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); + if (IS_ERR(gpio)) { + ret = PTR_ERR(gpio); + goto fail_cleanup; + } if (gpio) { udelay(1); gpiod_set_value_cansleep(gpio, 0); @@ -454,7 +535,7 @@ static int pca954x_probe(struct i2c_client *client) ret = i2c_get_device_id(client, &id); if (ret && ret != -EOPNOTSUPP) - return ret; + goto fail_cleanup; if (!ret && (id.manufacturer_id != data->chip->id.manufacturer_id || @@ -462,7 +543,8 @@ static int pca954x_probe(struct i2c_client *client) dev_warn(dev, "unexpected device id %03x-%03x-%x\n", id.manufacturer_id, id.part_id, id.die_revision); - return -ENODEV; + ret = -ENODEV; + goto fail_cleanup; } } @@ -481,7 +563,8 @@ static int pca954x_probe(struct i2c_client *client) ret = pca954x_init(client, data); if (ret < 0) { dev_warn(dev, "probe failed\n"); - return -ENODEV; + ret = -ENODEV; + goto fail_cleanup; } ret = pca954x_irq_setup(muxc); @@ -530,7 +613,6 @@ static void pca954x_remove(struct i2c_client *client) pca954x_cleanup(muxc); } -#ifdef CONFIG_PM_SLEEP static int pca954x_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -544,14 +626,13 @@ static int pca954x_resume(struct device *dev) return ret; } -#endif -static SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(pca954x_pm, NULL, pca954x_resume); static struct i2c_driver pca954x_driver = { .driver = { .name = "pca954x", - .pm = &pca954x_pm, + .pm = pm_sleep_ptr(&pca954x_pm), .of_match_table = pca954x_of_match, }, .probe = pca954x_probe, diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 08aeb69a7800301d1e1f99d9e688043e5723b690..87283e4a46076e2fe40fc701488c85d7d7f63a8c 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1308,7 +1308,11 @@ static int i3c_master_get_i3c_addrs(struct i3c_dev_desc *dev) if (dev->info.static_addr) { status = i3c_bus_get_addr_slot_status(&master->bus, dev->info.static_addr); - if (status != I3C_ADDR_SLOT_FREE) + /* Since static address and assigned dynamic address can be + * equal, allow this case to pass. + */ + if (status != I3C_ADDR_SLOT_FREE && + dev->info.static_addr != dev->boardinfo->init_dyn_addr) return -EBUSY; i3c_bus_set_addr_slot_status(&master->bus, diff --git a/drivers/i3c/master/ast2600-i3c-master.c b/drivers/i3c/master/ast2600-i3c-master.c index 09ed19d489e9c7320e6eff25d9dca74ab1c7d948..01a47d3dd499f56d9a71e69f502c16ce5145189f 100644 --- a/drivers/i3c/master/ast2600-i3c-master.c +++ b/drivers/i3c/master/ast2600-i3c-master.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c index 01610fa5b0ccf57bacc69024cfec6d0ea009c70f..49551db71bc96ba9df786f097dbfec9598dcab6d 100644 --- a/drivers/i3c/master/i3c-master-cdns.c +++ b/drivers/i3c/master/i3c-master-cdns.c @@ -22,7 +22,6 @@ #include #include #include -#include #define DEV_ID 0x0 #define DEV_ID_I3C_MASTER 0x5034 diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c index d97c3175e0e2784ed6f53111d5a8d4aa9f3e8334..6a781f89b0e4066beef10dcf641d242e97504600 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -339,7 +339,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) break; } if (RESP_STATUS(xfer[0].response) == RESP_ERR_NACK && - RESP_STATUS(xfer[0].response) == 1) { + RESP_DATA_LENGTH(xfer->response) == 1) { ret = 0; /* no more devices to be assigned */ break; } diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index 0d63b732ef0c82d076164c15ff455bd8e0fdeb54..8f8295acdadb3aa3aa039271beaf56fefed3617f 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -156,6 +156,7 @@ struct svc_i3c_regs_save { * @base: I3C master controller * @dev: Corresponding device * @regs: Memory mapping + * @saved_regs: Volatile values for PM operations * @free_slots: Bit array of available slots * @addrs: Array containing the dynamic addresses of each attached device * @descs: Array of descriptors, one per attached device @@ -789,6 +790,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, */ break; } else if (SVC_I3C_MSTATUS_NACKED(reg)) { + /* No I3C devices attached */ + if (dev_nb == 0) + break; + /* * A slave device nacked the address, this is * allowed only once, DAA will be stopped and @@ -1263,11 +1268,17 @@ static int svc_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, { struct svc_i3c_master *master = to_svc_i3c_master(m); bool broadcast = cmd->id < 0x80; + int ret; if (broadcast) - return svc_i3c_master_send_bdcast_ccc_cmd(master, cmd); + ret = svc_i3c_master_send_bdcast_ccc_cmd(master, cmd); else - return svc_i3c_master_send_direct_ccc_cmd(master, cmd); + ret = svc_i3c_master_send_direct_ccc_cmd(master, cmd); + + if (ret) + cmd->err = I3C_ERROR_M2; + + return ret; } static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev, @@ -1518,8 +1529,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev) return PTR_ERR(master->sclk); master->irq = platform_get_irq(pdev, 0); - if (master->irq <= 0) - return -ENOENT; + if (master->irq < 0) + return master->irq; master->dev = dev; diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 73f913cbd146a6090b1b89191fc51b93bcaa4481..7acc0f936dad38d8c03c1d546742ec9df5ddcdf1 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -1457,6 +1457,17 @@ static int config_non_roce_gid_cache(struct ib_device *device, i); goto err; } + + if (rdma_protocol_iwarp(device, port)) { + struct net_device *ndev; + + ndev = ib_device_get_netdev(device, port); + if (!ndev) + continue; + RCU_INIT_POINTER(gid_attr.ndev, ndev); + dev_put(ndev); + } + gid_attr.index = i; tprops->subnet_prefix = be64_to_cpu(gid_attr.gid.global.subnet_prefix); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 9891c7dc2af5662eaf5336153e396845177b1dd8..1e2cd7c8716e810da7de504199cabb30545948c2 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -686,30 +686,52 @@ cma_validate_port(struct ib_device *device, u32 port, struct rdma_id_private *id_priv) { struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; + const struct ib_gid_attr *sgid_attr = ERR_PTR(-ENODEV); int bound_if_index = dev_addr->bound_dev_if; - const struct ib_gid_attr *sgid_attr; int dev_type = dev_addr->dev_type; struct net_device *ndev = NULL; if (!rdma_dev_access_netns(device, id_priv->id.route.addr.dev_addr.net)) - return ERR_PTR(-ENODEV); + goto out; if ((dev_type == ARPHRD_INFINIBAND) && !rdma_protocol_ib(device, port)) - return ERR_PTR(-ENODEV); + goto out; if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port)) - return ERR_PTR(-ENODEV); + goto out; + + /* + * For drivers that do not associate more than one net device with + * their gid tables, such as iWARP drivers, it is sufficient to + * return the first table entry. + * + * Other driver classes might be included in the future. + */ + if (rdma_protocol_iwarp(device, port)) { + sgid_attr = rdma_get_gid_attr(device, port, 0); + if (IS_ERR(sgid_attr)) + goto out; + + rcu_read_lock(); + ndev = rcu_dereference(sgid_attr->ndev); + if (!net_eq(dev_net(ndev), dev_addr->net) || + ndev->ifindex != bound_if_index) + sgid_attr = ERR_PTR(-ENODEV); + rcu_read_unlock(); + goto out; + } if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) { ndev = dev_get_by_index(dev_addr->net, bound_if_index); if (!ndev) - return ERR_PTR(-ENODEV); + goto out; } else { gid_type = IB_GID_TYPE_IB; } sgid_attr = rdma_find_gid_by_port(device, gid, gid_type, port, ndev); dev_put(ndev); +out: return sgid_attr; } @@ -4946,7 +4968,7 @@ static int cma_iboe_join_multicast(struct rdma_id_private *id_priv, int err = 0; struct sockaddr *addr = (struct sockaddr *)&mc->addr; struct net_device *ndev = NULL; - struct ib_sa_multicast ib; + struct ib_sa_multicast ib = {}; enum ib_gid_type gid_type; bool send_only; diff --git a/drivers/infiniband/core/cma_configfs.c b/drivers/infiniband/core/cma_configfs.c index 7b68b3ea979f79dfd682062fa9f89f797742fdd9..f2fb2d8a65970a3c8fe6c7cfe42fd0b3e2a6bf93 100644 --- a/drivers/infiniband/core/cma_configfs.c +++ b/drivers/infiniband/core/cma_configfs.c @@ -217,7 +217,7 @@ static int make_cma_ports(struct cma_dev_group *cma_dev_group, return -ENOMEM; for (i = 0; i < ports_num; i++) { - char port_str[10]; + char port_str[11]; ports[i].port_num = i + 1; snprintf(port_str, sizeof(port_str), "%u", i + 1); diff --git a/drivers/infiniband/core/iwpm_util.c b/drivers/infiniband/core/iwpm_util.c index 358a2db38d232cd8b77dd69e82eccf84b40667cc..eecb369898f5005b7c55da4e38779f3148035222 100644 --- a/drivers/infiniband/core/iwpm_util.c +++ b/drivers/infiniband/core/iwpm_util.c @@ -307,7 +307,7 @@ int iwpm_get_remote_info(struct sockaddr_storage *mapped_loc_addr, struct iwpm_nlmsg_request *iwpm_get_nlmsg_request(__u32 nlmsg_seq, u8 nl_client, gfp_t gfp) { - struct iwpm_nlmsg_request *nlmsg_request = NULL; + struct iwpm_nlmsg_request *nlmsg_request; unsigned long flags; nlmsg_request = kzalloc(sizeof(struct iwpm_nlmsg_request), gfp); diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 1b2cc9e45ade53d8fe2d99211c25c5b76207f48a..ae2db0c70788bff251cb46e21ab5980c3e0e48de 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -75,7 +75,7 @@ static bool is_nl_msg_valid(unsigned int type, unsigned int op) if (type >= RDMA_NL_NUM_CLIENTS) return false; - return (op < max_num_ops[type]) ? true : false; + return op < max_num_ops[type]; } static const struct rdma_nl_cbs * diff --git a/drivers/infiniband/core/nldev.c b/drivers/infiniband/core/nldev.c index d5d3e4f0de779ef1e82f06482287b3de355323e1..6d1dbc97875906a8c5589b807eccf0fcf772add9 100644 --- a/drivers/infiniband/core/nldev.c +++ b/drivers/infiniband/core/nldev.c @@ -2529,6 +2529,7 @@ static const struct rdma_nl_cbs nldev_cb_table[RDMA_NLDEV_NUM_OPS] = { }, [RDMA_NLDEV_CMD_SYS_SET] = { .doit = nldev_set_sys_set_doit, + .flags = RDMA_NL_ADMIN_PERM, }, [RDMA_NLDEV_CMD_STAT_SET] = { .doit = nldev_stat_set_doit, diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 7c9c79c139411cc9efca2b12ad4ed56935f14491..495d5a5d0373f1e91db030de98bded5af8d44884 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -72,12 +72,23 @@ enum { #define IB_UVERBS_BASE_DEV MKDEV(IB_UVERBS_MAJOR, IB_UVERBS_BASE_MINOR) static dev_t dynamic_uverbs_dev; -static struct class *uverbs_class; static DEFINE_IDA(uverbs_ida); static int ib_uverbs_add_one(struct ib_device *device); static void ib_uverbs_remove_one(struct ib_device *device, void *client_data); +static char *uverbs_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0666; + return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev)); +} + +static const struct class uverbs_class = { + .name = "infiniband_verbs", + .devnode = uverbs_devnode, +}; + /* * Must be called with the ufile->device->disassociate_srcu held, and the lock * must be held until use of the ucontext is finished. @@ -535,7 +546,7 @@ static ssize_t verify_hdr(struct ib_uverbs_cmd_hdr *hdr, if (hdr->in_words * 4 != count) return -EINVAL; - if (count < method_elm->req_size + sizeof(hdr)) { + if (count < method_elm->req_size + sizeof(*hdr)) { /* * rdma-core v18 and v19 have a bug where they send DESTROY_CQ * with a 16 byte write instead of 24. Old kernels didn't @@ -1117,7 +1128,7 @@ static int ib_uverbs_add_one(struct ib_device *device) } device_initialize(&uverbs_dev->dev); - uverbs_dev->dev.class = uverbs_class; + uverbs_dev->dev.class = &uverbs_class; uverbs_dev->dev.parent = device->dev.parent; uverbs_dev->dev.release = ib_uverbs_release_dev; uverbs_dev->groups[0] = &dev_attr_group; @@ -1235,13 +1246,6 @@ static void ib_uverbs_remove_one(struct ib_device *device, void *client_data) put_device(&uverbs_dev->dev); } -static char *uverbs_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0666; - return kasprintf(GFP_KERNEL, "infiniband/%s", dev_name(dev)); -} - static int __init ib_uverbs_init(void) { int ret; @@ -1262,16 +1266,13 @@ static int __init ib_uverbs_init(void) goto out_alloc; } - uverbs_class = class_create("infiniband_verbs"); - if (IS_ERR(uverbs_class)) { - ret = PTR_ERR(uverbs_class); + ret = class_register(&uverbs_class); + if (ret) { pr_err("user_verbs: couldn't create class infiniband_verbs\n"); goto out_chrdev; } - uverbs_class->devnode = uverbs_devnode; - - ret = class_create_file(uverbs_class, &class_attr_abi_version.attr); + ret = class_create_file(&uverbs_class, &class_attr_abi_version.attr); if (ret) { pr_err("user_verbs: couldn't create abi_version attribute\n"); goto out_class; @@ -1286,7 +1287,7 @@ static int __init ib_uverbs_init(void) return 0; out_class: - class_destroy(uverbs_class); + class_unregister(&uverbs_class); out_chrdev: unregister_chrdev_region(dynamic_uverbs_dev, @@ -1303,7 +1304,7 @@ static int __init ib_uverbs_init(void) static void __exit ib_uverbs_cleanup(void) { ib_unregister_client(&uverbs_client); - class_destroy(uverbs_class); + class_unregister(&uverbs_class); unregister_chrdev_region(IB_UVERBS_BASE_DEV, IB_UVERBS_NUM_FIXED_MINOR); unregister_chrdev_region(dynamic_uverbs_dev, diff --git a/drivers/infiniband/core/uverbs_std_types_counters.c b/drivers/infiniband/core/uverbs_std_types_counters.c index 999da9c7986687dc2e8392675ef150c0702d76df..381aa57976417c51628d6c197cb353891b9473a9 100644 --- a/drivers/infiniband/core/uverbs_std_types_counters.c +++ b/drivers/infiniband/core/uverbs_std_types_counters.c @@ -107,6 +107,8 @@ static int UVERBS_HANDLER(UVERBS_METHOD_COUNTERS_READ)( return ret; uattr = uverbs_attr_get(attrs, UVERBS_ATTR_READ_COUNTERS_BUFF); + if (IS_ERR(uattr)) + return PTR_ERR(uattr); read_attr.ncounters = uattr->ptr_attr.len / sizeof(u64); read_attr.counters_buff = uverbs_zalloc( attrs, array_size(read_attr.ncounters, sizeof(u64))); diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index b99b3cc283b650b9d001bccbfe0bcfdee6100573..41ff5595c860623c9a09032c1a4e3af1f54cffab 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1880,6 +1880,89 @@ int ib_modify_qp_with_udata(struct ib_qp *ib_qp, struct ib_qp_attr *attr, } EXPORT_SYMBOL(ib_modify_qp_with_udata); +static void ib_get_width_and_speed(u32 netdev_speed, u32 lanes, + u16 *speed, u8 *width) +{ + if (!lanes) { + if (netdev_speed <= SPEED_1000) { + *width = IB_WIDTH_1X; + *speed = IB_SPEED_SDR; + } else if (netdev_speed <= SPEED_10000) { + *width = IB_WIDTH_1X; + *speed = IB_SPEED_FDR10; + } else if (netdev_speed <= SPEED_20000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_DDR; + } else if (netdev_speed <= SPEED_25000) { + *width = IB_WIDTH_1X; + *speed = IB_SPEED_EDR; + } else if (netdev_speed <= SPEED_40000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_FDR10; + } else if (netdev_speed <= SPEED_50000) { + *width = IB_WIDTH_2X; + *speed = IB_SPEED_EDR; + } else if (netdev_speed <= SPEED_100000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_EDR; + } else if (netdev_speed <= SPEED_200000) { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_HDR; + } else { + *width = IB_WIDTH_4X; + *speed = IB_SPEED_NDR; + } + + return; + } + + switch (lanes) { + case 1: + *width = IB_WIDTH_1X; + break; + case 2: + *width = IB_WIDTH_2X; + break; + case 4: + *width = IB_WIDTH_4X; + break; + case 8: + *width = IB_WIDTH_8X; + break; + case 12: + *width = IB_WIDTH_12X; + break; + default: + *width = IB_WIDTH_1X; + } + + switch (netdev_speed / lanes) { + case SPEED_2500: + *speed = IB_SPEED_SDR; + break; + case SPEED_5000: + *speed = IB_SPEED_DDR; + break; + case SPEED_10000: + *speed = IB_SPEED_FDR10; + break; + case SPEED_14000: + *speed = IB_SPEED_FDR; + break; + case SPEED_25000: + *speed = IB_SPEED_EDR; + break; + case SPEED_50000: + *speed = IB_SPEED_HDR; + break; + case SPEED_100000: + *speed = IB_SPEED_NDR; + break; + default: + *speed = IB_SPEED_SDR; + } +} + int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) { int rc; @@ -1904,29 +1987,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width) netdev_speed = lksettings.base.speed; } else { netdev_speed = SPEED_1000; - pr_warn("%s speed is unknown, defaulting to %u\n", netdev->name, - netdev_speed); + if (rc) + pr_warn("%s speed is unknown, defaulting to %u\n", + netdev->name, netdev_speed); } - if (netdev_speed <= SPEED_1000) { - *width = IB_WIDTH_1X; - *speed = IB_SPEED_SDR; - } else if (netdev_speed <= SPEED_10000) { - *width = IB_WIDTH_1X; - *speed = IB_SPEED_FDR10; - } else if (netdev_speed <= SPEED_20000) { - *width = IB_WIDTH_4X; - *speed = IB_SPEED_DDR; - } else if (netdev_speed <= SPEED_25000) { - *width = IB_WIDTH_1X; - *speed = IB_SPEED_EDR; - } else if (netdev_speed <= SPEED_40000) { - *width = IB_WIDTH_4X; - *speed = IB_SPEED_FDR10; - } else { - *width = IB_WIDTH_4X; - *speed = IB_SPEED_EDR; - } + ib_get_width_and_speed(netdev_speed, lksettings.lanes, + speed, width); return 0; } diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h index ea81b2497511a4ced31f7e65846d3bb877710855..9fd9849ebdd1428813e5d0b0a90b217629f0b077 100644 --- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -112,12 +112,34 @@ struct bnxt_re_gsi_context { #define BNXT_RE_NQ_IDX 1 #define BNXT_RE_GEN_P5_MAX_VF 64 +struct bnxt_re_pacing { + u64 dbr_db_fifo_reg_off; + void *dbr_page; + u64 dbr_bar_addr; + u32 pacing_algo_th; + u32 do_pacing_save; + u32 dbq_pacing_time; /* ms */ + u32 dbr_def_do_pacing; + bool dbr_pacing; + struct mutex dbq_lock; /* synchronize db pacing algo */ +}; + +#define BNXT_RE_MAX_DBR_DO_PACING 0xFFFF +#define BNXT_RE_DBR_PACING_TIME 5 /* ms */ +#define BNXT_RE_PACING_ALGO_THRESHOLD 250 /* Entries in DB FIFO */ +#define BNXT_RE_PACING_ALARM_TH_MULTIPLE 2 /* Multiple of pacing algo threshold */ +/* Default do_pacing value when there is no congestion */ +#define BNXT_RE_DBR_DO_PACING_NO_CONGESTION 0x7F /* 1 in 512 probability */ +#define BNXT_RE_DB_FIFO_ROOM_MASK 0x1FFF8000 +#define BNXT_RE_MAX_FIFO_DEPTH 0x2c00 +#define BNXT_RE_DB_FIFO_ROOM_SHIFT 15 +#define BNXT_RE_GRC_FIFO_REG_BASE 0x2000 + struct bnxt_re_dev { struct ib_device ibdev; struct list_head list; unsigned long flags; #define BNXT_RE_FLAG_NETDEV_REGISTERED 0 -#define BNXT_RE_FLAG_GOT_MSIX 2 #define BNXT_RE_FLAG_HAVE_L2_REF 3 #define BNXT_RE_FLAG_RCFW_CHANNEL_EN 4 #define BNXT_RE_FLAG_QOS_WORK_REG 5 @@ -152,16 +174,9 @@ struct bnxt_re_dev { struct bnxt_qplib_res qplib_res; struct bnxt_qplib_dpi dpi_privileged; - atomic_t qp_count; struct mutex qp_lock; /* protect qp list */ struct list_head qp_list; - atomic_t cq_count; - atomic_t srq_count; - atomic_t mr_count; - atomic_t mw_count; - atomic_t ah_count; - atomic_t pd_count; /* Max of 2 lossless traffic class supported per port */ u16 cosq[2]; @@ -171,6 +186,9 @@ struct bnxt_re_dev { atomic_t nq_alloc_cnt; u32 is_virtfn; u32 num_vfs; + struct bnxt_re_pacing pacing; + struct work_struct dbq_fifo_check_work; + struct delayed_work dbq_pacing_work; }; #define to_bnxt_re_dev(ptr, member) \ @@ -181,6 +199,7 @@ struct bnxt_re_dev { #define BNXT_RE_ROCEV2_IPV6_PACKET 3 #define BNXT_RE_CHECK_RC(x) ((x) && ((x) != -ETIMEDOUT)) +void bnxt_re_pacing_alert(struct bnxt_re_dev *rdev); static inline struct device *rdev_to_dev(struct bnxt_re_dev *rdev) { diff --git a/drivers/infiniband/hw/bnxt_re/hw_counters.c b/drivers/infiniband/hw/bnxt_re/hw_counters.c index 825d512799d90bfb13dc1b4591688f94df138492..93572405d6fae7fd1959e411c7a518658624d4e1 100644 --- a/drivers/infiniband/hw/bnxt_re/hw_counters.c +++ b/drivers/infiniband/hw/bnxt_re/hw_counters.c @@ -61,15 +61,29 @@ static const struct rdma_stat_desc bnxt_re_stat_descs[] = { [BNXT_RE_ACTIVE_PD].name = "active_pds", [BNXT_RE_ACTIVE_AH].name = "active_ahs", [BNXT_RE_ACTIVE_QP].name = "active_qps", + [BNXT_RE_ACTIVE_RC_QP].name = "active_rc_qps", + [BNXT_RE_ACTIVE_UD_QP].name = "active_ud_qps", [BNXT_RE_ACTIVE_SRQ].name = "active_srqs", [BNXT_RE_ACTIVE_CQ].name = "active_cqs", [BNXT_RE_ACTIVE_MR].name = "active_mrs", [BNXT_RE_ACTIVE_MW].name = "active_mws", + [BNXT_RE_WATERMARK_PD].name = "watermark_pds", + [BNXT_RE_WATERMARK_AH].name = "watermark_ahs", + [BNXT_RE_WATERMARK_QP].name = "watermark_qps", + [BNXT_RE_WATERMARK_RC_QP].name = "watermark_rc_qps", + [BNXT_RE_WATERMARK_UD_QP].name = "watermark_ud_qps", + [BNXT_RE_WATERMARK_SRQ].name = "watermark_srqs", + [BNXT_RE_WATERMARK_CQ].name = "watermark_cqs", + [BNXT_RE_WATERMARK_MR].name = "watermark_mrs", + [BNXT_RE_WATERMARK_MW].name = "watermark_mws", + [BNXT_RE_RESIZE_CQ_CNT].name = "resize_cq_cnt", [BNXT_RE_RX_PKTS].name = "rx_pkts", [BNXT_RE_RX_BYTES].name = "rx_bytes", [BNXT_RE_TX_PKTS].name = "tx_pkts", [BNXT_RE_TX_BYTES].name = "tx_bytes", [BNXT_RE_RECOVERABLE_ERRORS].name = "recoverable_errors", + [BNXT_RE_TX_ERRORS].name = "tx_roce_errors", + [BNXT_RE_TX_DISCARDS].name = "tx_roce_discards", [BNXT_RE_RX_ERRORS].name = "rx_roce_errors", [BNXT_RE_RX_DISCARDS].name = "rx_roce_discards", [BNXT_RE_TO_RETRANSMITS].name = "to_retransmits", @@ -117,14 +131,25 @@ static const struct rdma_stat_desc bnxt_re_stat_descs[] = { [BNXT_RE_TX_READ_RES].name = "tx_read_resp", [BNXT_RE_TX_WRITE_REQ].name = "tx_write_req", [BNXT_RE_TX_SEND_REQ].name = "tx_send_req", + [BNXT_RE_TX_ROCE_PKTS].name = "tx_roce_only_pkts", + [BNXT_RE_TX_ROCE_BYTES].name = "tx_roce_only_bytes", [BNXT_RE_RX_ATOMIC_REQ].name = "rx_atomic_req", [BNXT_RE_RX_READ_REQ].name = "rx_read_req", [BNXT_RE_RX_READ_RESP].name = "rx_read_resp", [BNXT_RE_RX_WRITE_REQ].name = "rx_write_req", [BNXT_RE_RX_SEND_REQ].name = "rx_send_req", + [BNXT_RE_RX_ROCE_PKTS].name = "rx_roce_only_pkts", + [BNXT_RE_RX_ROCE_BYTES].name = "rx_roce_only_bytes", [BNXT_RE_RX_ROCE_GOOD_PKTS].name = "rx_roce_good_pkts", [BNXT_RE_RX_ROCE_GOOD_BYTES].name = "rx_roce_good_bytes", - [BNXT_RE_OOB].name = "rx_out_of_buffer" + [BNXT_RE_OOB].name = "rx_out_of_buffer", + [BNXT_RE_TX_CNP].name = "tx_cnp_pkts", + [BNXT_RE_RX_CNP].name = "rx_cnp_pkts", + [BNXT_RE_RX_ECN].name = "rx_ecn_marked_pkts", + [BNXT_RE_PACING_RESCHED].name = "pacing_reschedule", + [BNXT_RE_PACING_CMPL].name = "pacing_complete", + [BNXT_RE_PACING_ALERT].name = "pacing_alerts", + [BNXT_RE_DB_FIFO_REG].name = "db_fifo_register", }; static void bnxt_re_copy_ext_stats(struct bnxt_re_dev *rdev, @@ -136,14 +161,22 @@ static void bnxt_re_copy_ext_stats(struct bnxt_re_dev *rdev, stats->value[BNXT_RE_TX_READ_RES] = s->tx_read_res; stats->value[BNXT_RE_TX_WRITE_REQ] = s->tx_write_req; stats->value[BNXT_RE_TX_SEND_REQ] = s->tx_send_req; + stats->value[BNXT_RE_TX_ROCE_PKTS] = s->tx_roce_pkts; + stats->value[BNXT_RE_TX_ROCE_BYTES] = s->tx_roce_bytes; stats->value[BNXT_RE_RX_ATOMIC_REQ] = s->rx_atomic_req; stats->value[BNXT_RE_RX_READ_REQ] = s->rx_read_req; stats->value[BNXT_RE_RX_READ_RESP] = s->rx_read_res; stats->value[BNXT_RE_RX_WRITE_REQ] = s->rx_write_req; stats->value[BNXT_RE_RX_SEND_REQ] = s->rx_send_req; + stats->value[BNXT_RE_RX_ROCE_PKTS] = s->rx_roce_pkts; + stats->value[BNXT_RE_RX_ROCE_BYTES] = s->rx_roce_bytes; stats->value[BNXT_RE_RX_ROCE_GOOD_PKTS] = s->rx_roce_good_pkts; stats->value[BNXT_RE_RX_ROCE_GOOD_BYTES] = s->rx_roce_good_bytes; stats->value[BNXT_RE_OOB] = s->rx_out_of_buffer; + stats->value[BNXT_RE_TX_CNP] = s->tx_cnp; + stats->value[BNXT_RE_RX_CNP] = s->rx_cnp; + stats->value[BNXT_RE_RX_ECN] = s->rx_ecn_marked; + stats->value[BNXT_RE_OUT_OF_SEQ_ERR] = s->rx_out_of_sequence; } static int bnxt_re_get_ext_stat(struct bnxt_re_dev *rdev, @@ -249,30 +282,59 @@ static void bnxt_re_copy_err_stats(struct bnxt_re_dev *rdev, err_s->res_oos_drop_count; } +static void bnxt_re_copy_db_pacing_stats(struct bnxt_re_dev *rdev, + struct rdma_hw_stats *stats) +{ + struct bnxt_re_db_pacing_stats *pacing_s = &rdev->stats.pacing; + + stats->value[BNXT_RE_PACING_RESCHED] = pacing_s->resched; + stats->value[BNXT_RE_PACING_CMPL] = pacing_s->complete; + stats->value[BNXT_RE_PACING_ALERT] = pacing_s->alerts; + stats->value[BNXT_RE_DB_FIFO_REG] = + readl(rdev->en_dev->bar0 + rdev->pacing.dbr_db_fifo_reg_off); +} + int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev, struct rdma_hw_stats *stats, u32 port, int index) { struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); - struct ctx_hw_stats *hw_stats = NULL; + struct bnxt_re_res_cntrs *res_s = &rdev->stats.res; struct bnxt_qplib_roce_stats *err_s = NULL; + struct ctx_hw_stats *hw_stats = NULL; int rc = 0; hw_stats = rdev->qplib_ctx.stats.dma; if (!port || !stats) return -EINVAL; - stats->value[BNXT_RE_ACTIVE_QP] = atomic_read(&rdev->qp_count); - stats->value[BNXT_RE_ACTIVE_SRQ] = atomic_read(&rdev->srq_count); - stats->value[BNXT_RE_ACTIVE_CQ] = atomic_read(&rdev->cq_count); - stats->value[BNXT_RE_ACTIVE_MR] = atomic_read(&rdev->mr_count); - stats->value[BNXT_RE_ACTIVE_MW] = atomic_read(&rdev->mw_count); - stats->value[BNXT_RE_ACTIVE_PD] = atomic_read(&rdev->pd_count); - stats->value[BNXT_RE_ACTIVE_AH] = atomic_read(&rdev->ah_count); + stats->value[BNXT_RE_ACTIVE_QP] = atomic_read(&res_s->qp_count); + stats->value[BNXT_RE_ACTIVE_RC_QP] = atomic_read(&res_s->rc_qp_count); + stats->value[BNXT_RE_ACTIVE_UD_QP] = atomic_read(&res_s->ud_qp_count); + stats->value[BNXT_RE_ACTIVE_SRQ] = atomic_read(&res_s->srq_count); + stats->value[BNXT_RE_ACTIVE_CQ] = atomic_read(&res_s->cq_count); + stats->value[BNXT_RE_ACTIVE_MR] = atomic_read(&res_s->mr_count); + stats->value[BNXT_RE_ACTIVE_MW] = atomic_read(&res_s->mw_count); + stats->value[BNXT_RE_ACTIVE_PD] = atomic_read(&res_s->pd_count); + stats->value[BNXT_RE_ACTIVE_AH] = atomic_read(&res_s->ah_count); + stats->value[BNXT_RE_WATERMARK_QP] = res_s->qp_watermark; + stats->value[BNXT_RE_WATERMARK_RC_QP] = res_s->rc_qp_watermark; + stats->value[BNXT_RE_WATERMARK_UD_QP] = res_s->ud_qp_watermark; + stats->value[BNXT_RE_WATERMARK_SRQ] = res_s->srq_watermark; + stats->value[BNXT_RE_WATERMARK_CQ] = res_s->cq_watermark; + stats->value[BNXT_RE_WATERMARK_MR] = res_s->mr_watermark; + stats->value[BNXT_RE_WATERMARK_MW] = res_s->mw_watermark; + stats->value[BNXT_RE_WATERMARK_PD] = res_s->pd_watermark; + stats->value[BNXT_RE_WATERMARK_AH] = res_s->ah_watermark; + stats->value[BNXT_RE_RESIZE_CQ_CNT] = atomic_read(&res_s->resize_count); if (hw_stats) { stats->value[BNXT_RE_RECOVERABLE_ERRORS] = le64_to_cpu(hw_stats->tx_bcast_pkts); + stats->value[BNXT_RE_TX_DISCARDS] = + le64_to_cpu(hw_stats->tx_discard_pkts); + stats->value[BNXT_RE_TX_ERRORS] = + le64_to_cpu(hw_stats->tx_error_pkts); stats->value[BNXT_RE_RX_ERRORS] = le64_to_cpu(hw_stats->rx_error_pkts); stats->value[BNXT_RE_RX_DISCARDS] = @@ -294,6 +356,7 @@ int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev, &rdev->flags); goto done; } + bnxt_re_copy_err_stats(rdev, stats, err_s); if (_is_ext_stats_supported(rdev->dev_attr.dev_cap_flags) && !rdev->is_virtfn) { rc = bnxt_re_get_ext_stat(rdev, stats); @@ -303,7 +366,8 @@ int bnxt_re_ib_get_hw_stats(struct ib_device *ibdev, goto done; } } - bnxt_re_copy_err_stats(rdev, stats, err_s); + if (rdev->pacing.dbr_pacing) + bnxt_re_copy_db_pacing_stats(rdev, stats); } done: diff --git a/drivers/infiniband/hw/bnxt_re/hw_counters.h b/drivers/infiniband/hw/bnxt_re/hw_counters.h index 7943b2c393e4222bcd0684acdf13c39702a7affe..e541b6f8ca9fe9cacb7597d57cf06b0623f7ab58 100644 --- a/drivers/infiniband/hw/bnxt_re/hw_counters.h +++ b/drivers/infiniband/hw/bnxt_re/hw_counters.h @@ -44,15 +44,29 @@ enum bnxt_re_hw_stats { BNXT_RE_ACTIVE_PD, BNXT_RE_ACTIVE_AH, BNXT_RE_ACTIVE_QP, + BNXT_RE_ACTIVE_RC_QP, + BNXT_RE_ACTIVE_UD_QP, BNXT_RE_ACTIVE_SRQ, BNXT_RE_ACTIVE_CQ, BNXT_RE_ACTIVE_MR, BNXT_RE_ACTIVE_MW, + BNXT_RE_WATERMARK_PD, + BNXT_RE_WATERMARK_AH, + BNXT_RE_WATERMARK_QP, + BNXT_RE_WATERMARK_RC_QP, + BNXT_RE_WATERMARK_UD_QP, + BNXT_RE_WATERMARK_SRQ, + BNXT_RE_WATERMARK_CQ, + BNXT_RE_WATERMARK_MR, + BNXT_RE_WATERMARK_MW, + BNXT_RE_RESIZE_CQ_CNT, BNXT_RE_RX_PKTS, BNXT_RE_RX_BYTES, BNXT_RE_TX_PKTS, BNXT_RE_TX_BYTES, BNXT_RE_RECOVERABLE_ERRORS, + BNXT_RE_TX_ERRORS, + BNXT_RE_TX_DISCARDS, BNXT_RE_RX_ERRORS, BNXT_RE_RX_DISCARDS, BNXT_RE_TO_RETRANSMITS, @@ -100,19 +114,58 @@ enum bnxt_re_hw_stats { BNXT_RE_TX_READ_RES, BNXT_RE_TX_WRITE_REQ, BNXT_RE_TX_SEND_REQ, + BNXT_RE_TX_ROCE_PKTS, + BNXT_RE_TX_ROCE_BYTES, BNXT_RE_RX_ATOMIC_REQ, BNXT_RE_RX_READ_REQ, BNXT_RE_RX_READ_RESP, BNXT_RE_RX_WRITE_REQ, BNXT_RE_RX_SEND_REQ, + BNXT_RE_RX_ROCE_PKTS, + BNXT_RE_RX_ROCE_BYTES, BNXT_RE_RX_ROCE_GOOD_PKTS, BNXT_RE_RX_ROCE_GOOD_BYTES, BNXT_RE_OOB, + BNXT_RE_TX_CNP, + BNXT_RE_RX_CNP, + BNXT_RE_RX_ECN, + BNXT_RE_PACING_RESCHED, + BNXT_RE_PACING_CMPL, + BNXT_RE_PACING_ALERT, + BNXT_RE_DB_FIFO_REG, BNXT_RE_NUM_EXT_COUNTERS }; #define BNXT_RE_NUM_STD_COUNTERS (BNXT_RE_OUT_OF_SEQ_ERR + 1) +struct bnxt_re_db_pacing_stats { + u64 resched; + u64 complete; + u64 alerts; +}; + +struct bnxt_re_res_cntrs { + atomic_t qp_count; + atomic_t rc_qp_count; + atomic_t ud_qp_count; + atomic_t cq_count; + atomic_t srq_count; + atomic_t mr_count; + atomic_t mw_count; + atomic_t ah_count; + atomic_t pd_count; + atomic_t resize_count; + u64 qp_watermark; + u64 rc_qp_watermark; + u64 ud_qp_watermark; + u64 cq_watermark; + u64 srq_watermark; + u64 mr_watermark; + u64 mw_watermark; + u64 ah_watermark; + u64 pd_watermark; +}; + struct bnxt_re_rstat { struct bnxt_qplib_roce_stats errs; struct bnxt_qplib_ext_stat ext_stat; @@ -120,6 +173,8 @@ struct bnxt_re_rstat { struct bnxt_re_stats { struct bnxt_re_rstat rstat; + struct bnxt_re_res_cntrs res; + struct bnxt_re_db_pacing_stats pacing; }; struct rdma_hw_stats *bnxt_re_ib_alloc_hw_port_stats(struct ib_device *ibdev, diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 03cc45a5458de9da19b297aeb333563b7d7ffd59..faa88d12ee8681788ab44b6f4861a2f6e170bcf9 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -284,7 +284,7 @@ int bnxt_re_query_gid(struct ib_device *ibdev, u32 port_num, int index, union ib_gid *gid) { struct bnxt_re_dev *rdev = to_bnxt_re_dev(ibdev, ibdev); - int rc = 0; + int rc; /* Ignore port_num */ memset(gid, 0, sizeof(*gid)); @@ -565,6 +565,8 @@ bnxt_re_mmap_entry_insert(struct bnxt_re_ucontext *uctx, u64 mem_offset, break; case BNXT_RE_MMAP_UC_DB: case BNXT_RE_MMAP_WC_DB: + case BNXT_RE_MMAP_DBR_BAR: + case BNXT_RE_MMAP_DBR_PAGE: ret = rdma_user_mmap_entry_insert(&uctx->ib_uctx, &entry->rdma_entry, PAGE_SIZE); break; @@ -600,7 +602,7 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd, struct ib_udata *udata) if (!bnxt_qplib_dealloc_pd(&rdev->qplib_res, &rdev->qplib_res.pd_tbl, &pd->qplib_pd)) - atomic_dec(&rdev->pd_count); + atomic_dec(&rdev->stats.res.pd_count); } return 0; } @@ -613,10 +615,11 @@ int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) udata, struct bnxt_re_ucontext, ib_uctx); struct bnxt_re_pd *pd = container_of(ibpd, struct bnxt_re_pd, ib_pd); struct bnxt_re_user_mmap_entry *entry = NULL; + u32 active_pds; int rc = 0; pd->rdev = rdev; - if (bnxt_qplib_alloc_pd(&rdev->qplib_res.pd_tbl, &pd->qplib_pd)) { + if (bnxt_qplib_alloc_pd(&rdev->qplib_res, &pd->qplib_pd)) { ibdev_err(&rdev->ibdev, "Failed to allocate HW PD"); rc = -ENOMEM; goto fail; @@ -663,7 +666,9 @@ int bnxt_re_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) if (bnxt_re_create_fence_mr(pd)) ibdev_warn(&rdev->ibdev, "Failed to create Fence-MR\n"); - atomic_inc(&rdev->pd_count); + active_pds = atomic_inc_return(&rdev->stats.res.pd_count); + if (active_pds > rdev->stats.res.pd_watermark) + rdev->stats.res.pd_watermark = active_pds; return 0; dbfail: @@ -679,7 +684,7 @@ int bnxt_re_destroy_ah(struct ib_ah *ib_ah, u32 flags) struct bnxt_re_ah *ah = container_of(ib_ah, struct bnxt_re_ah, ib_ah); struct bnxt_re_dev *rdev = ah->rdev; bool block = true; - int rc = 0; + int rc; block = !(flags & RDMA_DESTROY_AH_SLEEPABLE); rc = bnxt_qplib_destroy_ah(&rdev->qplib_res, &ah->qplib_ah, block); @@ -689,7 +694,7 @@ int bnxt_re_destroy_ah(struct ib_ah *ib_ah, u32 flags) else goto fail; } - atomic_dec(&rdev->ah_count); + atomic_dec(&rdev->stats.res.ah_count); fail: return rc; } @@ -723,6 +728,7 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, const struct ib_gid_attr *sgid_attr; struct bnxt_re_gid_ctx *ctx; struct bnxt_re_ah *ah = container_of(ib_ah, struct bnxt_re_ah, ib_ah); + u32 active_ahs; u8 nw_type; int rc; @@ -775,7 +781,9 @@ int bnxt_re_create_ah(struct ib_ah *ib_ah, struct rdma_ah_init_attr *init_attr, wmb(); /* make sure cache is updated. */ spin_unlock_irqrestore(&uctx->sh_lock, flag); } - atomic_inc(&rdev->ah_count); + active_ahs = atomic_inc_return(&rdev->stats.res.ah_count); + if (active_ahs > rdev->stats.res.ah_watermark) + rdev->stats.res.ah_watermark = active_ahs; return 0; } @@ -826,7 +834,7 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp) struct bnxt_re_qp *gsi_sqp; struct bnxt_re_ah *gsi_sah; struct bnxt_re_dev *rdev; - int rc = 0; + int rc; rdev = qp->rdev; gsi_sqp = rdev->gsi_ctx.gsi_sqp; @@ -836,7 +844,7 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp) bnxt_qplib_destroy_ah(&rdev->qplib_res, &gsi_sah->qplib_ah, true); - atomic_dec(&rdev->ah_count); + atomic_dec(&rdev->stats.res.ah_count); bnxt_qplib_clean_qp(&qp->qplib_qp); ibdev_dbg(&rdev->ibdev, "Destroy the shadow QP\n"); @@ -851,7 +859,7 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp) mutex_lock(&rdev->qp_lock); list_del(&gsi_sqp->list); mutex_unlock(&rdev->qp_lock); - atomic_dec(&rdev->qp_count); + atomic_dec(&rdev->stats.res.qp_count); kfree(rdev->gsi_ctx.sqp_tbl); kfree(gsi_sah); @@ -901,7 +909,11 @@ int bnxt_re_destroy_qp(struct ib_qp *ib_qp, struct ib_udata *udata) mutex_lock(&rdev->qp_lock); list_del(&qp->list); mutex_unlock(&rdev->qp_lock); - atomic_dec(&rdev->qp_count); + atomic_dec(&rdev->stats.res.qp_count); + if (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_RC) + atomic_dec(&rdev->stats.res.rc_qp_count); + else if (qp->qplib_qp.type == CMDQ_CREATE_QP_TYPE_UD) + atomic_dec(&rdev->stats.res.ud_qp_count); ib_umem_release(qp->rumem); ib_umem_release(qp->sumem); @@ -1095,7 +1107,7 @@ static struct bnxt_re_ah *bnxt_re_create_shadow_qp_ah "Failed to allocate HW AH for Shadow QP"); goto fail; } - atomic_inc(&rdev->ah_count); + atomic_inc(&rdev->stats.res.ah_count); return ah; @@ -1163,7 +1175,7 @@ static struct bnxt_re_qp *bnxt_re_create_shadow_qp INIT_LIST_HEAD(&qp->list); mutex_lock(&rdev->qp_lock); list_add_tail(&qp->list, &rdev->qp_list); - atomic_inc(&rdev->qp_count); + atomic_inc(&rdev->stats.res.qp_count); mutex_unlock(&rdev->qp_lock); return qp; fail: @@ -1340,8 +1352,7 @@ static int bnxt_re_init_qp_attr(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, qplqp->pd = &pd->qplib_pd; qplqp->qp_handle = (u64)qplqp; qplqp->max_inline_data = init_attr->cap.max_inline_data; - qplqp->sig_type = ((init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) ? - true : false); + qplqp->sig_type = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR; qptype = bnxt_re_init_qp_type(rdev, init_attr); if (qptype < 0) { rc = qptype; @@ -1446,7 +1457,7 @@ static int bnxt_re_create_gsi_qp(struct bnxt_re_qp *qp, struct bnxt_re_pd *pd, { struct bnxt_re_dev *rdev; struct bnxt_qplib_qp *qplqp; - int rc = 0; + int rc; rdev = qp->rdev; qplqp = &qp->qplib_qp; @@ -1497,6 +1508,7 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_qplib_dev_attr *dev_attr = &rdev->dev_attr; struct bnxt_re_qp *qp = container_of(ib_qp, struct bnxt_re_qp, ib_qp); + u32 active_qps; int rc; rc = bnxt_re_test_qp_limits(rdev, qp_init_attr, dev_attr); @@ -1545,7 +1557,18 @@ int bnxt_re_create_qp(struct ib_qp *ib_qp, struct ib_qp_init_attr *qp_init_attr, mutex_lock(&rdev->qp_lock); list_add_tail(&qp->list, &rdev->qp_list); mutex_unlock(&rdev->qp_lock); - atomic_inc(&rdev->qp_count); + active_qps = atomic_inc_return(&rdev->stats.res.qp_count); + if (active_qps > rdev->stats.res.qp_watermark) + rdev->stats.res.qp_watermark = active_qps; + if (qp_init_attr->qp_type == IB_QPT_RC) { + active_qps = atomic_inc_return(&rdev->stats.res.rc_qp_count); + if (active_qps > rdev->stats.res.rc_qp_watermark) + rdev->stats.res.rc_qp_watermark = active_qps; + } else if (qp_init_attr->qp_type == IB_QPT_UD) { + active_qps = atomic_inc_return(&rdev->stats.res.ud_qp_count); + if (active_qps > rdev->stats.res.ud_qp_watermark) + rdev->stats.res.ud_qp_watermark = active_qps; + } return 0; qp_destroy: @@ -1648,7 +1671,7 @@ int bnxt_re_destroy_srq(struct ib_srq *ib_srq, struct ib_udata *udata) nq = qplib_srq->cq->nq; bnxt_qplib_destroy_srq(&rdev->qplib_res, qplib_srq); ib_umem_release(srq->umem); - atomic_dec(&rdev->srq_count); + atomic_dec(&rdev->stats.res.srq_count); if (nq) nq->budget--; return 0; @@ -1696,6 +1719,7 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, struct bnxt_re_srq *srq; struct bnxt_re_pd *pd; struct ib_pd *ib_pd; + u32 active_srqs; int rc, entries; ib_pd = ib_srq->pd; @@ -1760,7 +1784,9 @@ int bnxt_re_create_srq(struct ib_srq *ib_srq, } if (nq) nq->budget++; - atomic_inc(&rdev->srq_count); + active_srqs = atomic_inc_return(&rdev->stats.res.srq_count); + if (active_srqs > rdev->stats.res.srq_watermark) + rdev->stats.res.srq_watermark = active_srqs; spin_lock_init(&srq->lock); return 0; @@ -1862,7 +1888,7 @@ static int bnxt_re_modify_shadow_qp(struct bnxt_re_dev *rdev, int qp_attr_mask) { struct bnxt_re_qp *qp = rdev->gsi_ctx.gsi_sqp; - int rc = 0; + int rc; if (qp_attr_mask & IB_QP_STATE) { qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_STATE; @@ -2212,7 +2238,7 @@ static int bnxt_re_build_qp1_send_v2(struct bnxt_re_qp *qp, u8 ip_version = 0; u16 vlan_id = 0xFFFF; void *buf; - int i, rc = 0; + int i, rc; memset(&qp->qp1_hdr, 0, sizeof(qp->qp1_hdr)); @@ -2250,7 +2276,7 @@ static int bnxt_re_build_qp1_send_v2(struct bnxt_re_qp *qp, } is_eth = true; - is_vlan = (vlan_id && (vlan_id < 0x1000)) ? true : false; + is_vlan = vlan_id && (vlan_id < 0x1000); ib_ud_header_init(payload_size, !is_eth, is_eth, is_vlan, is_grh, ip_version, is_udp, 0, &qp->qp1_hdr); @@ -2787,7 +2813,6 @@ static int bnxt_re_post_recv_shadow_qp(struct bnxt_re_dev *rdev, struct bnxt_qplib_swqe wqe; int rc = 0; - memset(&wqe, 0, sizeof(wqe)); while (wr) { /* House keeping */ memset(&wqe, 0, sizeof(wqe)); @@ -2886,7 +2911,7 @@ int bnxt_re_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) bnxt_qplib_destroy_cq(&rdev->qplib_res, &cq->qplib_cq); ib_umem_release(cq->umem); - atomic_dec(&rdev->cq_count); + atomic_dec(&rdev->stats.res.cq_count); nq->budget--; kfree(cq->cql); return 0; @@ -2902,6 +2927,7 @@ int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, int cqe = attr->cqe; struct bnxt_qplib_nq *nq = NULL; unsigned int nq_alloc_cnt; + u32 active_cqs; if (attr->flags) return -EOPNOTSUPP; @@ -2970,7 +2996,9 @@ int bnxt_re_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, cq->cq_period = cq->qplib_cq.period; nq->budget++; - atomic_inc(&rdev->cq_count); + active_cqs = atomic_inc_return(&rdev->stats.res.cq_count); + if (active_cqs > rdev->stats.res.cq_watermark) + rdev->stats.res.cq_watermark = active_cqs; spin_lock_init(&cq->cq_lock); if (udata) { @@ -3083,6 +3111,7 @@ int bnxt_re_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) } cq->ib_cq.cqe = cq->resize_cqe; + atomic_inc(&rdev->stats.res.resize_count); return 0; @@ -3319,26 +3348,21 @@ static int bnxt_re_process_raw_qp_pkt_rx(struct bnxt_re_qp *gsi_qp, struct bnxt_re_dev *rdev = gsi_qp->rdev; struct bnxt_re_sqp_entries *sqp_entry = NULL; struct bnxt_re_qp *gsi_sqp = rdev->gsi_ctx.gsi_sqp; + dma_addr_t shrq_hdr_buf_map; + struct ib_sge s_sge[2] = {}; + struct ib_sge r_sge[2] = {}; struct bnxt_re_ah *gsi_sah; + struct ib_recv_wr rwr = {}; + dma_addr_t rq_hdr_buf_map; + struct ib_ud_wr udwr = {}; struct ib_send_wr *swr; - struct ib_ud_wr udwr; - struct ib_recv_wr rwr; + u32 skip_bytes = 0; int pkt_type = 0; - u32 tbl_idx; void *rq_hdr_buf; - dma_addr_t rq_hdr_buf_map; - dma_addr_t shrq_hdr_buf_map; u32 offset = 0; - u32 skip_bytes = 0; - struct ib_sge s_sge[2]; - struct ib_sge r_sge[2]; + u32 tbl_idx; int rc; - memset(&udwr, 0, sizeof(udwr)); - memset(&rwr, 0, sizeof(rwr)); - memset(&s_sge, 0, sizeof(s_sge)); - memset(&r_sge, 0, sizeof(r_sge)); - swr = &udwr.wr; tbl_idx = cqe->wr_id; @@ -3578,7 +3602,7 @@ static int send_phantom_wqe(struct bnxt_re_qp *qp) { struct bnxt_qplib_qp *lib_qp = &qp->qplib_qp; unsigned long flags; - int rc = 0; + int rc; spin_lock_irqsave(&qp->sq_lock, flags); @@ -3768,6 +3792,7 @@ struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags) struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_re_mr *mr; + u32 active_mrs; int rc; mr = kzalloc(sizeof(*mr), GFP_KERNEL); @@ -3795,7 +3820,9 @@ struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags) if (mr_access_flags & (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_READ | IB_ACCESS_REMOTE_ATOMIC)) mr->ib_mr.rkey = mr->ib_mr.lkey; - atomic_inc(&rdev->mr_count); + active_mrs = atomic_inc_return(&rdev->stats.res.mr_count); + if (active_mrs > rdev->stats.res.mr_watermark) + rdev->stats.res.mr_watermark = active_mrs; return &mr->ib_mr; @@ -3828,7 +3855,7 @@ int bnxt_re_dereg_mr(struct ib_mr *ib_mr, struct ib_udata *udata) ib_umem_release(mr->ib_umem); kfree(mr); - atomic_dec(&rdev->mr_count); + atomic_dec(&rdev->stats.res.mr_count); return rc; } @@ -3858,6 +3885,7 @@ struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type, struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_re_mr *mr = NULL; + u32 active_mrs; int rc; if (type != IB_MR_TYPE_MEM_REG) { @@ -3896,7 +3924,9 @@ struct ib_mr *bnxt_re_alloc_mr(struct ib_pd *ib_pd, enum ib_mr_type type, goto fail_mr; } - atomic_inc(&rdev->mr_count); + active_mrs = atomic_inc_return(&rdev->stats.res.mr_count); + if (active_mrs > rdev->stats.res.mr_watermark) + rdev->stats.res.mr_watermark = active_mrs; return &mr->ib_mr; fail_mr: @@ -3914,6 +3944,7 @@ struct ib_mw *bnxt_re_alloc_mw(struct ib_pd *ib_pd, enum ib_mw_type type, struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; struct bnxt_re_mw *mw; + u32 active_mws; int rc; mw = kzalloc(sizeof(*mw), GFP_KERNEL); @@ -3932,7 +3963,9 @@ struct ib_mw *bnxt_re_alloc_mw(struct ib_pd *ib_pd, enum ib_mw_type type, } mw->ib_mw.rkey = mw->qplib_mw.rkey; - atomic_inc(&rdev->mw_count); + active_mws = atomic_inc_return(&rdev->stats.res.mw_count); + if (active_mws > rdev->stats.res.mw_watermark) + rdev->stats.res.mw_watermark = active_mws; return &mw->ib_mw; fail: @@ -3953,21 +3986,19 @@ int bnxt_re_dealloc_mw(struct ib_mw *ib_mw) } kfree(mw); - atomic_dec(&rdev->mw_count); + atomic_dec(&rdev->stats.res.mw_count); return rc; } -/* uverbs */ -struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, - u64 virt_addr, int mr_access_flags, - struct ib_udata *udata) +static struct ib_mr *__bnxt_re_user_reg_mr(struct ib_pd *ib_pd, u64 length, u64 virt_addr, + int mr_access_flags, struct ib_umem *umem) { struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); struct bnxt_re_dev *rdev = pd->rdev; - struct bnxt_re_mr *mr; - struct ib_umem *umem; unsigned long page_size; + struct bnxt_re_mr *mr; int umem_pgs, rc; + u32 active_mrs; if (length > BNXT_RE_MAX_MR_SIZE) { ibdev_err(&rdev->ibdev, "MR Size: %lld > Max supported:%lld\n", @@ -3975,6 +4006,12 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, return ERR_PTR(-ENOMEM); } + page_size = ib_umem_find_best_pgsz(umem, BNXT_RE_PAGE_SIZE_SUPPORTED, virt_addr); + if (!page_size) { + ibdev_err(&rdev->ibdev, "umem page size unsupported!"); + return ERR_PTR(-EINVAL); + } + mr = kzalloc(sizeof(*mr), GFP_KERNEL); if (!mr) return ERR_PTR(-ENOMEM); @@ -3986,45 +4023,33 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, rc = bnxt_qplib_alloc_mrw(&rdev->qplib_res, &mr->qplib_mr); if (rc) { - ibdev_err(&rdev->ibdev, "Failed to allocate MR"); + ibdev_err(&rdev->ibdev, "Failed to allocate MR rc = %d", rc); + rc = -EIO; goto free_mr; } /* The fixed portion of the rkey is the same as the lkey */ mr->ib_mr.rkey = mr->qplib_mr.rkey; - - umem = ib_umem_get(&rdev->ibdev, start, length, mr_access_flags); - if (IS_ERR(umem)) { - ibdev_err(&rdev->ibdev, "Failed to get umem"); - rc = -EFAULT; - goto free_mrw; - } mr->ib_umem = umem; - mr->qplib_mr.va = virt_addr; - page_size = ib_umem_find_best_pgsz( - umem, BNXT_RE_PAGE_SIZE_SUPPORTED, virt_addr); - if (!page_size) { - ibdev_err(&rdev->ibdev, "umem page size unsupported!"); - rc = -EFAULT; - goto free_umem; - } mr->qplib_mr.total_size = length; umem_pgs = ib_umem_num_dma_blocks(umem, page_size); rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, umem, umem_pgs, page_size); if (rc) { - ibdev_err(&rdev->ibdev, "Failed to register user MR"); - goto free_umem; + ibdev_err(&rdev->ibdev, "Failed to register user MR - rc = %d\n", rc); + rc = -EIO; + goto free_mrw; } mr->ib_mr.lkey = mr->qplib_mr.lkey; mr->ib_mr.rkey = mr->qplib_mr.lkey; - atomic_inc(&rdev->mr_count); + active_mrs = atomic_inc_return(&rdev->stats.res.mr_count); + if (active_mrs > rdev->stats.res.mr_watermark) + rdev->stats.res.mr_watermark = active_mrs; return &mr->ib_mr; -free_umem: - ib_umem_release(umem); + free_mrw: bnxt_qplib_free_mrw(&rdev->qplib_res, &mr->qplib_mr); free_mr: @@ -4032,6 +4057,48 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, return ERR_PTR(rc); } +struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, + u64 virt_addr, int mr_access_flags, + struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct ib_umem *umem; + struct ib_mr *ib_mr; + + umem = ib_umem_get(&rdev->ibdev, start, length, mr_access_flags); + if (IS_ERR(umem)) + return ERR_CAST(umem); + + ib_mr = __bnxt_re_user_reg_mr(ib_pd, length, virt_addr, mr_access_flags, umem); + if (IS_ERR(ib_mr)) + ib_umem_release(umem); + return ib_mr; +} + +struct ib_mr *bnxt_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start, + u64 length, u64 virt_addr, int fd, + int mr_access_flags, struct ib_udata *udata) +{ + struct bnxt_re_pd *pd = container_of(ib_pd, struct bnxt_re_pd, ib_pd); + struct bnxt_re_dev *rdev = pd->rdev; + struct ib_umem_dmabuf *umem_dmabuf; + struct ib_umem *umem; + struct ib_mr *ib_mr; + + umem_dmabuf = ib_umem_dmabuf_get_pinned(&rdev->ibdev, start, length, + fd, mr_access_flags); + if (IS_ERR(umem_dmabuf)) + return ERR_CAST(umem_dmabuf); + + umem = &umem_dmabuf->umem; + + ib_mr = __bnxt_re_user_reg_mr(ib_pd, length, virt_addr, mr_access_flags, umem); + if (IS_ERR(ib_mr)) + ib_umem_release(umem); + return ib_mr; +} + int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) { struct ib_device *ibdev = ctx->device; @@ -4087,6 +4154,8 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata) goto cfail; } uctx->shpage_mmap = &entry->rdma_entry; + if (rdev->pacing.dbr_pacing) + resp.comp_mask |= BNXT_RE_UCNTX_CMASK_DBR_PACING_ENABLED; rc = ib_copy_to_udata(udata, &resp, min(udata->outlen, sizeof(resp))); if (rc) { @@ -4159,6 +4228,19 @@ int bnxt_re_mmap(struct ib_ucontext *ib_uctx, struct vm_area_struct *vma) case BNXT_RE_MMAP_SH_PAGE: ret = vm_insert_page(vma, vma->vm_start, virt_to_page(uctx->shpg)); break; + case BNXT_RE_MMAP_DBR_BAR: + pfn = bnxt_entry->mem_offset >> PAGE_SHIFT; + ret = rdma_user_mmap_io(ib_uctx, vma, pfn, PAGE_SIZE, + pgprot_noncached(vma->vm_page_prot), + rdma_entry); + break; + case BNXT_RE_MMAP_DBR_PAGE: + /* Driver doesn't expect write access for user space */ + if (vma->vm_flags & VM_WRITE) + return -EFAULT; + ret = vm_insert_page(vma, vma->vm_start, + virt_to_page((void *)bnxt_entry->mem_offset)); + break; default: ret = -EINVAL; break; @@ -4178,6 +4260,15 @@ void bnxt_re_mmap_free(struct rdma_user_mmap_entry *rdma_entry) kfree(bnxt_entry); } +static int UVERBS_HANDLER(BNXT_RE_METHOD_NOTIFY_DRV)(struct uverbs_attr_bundle *attrs) +{ + struct bnxt_re_ucontext *uctx; + + uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); + bnxt_re_pacing_alert(uctx->rdev); + return 0; +} + static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle *attrs) { struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, BNXT_RE_ALLOC_PAGE_HANDLE); @@ -4190,7 +4281,7 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * u64 mmap_offset; u32 length; u32 dpi; - u64 dbr; + u64 addr; int err; uctx = container_of(ib_uverbs_get_ucontext(attrs), struct bnxt_re_ucontext, ib_uctx); @@ -4212,19 +4303,30 @@ static int UVERBS_HANDLER(BNXT_RE_METHOD_ALLOC_PAGE)(struct uverbs_attr_bundle * return -ENOMEM; length = PAGE_SIZE; dpi = uctx->wcdpi.dpi; - dbr = (u64)uctx->wcdpi.umdbr; + addr = (u64)uctx->wcdpi.umdbr; mmap_flag = BNXT_RE_MMAP_WC_DB; } else { return -EINVAL; } break; + case BNXT_RE_ALLOC_DBR_BAR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_bar_addr; + mmap_flag = BNXT_RE_MMAP_DBR_BAR; + break; + + case BNXT_RE_ALLOC_DBR_PAGE: + length = PAGE_SIZE; + addr = (u64)rdev->pacing.dbr_page; + mmap_flag = BNXT_RE_MMAP_DBR_PAGE; + break; default: return -EOPNOTSUPP; } - entry = bnxt_re_mmap_entry_insert(uctx, dbr, mmap_flag, &mmap_offset); + entry = bnxt_re_mmap_entry_insert(uctx, addr, mmap_flag, &mmap_offset); if (!entry) return -ENOMEM; @@ -4264,6 +4366,9 @@ static int alloc_page_obj_cleanup(struct ib_uobject *uobject, uctx->wcdpi.dbr = NULL; } break; + case BNXT_RE_MMAP_DBR_BAR: + case BNXT_RE_MMAP_DBR_PAGE: + break; default: goto exit; } @@ -4301,7 +4406,13 @@ DECLARE_UVERBS_NAMED_OBJECT(BNXT_RE_OBJECT_ALLOC_PAGE, &UVERBS_METHOD(BNXT_RE_METHOD_ALLOC_PAGE), &UVERBS_METHOD(BNXT_RE_METHOD_DESTROY_PAGE)); +DECLARE_UVERBS_NAMED_METHOD(BNXT_RE_METHOD_NOTIFY_DRV); + +DECLARE_UVERBS_GLOBAL_METHODS(BNXT_RE_OBJECT_NOTIFY_DRV, + &UVERBS_METHOD(BNXT_RE_METHOD_NOTIFY_DRV)); + const struct uapi_definition bnxt_re_uapi_defs[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_ALLOC_PAGE), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED(BNXT_RE_OBJECT_NOTIFY_DRV), {} }; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 32d9e9d09791ef48d56e5643747d9e733d2f57d5..84715b7e7a4e4f92938713ea1ba1cf29f059ff58 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -146,6 +146,8 @@ enum bnxt_re_mmap_flag { BNXT_RE_MMAP_SH_PAGE, BNXT_RE_MMAP_UC_DB, BNXT_RE_MMAP_WC_DB, + BNXT_RE_MMAP_DBR_PAGE, + BNXT_RE_MMAP_DBR_BAR, }; struct bnxt_re_user_mmap_entry { @@ -227,6 +229,10 @@ int bnxt_re_dealloc_mw(struct ib_mw *mw); struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt_addr, int mr_access_flags, struct ib_udata *udata); +struct ib_mr *bnxt_re_reg_user_mr_dmabuf(struct ib_pd *ib_pd, u64 start, + u64 length, u64 virt_addr, + int fd, int mr_access_flags, + struct ib_udata *udata); int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata); void bnxt_re_dealloc_ucontext(struct ib_ucontext *context); int bnxt_re_mmap(struct ib_ucontext *context, struct vm_area_struct *vma); diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 63e98e2d3596297bfcd16e206c853a1df4875bd8..c9066aade4125b18eba5d7f0cccd4cdacb294b2e 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -360,7 +360,7 @@ static struct bnxt_ulp_ops bnxt_re_ulp_ops = { static int bnxt_re_register_netdev(struct bnxt_re_dev *rdev) { struct bnxt_en_dev *en_dev; - int rc = 0; + int rc; en_dev = rdev->en_dev; @@ -395,10 +395,9 @@ static int bnxt_re_hwrm_qcfg(struct bnxt_re_dev *rdev, u32 *db_len, struct bnxt_en_dev *en_dev = rdev->en_dev; struct hwrm_func_qcfg_output resp = {0}; struct hwrm_func_qcfg_input req = {0}; - struct bnxt_fw_msg fw_msg; + struct bnxt_fw_msg fw_msg = {}; int rc; - memset(&fw_msg, 0, sizeof(fw_msg)); bnxt_re_init_hwrm_hdr((void *)&req, HWRM_FUNC_QCFG); req.fid = cpu_to_le16(0xffff); bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, @@ -432,9 +431,219 @@ int bnxt_re_hwrm_qcaps(struct bnxt_re_dev *rdev) return rc; cctx->modes.db_push = le32_to_cpu(resp.flags) & FUNC_QCAPS_RESP_FLAGS_WCB_PUSH_MODE; + cctx->modes.dbr_pacing = + le32_to_cpu(resp.flags_ext2) & + FUNC_QCAPS_RESP_FLAGS_EXT2_DBR_PACING_EXT_SUPPORTED; + return 0; +} + +static int bnxt_re_hwrm_dbr_pacing_qcfg(struct bnxt_re_dev *rdev) +{ + struct hwrm_func_dbr_pacing_qcfg_output resp = {}; + struct hwrm_func_dbr_pacing_qcfg_input req = {}; + struct bnxt_en_dev *en_dev = rdev->en_dev; + struct bnxt_qplib_chip_ctx *cctx; + struct bnxt_fw_msg fw_msg = {}; + int rc; + + cctx = rdev->chip_ctx; + bnxt_re_init_hwrm_hdr((void *)&req, HWRM_FUNC_DBR_PACING_QCFG); + bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, + sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); + rc = bnxt_send_msg(en_dev, &fw_msg); + if (rc) + return rc; + + if ((le32_to_cpu(resp.dbr_stat_db_fifo_reg) & + FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_MASK) == + FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_GRC) + cctx->dbr_stat_db_fifo = + le32_to_cpu(resp.dbr_stat_db_fifo_reg) & + ~FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_MASK; + return 0; +} + +/* Update the pacing tunable parameters to the default values */ +static void bnxt_re_set_default_pacing_data(struct bnxt_re_dev *rdev) +{ + struct bnxt_qplib_db_pacing_data *pacing_data = rdev->qplib_res.pacing_data; + + pacing_data->do_pacing = rdev->pacing.dbr_def_do_pacing; + pacing_data->pacing_th = rdev->pacing.pacing_algo_th; + pacing_data->alarm_th = + pacing_data->pacing_th * BNXT_RE_PACING_ALARM_TH_MULTIPLE; +} + +static void __wait_for_fifo_occupancy_below_th(struct bnxt_re_dev *rdev) +{ + u32 read_val, fifo_occup; + + /* loop shouldn't run infintely as the occupancy usually goes + * below pacing algo threshold as soon as pacing kicks in. + */ + while (1) { + read_val = readl(rdev->en_dev->bar0 + rdev->pacing.dbr_db_fifo_reg_off); + fifo_occup = BNXT_RE_MAX_FIFO_DEPTH - + ((read_val & BNXT_RE_DB_FIFO_ROOM_MASK) >> + BNXT_RE_DB_FIFO_ROOM_SHIFT); + /* Fifo occupancy cannot be greater the MAX FIFO depth */ + if (fifo_occup > BNXT_RE_MAX_FIFO_DEPTH) + break; + + if (fifo_occup < rdev->qplib_res.pacing_data->pacing_th) + break; + } +} + +static void bnxt_re_db_fifo_check(struct work_struct *work) +{ + struct bnxt_re_dev *rdev = container_of(work, struct bnxt_re_dev, + dbq_fifo_check_work); + struct bnxt_qplib_db_pacing_data *pacing_data; + u32 pacing_save; + + if (!mutex_trylock(&rdev->pacing.dbq_lock)) + return; + pacing_data = rdev->qplib_res.pacing_data; + pacing_save = rdev->pacing.do_pacing_save; + __wait_for_fifo_occupancy_below_th(rdev); + cancel_delayed_work_sync(&rdev->dbq_pacing_work); + if (pacing_save > rdev->pacing.dbr_def_do_pacing) { + /* Double the do_pacing value during the congestion */ + pacing_save = pacing_save << 1; + } else { + /* + * when a new congestion is detected increase the do_pacing + * by 8 times. And also increase the pacing_th by 4 times. The + * reason to increase pacing_th is to give more space for the + * queue to oscillate down without getting empty, but also more + * room for the queue to increase without causing another alarm. + */ + pacing_save = pacing_save << 3; + pacing_data->pacing_th = rdev->pacing.pacing_algo_th * 4; + } + + if (pacing_save > BNXT_RE_MAX_DBR_DO_PACING) + pacing_save = BNXT_RE_MAX_DBR_DO_PACING; + + pacing_data->do_pacing = pacing_save; + rdev->pacing.do_pacing_save = pacing_data->do_pacing; + pacing_data->alarm_th = + pacing_data->pacing_th * BNXT_RE_PACING_ALARM_TH_MULTIPLE; + schedule_delayed_work(&rdev->dbq_pacing_work, + msecs_to_jiffies(rdev->pacing.dbq_pacing_time)); + rdev->stats.pacing.alerts++; + mutex_unlock(&rdev->pacing.dbq_lock); +} + +static void bnxt_re_pacing_timer_exp(struct work_struct *work) +{ + struct bnxt_re_dev *rdev = container_of(work, struct bnxt_re_dev, + dbq_pacing_work.work); + struct bnxt_qplib_db_pacing_data *pacing_data; + u32 read_val, fifo_occup; + + if (!mutex_trylock(&rdev->pacing.dbq_lock)) + return; + + pacing_data = rdev->qplib_res.pacing_data; + read_val = readl(rdev->en_dev->bar0 + rdev->pacing.dbr_db_fifo_reg_off); + fifo_occup = BNXT_RE_MAX_FIFO_DEPTH - + ((read_val & BNXT_RE_DB_FIFO_ROOM_MASK) >> + BNXT_RE_DB_FIFO_ROOM_SHIFT); + + if (fifo_occup > pacing_data->pacing_th) + goto restart_timer; + + /* + * Instead of immediately going back to the default do_pacing + * reduce it by 1/8 times and restart the timer. + */ + pacing_data->do_pacing = pacing_data->do_pacing - (pacing_data->do_pacing >> 3); + pacing_data->do_pacing = max_t(u32, rdev->pacing.dbr_def_do_pacing, pacing_data->do_pacing); + if (pacing_data->do_pacing <= rdev->pacing.dbr_def_do_pacing) { + bnxt_re_set_default_pacing_data(rdev); + rdev->stats.pacing.complete++; + goto dbq_unlock; + } + +restart_timer: + schedule_delayed_work(&rdev->dbq_pacing_work, + msecs_to_jiffies(rdev->pacing.dbq_pacing_time)); + rdev->stats.pacing.resched++; +dbq_unlock: + rdev->pacing.do_pacing_save = pacing_data->do_pacing; + mutex_unlock(&rdev->pacing.dbq_lock); +} + +void bnxt_re_pacing_alert(struct bnxt_re_dev *rdev) +{ + struct bnxt_qplib_db_pacing_data *pacing_data; + + if (!rdev->pacing.dbr_pacing) + return; + mutex_lock(&rdev->pacing.dbq_lock); + pacing_data = rdev->qplib_res.pacing_data; + + /* + * Increase the alarm_th to max so that other user lib instances do not + * keep alerting the driver. + */ + pacing_data->alarm_th = BNXT_RE_MAX_FIFO_DEPTH; + pacing_data->do_pacing = BNXT_RE_MAX_DBR_DO_PACING; + cancel_work_sync(&rdev->dbq_fifo_check_work); + schedule_work(&rdev->dbq_fifo_check_work); + mutex_unlock(&rdev->pacing.dbq_lock); +} + +static int bnxt_re_initialize_dbr_pacing(struct bnxt_re_dev *rdev) +{ + if (bnxt_re_hwrm_dbr_pacing_qcfg(rdev)) + return -EIO; + + /* Allocate a page for app use */ + rdev->pacing.dbr_page = (void *)__get_free_page(GFP_KERNEL); + if (!rdev->pacing.dbr_page) + return -ENOMEM; + + memset((u8 *)rdev->pacing.dbr_page, 0, PAGE_SIZE); + rdev->qplib_res.pacing_data = (struct bnxt_qplib_db_pacing_data *)rdev->pacing.dbr_page; + + /* MAP HW window 2 for reading db fifo depth */ + writel(rdev->chip_ctx->dbr_stat_db_fifo & BNXT_GRC_BASE_MASK, + rdev->en_dev->bar0 + BNXT_GRCPF_REG_WINDOW_BASE_OUT + 4); + rdev->pacing.dbr_db_fifo_reg_off = + (rdev->chip_ctx->dbr_stat_db_fifo & BNXT_GRC_OFFSET_MASK) + + BNXT_RE_GRC_FIFO_REG_BASE; + rdev->pacing.dbr_bar_addr = + pci_resource_start(rdev->qplib_res.pdev, 0) + rdev->pacing.dbr_db_fifo_reg_off; + + rdev->pacing.pacing_algo_th = BNXT_RE_PACING_ALGO_THRESHOLD; + rdev->pacing.dbq_pacing_time = BNXT_RE_DBR_PACING_TIME; + rdev->pacing.dbr_def_do_pacing = BNXT_RE_DBR_DO_PACING_NO_CONGESTION; + rdev->pacing.do_pacing_save = rdev->pacing.dbr_def_do_pacing; + rdev->qplib_res.pacing_data->fifo_max_depth = BNXT_RE_MAX_FIFO_DEPTH; + rdev->qplib_res.pacing_data->fifo_room_mask = BNXT_RE_DB_FIFO_ROOM_MASK; + rdev->qplib_res.pacing_data->fifo_room_shift = BNXT_RE_DB_FIFO_ROOM_SHIFT; + rdev->qplib_res.pacing_data->grc_reg_offset = rdev->pacing.dbr_db_fifo_reg_off; + bnxt_re_set_default_pacing_data(rdev); + /* Initialize worker for DBR Pacing */ + INIT_WORK(&rdev->dbq_fifo_check_work, bnxt_re_db_fifo_check); + INIT_DELAYED_WORK(&rdev->dbq_pacing_work, bnxt_re_pacing_timer_exp); return 0; } +static void bnxt_re_deinitialize_dbr_pacing(struct bnxt_re_dev *rdev) +{ + cancel_work_sync(&rdev->dbq_fifo_check_work); + cancel_delayed_work_sync(&rdev->dbq_pacing_work); + if (rdev->pacing.dbr_page) + free_page((u64)rdev->pacing.dbr_page); + + rdev->pacing.dbr_page = NULL; + rdev->pacing.dbr_pacing = false; +} + static int bnxt_re_net_ring_free(struct bnxt_re_dev *rdev, u16 fw_ring_id, int type) { @@ -652,6 +861,7 @@ static const struct ib_device_ops bnxt_re_dev_ops = { .query_qp = bnxt_re_query_qp, .query_srq = bnxt_re_query_srq, .reg_user_mr = bnxt_re_reg_user_mr, + .reg_user_mr_dmabuf = bnxt_re_reg_user_mr_dmabuf, .req_notify_cq = bnxt_re_req_notify_cq, .resize_cq = bnxt_re_resize_cq, INIT_RDMA_OBJ_SIZE(ib_ah, bnxt_re_ah, ib_ah), @@ -711,13 +921,14 @@ static struct bnxt_re_dev *bnxt_re_dev_add(struct bnxt_aux_priv *aux_priv, rdev->id = rdev->en_dev->pdev->devfn; INIT_LIST_HEAD(&rdev->qp_list); mutex_init(&rdev->qp_lock); - atomic_set(&rdev->qp_count, 0); - atomic_set(&rdev->cq_count, 0); - atomic_set(&rdev->srq_count, 0); - atomic_set(&rdev->mr_count, 0); - atomic_set(&rdev->mw_count, 0); - atomic_set(&rdev->ah_count, 0); - atomic_set(&rdev->pd_count, 0); + mutex_init(&rdev->pacing.dbq_lock); + atomic_set(&rdev->stats.res.qp_count, 0); + atomic_set(&rdev->stats.res.cq_count, 0); + atomic_set(&rdev->stats.res.srq_count, 0); + atomic_set(&rdev->stats.res.mr_count, 0); + atomic_set(&rdev->stats.res.mw_count, 0); + atomic_set(&rdev->stats.res.ah_count, 0); + atomic_set(&rdev->stats.res.pd_count, 0); rdev->cosq[0] = 0xFFFF; rdev->cosq[1] = 0xFFFF; @@ -759,7 +970,7 @@ static int bnxt_re_handle_unaffi_async_event(struct creq_func_event static int bnxt_re_handle_qp_async_event(struct creq_qp_event *qp_event, struct bnxt_re_qp *qp) { - struct ib_event event; + struct ib_event event = {}; unsigned int flags; if (qp->qplib_qp.state == CMDQ_MODIFY_QP_NEW_STATE_ERR && @@ -769,7 +980,6 @@ static int bnxt_re_handle_qp_async_event(struct creq_qp_event *qp_event, bnxt_re_unlock_cqs(qp, flags); } - memset(&event, 0, sizeof(event)); if (qp->qplib_qp.srq) { event.device = &qp->rdev->ibdev; event.element.qp = &qp->ib_qp; @@ -937,13 +1147,12 @@ static int bnxt_re_alloc_res(struct bnxt_re_dev *rdev) { struct bnxt_re_ring_attr rattr = {}; int num_vec_created = 0; - int rc = 0, i; + int rc, i; u8 type; /* Configure and allocate resources for qplib */ rdev->qplib_res.rcfw = &rdev->rcfw; - rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr, - rdev->is_virtfn); + rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr); if (rc) goto fail; @@ -1090,11 +1299,10 @@ static u32 bnxt_re_get_priority_mask(struct bnxt_re_dev *rdev) { u32 prio_map = 0, tmp_map = 0; struct net_device *netdev; - struct dcb_app app; + struct dcb_app app = {}; netdev = rdev->netdev; - memset(&app, 0, sizeof(app)); app.selector = IEEE_8021QAZ_APP_SEL_ETHERTYPE; app.protocol = ETH_P_IBOE; tmp_map = dcb_ieee_getapp_mask(netdev, &app); @@ -1123,8 +1331,7 @@ static int bnxt_re_setup_qos(struct bnxt_re_dev *rdev) */ if ((prio_map == 0 && rdev->qplib_res.prio) || (prio_map != 0 && !rdev->qplib_res.prio)) { - rdev->qplib_res.prio = prio_map ? true : false; - + rdev->qplib_res.prio = prio_map; bnxt_re_update_gid(rdev); } @@ -1138,7 +1345,7 @@ static void bnxt_re_query_hwrm_intf_version(struct bnxt_re_dev *rdev) struct hwrm_ver_get_input req = {}; struct bnxt_qplib_chip_ctx *cctx; struct bnxt_fw_msg fw_msg = {}; - int rc = 0; + int rc; bnxt_re_init_hwrm_hdr((void *)&req, HWRM_VER_GET); req.hwrm_intf_maj = HWRM_VERSION_MAJOR; @@ -1168,7 +1375,7 @@ static void bnxt_re_query_hwrm_intf_version(struct bnxt_re_dev *rdev) static int bnxt_re_ib_init(struct bnxt_re_dev *rdev) { - int rc = 0; + int rc; u32 event; /* Register ib dev */ @@ -1214,8 +1421,11 @@ static void bnxt_re_dev_uninit(struct bnxt_re_dev *rdev) bnxt_re_net_ring_free(rdev, rdev->rcfw.creq.ring_id, type); bnxt_qplib_free_rcfw_channel(&rdev->rcfw); } - if (test_and_clear_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags)) - rdev->num_msix = 0; + + rdev->num_msix = 0; + + if (rdev->pacing.dbr_pacing) + bnxt_re_deinitialize_dbr_pacing(rdev); bnxt_re_destroy_chip_ctx(rdev); if (test_and_clear_bit(BNXT_RE_FLAG_NETDEV_REGISTERED, &rdev->flags)) @@ -1234,15 +1444,14 @@ static void bnxt_re_worker(struct work_struct *work) static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) { + struct bnxt_re_ring_attr rattr = {}; struct bnxt_qplib_creq_ctx *creq; - struct bnxt_re_ring_attr rattr; u32 db_offt; int vid; u8 type; int rc; /* Registered a new RoCE device instance to netdev */ - memset(&rattr, 0, sizeof(rattr)); rc = bnxt_re_register_netdev(rdev); if (rc) { ibdev_err(&rdev->ibdev, @@ -1271,7 +1480,6 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) ibdev_dbg(&rdev->ibdev, "Got %d MSI-X vectors\n", rdev->en_dev->ulp_tbl->msix_requested); rdev->num_msix = rdev->en_dev->ulp_tbl->msix_requested; - set_bit(BNXT_RE_FLAG_GOT_MSIX, &rdev->flags); bnxt_re_query_hwrm_intf_version(rdev); @@ -1311,8 +1519,17 @@ static int bnxt_re_dev_init(struct bnxt_re_dev *rdev, u8 wqe_mode) goto free_ring; } - rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr, - rdev->is_virtfn); + if (bnxt_qplib_dbr_pacing_en(rdev->chip_ctx)) { + rc = bnxt_re_initialize_dbr_pacing(rdev); + if (!rc) { + rdev->pacing.dbr_pacing = true; + } else { + ibdev_err(&rdev->ibdev, + "DBR pacing disabled with error : %d\n", rc); + rdev->pacing.dbr_pacing = false; + } + } + rc = bnxt_qplib_get_dev_attr(&rdev->rcfw, &rdev->dev_attr); if (rc) goto disable_rcfw; @@ -1400,7 +1617,7 @@ static int bnxt_re_add_device(struct auxiliary_device *adev, u8 wqe_mode) container_of(adev, struct bnxt_aux_priv, aux_dev); struct bnxt_en_dev *en_dev; struct bnxt_re_dev *rdev; - int rc = 0; + int rc; /* en_dev should never be NULL as long as adev and aux_dev are valid. */ en_dev = aux_priv->edev; @@ -1646,7 +1863,7 @@ static struct auxiliary_driver bnxt_re_driver = { static int __init bnxt_re_mod_init(void) { - int rc = 0; + int rc; pr_info("%s: %s", ROCE_DRV_MODULE_NAME, version); rc = auxiliary_driver_register(&bnxt_re_driver); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index a42555623aedfe7eae7c47368e55e086e1f2e525..abbabea7f5fa38ef56fa27e7f991334403a711d0 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -535,7 +535,7 @@ int bnxt_qplib_enable_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq, cqn_handler_t cqn_handler, srqn_handler_t srqn_handler) { - int rc = -1; + int rc; nq->pdev = pdev; nq->cqn_handler = cqn_handler; @@ -727,27 +727,30 @@ int bnxt_qplib_query_srq(struct bnxt_qplib_res *res, struct bnxt_qplib_rcfw *rcfw = res->rcfw; struct creq_query_srq_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; struct creq_query_srq_resp_sb *sb; struct cmdq_query_srq req = {}; - int rc = 0; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_SRQ, sizeof(req)); /* Configure the request */ - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; req.srq_cid = cpu_to_le32(srq->id); - sb = sbuf->sb; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + sb = sbuf.sb; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); srq->threshold = le16_to_cpu(sb->srq_limit); - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } @@ -1365,24 +1368,26 @@ int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) struct bnxt_qplib_rcfw *rcfw = res->rcfw; struct creq_query_qp_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; struct creq_query_qp_resp_sb *sb; struct cmdq_query_qp req = {}; u32 temp32[4]; - int i, rc = 0; + int i, rc; + + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) + return -ENOMEM; + sb = sbuf.sb; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_QP, sizeof(req)); - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) - return -ENOMEM; - sb = sbuf->sb; - req.qp_cid = cpu_to_le32(qp->id); - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) @@ -1391,8 +1396,7 @@ int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) qp->state = sb->en_sqd_async_notify_state & CREQ_QUERY_QP_RESP_SB_STATE_MASK; qp->en_sqd_async_notify = sb->en_sqd_async_notify_state & - CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY ? - true : false; + CREQ_QUERY_QP_RESP_SB_EN_SQD_ASYNC_NOTIFY; qp->access = sb->access; qp->pkey_index = le16_to_cpu(sb->pkey); qp->qkey = le32_to_cpu(sb->qkey); @@ -1442,7 +1446,8 @@ int bnxt_qplib_query_qp(struct bnxt_qplib_res *res, struct bnxt_qplib_qp *qp) memcpy(qp->smac, sb->src_mac, 6); qp->vlan_id = le16_to_cpu(sb->vlan_pcp_vlan_dei_vlan_id); bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c index bc3aea4592b9df35887903722494d38088e0aaf1..e47b4ca64d33ef274355215056da15a35360afc7 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_rcfw.c @@ -55,7 +55,7 @@ static void bnxt_qplib_service_creq(struct tasklet_struct *t); /** * bnxt_qplib_map_rc - map return type based on opcode - * @opcode - roce slow path opcode + * @opcode: roce slow path opcode * * case #1 * Firmware initiated error recovery is a safe state machine and @@ -98,8 +98,8 @@ static int bnxt_qplib_map_rc(u8 opcode) /** * bnxt_re_is_fw_stalled - Check firmware health - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * If firmware has not responded any rcfw command within * rcfw->max_timeout, consider firmware as stalled. @@ -133,8 +133,8 @@ static int bnxt_re_is_fw_stalled(struct bnxt_qplib_rcfw *rcfw, /** * __wait_for_resp - Don't hold the cpu context and wait for response - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * Wait for command completion in sleepable context. * @@ -179,8 +179,8 @@ static int __wait_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie) /** * __block_for_resp - hold the cpu context and wait for response - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * This function will hold the cpu (non-sleepable context) and * wait for command completion. Maximum holding interval is 8 second. @@ -216,8 +216,8 @@ static int __block_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie) }; /* __send_message_no_waiter - get cookie and post the message. - * @rcfw - rcfw channel instance of rdev - * @msg - qplib message internal + * @rcfw: rcfw channel instance of rdev + * @msg: qplib message internal * * This function will just post and don't bother about completion. * Current design of this function is - @@ -335,7 +335,8 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, cpu_to_le64(sbuf->dma_addr)); __set_cmdq_base_resp_size(msg->req, msg->req_sz, ALIGN(sbuf->size, - BNXT_QPLIB_CMDQE_UNITS)); + BNXT_QPLIB_CMDQE_UNITS) / + BNXT_QPLIB_CMDQE_UNITS); } preq = (u8 *)msg->req; @@ -373,8 +374,8 @@ static int __send_message(struct bnxt_qplib_rcfw *rcfw, /** * __poll_for_resp - self poll completion for rcfw command - * @rcfw - rcfw channel instance of rdev - * @cookie - cookie to track the command + * @rcfw: rcfw channel instance of rdev + * @cookie: cookie to track the command * * It works same as __wait_for_resp except this function will * do self polling in sort interval since interrupt is disabled. @@ -470,8 +471,8 @@ static void __destroy_timedout_ah(struct bnxt_qplib_rcfw *rcfw, /** * __bnxt_qplib_rcfw_send_message - qplib interface to send * and complete rcfw command. - * @rcfw - rcfw channel instance of rdev - * @msg - qplib message internal + * @rcfw: rcfw channel instance of rdev + * @msg: qplib message internal * * This function does not account shadow queue depth. It will send * all the command unconditionally as long as send queue is not full. @@ -487,7 +488,7 @@ static int __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_crsqe *crsqe; unsigned long flags; u16 cookie; - int rc = 0; + int rc; u8 opcode; opcode = __get_cmdq_base_opcode(msg->req, msg->req_sz); @@ -533,8 +534,8 @@ static int __bnxt_qplib_rcfw_send_message(struct bnxt_qplib_rcfw *rcfw, /** * bnxt_qplib_rcfw_send_message - qplib interface to send * and complete rcfw command. - * @rcfw - rcfw channel instance of rdev - * @msg - qplib message internal + * @rcfw: rcfw channel instance of rdev + * @msg: qplib message internal * * Driver interact with Firmware through rcfw channel/slow path in two ways. * a. Blocking rcfw command send. In this path, driver cannot hold @@ -664,7 +665,6 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, blocked = cookie & RCFW_CMD_IS_BLOCKING; cookie &= RCFW_MAX_COOKIE_VALUE; crsqe = &rcfw->crsqe_tbl[cookie]; - crsqe->is_in_used = false; if (WARN_ONCE(test_bit(FIRMWARE_STALL_DETECTED, &rcfw->cmdq.flags), @@ -680,8 +680,14 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, atomic_dec(&rcfw->timeout_send); if (crsqe->is_waiter_alive) { - if (crsqe->resp) + if (crsqe->resp) { memcpy(crsqe->resp, qp_event, sizeof(*qp_event)); + /* Insert write memory barrier to ensure that + * response data is copied before clearing the + * flags + */ + smp_wmb(); + } if (!blocked) wait_cmds++; } @@ -693,6 +699,8 @@ static int bnxt_qplib_process_qp_event(struct bnxt_qplib_rcfw *rcfw, if (!is_waiter_alive) crsqe->resp = NULL; + crsqe->is_in_used = false; + hwq->cons += req_size; /* This is a case to handle below scenario - @@ -1195,34 +1203,3 @@ int bnxt_qplib_enable_rcfw_channel(struct bnxt_qplib_rcfw *rcfw, return 0; } - -struct bnxt_qplib_rcfw_sbuf *bnxt_qplib_rcfw_alloc_sbuf( - struct bnxt_qplib_rcfw *rcfw, - u32 size) -{ - struct bnxt_qplib_rcfw_sbuf *sbuf; - - sbuf = kzalloc(sizeof(*sbuf), GFP_KERNEL); - if (!sbuf) - return NULL; - - sbuf->size = size; - sbuf->sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf->size, - &sbuf->dma_addr, GFP_KERNEL); - if (!sbuf->sb) - goto bail; - - return sbuf; -bail: - kfree(sbuf); - return NULL; -} - -void bnxt_qplib_rcfw_free_sbuf(struct bnxt_qplib_rcfw *rcfw, - struct bnxt_qplib_rcfw_sbuf *sbuf) -{ - if (sbuf->sb) - dma_free_coherent(&rcfw->pdev->dev, sbuf->size, - sbuf->sb, sbuf->dma_addr); - kfree(sbuf); -} diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.c b/drivers/infiniband/hw/bnxt_re/qplib_res.c index 739d942761d16de6f9e187ddd98b0ced6612d9d6..157db6b7e1193711fa7c65c86f039a96c9033ffb 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c @@ -118,11 +118,11 @@ static int __alloc_pbl(struct bnxt_qplib_res *res, else pages = sginfo->npages; /* page ptr arrays */ - pbl->pg_arr = vmalloc(pages * sizeof(void *)); + pbl->pg_arr = vmalloc_array(pages, sizeof(void *)); if (!pbl->pg_arr) return -ENOMEM; - pbl->pg_map_arr = vmalloc(pages * sizeof(dma_addr_t)); + pbl->pg_map_arr = vmalloc_array(pages, sizeof(dma_addr_t)); if (!pbl->pg_map_arr) { vfree(pbl->pg_arr); pbl->pg_arr = NULL; @@ -385,7 +385,7 @@ static int bnxt_qplib_alloc_tqm_rings(struct bnxt_qplib_res *res, struct bnxt_qplib_hwq_attr hwq_attr = {}; struct bnxt_qplib_sg_info sginfo = {}; struct bnxt_qplib_tqm_ctx *tqmctx; - int rc = 0; + int rc; int i; tqmctx = &ctx->tqm_ctx; @@ -463,7 +463,7 @@ static void bnxt_qplib_map_tqm_pgtbl(struct bnxt_qplib_tqm_ctx *ctx) static int bnxt_qplib_setup_tqm_rings(struct bnxt_qplib_res *res, struct bnxt_qplib_ctx *ctx) { - int rc = 0; + int rc; rc = bnxt_qplib_alloc_tqm_rings(res, ctx); if (rc) @@ -501,7 +501,7 @@ int bnxt_qplib_alloc_ctx(struct bnxt_qplib_res *res, { struct bnxt_qplib_hwq_attr hwq_attr = {}; struct bnxt_qplib_sg_info sginfo = {}; - int rc = 0; + int rc; if (virt_fn || is_p5) goto stats_alloc; @@ -642,31 +642,44 @@ static void bnxt_qplib_init_sgid_tbl(struct bnxt_qplib_sgid_tbl *sgid_tbl, } /* PDs */ -int bnxt_qplib_alloc_pd(struct bnxt_qplib_pd_tbl *pdt, struct bnxt_qplib_pd *pd) +int bnxt_qplib_alloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd *pd) { + struct bnxt_qplib_pd_tbl *pdt = &res->pd_tbl; u32 bit_num; + int rc = 0; + mutex_lock(&res->pd_tbl_lock); bit_num = find_first_bit(pdt->tbl, pdt->max); - if (bit_num == pdt->max) - return -ENOMEM; + if (bit_num == pdt->max) { + rc = -ENOMEM; + goto exit; + } /* Found unused PD */ clear_bit(bit_num, pdt->tbl); pd->id = bit_num; - return 0; +exit: + mutex_unlock(&res->pd_tbl_lock); + return rc; } int bnxt_qplib_dealloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd_tbl *pdt, struct bnxt_qplib_pd *pd) { + int rc = 0; + + mutex_lock(&res->pd_tbl_lock); if (test_and_set_bit(pd->id, pdt->tbl)) { dev_warn(&res->pdev->dev, "Freeing an unused PD? pdn = %d\n", pd->id); - return -EINVAL; + rc = -EINVAL; + goto exit; } pd->id = 0; - return 0; +exit: + mutex_unlock(&res->pd_tbl_lock); + return rc; } static void bnxt_qplib_free_pd_tbl(struct bnxt_qplib_pd_tbl *pdt) @@ -691,6 +704,7 @@ static int bnxt_qplib_alloc_pd_tbl(struct bnxt_qplib_res *res, pdt->max = max; memset((u8 *)pdt->tbl, 0xFF, bytes); + mutex_init(&res->pd_tbl_lock); return 0; } @@ -877,7 +891,7 @@ int bnxt_qplib_alloc_res(struct bnxt_qplib_res *res, struct pci_dev *pdev, struct net_device *netdev, struct bnxt_qplib_dev_attr *dev_attr) { - int rc = 0; + int rc; res->pdev = pdev; res->netdev = netdev; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_res.h b/drivers/infiniband/hw/bnxt_re/qplib_res.h index d850a553821e398c474f1a89507313ca4e958394..5949f004f78561f5628629fbc61089a1080c8f3f 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_res.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_res.h @@ -48,6 +48,7 @@ extern const struct bnxt_qplib_gid bnxt_qplib_gid_zero; struct bnxt_qplib_drv_modes { u8 wqe_mode; bool db_push; + bool dbr_pacing; }; struct bnxt_qplib_chip_ctx { @@ -58,6 +59,17 @@ struct bnxt_qplib_chip_ctx { u16 hwrm_cmd_max_timeout; struct bnxt_qplib_drv_modes modes; u64 hwrm_intf_ver; + u32 dbr_stat_db_fifo; +}; + +struct bnxt_qplib_db_pacing_data { + u32 do_pacing; + u32 pacing_th; + u32 alarm_th; + u32 fifo_max_depth; + u32 fifo_room_mask; + u32 fifo_room_shift; + u32 grc_reg_offset; }; #define BNXT_QPLIB_DBR_PF_DB_OFFSET 0x10000 @@ -265,12 +277,15 @@ struct bnxt_qplib_res { struct net_device *netdev; struct bnxt_qplib_rcfw *rcfw; struct bnxt_qplib_pd_tbl pd_tbl; + /* To protect the pd table bit map */ + struct mutex pd_tbl_lock; struct bnxt_qplib_sgid_tbl sgid_tbl; struct bnxt_qplib_dpi_tbl dpi_tbl; /* To protect the dpi table bit map */ struct mutex dpi_tbl_lock; bool prio; bool is_vf; + struct bnxt_qplib_db_pacing_data *pacing_data; }; static inline bool bnxt_qplib_is_chip_gen_p5(struct bnxt_qplib_chip_ctx *cctx) @@ -355,7 +370,7 @@ void bnxt_qplib_free_hwq(struct bnxt_qplib_res *res, struct bnxt_qplib_hwq *hwq); int bnxt_qplib_alloc_init_hwq(struct bnxt_qplib_hwq *hwq, struct bnxt_qplib_hwq_attr *hwq_attr); -int bnxt_qplib_alloc_pd(struct bnxt_qplib_pd_tbl *pd_tbl, +int bnxt_qplib_alloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd *pd); int bnxt_qplib_dealloc_pd(struct bnxt_qplib_res *res, struct bnxt_qplib_pd_tbl *pd_tbl, @@ -467,4 +482,10 @@ static inline bool _is_ext_stats_supported(u16 dev_cap_flags) return dev_cap_flags & CREQ_QUERY_FUNC_RESP_SB_EXT_STATS; } + +static inline u8 bnxt_qplib_dbr_pacing_en(struct bnxt_qplib_chip_ctx *cctx) +{ + return cctx->modes.dbr_pacing; +} + #endif /* __BNXT_QPLIB_RES_H__ */ diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c index ab45f9d4bb02fecfb448136875a6addf0286a12d..a27b685151647700e23afdbe9d6599ea6e2da1bb 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -72,7 +72,7 @@ static void bnxt_qplib_query_version(struct bnxt_qplib_rcfw *rcfw, struct creq_query_version_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; struct cmdq_query_version req = {}; - int rc = 0; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_VERSION, @@ -89,31 +89,29 @@ static void bnxt_qplib_query_version(struct bnxt_qplib_rcfw *rcfw, } int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, - struct bnxt_qplib_dev_attr *attr, bool vf) + struct bnxt_qplib_dev_attr *attr) { struct creq_query_func_resp resp = {}; struct bnxt_qplib_cmdqmsg msg = {}; struct creq_query_func_resp_sb *sb; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; struct cmdq_query_func req = {}; u8 *tqm_alloc; - int i, rc = 0; + int i, rc; u32 temp; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_FUNC, sizeof(req)); - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) { - dev_err(&rcfw->pdev->dev, - "SP: QUERY_FUNC alloc side buffer failed\n"); + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - } - - sb = sbuf->sb; - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + sb = sbuf.sb; + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) @@ -121,9 +119,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, /* Extract the context from the side buffer */ attr->max_qp = le32_to_cpu(sb->max_qp); - /* max_qp value reported by FW for PF doesn't include the QP1 for PF */ - if (!vf) - attr->max_qp += 1; + /* max_qp value reported by FW doesn't include the QP1 */ + attr->max_qp += 1; attr->max_qp_rd_atom = sb->max_qp_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_rd_atom; @@ -175,7 +172,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, attr->is_atomic = bnxt_qplib_is_atomic_cap(rcfw); bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } @@ -186,7 +184,7 @@ int bnxt_qplib_set_func_resources(struct bnxt_qplib_res *res, struct creq_set_func_resources_resp resp = {}; struct cmdq_set_func_resources req = {}; struct bnxt_qplib_cmdqmsg msg = {}; - int rc = 0; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_SET_FUNC_RESOURCES, @@ -718,23 +716,22 @@ int bnxt_qplib_get_roce_stats(struct bnxt_qplib_rcfw *rcfw, struct creq_query_roce_stats_resp_sb *sb; struct cmdq_query_roce_stats req = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; - int rc = 0; + struct bnxt_qplib_rcfw_sbuf sbuf; + int rc; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_BASE_OPCODE_QUERY_ROCE_STATS, sizeof(req)); - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) { - dev_err(&rcfw->pdev->dev, - "SP: QUERY_ROCE_STATS alloc side buffer failed\n"); + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - } + sb = sbuf.sb; - sb = sbuf->sb; - req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) @@ -790,7 +787,8 @@ int bnxt_qplib_get_roce_stats(struct bnxt_qplib_rcfw *rcfw, } bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } @@ -801,49 +799,56 @@ int bnxt_qplib_qext_stat(struct bnxt_qplib_rcfw *rcfw, u32 fid, struct creq_query_roce_stats_ext_resp_sb *sb; struct cmdq_query_roce_stats_ext req = {}; struct bnxt_qplib_cmdqmsg msg = {}; - struct bnxt_qplib_rcfw_sbuf *sbuf; + struct bnxt_qplib_rcfw_sbuf sbuf; int rc; - sbuf = bnxt_qplib_rcfw_alloc_sbuf(rcfw, sizeof(*sb)); - if (!sbuf) { - dev_err(&rcfw->pdev->dev, - "SP: QUERY_ROCE_STATS_EXT alloc sb failed"); + sbuf.size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); + sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size, + &sbuf.dma_addr, GFP_KERNEL); + if (!sbuf.sb) return -ENOMEM; - } + sb = sbuf.sb; bnxt_qplib_rcfw_cmd_prep((struct cmdq_base *)&req, CMDQ_QUERY_ROCE_STATS_EXT_OPCODE_QUERY_ROCE_STATS, sizeof(req)); - req.resp_size = ALIGN(sizeof(*sb), BNXT_QPLIB_CMDQE_UNITS); - req.resp_addr = cpu_to_le64(sbuf->dma_addr); + req.resp_size = sbuf.size / BNXT_QPLIB_CMDQE_UNITS; + req.resp_addr = cpu_to_le64(sbuf.dma_addr); req.function_id = cpu_to_le32(fid); req.flags = cpu_to_le16(CMDQ_QUERY_ROCE_STATS_EXT_FLAGS_FUNCTION_ID); - bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, sbuf, sizeof(req), + bnxt_qplib_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req), sizeof(resp), 0); rc = bnxt_qplib_rcfw_send_message(rcfw, &msg); if (rc) goto bail; - sb = sbuf->sb; estat->tx_atomic_req = le64_to_cpu(sb->tx_atomic_req_pkts); estat->tx_read_req = le64_to_cpu(sb->tx_read_req_pkts); estat->tx_read_res = le64_to_cpu(sb->tx_read_res_pkts); estat->tx_write_req = le64_to_cpu(sb->tx_write_req_pkts); estat->tx_send_req = le64_to_cpu(sb->tx_send_req_pkts); + estat->tx_roce_pkts = le64_to_cpu(sb->tx_roce_pkts); + estat->tx_roce_bytes = le64_to_cpu(sb->tx_roce_bytes); estat->rx_atomic_req = le64_to_cpu(sb->rx_atomic_req_pkts); estat->rx_read_req = le64_to_cpu(sb->rx_read_req_pkts); estat->rx_read_res = le64_to_cpu(sb->rx_read_res_pkts); estat->rx_write_req = le64_to_cpu(sb->rx_write_req_pkts); estat->rx_send_req = le64_to_cpu(sb->rx_send_req_pkts); + estat->rx_roce_pkts = le64_to_cpu(sb->rx_roce_pkts); + estat->rx_roce_bytes = le64_to_cpu(sb->rx_roce_bytes); estat->rx_roce_good_pkts = le64_to_cpu(sb->rx_roce_good_pkts); estat->rx_roce_good_bytes = le64_to_cpu(sb->rx_roce_good_bytes); estat->rx_out_of_buffer = le64_to_cpu(sb->rx_out_of_buffer_pkts); estat->rx_out_of_sequence = le64_to_cpu(sb->rx_out_of_sequence_pkts); + estat->tx_cnp = le64_to_cpu(sb->tx_cnp_pkts); + estat->rx_cnp = le64_to_cpu(sb->rx_cnp_pkts); + estat->rx_ecn_marked = le64_to_cpu(sb->rx_ecn_marked_pkts); bail: - bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); + dma_free_coherent(&rcfw->pdev->dev, sbuf.size, + sbuf.sb, sbuf.dma_addr); return rc; } diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.h b/drivers/infiniband/hw/bnxt_re/qplib_sp.h index 264ef3cedc45b0e3e1b6847b03fc177e62a04505..d33c78b96217a8c76cd603d489328afd85937c69 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.h @@ -322,7 +322,7 @@ int bnxt_qplib_update_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, struct bnxt_qplib_gid *gid, u16 gid_idx, const u8 *smac); int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, - struct bnxt_qplib_dev_attr *attr, bool vf); + struct bnxt_qplib_dev_attr *attr); int bnxt_qplib_set_func_resources(struct bnxt_qplib_res *res, struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_ctx *ctx); diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index ced615b5ea096ed58d4198ceaf8b960428d98ecd..040ba2224f9ff67e508f5ecd443c96f8cb8f645c 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -1965,6 +1965,9 @@ static int send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) int win; skb = get_skb(NULL, sizeof(*req), GFP_KERNEL); + if (!skb) + return -ENOMEM; + req = __skb_put_zero(skb, sizeof(*req)); req->op_compl = htonl(WR_OP_V(FW_OFLD_CONNECTION_WR)); req->len16_pkd = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16))); diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index ffbd9a89981e748cd17cfc938f04c646886125bb..d16d8eaa141502f6793c99fd07eab2f0bd7812a8 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -2466,7 +2466,7 @@ int c4iw_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, init_attr->cap.max_send_sge = qhp->attr.sq_max_sges; init_attr->cap.max_recv_sge = qhp->attr.rq_max_sges; init_attr->cap.max_inline_data = T4_MAX_SEND_INLINE; - init_attr->sq_sig_type = qhp->sq_sig_all ? IB_SIGNAL_ALL_WR : 0; + init_attr->sq_sig_type = qhp->sq_sig_all ? IB_SIGNAL_ALL_WR : IB_SIGNAL_REQ_WR; return 0; } diff --git a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h index 4e93ef7f84ee8a5803e9e1638bb42ef8c13c7369..9c65bd27bae09ae4b49500ba7fa32b7d23c2601e 100644 --- a/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h +++ b/drivers/infiniband/hw/efa/efa_admin_cmds_defs.h @@ -66,6 +66,7 @@ enum efa_admin_get_stats_type { EFA_ADMIN_GET_STATS_TYPE_BASIC = 0, EFA_ADMIN_GET_STATS_TYPE_MESSAGES = 1, EFA_ADMIN_GET_STATS_TYPE_RDMA_READ = 2, + EFA_ADMIN_GET_STATS_TYPE_RDMA_WRITE = 3, }; enum efa_admin_get_stats_scope { @@ -570,6 +571,16 @@ struct efa_admin_rdma_read_stats { u64 read_resp_bytes; }; +struct efa_admin_rdma_write_stats { + u64 write_wrs; + + u64 write_bytes; + + u64 write_wr_err; + + u64 write_recv_bytes; +}; + struct efa_admin_acq_get_stats_resp { struct efa_admin_acq_common_desc acq_common_desc; @@ -579,6 +590,8 @@ struct efa_admin_acq_get_stats_resp { struct efa_admin_messages_stats messages_stats; struct efa_admin_rdma_read_stats rdma_read_stats; + + struct efa_admin_rdma_write_stats rdma_write_stats; } u; }; diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.c b/drivers/infiniband/hw/efa/efa_com_cmd.c index 8f8885e002ba4b9581be6625f744b6ffd6d0642f..576811885d59e4548835f0e2ed3555ea358e2f70 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.c +++ b/drivers/infiniband/hw/efa/efa_com_cmd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause /* - * Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All rights reserved. */ #include "efa_com.h" @@ -794,6 +794,12 @@ int efa_com_get_stats(struct efa_com_dev *edev, result->rdma_read_stats.read_wr_err = resp.u.rdma_read_stats.read_wr_err; result->rdma_read_stats.read_resp_bytes = resp.u.rdma_read_stats.read_resp_bytes; break; + case EFA_ADMIN_GET_STATS_TYPE_RDMA_WRITE: + result->rdma_write_stats.write_wrs = resp.u.rdma_write_stats.write_wrs; + result->rdma_write_stats.write_bytes = resp.u.rdma_write_stats.write_bytes; + result->rdma_write_stats.write_wr_err = resp.u.rdma_write_stats.write_wr_err; + result->rdma_write_stats.write_recv_bytes = resp.u.rdma_write_stats.write_recv_bytes; + break; } return 0; diff --git a/drivers/infiniband/hw/efa/efa_com_cmd.h b/drivers/infiniband/hw/efa/efa_com_cmd.h index 0898ad5bc340526a0f246ab2008c5b9587571f28..fc97f37bb39bb34d5edb38a12ed7bd8120a6f829 100644 --- a/drivers/infiniband/hw/efa/efa_com_cmd.h +++ b/drivers/infiniband/hw/efa/efa_com_cmd.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ /* - * Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All rights reserved. + * Copyright 2018-2023 Amazon.com, Inc. or its affiliates. All rights reserved. */ #ifndef _EFA_COM_CMD_H_ @@ -262,10 +262,18 @@ struct efa_com_rdma_read_stats { u64 read_resp_bytes; }; +struct efa_com_rdma_write_stats { + u64 write_wrs; + u64 write_bytes; + u64 write_wr_err; + u64 write_recv_bytes; +}; + union efa_com_get_stats_result { struct efa_com_basic_stats basic_stats; struct efa_com_messages_stats messages_stats; struct efa_com_rdma_read_stats rdma_read_stats; + struct efa_com_rdma_write_stats rdma_write_stats; }; int efa_com_create_qp(struct efa_com_dev *edev, diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c index 2a195c4b0f17de077a87b6ff7a8fed4e837f03ad..0f8ca99d0827642db5c08f3301a0f870f657635b 100644 --- a/drivers/infiniband/hw/efa/efa_verbs.c +++ b/drivers/infiniband/hw/efa/efa_verbs.c @@ -61,6 +61,10 @@ struct efa_user_mmap_entry { op(EFA_RDMA_READ_BYTES, "rdma_read_bytes") \ op(EFA_RDMA_READ_WR_ERR, "rdma_read_wr_err") \ op(EFA_RDMA_READ_RESP_BYTES, "rdma_read_resp_bytes") \ + op(EFA_RDMA_WRITE_WRS, "rdma_write_wrs") \ + op(EFA_RDMA_WRITE_BYTES, "rdma_write_bytes") \ + op(EFA_RDMA_WRITE_WR_ERR, "rdma_write_wr_err") \ + op(EFA_RDMA_WRITE_RECV_BYTES, "rdma_write_recv_bytes") \ #define EFA_STATS_ENUM(ename, name) ename, #define EFA_STATS_STR(ename, nam) \ @@ -449,12 +453,12 @@ int efa_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) ibdev_dbg(&dev->ibdev, "Destroy qp[%u]\n", ibqp->qp_num); - efa_qp_user_mmap_entries_remove(qp); - err = efa_destroy_qp_handle(dev, qp->qp_handle); if (err) return err; + efa_qp_user_mmap_entries_remove(qp); + if (qp->rq_cpu_addr) { ibdev_dbg(&dev->ibdev, "qp->cpu_addr[0x%p] freed: size[%lu], dma[%pad]\n", @@ -1013,8 +1017,8 @@ int efa_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) "Destroy cq[%d] virt[0x%p] freed: size[%lu], dma[%pad]\n", cq->cq_idx, cq->cpu_addr, cq->size, &cq->dma_addr); - efa_cq_user_mmap_entries_remove(cq); efa_destroy_cq_idx(dev, cq->cq_idx); + efa_cq_user_mmap_entries_remove(cq); if (cq->eq) { xa_erase(&dev->cqs_xa, cq->cq_idx); synchronize_irq(cq->eq->irq.irqn); @@ -2080,6 +2084,7 @@ static int efa_fill_port_stats(struct efa_dev *dev, struct rdma_hw_stats *stats, { struct efa_com_get_stats_params params = {}; union efa_com_get_stats_result result; + struct efa_com_rdma_write_stats *rws; struct efa_com_rdma_read_stats *rrs; struct efa_com_messages_stats *ms; struct efa_com_basic_stats *bs; @@ -2121,6 +2126,19 @@ static int efa_fill_port_stats(struct efa_dev *dev, struct rdma_hw_stats *stats, stats->value[EFA_RDMA_READ_WR_ERR] = rrs->read_wr_err; stats->value[EFA_RDMA_READ_RESP_BYTES] = rrs->read_resp_bytes; + if (EFA_DEV_CAP(dev, RDMA_WRITE)) { + params.type = EFA_ADMIN_GET_STATS_TYPE_RDMA_WRITE; + err = efa_com_get_stats(&dev->edev, ¶ms, &result); + if (err) + return err; + + rws = &result.rdma_write_stats; + stats->value[EFA_RDMA_WRITE_WRS] = rws->write_wrs; + stats->value[EFA_RDMA_WRITE_BYTES] = rws->write_bytes; + stats->value[EFA_RDMA_WRITE_WR_ERR] = rws->write_wr_err; + stats->value[EFA_RDMA_WRITE_RECV_BYTES] = rws->write_recv_bytes; + } + return ARRAY_SIZE(efa_port_stats_descs); } diff --git a/drivers/infiniband/hw/erdma/erdma_hw.h b/drivers/infiniband/hw/erdma/erdma_hw.h index a882b57aa118585351215678d7bc4e26f5d06f07..9d316fdc6f9a55a5055f793197117424800ccb01 100644 --- a/drivers/infiniband/hw/erdma/erdma_hw.h +++ b/drivers/infiniband/hw/erdma/erdma_hw.h @@ -228,7 +228,7 @@ struct erdma_cmdq_ext_db_req { /* create_cq cfg1 */ #define ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK GENMASK(31, 16) -#define ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK BIT(15) +#define ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK BIT(15) #define ERDMA_CMD_CREATE_CQ_MTT_DB_CFG_MASK BIT(11) #define ERDMA_CMD_CREATE_CQ_EQN_MASK GENMASK(9, 0) @@ -248,6 +248,7 @@ struct erdma_cmdq_create_cq_req { /* regmr/deregmr cfg0 */ #define ERDMA_CMD_MR_VALID_MASK BIT(31) +#define ERDMA_CMD_MR_VERSION_MASK GENMASK(30, 28) #define ERDMA_CMD_MR_KEY_MASK GENMASK(27, 20) #define ERDMA_CMD_MR_MPT_IDX_MASK GENMASK(19, 0) @@ -258,7 +259,8 @@ struct erdma_cmdq_create_cq_req { /* regmr cfg2 */ #define ERDMA_CMD_REGMR_PAGESIZE_MASK GENMASK(31, 27) -#define ERDMA_CMD_REGMR_MTT_TYPE_MASK GENMASK(21, 20) +#define ERDMA_CMD_REGMR_MTT_PAGESIZE_MASK GENMASK(26, 24) +#define ERDMA_CMD_REGMR_MTT_LEVEL_MASK GENMASK(21, 20) #define ERDMA_CMD_REGMR_MTT_CNT_MASK GENMASK(19, 0) struct erdma_cmdq_reg_mr_req { @@ -268,7 +270,14 @@ struct erdma_cmdq_reg_mr_req { u64 start_va; u32 size; u32 cfg2; - u64 phy_addr[4]; + union { + u64 phy_addr[4]; + struct { + u64 rsvd; + u32 size_h; + u32 mtt_cnt_h; + }; + }; }; struct erdma_cmdq_dereg_mr_req { @@ -309,7 +318,7 @@ struct erdma_cmdq_modify_qp_req { /* create qp mtt_cfg */ #define ERDMA_CMD_CREATE_QP_PAGE_OFFSET_MASK GENMASK(31, 12) #define ERDMA_CMD_CREATE_QP_MTT_CNT_MASK GENMASK(11, 1) -#define ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK BIT(0) +#define ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK BIT(0) /* create qp db cfg */ #define ERDMA_CMD_CREATE_QP_SQDB_CFG_MASK GENMASK(31, 16) @@ -364,6 +373,7 @@ struct erdma_cmdq_reflush_req { enum { ERDMA_DEV_CAP_FLAGS_ATOMIC = 1 << 7, + ERDMA_DEV_CAP_FLAGS_MTT_VA = 1 << 5, ERDMA_DEV_CAP_FLAGS_EXTEND_DB = 1 << 3, }; diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c index 44923c51a01b44d7dc322fd472e0a0988d8dec2b..6d0330badd68e86d1db20104803f166098590407 100644 --- a/drivers/infiniband/hw/erdma/erdma_qp.c +++ b/drivers/infiniband/hw/erdma/erdma_qp.c @@ -410,7 +410,7 @@ static int erdma_push_one_sqe(struct erdma_qp *qp, u16 *pi, /* Copy SGLs to SQE content to accelerate */ memcpy(get_queue_entry(qp->kern_qp.sq_buf, idx + 1, qp->attrs.sq_size, SQEBB_SHIFT), - mr->mem.mtt_buf, MTT_SIZE(mr->mem.mtt_nents)); + mr->mem.mtt->buf, MTT_SIZE(mr->mem.mtt_nents)); wqe_size = sizeof(struct erdma_reg_mr_sqe) + MTT_SIZE(mr->mem.mtt_nents); } else { diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c index 517676fbb8b111283736fd09b95c16128e6b3450..c317947563fbc650d7eb2070f3f0131ca8871660 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.c +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -19,6 +19,23 @@ #include "erdma_cm.h" #include "erdma_verbs.h" +static void assemble_qbuf_mtt_for_cmd(struct erdma_mem *mem, u32 *cfg, + u64 *addr0, u64 *addr1) +{ + struct erdma_mtt *mtt = mem->mtt; + + if (mem->mtt_nents > ERDMA_MAX_INLINE_MTT_ENTRIES) { + *addr0 = mtt->buf_dma; + *cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK, + ERDMA_MR_MTT_1LEVEL); + } else { + *addr0 = mtt->buf[0]; + memcpy(addr1, mtt->buf + 1, MTT_SIZE(mem->mtt_nents - 1)); + *cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); + } +} + static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) { struct erdma_dev *dev = to_edev(qp->ibqp.device); @@ -53,8 +70,8 @@ static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) req.sq_mtt_cfg = FIELD_PREP(ERDMA_CMD_CREATE_QP_PAGE_OFFSET_MASK, 0) | FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, 1) | - FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, - ERDMA_MR_INLINE_MTT); + FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); req.rq_mtt_cfg = req.sq_mtt_cfg; req.rq_buf_addr = qp->kern_qp.rq_buf_dma_addr; @@ -67,30 +84,28 @@ static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) user_qp = &qp->user_qp; req.sq_cqn_mtt_cfg = FIELD_PREP( ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, - ilog2(user_qp->sq_mtt.page_size) - ERDMA_HW_PAGE_SHIFT); + ilog2(user_qp->sq_mem.page_size) - ERDMA_HW_PAGE_SHIFT); req.sq_cqn_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->scq->cqn); req.rq_cqn_mtt_cfg = FIELD_PREP( ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, - ilog2(user_qp->rq_mtt.page_size) - ERDMA_HW_PAGE_SHIFT); + ilog2(user_qp->rq_mem.page_size) - ERDMA_HW_PAGE_SHIFT); req.rq_cqn_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->rcq->cqn); - req.sq_mtt_cfg = user_qp->sq_mtt.page_offset; + req.sq_mtt_cfg = user_qp->sq_mem.page_offset; req.sq_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, - user_qp->sq_mtt.mtt_nents) | - FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, - user_qp->sq_mtt.mtt_type); + user_qp->sq_mem.mtt_nents); - req.rq_mtt_cfg = user_qp->rq_mtt.page_offset; + req.rq_mtt_cfg = user_qp->rq_mem.page_offset; req.rq_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, - user_qp->rq_mtt.mtt_nents) | - FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, - user_qp->rq_mtt.mtt_type); + user_qp->rq_mem.mtt_nents); - req.sq_buf_addr = user_qp->sq_mtt.mtt_entry[0]; - req.rq_buf_addr = user_qp->rq_mtt.mtt_entry[0]; + assemble_qbuf_mtt_for_cmd(&user_qp->sq_mem, &req.sq_mtt_cfg, + &req.sq_buf_addr, req.sq_mtt_entry); + assemble_qbuf_mtt_for_cmd(&user_qp->rq_mem, &req.rq_mtt_cfg, + &req.rq_buf_addr, req.rq_mtt_entry); req.sq_db_info_dma_addr = user_qp->sq_db_info_dma_addr; req.rq_db_info_dma_addr = user_qp->rq_db_info_dma_addr; @@ -117,13 +132,26 @@ static int create_qp_cmd(struct erdma_ucontext *uctx, struct erdma_qp *qp) static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) { - struct erdma_cmdq_reg_mr_req req; struct erdma_pd *pd = to_epd(mr->ibmr.pd); - u64 *phy_addr; - int i; + u32 mtt_level = ERDMA_MR_MTT_0LEVEL; + struct erdma_cmdq_reg_mr_req req; erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, CMDQ_OPCODE_REG_MR); + if (mr->type == ERDMA_MR_TYPE_FRMR || + mr->mem.page_cnt > ERDMA_MAX_INLINE_MTT_ENTRIES) { + if (mr->mem.mtt->continuous) { + req.phy_addr[0] = mr->mem.mtt->buf_dma; + mtt_level = ERDMA_MR_MTT_1LEVEL; + } else { + req.phy_addr[0] = sg_dma_address(mr->mem.mtt->sglist); + mtt_level = mr->mem.mtt->level; + } + } else if (mr->type != ERDMA_MR_TYPE_DMA) { + memcpy(req.phy_addr, mr->mem.mtt->buf, + MTT_SIZE(mr->mem.page_cnt)); + } + req.cfg0 = FIELD_PREP(ERDMA_CMD_MR_VALID_MASK, mr->valid) | FIELD_PREP(ERDMA_CMD_MR_KEY_MASK, mr->ibmr.lkey & 0xFF) | FIELD_PREP(ERDMA_CMD_MR_MPT_IDX_MASK, mr->ibmr.lkey >> 8); @@ -132,7 +160,7 @@ static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) FIELD_PREP(ERDMA_CMD_REGMR_RIGHT_MASK, mr->access); req.cfg2 = FIELD_PREP(ERDMA_CMD_REGMR_PAGESIZE_MASK, ilog2(mr->mem.page_size)) | - FIELD_PREP(ERDMA_CMD_REGMR_MTT_TYPE_MASK, mr->mem.mtt_type) | + FIELD_PREP(ERDMA_CMD_REGMR_MTT_LEVEL_MASK, mtt_level) | FIELD_PREP(ERDMA_CMD_REGMR_MTT_CNT_MASK, mr->mem.page_cnt); if (mr->type == ERDMA_MR_TYPE_DMA) @@ -143,14 +171,12 @@ static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) req.size = mr->mem.len; } - if (mr->type == ERDMA_MR_TYPE_FRMR || - mr->mem.mtt_type == ERDMA_MR_INDIRECT_MTT) { - phy_addr = req.phy_addr; - *phy_addr = mr->mem.mtt_entry[0]; - } else { - phy_addr = req.phy_addr; - for (i = 0; i < mr->mem.mtt_nents; i++) - *phy_addr++ = mr->mem.mtt_entry[i]; + if (!mr->mem.mtt->continuous && mr->mem.mtt->level > 1) { + req.cfg0 |= FIELD_PREP(ERDMA_CMD_MR_VERSION_MASK, 1); + req.cfg2 |= FIELD_PREP(ERDMA_CMD_REGMR_MTT_PAGESIZE_MASK, + PAGE_SHIFT - ERDMA_HW_PAGE_SHIFT); + req.size_h = upper_32_bits(mr->mem.len); + req.mtt_cnt_h = mr->mem.page_cnt >> 20; } post_cmd: @@ -161,7 +187,7 @@ static int create_cq_cmd(struct erdma_ucontext *uctx, struct erdma_cq *cq) { struct erdma_dev *dev = to_edev(cq->ibcq.device); struct erdma_cmdq_create_cq_req req; - struct erdma_mem *mtt; + struct erdma_mem *mem; u32 page_size; erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, @@ -179,30 +205,34 @@ static int create_cq_cmd(struct erdma_ucontext *uctx, struct erdma_cq *cq) req.qbuf_addr_h = upper_32_bits(cq->kern_cq.qbuf_dma_addr); req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK, 1) | - FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK, - ERDMA_MR_INLINE_MTT); + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); req.first_page_offset = 0; req.cq_db_info_addr = cq->kern_cq.qbuf_dma_addr + (cq->depth << CQE_SHIFT); } else { - mtt = &cq->user_cq.qbuf_mtt; + mem = &cq->user_cq.qbuf_mem; req.cfg0 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_PAGESIZE_MASK, - ilog2(mtt->page_size) - ERDMA_HW_PAGE_SHIFT); - if (mtt->mtt_nents == 1) { - req.qbuf_addr_l = lower_32_bits(*(u64 *)mtt->mtt_buf); - req.qbuf_addr_h = upper_32_bits(*(u64 *)mtt->mtt_buf); + ilog2(mem->page_size) - ERDMA_HW_PAGE_SHIFT); + if (mem->mtt_nents == 1) { + req.qbuf_addr_l = lower_32_bits(mem->mtt->buf[0]); + req.qbuf_addr_h = upper_32_bits(mem->mtt->buf[0]); + req.cfg1 |= + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK, + ERDMA_MR_MTT_0LEVEL); } else { - req.qbuf_addr_l = lower_32_bits(mtt->mtt_entry[0]); - req.qbuf_addr_h = upper_32_bits(mtt->mtt_entry[0]); + req.qbuf_addr_l = lower_32_bits(mem->mtt->buf_dma); + req.qbuf_addr_h = upper_32_bits(mem->mtt->buf_dma); + req.cfg1 |= + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_LEVEL_MASK, + ERDMA_MR_MTT_1LEVEL); } req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK, - mtt->mtt_nents); - req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK, - mtt->mtt_type); + mem->mtt_nents); - req.first_page_offset = mtt->page_offset; + req.first_page_offset = mem->page_offset; req.cq_db_info_addr = cq->user_cq.db_info_dma_addr; if (uctx->ext_db.enable) { @@ -481,8 +511,8 @@ static int init_kernel_qp(struct erdma_dev *dev, struct erdma_qp *qp, dev->func_bar + (ERDMA_SDB_SHARED_PAGE_INDEX << PAGE_SHIFT); kqp->hw_rq_db = dev->func_bar + ERDMA_BAR_RQDB_SPACE_OFFSET; - kqp->swr_tbl = vmalloc(qp->attrs.sq_size * sizeof(u64)); - kqp->rwr_tbl = vmalloc(qp->attrs.rq_size * sizeof(u64)); + kqp->swr_tbl = vmalloc_array(qp->attrs.sq_size, sizeof(u64)); + kqp->rwr_tbl = vmalloc_array(qp->attrs.rq_size, sizeof(u64)); if (!kqp->swr_tbl || !kqp->rwr_tbl) goto err_out; @@ -508,12 +538,223 @@ static int init_kernel_qp(struct erdma_dev *dev, struct erdma_qp *qp, return -ENOMEM; } +static void erdma_fill_bottom_mtt(struct erdma_dev *dev, struct erdma_mem *mem) +{ + struct erdma_mtt *mtt = mem->mtt; + struct ib_block_iter biter; + u32 idx = 0; + + while (mtt->low_level) + mtt = mtt->low_level; + + rdma_umem_for_each_dma_block(mem->umem, &biter, mem->page_size) + mtt->buf[idx++] = rdma_block_iter_dma_address(&biter); +} + +static struct erdma_mtt *erdma_create_cont_mtt(struct erdma_dev *dev, + size_t size) +{ + struct erdma_mtt *mtt; + + mtt = kzalloc(sizeof(*mtt), GFP_KERNEL); + if (!mtt) + return ERR_PTR(-ENOMEM); + + mtt->size = size; + mtt->buf = kzalloc(mtt->size, GFP_KERNEL); + if (!mtt->buf) + goto err_free_mtt; + + mtt->continuous = true; + mtt->buf_dma = dma_map_single(&dev->pdev->dev, mtt->buf, mtt->size, + DMA_TO_DEVICE); + if (dma_mapping_error(&dev->pdev->dev, mtt->buf_dma)) + goto err_free_mtt_buf; + + return mtt; + +err_free_mtt_buf: + kfree(mtt->buf); + +err_free_mtt: + kfree(mtt); + + return ERR_PTR(-ENOMEM); +} + +static void erdma_destroy_mtt_buf_sg(struct erdma_dev *dev, + struct erdma_mtt *mtt) +{ + dma_unmap_sg(&dev->pdev->dev, mtt->sglist, mtt->nsg, DMA_TO_DEVICE); + vfree(mtt->sglist); +} + +static void erdma_destroy_scatter_mtt(struct erdma_dev *dev, + struct erdma_mtt *mtt) +{ + erdma_destroy_mtt_buf_sg(dev, mtt); + vfree(mtt->buf); + kfree(mtt); +} + +static void erdma_init_middle_mtt(struct erdma_mtt *mtt, + struct erdma_mtt *low_mtt) +{ + struct scatterlist *sg; + u32 idx = 0, i; + + for_each_sg(low_mtt->sglist, sg, low_mtt->nsg, i) + mtt->buf[idx++] = sg_dma_address(sg); +} + +static int erdma_create_mtt_buf_sg(struct erdma_dev *dev, struct erdma_mtt *mtt) +{ + struct scatterlist *sglist; + void *buf = mtt->buf; + u32 npages, i, nsg; + struct page *pg; + + /* Failed if buf is not page aligned */ + if ((uintptr_t)buf & ~PAGE_MASK) + return -EINVAL; + + npages = DIV_ROUND_UP(mtt->size, PAGE_SIZE); + sglist = vzalloc(npages * sizeof(*sglist)); + if (!sglist) + return -ENOMEM; + + sg_init_table(sglist, npages); + for (i = 0; i < npages; i++) { + pg = vmalloc_to_page(buf); + if (!pg) + goto err; + sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); + buf += PAGE_SIZE; + } + + nsg = dma_map_sg(&dev->pdev->dev, sglist, npages, DMA_TO_DEVICE); + if (!nsg) + goto err; + + mtt->sglist = sglist; + mtt->nsg = nsg; + + return 0; +err: + vfree(sglist); + + return -ENOMEM; +} + +static struct erdma_mtt *erdma_create_scatter_mtt(struct erdma_dev *dev, + size_t size) +{ + struct erdma_mtt *mtt; + int ret = -ENOMEM; + + mtt = kzalloc(sizeof(*mtt), GFP_KERNEL); + if (!mtt) + return ERR_PTR(-ENOMEM); + + mtt->size = ALIGN(size, PAGE_SIZE); + mtt->buf = vzalloc(mtt->size); + mtt->continuous = false; + if (!mtt->buf) + goto err_free_mtt; + + ret = erdma_create_mtt_buf_sg(dev, mtt); + if (ret) + goto err_free_mtt_buf; + + ibdev_dbg(&dev->ibdev, "create scatter mtt, size:%lu, nsg:%u\n", + mtt->size, mtt->nsg); + + return mtt; + +err_free_mtt_buf: + vfree(mtt->buf); + +err_free_mtt: + kfree(mtt); + + return ERR_PTR(ret); +} + +static struct erdma_mtt *erdma_create_mtt(struct erdma_dev *dev, size_t size, + bool force_continuous) +{ + struct erdma_mtt *mtt, *tmp_mtt; + int ret, level = 0; + + ibdev_dbg(&dev->ibdev, "create_mtt, size:%lu, force cont:%d\n", size, + force_continuous); + + if (!(dev->attrs.cap_flags & ERDMA_DEV_CAP_FLAGS_MTT_VA)) + force_continuous = true; + + if (force_continuous) + return erdma_create_cont_mtt(dev, size); + + mtt = erdma_create_scatter_mtt(dev, size); + if (IS_ERR(mtt)) + return mtt; + level = 1; + + /* convergence the mtt table. */ + while (mtt->nsg != 1 && level <= 3) { + tmp_mtt = erdma_create_scatter_mtt(dev, MTT_SIZE(mtt->nsg)); + if (IS_ERR(tmp_mtt)) { + ret = PTR_ERR(tmp_mtt); + goto err_free_mtt; + } + erdma_init_middle_mtt(tmp_mtt, mtt); + tmp_mtt->low_level = mtt; + mtt = tmp_mtt; + level++; + } + + if (level > 3) { + ret = -ENOMEM; + goto err_free_mtt; + } + + mtt->level = level; + ibdev_dbg(&dev->ibdev, "top mtt: level:%d, dma_addr 0x%llx\n", + mtt->level, mtt->sglist[0].dma_address); + + return mtt; +err_free_mtt: + while (mtt) { + tmp_mtt = mtt->low_level; + erdma_destroy_scatter_mtt(dev, mtt); + mtt = tmp_mtt; + } + + return ERR_PTR(ret); +} + +static void erdma_destroy_mtt(struct erdma_dev *dev, struct erdma_mtt *mtt) +{ + struct erdma_mtt *tmp_mtt; + + if (mtt->continuous) { + dma_unmap_single(&dev->pdev->dev, mtt->buf_dma, mtt->size, + DMA_TO_DEVICE); + kfree(mtt->buf); + kfree(mtt); + } else { + while (mtt) { + tmp_mtt = mtt->low_level; + erdma_destroy_scatter_mtt(dev, mtt); + mtt = tmp_mtt; + } + } +} + static int get_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem, u64 start, u64 len, int access, u64 virt, - unsigned long req_page_size, u8 force_indirect_mtt) + unsigned long req_page_size, bool force_continuous) { - struct ib_block_iter biter; - uint64_t *phy_addr = NULL; int ret = 0; mem->umem = ib_umem_get(&dev->ibdev, start, len, access); @@ -529,38 +770,14 @@ static int get_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem, mem->page_offset = start & (mem->page_size - 1); mem->mtt_nents = ib_umem_num_dma_blocks(mem->umem, mem->page_size); mem->page_cnt = mem->mtt_nents; - - if (mem->page_cnt > ERDMA_MAX_INLINE_MTT_ENTRIES || - force_indirect_mtt) { - mem->mtt_type = ERDMA_MR_INDIRECT_MTT; - mem->mtt_buf = - alloc_pages_exact(MTT_SIZE(mem->page_cnt), GFP_KERNEL); - if (!mem->mtt_buf) { - ret = -ENOMEM; - goto error_ret; - } - phy_addr = mem->mtt_buf; - } else { - mem->mtt_type = ERDMA_MR_INLINE_MTT; - phy_addr = mem->mtt_entry; + mem->mtt = erdma_create_mtt(dev, MTT_SIZE(mem->page_cnt), + force_continuous); + if (IS_ERR(mem->mtt)) { + ret = PTR_ERR(mem->mtt); + goto error_ret; } - rdma_umem_for_each_dma_block(mem->umem, &biter, mem->page_size) { - *phy_addr = rdma_block_iter_dma_address(&biter); - phy_addr++; - } - - if (mem->mtt_type == ERDMA_MR_INDIRECT_MTT) { - mem->mtt_entry[0] = - dma_map_single(&dev->pdev->dev, mem->mtt_buf, - MTT_SIZE(mem->page_cnt), DMA_TO_DEVICE); - if (dma_mapping_error(&dev->pdev->dev, mem->mtt_entry[0])) { - free_pages_exact(mem->mtt_buf, MTT_SIZE(mem->page_cnt)); - mem->mtt_buf = NULL; - ret = -ENOMEM; - goto error_ret; - } - } + erdma_fill_bottom_mtt(dev, mem); return 0; @@ -575,11 +792,8 @@ static int get_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem, static void put_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem) { - if (mem->mtt_buf) { - dma_unmap_single(&dev->pdev->dev, mem->mtt_entry[0], - MTT_SIZE(mem->page_cnt), DMA_TO_DEVICE); - free_pages_exact(mem->mtt_buf, MTT_SIZE(mem->page_cnt)); - } + if (mem->mtt) + erdma_destroy_mtt(dev, mem->mtt); if (mem->umem) { ib_umem_release(mem->umem); @@ -660,18 +874,18 @@ static int init_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx, qp->attrs.rq_size * RQE_SIZE)) return -EINVAL; - ret = get_mtt_entries(qp->dev, &qp->user_qp.sq_mtt, va, + ret = get_mtt_entries(qp->dev, &qp->user_qp.sq_mem, va, qp->attrs.sq_size << SQEBB_SHIFT, 0, va, - (SZ_1M - SZ_4K), 1); + (SZ_1M - SZ_4K), true); if (ret) return ret; rq_offset = ALIGN(qp->attrs.sq_size << SQEBB_SHIFT, ERDMA_HW_PAGE_SIZE); qp->user_qp.rq_offset = rq_offset; - ret = get_mtt_entries(qp->dev, &qp->user_qp.rq_mtt, va + rq_offset, + ret = get_mtt_entries(qp->dev, &qp->user_qp.rq_mem, va + rq_offset, qp->attrs.rq_size << RQE_SHIFT, 0, va + rq_offset, - (SZ_1M - SZ_4K), 1); + (SZ_1M - SZ_4K), true); if (ret) goto put_sq_mtt; @@ -687,18 +901,18 @@ static int init_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx, return 0; put_rq_mtt: - put_mtt_entries(qp->dev, &qp->user_qp.rq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.rq_mem); put_sq_mtt: - put_mtt_entries(qp->dev, &qp->user_qp.sq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.sq_mem); return ret; } static void free_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx) { - put_mtt_entries(qp->dev, &qp->user_qp.sq_mtt); - put_mtt_entries(qp->dev, &qp->user_qp.rq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.sq_mem); + put_mtt_entries(qp->dev, &qp->user_qp.rq_mem); erdma_unmap_user_dbrecords(uctx, &qp->user_qp.user_dbr_page); } @@ -875,33 +1089,20 @@ struct ib_mr *erdma_ib_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, mr->mem.page_size = PAGE_SIZE; /* update it later. */ mr->mem.page_cnt = max_num_sg; - mr->mem.mtt_type = ERDMA_MR_INDIRECT_MTT; - mr->mem.mtt_buf = - alloc_pages_exact(MTT_SIZE(mr->mem.page_cnt), GFP_KERNEL); - if (!mr->mem.mtt_buf) { - ret = -ENOMEM; + mr->mem.mtt = erdma_create_mtt(dev, MTT_SIZE(max_num_sg), true); + if (IS_ERR(mr->mem.mtt)) { + ret = PTR_ERR(mr->mem.mtt); goto out_remove_stag; } - mr->mem.mtt_entry[0] = - dma_map_single(&dev->pdev->dev, mr->mem.mtt_buf, - MTT_SIZE(mr->mem.page_cnt), DMA_TO_DEVICE); - if (dma_mapping_error(&dev->pdev->dev, mr->mem.mtt_entry[0])) { - ret = -ENOMEM; - goto out_free_mtt; - } - ret = regmr_cmd(dev, mr); if (ret) - goto out_dma_unmap; + goto out_destroy_mtt; return &mr->ibmr; -out_dma_unmap: - dma_unmap_single(&dev->pdev->dev, mr->mem.mtt_entry[0], - MTT_SIZE(mr->mem.page_cnt), DMA_TO_DEVICE); -out_free_mtt: - free_pages_exact(mr->mem.mtt_buf, MTT_SIZE(mr->mem.page_cnt)); +out_destroy_mtt: + erdma_destroy_mtt(dev, mr->mem.mtt); out_remove_stag: erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], @@ -920,7 +1121,7 @@ static int erdma_set_page(struct ib_mr *ibmr, u64 addr) if (mr->mem.mtt_nents >= mr->mem.page_cnt) return -1; - *((u64 *)mr->mem.mtt_buf + mr->mem.mtt_nents) = addr; + mr->mem.mtt->buf[mr->mem.mtt_nents] = addr; mr->mem.mtt_nents++; return 0; @@ -956,7 +1157,7 @@ struct ib_mr *erdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, return ERR_PTR(-ENOMEM); ret = get_mtt_entries(dev, &mr->mem, start, len, access, virt, - SZ_2G - SZ_4K, 0); + SZ_2G - SZ_4K, false); if (ret) goto err_out_free; @@ -1041,7 +1242,7 @@ int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) cq->kern_cq.qbuf, cq->kern_cq.qbuf_dma_addr); } else { erdma_unmap_user_dbrecords(ctx, &cq->user_cq.user_dbr_page); - put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + put_mtt_entries(dev, &cq->user_cq.qbuf_mem); } xa_erase(&dev->cq_xa, cq->cqn); @@ -1089,8 +1290,8 @@ int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) WARPPED_BUFSIZE(qp->attrs.sq_size << SQEBB_SHIFT), qp->kern_qp.sq_buf, qp->kern_qp.sq_buf_dma_addr); } else { - put_mtt_entries(dev, &qp->user_qp.sq_mtt); - put_mtt_entries(dev, &qp->user_qp.rq_mtt); + put_mtt_entries(dev, &qp->user_qp.sq_mem); + put_mtt_entries(dev, &qp->user_qp.rq_mem); erdma_unmap_user_dbrecords(ctx, &qp->user_qp.user_dbr_page); } @@ -1379,9 +1580,9 @@ static int erdma_init_user_cq(struct erdma_ucontext *ctx, struct erdma_cq *cq, int ret; struct erdma_dev *dev = to_edev(cq->ibcq.device); - ret = get_mtt_entries(dev, &cq->user_cq.qbuf_mtt, ureq->qbuf_va, + ret = get_mtt_entries(dev, &cq->user_cq.qbuf_mem, ureq->qbuf_va, ureq->qbuf_len, 0, ureq->qbuf_va, SZ_64M - SZ_4K, - 1); + true); if (ret) return ret; @@ -1389,7 +1590,7 @@ static int erdma_init_user_cq(struct erdma_ucontext *ctx, struct erdma_cq *cq, &cq->user_cq.user_dbr_page, &cq->user_cq.db_info_dma_addr); if (ret) - put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + put_mtt_entries(dev, &cq->user_cq.qbuf_mem); return ret; } @@ -1473,7 +1674,7 @@ int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, err_free_res: if (!rdma_is_kernel_res(&ibcq->res)) { erdma_unmap_user_dbrecords(ctx, &cq->user_cq.user_dbr_page); - put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + put_mtt_entries(dev, &cq->user_cq.qbuf_mem); } else { dma_free_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(depth << CQE_SHIFT), diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h index 429fc3063f980715e2a1ed9b842f2d565c748cd3..eb9c0f92fb6f0f99057d47ac85c87522e4ba0d68 100644 --- a/drivers/infiniband/hw/erdma/erdma_verbs.h +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -65,7 +65,7 @@ struct erdma_pd { * MemoryRegion definition. */ #define ERDMA_MAX_INLINE_MTT_ENTRIES 4 -#define MTT_SIZE(mtt_cnt) (mtt_cnt << 3) /* per mtt takes 8 Bytes. */ +#define MTT_SIZE(mtt_cnt) ((mtt_cnt) << 3) /* per mtt entry takes 8 Bytes. */ #define ERDMA_MR_MAX_MTT_CNT 524288 #define ERDMA_MTT_ENTRY_SIZE 8 @@ -73,8 +73,8 @@ struct erdma_pd { #define ERDMA_MR_TYPE_FRMR 1 #define ERDMA_MR_TYPE_DMA 2 -#define ERDMA_MR_INLINE_MTT 0 -#define ERDMA_MR_INDIRECT_MTT 1 +#define ERDMA_MR_MTT_0LEVEL 0 +#define ERDMA_MR_MTT_1LEVEL 1 #define ERDMA_MR_ACC_RA BIT(0) #define ERDMA_MR_ACC_LR BIT(1) @@ -90,10 +90,28 @@ static inline u8 to_erdma_access_flags(int access) (access & IB_ACCESS_REMOTE_ATOMIC ? ERDMA_MR_ACC_RA : 0); } +/* Hierarchical storage structure for MTT entries */ +struct erdma_mtt { + u64 *buf; + size_t size; + + bool continuous; + union { + dma_addr_t buf_dma; + struct { + struct scatterlist *sglist; + u32 nsg; + u32 level; + }; + }; + + struct erdma_mtt *low_level; +}; + struct erdma_mem { struct ib_umem *umem; - void *mtt_buf; - u32 mtt_type; + struct erdma_mtt *mtt; + u32 page_size; u32 page_offset; u32 page_cnt; @@ -101,8 +119,6 @@ struct erdma_mem { u64 va; u64 len; - - u64 mtt_entry[ERDMA_MAX_INLINE_MTT_ENTRIES]; }; struct erdma_mr { @@ -121,8 +137,8 @@ struct erdma_user_dbrecords_page { }; struct erdma_uqp { - struct erdma_mem sq_mtt; - struct erdma_mem rq_mtt; + struct erdma_mem sq_mem; + struct erdma_mem rq_mem; dma_addr_t sq_db_info_dma_addr; dma_addr_t rq_db_info_dma_addr; @@ -234,7 +250,7 @@ struct erdma_kcq_info { }; struct erdma_ucq_info { - struct erdma_mem qbuf_mtt; + struct erdma_mem qbuf_mem; struct erdma_user_dbrecords_page *user_dbr_page; dma_addr_t db_info_dma_addr; }; diff --git a/drivers/infiniband/hw/hfi1/Makefile b/drivers/infiniband/hw/hfi1/Makefile index 2e89ec10efeda8d5009c1a2d81bbb341a8ec2e0d..5d977f3636847a0c06d0aabe1d979e99247bca57 100644 --- a/drivers/infiniband/hw/hfi1/Makefile +++ b/drivers/infiniband/hw/hfi1/Makefile @@ -31,6 +31,7 @@ hfi1-y := \ netdev_rx.o \ opfn.o \ pcie.o \ + pin_system.o \ pio.o \ pio_copy.o \ platform.o \ diff --git a/drivers/infiniband/hw/hfi1/affinity.c b/drivers/infiniband/hw/hfi1/affinity.c index 77ee77d4000fbfdedde9978473a1865a998eec9b..bbc957c578e1e516a84564600466e64b6581f095 100644 --- a/drivers/infiniband/hw/hfi1/affinity.c +++ b/drivers/infiniband/hw/hfi1/affinity.c @@ -230,11 +230,9 @@ static void node_affinity_add_tail(struct hfi1_affinity_node *entry) /* It must be called with node_affinity.lock held */ static struct hfi1_affinity_node *node_affinity_lookup(int node) { - struct list_head *pos; struct hfi1_affinity_node *entry; - list_for_each(pos, &node_affinity.list) { - entry = list_entry(pos, struct hfi1_affinity_node, list); + list_for_each_entry(entry, &node_affinity.list, list) { if (entry->node == node) return entry; } diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index baaa4406d5e60f464a2f95f5ba23467daa62b25f..0814291a04120fc8b344b21e454db58392b4dde4 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -1461,7 +1461,8 @@ static u64 dc_access_lcb_cntr(const struct cntr_entry *entry, void *context, ret = write_lcb_csr(dd, csr, data); if (ret) { - dd_dev_err(dd, "Could not acquire LCB for counter 0x%x", csr); + if (!(dd->flags & HFI1_SHUTDOWN)) + dd_dev_err(dd, "Could not acquire LCB for counter 0x%x", csr); return 0; } @@ -6160,7 +6161,7 @@ static int request_host_lcb_access(struct hfi1_devdata *dd) ret = do_8051_command(dd, HCMD_MISC, (u64)HCMD_MISC_REQUEST_LCB_ACCESS << LOAD_DATA_FIELD_ID_SHIFT, NULL); - if (ret != HCMD_SUCCESS) { + if (ret != HCMD_SUCCESS && !(dd->flags & HFI1_SHUTDOWN)) { dd_dev_err(dd, "%s: command failed with error %d\n", __func__, ret); } @@ -6241,7 +6242,8 @@ int acquire_lcb_access(struct hfi1_devdata *dd, int sleep_ok) if (dd->lcb_access_count == 0) { ret = request_host_lcb_access(dd); if (ret) { - dd_dev_err(dd, + if (!(dd->flags & HFI1_SHUTDOWN)) + dd_dev_err(dd, "%s: unable to acquire LCB access, err %d\n", __func__, ret); goto done; diff --git a/drivers/infiniband/hw/hfi1/device.c b/drivers/infiniband/hw/hfi1/device.c index 05be0d119f796f21403a96f798885bdd7940b316..b0a00b7aaec59c80df1ac23b96db953238c491cf 100644 --- a/drivers/infiniband/hw/hfi1/device.c +++ b/drivers/infiniband/hw/hfi1/device.c @@ -10,8 +10,29 @@ #include "hfi.h" #include "device.h" -static struct class *class; -static struct class *user_class; +static char *hfi1_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0600; + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + +static const struct class class = { + .name = "hfi1", + .devnode = hfi1_devnode, +}; + +static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) +{ + if (mode) + *mode = 0666; + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} + +static const struct class user_class = { + .name = "hfi1_user", + .devnode = hfi1_user_devnode, +}; static dev_t hfi1_dev; int hfi1_cdev_init(int minor, const char *name, @@ -37,9 +58,9 @@ int hfi1_cdev_init(int minor, const char *name, } if (user_accessible) - device = device_create(user_class, NULL, dev, NULL, "%s", name); + device = device_create(&user_class, NULL, dev, NULL, "%s", name); else - device = device_create(class, NULL, dev, NULL, "%s", name); + device = device_create(&class, NULL, dev, NULL, "%s", name); if (IS_ERR(device)) { ret = PTR_ERR(device); @@ -72,26 +93,6 @@ const char *class_name(void) return hfi1_class_name; } -static char *hfi1_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0600; - return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); -} - -static const char *hfi1_class_name_user = "hfi1_user"; -static const char *class_name_user(void) -{ - return hfi1_class_name_user; -} - -static char *hfi1_user_devnode(const struct device *dev, umode_t *mode) -{ - if (mode) - *mode = 0666; - return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); -} - int __init dev_init(void) { int ret; @@ -102,27 +103,21 @@ int __init dev_init(void) goto done; } - class = class_create(class_name()); - if (IS_ERR(class)) { - ret = PTR_ERR(class); + ret = class_register(&class); + if (ret) { pr_err("Could not create device class (err %d)\n", -ret); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); goto done; } - class->devnode = hfi1_devnode; - user_class = class_create(class_name_user()); - if (IS_ERR(user_class)) { - ret = PTR_ERR(user_class); + ret = class_register(&user_class); + if (ret) { pr_err("Could not create device class for user accessible files (err %d)\n", -ret); - class_destroy(class); - class = NULL; - user_class = NULL; + class_unregister(&class); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); goto done; } - user_class->devnode = hfi1_user_devnode; done: return ret; @@ -130,11 +125,8 @@ int __init dev_init(void) void dev_cleanup(void) { - class_destroy(class); - class = NULL; - - class_destroy(user_class); - user_class = NULL; + class_unregister(&class); + class_unregister(&user_class); unregister_chrdev_region(hfi1_dev, HFI1_NMINORS); } diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index 7fa9cd39254fd53fac23499fd6392b5aa7d21888..38772e52d7edb441ef1df38ec582cfcd4df88d7e 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ /* - * Copyright(c) 2020 Cornelis Networks, Inc. + * Copyright(c) 2020-2023 Cornelis Networks, Inc. * Copyright(c) 2015-2020 Intel Corporation. */ @@ -1378,8 +1378,6 @@ struct hfi1_devdata { #define PT_INVALID 3 struct tid_rb_node; -struct mmu_rb_node; -struct mmu_rb_handler; /* Private data for file operations */ struct hfi1_filedata { diff --git a/drivers/infiniband/hw/hfi1/pin_system.c b/drivers/infiniband/hw/hfi1/pin_system.c new file mode 100644 index 0000000000000000000000000000000000000000..384f722093e0b13737a671b1c6a3d5f9c2ece844 --- /dev/null +++ b/drivers/infiniband/hw/hfi1/pin_system.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause +/* + * Copyright(c) 2023 - Cornelis Networks, Inc. + */ + +#include + +#include "hfi.h" +#include "common.h" +#include "device.h" +#include "pinning.h" +#include "mmu_rb.h" +#include "user_sdma.h" +#include "trace.h" + +struct sdma_mmu_node { + struct mmu_rb_node rb; + struct hfi1_user_sdma_pkt_q *pq; + struct page **pages; + unsigned int npages; +}; + +static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, + unsigned long len); +static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, void *arg2, + bool *stop); +static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode); + +static struct mmu_rb_ops sdma_rb_ops = { + .filter = sdma_rb_filter, + .evict = sdma_rb_evict, + .remove = sdma_rb_remove, +}; + +int hfi1_init_system_pinning(struct hfi1_user_sdma_pkt_q *pq) +{ + struct hfi1_devdata *dd = pq->dd; + int ret; + + ret = hfi1_mmu_rb_register(pq, &sdma_rb_ops, dd->pport->hfi1_wq, + &pq->handler); + if (ret) + dd_dev_err(dd, + "[%u:%u] Failed to register system memory DMA support with MMU: %d\n", + pq->ctxt, pq->subctxt, ret); + return ret; +} + +void hfi1_free_system_pinning(struct hfi1_user_sdma_pkt_q *pq) +{ + if (pq->handler) + hfi1_mmu_rb_unregister(pq->handler); +} + +static u32 sdma_cache_evict(struct hfi1_user_sdma_pkt_q *pq, u32 npages) +{ + struct evict_data evict_data; + + evict_data.cleared = 0; + evict_data.target = npages; + hfi1_mmu_rb_evict(pq->handler, &evict_data); + return evict_data.cleared; +} + +static void unpin_vector_pages(struct mm_struct *mm, struct page **pages, + unsigned int start, unsigned int npages) +{ + hfi1_release_user_pages(mm, pages + start, npages, false); + kfree(pages); +} + +static inline struct mm_struct *mm_from_sdma_node(struct sdma_mmu_node *node) +{ + return node->rb.handler->mn.mm; +} + +static void free_system_node(struct sdma_mmu_node *node) +{ + if (node->npages) { + unpin_vector_pages(mm_from_sdma_node(node), node->pages, 0, + node->npages); + atomic_sub(node->npages, &node->pq->n_locked); + } + kfree(node); +} + +/* + * kref_get()'s an additional kref on the returned rb_node to prevent rb_node + * from being released until after rb_node is assigned to an SDMA descriptor + * (struct sdma_desc) under add_system_iovec_to_sdma_packet(), even if the + * virtual address range for rb_node is invalidated between now and then. + */ +static struct sdma_mmu_node *find_system_node(struct mmu_rb_handler *handler, + unsigned long start, + unsigned long end) +{ + struct mmu_rb_node *rb_node; + unsigned long flags; + + spin_lock_irqsave(&handler->lock, flags); + rb_node = hfi1_mmu_rb_get_first(handler, start, (end - start)); + if (!rb_node) { + spin_unlock_irqrestore(&handler->lock, flags); + return NULL; + } + + /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ + kref_get(&rb_node->refcount); + spin_unlock_irqrestore(&handler->lock, flags); + + return container_of(rb_node, struct sdma_mmu_node, rb); +} + +static int pin_system_pages(struct user_sdma_request *req, + uintptr_t start_address, size_t length, + struct sdma_mmu_node *node, int npages) +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + int pinned, cleared; + struct page **pages; + + pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL); + if (!pages) + return -ENOMEM; + +retry: + if (!hfi1_can_pin_pages(pq->dd, current->mm, atomic_read(&pq->n_locked), + npages)) { + SDMA_DBG(req, "Evicting: nlocked %u npages %u", + atomic_read(&pq->n_locked), npages); + cleared = sdma_cache_evict(pq, npages); + if (cleared >= npages) + goto retry; + } + + SDMA_DBG(req, "Acquire user pages start_address %lx node->npages %u npages %u", + start_address, node->npages, npages); + pinned = hfi1_acquire_user_pages(current->mm, start_address, npages, 0, + pages); + + if (pinned < 0) { + kfree(pages); + SDMA_DBG(req, "pinned %d", pinned); + return pinned; + } + if (pinned != npages) { + unpin_vector_pages(current->mm, pages, node->npages, pinned); + SDMA_DBG(req, "npages %u pinned %d", npages, pinned); + return -EFAULT; + } + node->rb.addr = start_address; + node->rb.len = length; + node->pages = pages; + node->npages = npages; + atomic_add(pinned, &pq->n_locked); + SDMA_DBG(req, "done. pinned %d", pinned); + return 0; +} + +/* + * kref refcount on *node_p will be 2 on successful addition: one kref from + * kref_init() for mmu_rb_handler and one kref to prevent *node_p from being + * released until after *node_p is assigned to an SDMA descriptor (struct + * sdma_desc) under add_system_iovec_to_sdma_packet(), even if the virtual + * address range for *node_p is invalidated between now and then. + */ +static int add_system_pinning(struct user_sdma_request *req, + struct sdma_mmu_node **node_p, + unsigned long start, unsigned long len) + +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + struct sdma_mmu_node *node; + int ret; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + /* First kref "moves" to mmu_rb_handler */ + kref_init(&node->rb.refcount); + + /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ + kref_get(&node->rb.refcount); + + node->pq = pq; + ret = pin_system_pages(req, start, len, node, PFN_DOWN(len)); + if (ret == 0) { + ret = hfi1_mmu_rb_insert(pq->handler, &node->rb); + if (ret) + free_system_node(node); + else + *node_p = node; + + return ret; + } + + kfree(node); + return ret; +} + +static int get_system_cache_entry(struct user_sdma_request *req, + struct sdma_mmu_node **node_p, + size_t req_start, size_t req_len) +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + u64 start = ALIGN_DOWN(req_start, PAGE_SIZE); + u64 end = PFN_ALIGN(req_start + req_len); + int ret; + + if ((end - start) == 0) { + SDMA_DBG(req, + "Request for empty cache entry req_start %lx req_len %lx start %llx end %llx", + req_start, req_len, start, end); + return -EINVAL; + } + + SDMA_DBG(req, "req_start %lx req_len %lu", req_start, req_len); + + while (1) { + struct sdma_mmu_node *node = + find_system_node(pq->handler, start, end); + u64 prepend_len = 0; + + SDMA_DBG(req, "node %p start %llx end %llu", node, start, end); + if (!node) { + ret = add_system_pinning(req, node_p, start, + end - start); + if (ret == -EEXIST) { + /* + * Another execution context has inserted a + * conficting entry first. + */ + continue; + } + return ret; + } + + if (node->rb.addr <= start) { + /* + * This entry covers at least part of the region. If it doesn't extend + * to the end, then this will be called again for the next segment. + */ + *node_p = node; + return 0; + } + + SDMA_DBG(req, "prepend: node->rb.addr %lx, node->rb.refcount %d", + node->rb.addr, kref_read(&node->rb.refcount)); + prepend_len = node->rb.addr - start; + + /* + * This node will not be returned, instead a new node + * will be. So release the reference. + */ + kref_put(&node->rb.refcount, hfi1_mmu_rb_release); + + /* Prepend a node to cover the beginning of the allocation */ + ret = add_system_pinning(req, node_p, start, prepend_len); + if (ret == -EEXIST) { + /* Another execution context has inserted a conficting entry first. */ + continue; + } + return ret; + } +} + +static void sdma_mmu_rb_node_get(void *ctx) +{ + struct mmu_rb_node *node = ctx; + + kref_get(&node->refcount); +} + +static void sdma_mmu_rb_node_put(void *ctx) +{ + struct sdma_mmu_node *node = ctx; + + kref_put(&node->rb.refcount, hfi1_mmu_rb_release); +} + +static int add_mapping_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct sdma_mmu_node *cache_entry, + size_t start, + size_t from_this_cache_entry) +{ + struct hfi1_user_sdma_pkt_q *pq = req->pq; + unsigned int page_offset; + unsigned int from_this_page; + size_t page_index; + void *ctx; + int ret; + + /* + * Because the cache may be more fragmented than the memory that is being accessed, + * it's not strictly necessary to have a descriptor per cache entry. + */ + + while (from_this_cache_entry) { + page_index = PFN_DOWN(start - cache_entry->rb.addr); + + if (page_index >= cache_entry->npages) { + SDMA_DBG(req, + "Request for page_index %zu >= cache_entry->npages %u", + page_index, cache_entry->npages); + return -EINVAL; + } + + page_offset = start - ALIGN_DOWN(start, PAGE_SIZE); + from_this_page = PAGE_SIZE - page_offset; + + if (from_this_page < from_this_cache_entry) { + ctx = NULL; + } else { + /* + * In the case they are equal the next line has no practical effect, + * but it's better to do a register to register copy than a conditional + * branch. + */ + from_this_page = from_this_cache_entry; + ctx = cache_entry; + } + + ret = sdma_txadd_page(pq->dd, &tx->txreq, + cache_entry->pages[page_index], + page_offset, from_this_page, + ctx, + sdma_mmu_rb_node_get, + sdma_mmu_rb_node_put); + if (ret) { + /* + * When there's a failure, the entire request is freed by + * user_sdma_send_pkts(). + */ + SDMA_DBG(req, + "sdma_txadd_page failed %d page_index %lu page_offset %u from_this_page %u", + ret, page_index, page_offset, from_this_page); + return ret; + } + start += from_this_page; + from_this_cache_entry -= from_this_page; + } + return 0; +} + +static int add_system_iovec_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct user_sdma_iovec *iovec, + size_t from_this_iovec) +{ + while (from_this_iovec > 0) { + struct sdma_mmu_node *cache_entry; + size_t from_this_cache_entry; + size_t start; + int ret; + + start = (uintptr_t)iovec->iov.iov_base + iovec->offset; + ret = get_system_cache_entry(req, &cache_entry, start, + from_this_iovec); + if (ret) { + SDMA_DBG(req, "pin system segment failed %d", ret); + return ret; + } + + from_this_cache_entry = cache_entry->rb.len - (start - cache_entry->rb.addr); + if (from_this_cache_entry > from_this_iovec) + from_this_cache_entry = from_this_iovec; + + ret = add_mapping_to_sdma_packet(req, tx, cache_entry, start, + from_this_cache_entry); + + /* + * Done adding cache_entry to zero or more sdma_desc. Can + * kref_put() the "safety" kref taken under + * get_system_cache_entry(). + */ + kref_put(&cache_entry->rb.refcount, hfi1_mmu_rb_release); + + if (ret) { + SDMA_DBG(req, "add system segment failed %d", ret); + return ret; + } + + iovec->offset += from_this_cache_entry; + from_this_iovec -= from_this_cache_entry; + } + + return 0; +} + +/* + * Add up to pkt_data_remaining bytes to the txreq, starting at the current + * offset in the given iovec entry and continuing until all data has been added + * to the iovec or the iovec entry type changes. + * + * On success, prior to returning, adjust pkt_data_remaining, req->iov_idx, and + * the offset value in req->iov[req->iov_idx] to reflect the data that has been + * consumed. + */ +int hfi1_add_pages_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct user_sdma_iovec *iovec, + u32 *pkt_data_remaining) +{ + size_t remaining_to_add = *pkt_data_remaining; + /* + * Walk through iovec entries, ensure the associated pages + * are pinned and mapped, add data to the packet until no more + * data remains to be added or the iovec entry type changes. + */ + while (remaining_to_add > 0) { + struct user_sdma_iovec *cur_iovec; + size_t from_this_iovec; + int ret; + + cur_iovec = iovec; + from_this_iovec = iovec->iov.iov_len - iovec->offset; + + if (from_this_iovec > remaining_to_add) { + from_this_iovec = remaining_to_add; + } else { + /* The current iovec entry will be consumed by this pass. */ + req->iov_idx++; + iovec++; + } + + ret = add_system_iovec_to_sdma_packet(req, tx, cur_iovec, + from_this_iovec); + if (ret) + return ret; + + remaining_to_add -= from_this_iovec; + } + *pkt_data_remaining = remaining_to_add; + + return 0; +} + +static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, + unsigned long len) +{ + return (bool)(node->addr == addr); +} + +/* + * Return 1 to remove the node from the rb tree and call the remove op. + * + * Called with the rb tree lock held. + */ +static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, + void *evict_arg, bool *stop) +{ + struct sdma_mmu_node *node = + container_of(mnode, struct sdma_mmu_node, rb); + struct evict_data *evict_data = evict_arg; + + /* this node will be evicted, add its pages to our count */ + evict_data->cleared += node->npages; + + /* have enough pages been cleared? */ + if (evict_data->cleared >= evict_data->target) + *stop = true; + + return 1; /* remove this node */ +} + +static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode) +{ + struct sdma_mmu_node *node = + container_of(mnode, struct sdma_mmu_node, rb); + + free_system_node(node); +} diff --git a/drivers/infiniband/hw/hfi1/pinning.h b/drivers/infiniband/hw/hfi1/pinning.h new file mode 100644 index 0000000000000000000000000000000000000000..a814a3aa96542fe6f6231ed3c497ce56edb77494 --- /dev/null +++ b/drivers/infiniband/hw/hfi1/pinning.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ +/* + * Copyright(c) 2023 Cornelis Networks, Inc. + */ +#ifndef _HFI1_PINNING_H +#define _HFI1_PINNING_H + +struct hfi1_user_sdma_pkt_q; +struct user_sdma_request; +struct user_sdma_txreq; +struct user_sdma_iovec; + +int hfi1_init_system_pinning(struct hfi1_user_sdma_pkt_q *pq); +void hfi1_free_system_pinning(struct hfi1_user_sdma_pkt_q *pq); +int hfi1_add_pages_to_sdma_packet(struct user_sdma_request *req, + struct user_sdma_txreq *tx, + struct user_sdma_iovec *iovec, + u32 *pkt_data_remaining); + +#endif /* _HFI1_PINNING_H */ diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c index 62e7dc9bea7bde69e835ffe8c75bfe1b85de9af4..dfea53e0fdeb8abda0aaf194f94d05f7fb996a7d 100644 --- a/drivers/infiniband/hw/hfi1/pio.c +++ b/drivers/infiniband/hw/hfi1/pio.c @@ -1893,9 +1893,7 @@ int pio_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, u8 *vl_scontexts) vl_scontexts[i] = sc_per_vl + (extra > 0 ? 1 : 0); } /* build new map */ - newmap = kzalloc(sizeof(*newmap) + - roundup_pow_of_two(num_vls) * - sizeof(struct pio_map_elem *), + newmap = kzalloc(struct_size(newmap, map, roundup_pow_of_two(num_vls)), GFP_KERNEL); if (!newmap) goto bail; @@ -1910,9 +1908,8 @@ int pio_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, u8 *vl_scontexts) int sz = roundup_pow_of_two(vl_scontexts[i]); /* only allocate once */ - newmap->map[i] = kzalloc(sizeof(*newmap->map[i]) + - sz * sizeof(struct - send_context *), + newmap->map[i] = kzalloc(struct_size(newmap->map[i], + ksc, sz), GFP_KERNEL); if (!newmap->map[i]) goto bail; diff --git a/drivers/infiniband/hw/hfi1/user_sdma.c b/drivers/infiniband/hw/hfi1/user_sdma.c index 02bd62b857b75beae5e5d741bca6ebb2fe5df625..29ae7beb9b036749d9c10d80fb956c6c736de0b4 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.c +++ b/drivers/infiniband/hw/hfi1/user_sdma.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause /* - * Copyright(c) 2020 - Cornelis Networks, Inc. + * Copyright(c) 2020 - 2023 Cornelis Networks, Inc. * Copyright(c) 2015 - 2018 Intel Corporation. */ @@ -60,22 +60,6 @@ static int defer_packet_queue( uint seq, bool pkts_sent); static void activate_packet_queue(struct iowait *wait, int reason); -static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, - unsigned long len); -static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, - void *arg2, bool *stop); -static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode); - -static struct mmu_rb_ops sdma_rb_ops = { - .filter = sdma_rb_filter, - .evict = sdma_rb_evict, - .remove = sdma_rb_remove, -}; - -static int add_system_pages_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct user_sdma_iovec *iovec, - u32 *pkt_remaining); static int defer_packet_queue( struct sdma_engine *sde, @@ -185,12 +169,9 @@ int hfi1_user_sdma_alloc_queues(struct hfi1_ctxtdata *uctxt, cq->nentries = hfi1_sdma_comp_ring_size; - ret = hfi1_mmu_rb_register(pq, &sdma_rb_ops, dd->pport->hfi1_wq, - &pq->handler); - if (ret) { - dd_dev_err(dd, "Failed to register with MMU %d", ret); + ret = hfi1_init_system_pinning(pq); + if (ret) goto pq_mmu_fail; - } rcu_assign_pointer(fd->pq, pq); fd->cq = cq; @@ -249,8 +230,7 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, pq->wait, !atomic_read(&pq->n_reqs)); kfree(pq->reqs); - if (pq->handler) - hfi1_mmu_rb_unregister(pq->handler); + hfi1_free_system_pinning(pq); bitmap_free(pq->req_in_use); kmem_cache_destroy(pq->txreq_cache); flush_pq_iowait(pq); @@ -821,8 +801,8 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, u16 maxpkts) req->tidoffset += datalen; req->sent += datalen; while (datalen) { - ret = add_system_pages_to_sdma_packet(req, tx, iovec, - &datalen); + ret = hfi1_add_pages_to_sdma_packet(req, tx, iovec, + &datalen); if (ret) goto free_txreq; iovec = &req->iovs[req->iov_idx]; @@ -860,17 +840,6 @@ static int user_sdma_send_pkts(struct user_sdma_request *req, u16 maxpkts) return ret; } -static u32 sdma_cache_evict(struct hfi1_user_sdma_pkt_q *pq, u32 npages) -{ - struct evict_data evict_data; - struct mmu_rb_handler *handler = pq->handler; - - evict_data.cleared = 0; - evict_data.target = npages; - hfi1_mmu_rb_evict(handler, &evict_data); - return evict_data.cleared; -} - static int check_header_template(struct user_sdma_request *req, struct hfi1_pkt_header *hdr, u32 lrhlen, u32 datalen) @@ -1253,401 +1222,3 @@ static inline void set_comp_state(struct hfi1_user_sdma_pkt_q *pq, trace_hfi1_sdma_user_completion(pq->dd, pq->ctxt, pq->subctxt, idx, state, ret); } - -static void unpin_vector_pages(struct mm_struct *mm, struct page **pages, - unsigned int start, unsigned int npages) -{ - hfi1_release_user_pages(mm, pages + start, npages, false); - kfree(pages); -} - -static void free_system_node(struct sdma_mmu_node *node) -{ - if (node->npages) { - unpin_vector_pages(mm_from_sdma_node(node), node->pages, 0, - node->npages); - atomic_sub(node->npages, &node->pq->n_locked); - } - kfree(node); -} - -/* - * kref_get()'s an additional kref on the returned rb_node to prevent rb_node - * from being released until after rb_node is assigned to an SDMA descriptor - * (struct sdma_desc) under add_system_iovec_to_sdma_packet(), even if the - * virtual address range for rb_node is invalidated between now and then. - */ -static struct sdma_mmu_node *find_system_node(struct mmu_rb_handler *handler, - unsigned long start, - unsigned long end) -{ - struct mmu_rb_node *rb_node; - unsigned long flags; - - spin_lock_irqsave(&handler->lock, flags); - rb_node = hfi1_mmu_rb_get_first(handler, start, (end - start)); - if (!rb_node) { - spin_unlock_irqrestore(&handler->lock, flags); - return NULL; - } - - /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ - kref_get(&rb_node->refcount); - spin_unlock_irqrestore(&handler->lock, flags); - - return container_of(rb_node, struct sdma_mmu_node, rb); -} - -static int pin_system_pages(struct user_sdma_request *req, - uintptr_t start_address, size_t length, - struct sdma_mmu_node *node, int npages) -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - int pinned, cleared; - struct page **pages; - - pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL); - if (!pages) - return -ENOMEM; - -retry: - if (!hfi1_can_pin_pages(pq->dd, current->mm, atomic_read(&pq->n_locked), - npages)) { - SDMA_DBG(req, "Evicting: nlocked %u npages %u", - atomic_read(&pq->n_locked), npages); - cleared = sdma_cache_evict(pq, npages); - if (cleared >= npages) - goto retry; - } - - SDMA_DBG(req, "Acquire user pages start_address %lx node->npages %u npages %u", - start_address, node->npages, npages); - pinned = hfi1_acquire_user_pages(current->mm, start_address, npages, 0, - pages); - - if (pinned < 0) { - kfree(pages); - SDMA_DBG(req, "pinned %d", pinned); - return pinned; - } - if (pinned != npages) { - unpin_vector_pages(current->mm, pages, node->npages, pinned); - SDMA_DBG(req, "npages %u pinned %d", npages, pinned); - return -EFAULT; - } - node->rb.addr = start_address; - node->rb.len = length; - node->pages = pages; - node->npages = npages; - atomic_add(pinned, &pq->n_locked); - SDMA_DBG(req, "done. pinned %d", pinned); - return 0; -} - -/* - * kref refcount on *node_p will be 2 on successful addition: one kref from - * kref_init() for mmu_rb_handler and one kref to prevent *node_p from being - * released until after *node_p is assigned to an SDMA descriptor (struct - * sdma_desc) under add_system_iovec_to_sdma_packet(), even if the virtual - * address range for *node_p is invalidated between now and then. - */ -static int add_system_pinning(struct user_sdma_request *req, - struct sdma_mmu_node **node_p, - unsigned long start, unsigned long len) - -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - struct sdma_mmu_node *node; - int ret; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - return -ENOMEM; - - /* First kref "moves" to mmu_rb_handler */ - kref_init(&node->rb.refcount); - - /* "safety" kref to prevent release before add_system_iovec_to_sdma_packet() */ - kref_get(&node->rb.refcount); - - node->pq = pq; - ret = pin_system_pages(req, start, len, node, PFN_DOWN(len)); - if (ret == 0) { - ret = hfi1_mmu_rb_insert(pq->handler, &node->rb); - if (ret) - free_system_node(node); - else - *node_p = node; - - return ret; - } - - kfree(node); - return ret; -} - -static int get_system_cache_entry(struct user_sdma_request *req, - struct sdma_mmu_node **node_p, - size_t req_start, size_t req_len) -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - u64 start = ALIGN_DOWN(req_start, PAGE_SIZE); - u64 end = PFN_ALIGN(req_start + req_len); - struct mmu_rb_handler *handler = pq->handler; - int ret; - - if ((end - start) == 0) { - SDMA_DBG(req, - "Request for empty cache entry req_start %lx req_len %lx start %llx end %llx", - req_start, req_len, start, end); - return -EINVAL; - } - - SDMA_DBG(req, "req_start %lx req_len %lu", req_start, req_len); - - while (1) { - struct sdma_mmu_node *node = - find_system_node(handler, start, end); - u64 prepend_len = 0; - - SDMA_DBG(req, "node %p start %llx end %llu", node, start, end); - if (!node) { - ret = add_system_pinning(req, node_p, start, - end - start); - if (ret == -EEXIST) { - /* - * Another execution context has inserted a - * conficting entry first. - */ - continue; - } - return ret; - } - - if (node->rb.addr <= start) { - /* - * This entry covers at least part of the region. If it doesn't extend - * to the end, then this will be called again for the next segment. - */ - *node_p = node; - return 0; - } - - SDMA_DBG(req, "prepend: node->rb.addr %lx, node->rb.refcount %d", - node->rb.addr, kref_read(&node->rb.refcount)); - prepend_len = node->rb.addr - start; - - /* - * This node will not be returned, instead a new node - * will be. So release the reference. - */ - kref_put(&node->rb.refcount, hfi1_mmu_rb_release); - - /* Prepend a node to cover the beginning of the allocation */ - ret = add_system_pinning(req, node_p, start, prepend_len); - if (ret == -EEXIST) { - /* Another execution context has inserted a conficting entry first. */ - continue; - } - return ret; - } -} - -static void sdma_mmu_rb_node_get(void *ctx) -{ - struct mmu_rb_node *node = ctx; - - kref_get(&node->refcount); -} - -static void sdma_mmu_rb_node_put(void *ctx) -{ - struct sdma_mmu_node *node = ctx; - - kref_put(&node->rb.refcount, hfi1_mmu_rb_release); -} - -static int add_mapping_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct sdma_mmu_node *cache_entry, - size_t start, - size_t from_this_cache_entry) -{ - struct hfi1_user_sdma_pkt_q *pq = req->pq; - unsigned int page_offset; - unsigned int from_this_page; - size_t page_index; - void *ctx; - int ret; - - /* - * Because the cache may be more fragmented than the memory that is being accessed, - * it's not strictly necessary to have a descriptor per cache entry. - */ - - while (from_this_cache_entry) { - page_index = PFN_DOWN(start - cache_entry->rb.addr); - - if (page_index >= cache_entry->npages) { - SDMA_DBG(req, - "Request for page_index %zu >= cache_entry->npages %u", - page_index, cache_entry->npages); - return -EINVAL; - } - - page_offset = start - ALIGN_DOWN(start, PAGE_SIZE); - from_this_page = PAGE_SIZE - page_offset; - - if (from_this_page < from_this_cache_entry) { - ctx = NULL; - } else { - /* - * In the case they are equal the next line has no practical effect, - * but it's better to do a register to register copy than a conditional - * branch. - */ - from_this_page = from_this_cache_entry; - ctx = cache_entry; - } - - ret = sdma_txadd_page(pq->dd, &tx->txreq, - cache_entry->pages[page_index], - page_offset, from_this_page, - ctx, - sdma_mmu_rb_node_get, - sdma_mmu_rb_node_put); - if (ret) { - /* - * When there's a failure, the entire request is freed by - * user_sdma_send_pkts(). - */ - SDMA_DBG(req, - "sdma_txadd_page failed %d page_index %lu page_offset %u from_this_page %u", - ret, page_index, page_offset, from_this_page); - return ret; - } - start += from_this_page; - from_this_cache_entry -= from_this_page; - } - return 0; -} - -static int add_system_iovec_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct user_sdma_iovec *iovec, - size_t from_this_iovec) -{ - while (from_this_iovec > 0) { - struct sdma_mmu_node *cache_entry; - size_t from_this_cache_entry; - size_t start; - int ret; - - start = (uintptr_t)iovec->iov.iov_base + iovec->offset; - ret = get_system_cache_entry(req, &cache_entry, start, - from_this_iovec); - if (ret) { - SDMA_DBG(req, "pin system segment failed %d", ret); - return ret; - } - - from_this_cache_entry = cache_entry->rb.len - (start - cache_entry->rb.addr); - if (from_this_cache_entry > from_this_iovec) - from_this_cache_entry = from_this_iovec; - - ret = add_mapping_to_sdma_packet(req, tx, cache_entry, start, - from_this_cache_entry); - - /* - * Done adding cache_entry to zero or more sdma_desc. Can - * kref_put() the "safety" kref taken under - * get_system_cache_entry(). - */ - kref_put(&cache_entry->rb.refcount, hfi1_mmu_rb_release); - - if (ret) { - SDMA_DBG(req, "add system segment failed %d", ret); - return ret; - } - - iovec->offset += from_this_cache_entry; - from_this_iovec -= from_this_cache_entry; - } - - return 0; -} - -static int add_system_pages_to_sdma_packet(struct user_sdma_request *req, - struct user_sdma_txreq *tx, - struct user_sdma_iovec *iovec, - u32 *pkt_data_remaining) -{ - size_t remaining_to_add = *pkt_data_remaining; - /* - * Walk through iovec entries, ensure the associated pages - * are pinned and mapped, add data to the packet until no more - * data remains to be added. - */ - while (remaining_to_add > 0) { - struct user_sdma_iovec *cur_iovec; - size_t from_this_iovec; - int ret; - - cur_iovec = iovec; - from_this_iovec = iovec->iov.iov_len - iovec->offset; - - if (from_this_iovec > remaining_to_add) { - from_this_iovec = remaining_to_add; - } else { - /* The current iovec entry will be consumed by this pass. */ - req->iov_idx++; - iovec++; - } - - ret = add_system_iovec_to_sdma_packet(req, tx, cur_iovec, - from_this_iovec); - if (ret) - return ret; - - remaining_to_add -= from_this_iovec; - } - *pkt_data_remaining = remaining_to_add; - - return 0; -} - -static bool sdma_rb_filter(struct mmu_rb_node *node, unsigned long addr, - unsigned long len) -{ - return (bool)(node->addr == addr); -} - -/* - * Return 1 to remove the node from the rb tree and call the remove op. - * - * Called with the rb tree lock held. - */ -static int sdma_rb_evict(void *arg, struct mmu_rb_node *mnode, - void *evict_arg, bool *stop) -{ - struct sdma_mmu_node *node = - container_of(mnode, struct sdma_mmu_node, rb); - struct evict_data *evict_data = evict_arg; - - /* this node will be evicted, add its pages to our count */ - evict_data->cleared += node->npages; - - /* have enough pages been cleared? */ - if (evict_data->cleared >= evict_data->target) - *stop = true; - - return 1; /* remove this node */ -} - -static void sdma_rb_remove(void *arg, struct mmu_rb_node *mnode) -{ - struct sdma_mmu_node *node = - container_of(mnode, struct sdma_mmu_node, rb); - - free_system_node(node); -} diff --git a/drivers/infiniband/hw/hfi1/user_sdma.h b/drivers/infiniband/hw/hfi1/user_sdma.h index 548347d4c5bc274e4751897d75404b33f4fa6fe4..742ec1470cc523fa2e345abb4be9b04fd3d25238 100644 --- a/drivers/infiniband/hw/hfi1/user_sdma.h +++ b/drivers/infiniband/hw/hfi1/user_sdma.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */ /* - * Copyright(c) 2020 - Cornelis Networks, Inc. + * Copyright(c) 2023 - Cornelis Networks, Inc. * Copyright(c) 2015 - 2018 Intel Corporation. */ #ifndef _HFI1_USER_SDMA_H @@ -13,6 +13,8 @@ #include "iowait.h" #include "user_exp_rcv.h" #include "mmu_rb.h" +#include "pinning.h" +#include "sdma.h" /* The maximum number of Data io vectors per message/request */ #define MAX_VECTORS_PER_REQ 8 @@ -101,13 +103,6 @@ struct hfi1_user_sdma_comp_q { struct hfi1_sdma_comp_entry *comps; }; -struct sdma_mmu_node { - struct mmu_rb_node rb; - struct hfi1_user_sdma_pkt_q *pq; - struct page **pages; - unsigned int npages; -}; - struct user_sdma_iovec { struct list_head list; struct iovec iov; @@ -203,10 +198,4 @@ int hfi1_user_sdma_free_queues(struct hfi1_filedata *fd, int hfi1_user_sdma_process_request(struct hfi1_filedata *fd, struct iovec *iovec, unsigned long dim, unsigned long *count); - -static inline struct mm_struct *mm_from_sdma_node(struct sdma_mmu_node *node) -{ - return node->rb.handler->mn.mm; -} - #endif /* _HFI1_USER_SDMA_H */ diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 84239b907de2aeef03d3440c43d6d3404fa67ca2..7f0d0288beb1e0ee02c49ca3b692885c85d79bb8 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -97,6 +97,7 @@ #define HNS_ROCE_CQ_BANK_NUM 4 #define CQ_BANKID_SHIFT 2 +#define CQ_BANKID_MASK GENMASK(1, 0) enum { SERV_TYPE_RC, @@ -714,7 +715,6 @@ struct hns_roce_caps { u32 max_rq_sg; u32 rsv0; u32 num_qps; - u32 num_pi_qps; u32 reserved_qps; u32 num_srqs; u32 max_wqes; @@ -840,6 +840,32 @@ enum hns_roce_device_state { HNS_ROCE_DEVICE_STATE_UNINIT, }; +enum hns_roce_hw_pkt_stat_index { + HNS_ROCE_HW_RX_RC_PKT_CNT, + HNS_ROCE_HW_RX_UC_PKT_CNT, + HNS_ROCE_HW_RX_UD_PKT_CNT, + HNS_ROCE_HW_RX_XRC_PKT_CNT, + HNS_ROCE_HW_RX_PKT_CNT, + HNS_ROCE_HW_RX_ERR_PKT_CNT, + HNS_ROCE_HW_RX_CNP_PKT_CNT, + HNS_ROCE_HW_TX_RC_PKT_CNT, + HNS_ROCE_HW_TX_UC_PKT_CNT, + HNS_ROCE_HW_TX_UD_PKT_CNT, + HNS_ROCE_HW_TX_XRC_PKT_CNT, + HNS_ROCE_HW_TX_PKT_CNT, + HNS_ROCE_HW_TX_ERR_PKT_CNT, + HNS_ROCE_HW_TX_CNP_PKT_CNT, + HNS_ROCE_HW_TRP_GET_MPT_ERR_PKT_CNT, + HNS_ROCE_HW_TRP_GET_IRRL_ERR_PKT_CNT, + HNS_ROCE_HW_ECN_DB_CNT, + HNS_ROCE_HW_RX_BUF_CNT, + HNS_ROCE_HW_TRP_RX_SOF_CNT, + HNS_ROCE_HW_CQ_CQE_CNT, + HNS_ROCE_HW_CQ_POE_CNT, + HNS_ROCE_HW_CQ_NOTIFY_CNT, + HNS_ROCE_HW_CNT_TOTAL +}; + struct hns_roce_hw { int (*cmq_init)(struct hns_roce_dev *hr_dev); void (*cmq_exit)(struct hns_roce_dev *hr_dev); @@ -882,6 +908,8 @@ struct hns_roce_hw { int (*query_cqc)(struct hns_roce_dev *hr_dev, u32 cqn, void *buffer); int (*query_qpc)(struct hns_roce_dev *hr_dev, u32 qpn, void *buffer); int (*query_mpt)(struct hns_roce_dev *hr_dev, u32 key, void *buffer); + int (*query_hw_counter)(struct hns_roce_dev *hr_dev, + u64 *stats, u32 port, int *hw_counters); const struct ib_device_ops *hns_roce_dev_ops; const struct ib_device_ops *hns_roce_dev_srq_ops; }; @@ -1112,7 +1140,6 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev); void hns_roce_init_srq_table(struct hns_roce_dev *hr_dev); void hns_roce_init_xrcd_table(struct hns_roce_dev *hr_dev); -void hns_roce_cleanup_eq_table(struct hns_roce_dev *hr_dev); void hns_roce_cleanup_cq_table(struct hns_roce_dev *hr_dev); void hns_roce_cleanup_qp_table(struct hns_roce_dev *hr_dev); @@ -1161,9 +1188,6 @@ int hns_roce_get_umem_bufs(struct hns_roce_dev *hr_dev, dma_addr_t *bufs, int hns_roce_create_srq(struct ib_srq *srq, struct ib_srq_init_attr *srq_init_attr, struct ib_udata *udata); -int hns_roce_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *srq_attr, - enum ib_srq_attr_mask srq_attr_mask, - struct ib_udata *udata); int hns_roce_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata); int hns_roce_alloc_xrcd(struct ib_xrcd *ib_xrcd, struct ib_udata *udata); @@ -1206,7 +1230,6 @@ void hns_roce_cq_event(struct hns_roce_dev *hr_dev, u32 cqn, int event_type); void flush_cqe(struct hns_roce_dev *dev, struct hns_roce_qp *qp); void hns_roce_qp_event(struct hns_roce_dev *hr_dev, u32 qpn, int event_type); void hns_roce_srq_event(struct hns_roce_dev *hr_dev, u32 srqn, int event_type); -u8 hns_get_gid_index(struct hns_roce_dev *hr_dev, u32 port, int gid_index); void hns_roce_handle_device_err(struct hns_roce_dev *hr_dev); int hns_roce_init(struct hns_roce_dev *hr_dev); void hns_roce_exit(struct hns_roce_dev *hr_dev); diff --git a/drivers/infiniband/hw/hns/hns_roce_hem.c b/drivers/infiniband/hw/hns/hns_roce_hem.c index 47c0efed18211a31d38df3e7f8ee9f76038e279c..c4ac06a338696910939656703a308342832f5ba0 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hem.c +++ b/drivers/infiniband/hw/hns/hns_roce_hem.c @@ -78,7 +78,7 @@ bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type) return false; } - return hop_num ? true : false; + return hop_num; } static bool hns_roce_check_hem_null(struct hns_roce_hem **hem, u64 hem_idx, diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index 8f7eb11066b43e8a12f9db59cbb5dafe6a6de7ca..d82daff2d9bd5c8b817246617167365cf0d72ea2 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -750,7 +750,8 @@ static int hns_roce_v2_post_send(struct ib_qp *ibqp, qp->sq.head += nreq; qp->next_sge = sge_idx; - if (nreq == 1 && (qp->en_flags & HNS_ROCE_QP_CAP_DIRECT_WQE)) + if (nreq == 1 && !ret && + (qp->en_flags & HNS_ROCE_QP_CAP_DIRECT_WQE)) write_dwqe(hr_dev, qp, wqe); else update_sq_db(hr_dev, qp); @@ -1612,6 +1613,56 @@ static int hns_roce_query_func_info(struct hns_roce_dev *hr_dev) return 0; } +static int hns_roce_hw_v2_query_counter(struct hns_roce_dev *hr_dev, + u64 *stats, u32 port, int *num_counters) +{ +#define CNT_PER_DESC 3 + struct hns_roce_cmq_desc *desc; + int bd_idx, cnt_idx; + __le64 *cnt_data; + int desc_num; + int ret; + int i; + + if (port > hr_dev->caps.num_ports) + return -EINVAL; + + desc_num = DIV_ROUND_UP(HNS_ROCE_HW_CNT_TOTAL, CNT_PER_DESC); + desc = kcalloc(desc_num, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + for (i = 0; i < desc_num; i++) { + hns_roce_cmq_setup_basic_desc(&desc[i], + HNS_ROCE_OPC_QUERY_COUNTER, true); + if (i != desc_num - 1) + desc[i].flag |= cpu_to_le16(HNS_ROCE_CMD_FLAG_NEXT); + } + + ret = hns_roce_cmq_send(hr_dev, desc, desc_num); + if (ret) { + ibdev_err(&hr_dev->ib_dev, + "failed to get counter, ret = %d.\n", ret); + goto err_out; + } + + for (i = 0; i < HNS_ROCE_HW_CNT_TOTAL && i < *num_counters; i++) { + bd_idx = i / CNT_PER_DESC; + if (!(desc[bd_idx].flag & HNS_ROCE_CMD_FLAG_NEXT) && + bd_idx != HNS_ROCE_HW_CNT_TOTAL / CNT_PER_DESC) + break; + + cnt_data = (__le64 *)&desc[bd_idx].data[0]; + cnt_idx = i % CNT_PER_DESC; + stats[i] = le64_to_cpu(cnt_data[cnt_idx]); + } + *num_counters = i; + +err_out: + kfree(desc); + return ret; +} + static int hns_roce_config_global_param(struct hns_roce_dev *hr_dev) { struct hns_roce_cmq_desc desc; @@ -1680,29 +1731,6 @@ static int load_func_res_caps(struct hns_roce_dev *hr_dev, bool is_vf) return 0; } -static int load_ext_cfg_caps(struct hns_roce_dev *hr_dev, bool is_vf) -{ - struct hns_roce_cmq_desc desc; - struct hns_roce_cmq_req *req = (struct hns_roce_cmq_req *)desc.data; - struct hns_roce_caps *caps = &hr_dev->caps; - u32 func_num, qp_num; - int ret; - - hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_EXT_CFG, true); - ret = hns_roce_cmq_send(hr_dev, &desc, 1); - if (ret) - return ret; - - func_num = is_vf ? 1 : max_t(u32, 1, hr_dev->func_num); - qp_num = hr_reg_read(req, EXT_CFG_QP_PI_NUM) / func_num; - caps->num_pi_qps = round_down(qp_num, HNS_ROCE_QP_BANK_NUM); - - qp_num = hr_reg_read(req, EXT_CFG_QP_NUM) / func_num; - caps->num_qps = round_down(qp_num, HNS_ROCE_QP_BANK_NUM); - - return 0; -} - static int load_pf_timer_res_caps(struct hns_roce_dev *hr_dev) { struct hns_roce_cmq_desc desc; @@ -1723,50 +1751,37 @@ static int load_pf_timer_res_caps(struct hns_roce_dev *hr_dev) return 0; } -static int query_func_resource_caps(struct hns_roce_dev *hr_dev, bool is_vf) +static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev) { struct device *dev = hr_dev->dev; int ret; - ret = load_func_res_caps(hr_dev, is_vf); + ret = load_func_res_caps(hr_dev, false); if (ret) { - dev_err(dev, "failed to load res caps, ret = %d (%s).\n", ret, - is_vf ? "vf" : "pf"); + dev_err(dev, "failed to load pf res caps, ret = %d.\n", ret); return ret; } - if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) { - ret = load_ext_cfg_caps(hr_dev, is_vf); - if (ret) - dev_err(dev, "failed to load ext cfg, ret = %d (%s).\n", - ret, is_vf ? "vf" : "pf"); - } + ret = load_pf_timer_res_caps(hr_dev); + if (ret) + dev_err(dev, "failed to load pf timer resource, ret = %d.\n", + ret); return ret; } -static int hns_roce_query_pf_resource(struct hns_roce_dev *hr_dev) +static int hns_roce_query_vf_resource(struct hns_roce_dev *hr_dev) { struct device *dev = hr_dev->dev; int ret; - ret = query_func_resource_caps(hr_dev, false); + ret = load_func_res_caps(hr_dev, true); if (ret) - return ret; - - ret = load_pf_timer_res_caps(hr_dev); - if (ret) - dev_err(dev, "failed to load pf timer resource, ret = %d.\n", - ret); + dev_err(dev, "failed to load vf res caps, ret = %d.\n", ret); return ret; } -static int hns_roce_query_vf_resource(struct hns_roce_dev *hr_dev) -{ - return query_func_resource_caps(hr_dev, true); -} - static int __hns_roce_set_vf_switch_param(struct hns_roce_dev *hr_dev, u32 vf_id) { @@ -1849,24 +1864,6 @@ static int config_vf_hem_resource(struct hns_roce_dev *hr_dev, int vf_id) return hns_roce_cmq_send(hr_dev, desc, 2); } -static int config_vf_ext_resource(struct hns_roce_dev *hr_dev, u32 vf_id) -{ - struct hns_roce_cmq_desc desc; - struct hns_roce_cmq_req *req = (struct hns_roce_cmq_req *)desc.data; - struct hns_roce_caps *caps = &hr_dev->caps; - - hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_EXT_CFG, false); - - hr_reg_write(req, EXT_CFG_VF_ID, vf_id); - - hr_reg_write(req, EXT_CFG_QP_PI_NUM, caps->num_pi_qps); - hr_reg_write(req, EXT_CFG_QP_PI_IDX, vf_id * caps->num_pi_qps); - hr_reg_write(req, EXT_CFG_QP_NUM, caps->num_qps); - hr_reg_write(req, EXT_CFG_QP_IDX, vf_id * caps->num_qps); - - return hns_roce_cmq_send(hr_dev, &desc, 1); -} - static int hns_roce_alloc_vf_resource(struct hns_roce_dev *hr_dev) { u32 func_num = max_t(u32, 1, hr_dev->func_num); @@ -1881,16 +1878,6 @@ static int hns_roce_alloc_vf_resource(struct hns_roce_dev *hr_dev) vf_id, ret); return ret; } - - if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) { - ret = config_vf_ext_resource(hr_dev, vf_id); - if (ret) { - dev_err(hr_dev->dev, - "failed to config vf-%u ext res, ret = %d.\n", - vf_id, ret); - return ret; - } - } } return 0; @@ -2075,9 +2062,6 @@ static void apply_func_caps(struct hns_roce_dev *hr_dev) caps->qpc_timer_hop_num = HNS_ROCE_HOP_NUM_0; caps->cqc_timer_hop_num = HNS_ROCE_HOP_NUM_0; - caps->num_xrcds = HNS_ROCE_V2_MAX_XRCD_NUM; - caps->reserved_xrcds = HNS_ROCE_V2_RSV_XRCD_NUM; - caps->num_srqwqe_segs = HNS_ROCE_V2_MAX_SRQWQE_SEGS; caps->num_idx_segs = HNS_ROCE_V2_MAX_IDX_SEGS; @@ -2200,6 +2184,7 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev) caps->num_cqs = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_CQS); caps->gid_table_len[0] = hr_reg_read(resp_c, PF_CAPS_C_MAX_GID); caps->max_cqes = 1 << hr_reg_read(resp_c, PF_CAPS_C_CQ_DEPTH); + caps->num_xrcds = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_XRCDS); caps->num_mtpts = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_MRWS); caps->num_qps = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_QPS); caps->max_qp_init_rdma = hr_reg_read(resp_c, PF_CAPS_C_MAX_ORD); @@ -2220,6 +2205,7 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev) caps->reserved_mrws = hr_reg_read(resp_e, PF_CAPS_E_RSV_MRWS); caps->chunk_sz = 1 << hr_reg_read(resp_e, PF_CAPS_E_CHUNK_SIZE_SHIFT); caps->reserved_cqs = hr_reg_read(resp_e, PF_CAPS_E_RSV_CQS); + caps->reserved_xrcds = hr_reg_read(resp_e, PF_CAPS_E_RSV_XRCDS); caps->reserved_srqs = hr_reg_read(resp_e, PF_CAPS_E_RSV_SRQS); caps->reserved_lkey = hr_reg_read(resp_e, PF_CAPS_E_RSV_LKEYS); @@ -6646,6 +6632,7 @@ static const struct hns_roce_hw hns_roce_hw_v2 = { .query_cqc = hns_roce_v2_query_cqc, .query_qpc = hns_roce_v2_query_qpc, .query_mpt = hns_roce_v2_query_mpt, + .query_hw_counter = hns_roce_hw_v2_query_counter, .hns_roce_dev_ops = &hns_roce_v2_dev_ops, .hns_roce_dev_srq_ops = &hns_roce_v2_dev_srq_ops, }; @@ -6722,14 +6709,14 @@ static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) ret = hns_roce_init(hr_dev); if (ret) { dev_err(hr_dev->dev, "RoCE Engine init failed!\n"); - goto error_failed_cfg; + goto error_failed_roce_init; } if (hr_dev->pci_dev->revision == PCI_REVISION_ID_HIP08) { ret = free_mr_init(hr_dev); if (ret) { dev_err(hr_dev->dev, "failed to init free mr!\n"); - goto error_failed_roce_init; + goto error_failed_free_mr_init; } } @@ -6737,10 +6724,10 @@ static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle) return 0; -error_failed_roce_init: +error_failed_free_mr_init: hns_roce_exit(hr_dev); -error_failed_cfg: +error_failed_roce_init: kfree(hr_dev->priv); error_failed_kzalloc: diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 7033eae2407c541c88a07d4a8543678c1fb630f7..cd97cbee682a6a51a4290720881a7a3b89d26a42 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -42,7 +42,6 @@ #define HNS_ROCE_V2_MAX_SRQWQE_SEGS 0x1000000 #define HNS_ROCE_V2_MAX_IDX_SEGS 0x1000000 #define HNS_ROCE_V2_MAX_XRCD_NUM 0x1000000 -#define HNS_ROCE_V2_RSV_XRCD_NUM 0 #define HNS_ROCE_V2_QP_ACK_TIMEOUT_OFS_HIP08 10 @@ -199,6 +198,7 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_QUERY_HW_VER = 0x8000, HNS_ROCE_OPC_CFG_GLOBAL_PARAM = 0x8001, HNS_ROCE_OPC_ALLOC_PF_RES = 0x8004, + HNS_ROCE_OPC_QUERY_COUNTER = 0x8206, HNS_ROCE_OPC_QUERY_PF_RES = 0x8400, HNS_ROCE_OPC_ALLOC_VF_RES = 0x8401, HNS_ROCE_OPC_CFG_EXT_LLM = 0x8403, @@ -220,7 +220,6 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_QUERY_VF_RES = 0x850e, HNS_ROCE_OPC_CFG_GMV_TBL = 0x850f, HNS_ROCE_OPC_CFG_GMV_BT = 0x8510, - HNS_ROCE_OPC_EXT_CFG = 0x8512, HNS_ROCE_QUERY_RAM_ECC = 0x8513, HNS_SWITCH_PARAMETER_CFG = 0x1033, }; @@ -957,15 +956,6 @@ struct hns_roce_func_clear { #define HNS_ROCE_V2_READ_FUNC_CLEAR_FLAG_INTERVAL 40 #define HNS_ROCE_V2_READ_FUNC_CLEAR_FLAG_FAIL_WAIT 20 -/* Fields of HNS_ROCE_OPC_EXT_CFG */ -#define EXT_CFG_VF_ID CMQ_REQ_FIELD_LOC(31, 0) -#define EXT_CFG_QP_PI_IDX CMQ_REQ_FIELD_LOC(45, 32) -#define EXT_CFG_QP_PI_NUM CMQ_REQ_FIELD_LOC(63, 48) -#define EXT_CFG_QP_NUM CMQ_REQ_FIELD_LOC(87, 64) -#define EXT_CFG_QP_IDX CMQ_REQ_FIELD_LOC(119, 96) -#define EXT_CFG_LLM_IDX CMQ_REQ_FIELD_LOC(139, 128) -#define EXT_CFG_LLM_NUM CMQ_REQ_FIELD_LOC(156, 144) - #define CFG_LLM_A_BA_L CMQ_REQ_FIELD_LOC(31, 0) #define CFG_LLM_A_BA_H CMQ_REQ_FIELD_LOC(63, 32) #define CFG_LLM_A_DEPTH CMQ_REQ_FIELD_LOC(76, 64) @@ -1202,6 +1192,7 @@ struct hns_roce_query_pf_caps_c { #define PF_CAPS_C_NUM_CQS PF_CAPS_C_FIELD_LOC(51, 32) #define PF_CAPS_C_MAX_GID PF_CAPS_C_FIELD_LOC(60, 52) #define PF_CAPS_C_CQ_DEPTH PF_CAPS_C_FIELD_LOC(86, 64) +#define PF_CAPS_C_NUM_XRCDS PF_CAPS_C_FIELD_LOC(91, 87) #define PF_CAPS_C_NUM_MRWS PF_CAPS_C_FIELD_LOC(115, 96) #define PF_CAPS_C_NUM_QPS PF_CAPS_C_FIELD_LOC(147, 128) #define PF_CAPS_C_MAX_ORD PF_CAPS_C_FIELD_LOC(155, 148) @@ -1260,6 +1251,7 @@ struct hns_roce_query_pf_caps_e { #define PF_CAPS_E_RSV_MRWS PF_CAPS_E_FIELD_LOC(19, 0) #define PF_CAPS_E_CHUNK_SIZE_SHIFT PF_CAPS_E_FIELD_LOC(31, 20) #define PF_CAPS_E_RSV_CQS PF_CAPS_E_FIELD_LOC(51, 32) +#define PF_CAPS_E_RSV_XRCDS PF_CAPS_E_FIELD_LOC(63, 52) #define PF_CAPS_E_RSV_SRQS PF_CAPS_E_FIELD_LOC(83, 64) #define PF_CAPS_E_RSV_LKEYS PF_CAPS_E_FIELD_LOC(115, 96) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index 485e110ca43336e7efea48a6539d19c147f16eea..d9d546cdef525e83844628cfa0d5b61f801ef25a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -219,6 +219,7 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num, unsigned long flags; enum ib_mtu mtu; u32 port; + int ret; port = port_num - 1; @@ -231,8 +232,10 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num, IB_PORT_BOOT_MGMT_SUP; props->max_msg_sz = HNS_ROCE_MAX_MSG_LEN; props->pkey_tbl_len = 1; - props->active_width = IB_WIDTH_4X; - props->active_speed = 1; + ret = ib_get_eth_speed(ib_dev, port_num, &props->active_speed, + &props->active_width); + if (ret) + ibdev_warn(ib_dev, "failed to get speed, ret = %d.\n", ret); spin_lock_irqsave(&hr_dev->iboe.lock, flags); @@ -512,6 +515,83 @@ static void hns_roce_get_fw_ver(struct ib_device *device, char *str) sub_minor); } +#define HNS_ROCE_HW_CNT(ename, cname) \ + [HNS_ROCE_HW_##ename##_CNT].name = cname + +static const struct rdma_stat_desc hns_roce_port_stats_descs[] = { + HNS_ROCE_HW_CNT(RX_RC_PKT, "rx_rc_pkt"), + HNS_ROCE_HW_CNT(RX_UC_PKT, "rx_uc_pkt"), + HNS_ROCE_HW_CNT(RX_UD_PKT, "rx_ud_pkt"), + HNS_ROCE_HW_CNT(RX_XRC_PKT, "rx_xrc_pkt"), + HNS_ROCE_HW_CNT(RX_PKT, "rx_pkt"), + HNS_ROCE_HW_CNT(RX_ERR_PKT, "rx_err_pkt"), + HNS_ROCE_HW_CNT(RX_CNP_PKT, "rx_cnp_pkt"), + HNS_ROCE_HW_CNT(TX_RC_PKT, "tx_rc_pkt"), + HNS_ROCE_HW_CNT(TX_UC_PKT, "tx_uc_pkt"), + HNS_ROCE_HW_CNT(TX_UD_PKT, "tx_ud_pkt"), + HNS_ROCE_HW_CNT(TX_XRC_PKT, "tx_xrc_pkt"), + HNS_ROCE_HW_CNT(TX_PKT, "tx_pkt"), + HNS_ROCE_HW_CNT(TX_ERR_PKT, "tx_err_pkt"), + HNS_ROCE_HW_CNT(TX_CNP_PKT, "tx_cnp_pkt"), + HNS_ROCE_HW_CNT(TRP_GET_MPT_ERR_PKT, "trp_get_mpt_err_pkt"), + HNS_ROCE_HW_CNT(TRP_GET_IRRL_ERR_PKT, "trp_get_irrl_err_pkt"), + HNS_ROCE_HW_CNT(ECN_DB, "ecn_doorbell"), + HNS_ROCE_HW_CNT(RX_BUF, "rx_buffer"), + HNS_ROCE_HW_CNT(TRP_RX_SOF, "trp_rx_sof"), + HNS_ROCE_HW_CNT(CQ_CQE, "cq_cqe"), + HNS_ROCE_HW_CNT(CQ_POE, "cq_poe"), + HNS_ROCE_HW_CNT(CQ_NOTIFY, "cq_notify"), +}; + +static struct rdma_hw_stats *hns_roce_alloc_hw_port_stats( + struct ib_device *device, u32 port_num) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(device); + u32 port = port_num - 1; + + if (port > hr_dev->caps.num_ports) { + ibdev_err(device, "invalid port num.\n"); + return NULL; + } + + if (hr_dev->pci_dev->revision <= PCI_REVISION_ID_HIP08 || + hr_dev->is_vf) + return NULL; + + return rdma_alloc_hw_stats_struct(hns_roce_port_stats_descs, + ARRAY_SIZE(hns_roce_port_stats_descs), + RDMA_HW_STATS_DEFAULT_LIFESPAN); +} + +static int hns_roce_get_hw_stats(struct ib_device *device, + struct rdma_hw_stats *stats, + u32 port, int index) +{ + struct hns_roce_dev *hr_dev = to_hr_dev(device); + int num_counters = HNS_ROCE_HW_CNT_TOTAL; + int ret; + + if (port == 0) + return 0; + + if (port > hr_dev->caps.num_ports) + return -EINVAL; + + if (hr_dev->pci_dev->revision <= PCI_REVISION_ID_HIP08 || + hr_dev->is_vf) + return -EOPNOTSUPP; + + ret = hr_dev->hw->query_hw_counter(hr_dev, stats->value, port, + &num_counters); + if (ret) { + ibdev_err(device, "failed to query hw counter, ret = %d\n", + ret); + return ret; + } + + return num_counters; +} + static void hns_roce_unregister_device(struct hns_roce_dev *hr_dev) { struct hns_roce_ib_iboe *iboe = &hr_dev->iboe; @@ -554,6 +634,8 @@ static const struct ib_device_ops hns_roce_dev_ops = { .query_pkey = hns_roce_query_pkey, .query_port = hns_roce_query_port, .reg_user_mr = hns_roce_reg_user_mr, + .alloc_hw_port_stats = hns_roce_alloc_hw_port_stats, + .get_hw_stats = hns_roce_get_hw_stats, INIT_RDMA_OBJ_SIZE(ib_ah, hns_roce_ah, ibah), INIT_RDMA_OBJ_SIZE(ib_cq, hns_roce_cq, ib_cq), diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c index d855a917f4cfa5c95458c93d97a9dd427eb58af1..cdc1c6de43a174e25b177e6dac9de06c0163d4b4 100644 --- a/drivers/infiniband/hw/hns/hns_roce_qp.c +++ b/drivers/infiniband/hw/hns/hns_roce_qp.c @@ -170,14 +170,29 @@ static void hns_roce_ib_qp_event(struct hns_roce_qp *hr_qp, } } -static u8 get_least_load_bankid_for_qp(struct hns_roce_bank *bank) +static u8 get_affinity_cq_bank(u8 qp_bank) { - u32 least_load = bank[0].inuse; + return (qp_bank >> 1) & CQ_BANKID_MASK; +} + +static u8 get_least_load_bankid_for_qp(struct ib_qp_init_attr *init_attr, + struct hns_roce_bank *bank) +{ +#define INVALID_LOAD_QPNUM 0xFFFFFFFF + struct ib_cq *scq = init_attr->send_cq; + u32 least_load = INVALID_LOAD_QPNUM; + unsigned long cqn = 0; u8 bankid = 0; u32 bankcnt; u8 i; - for (i = 1; i < HNS_ROCE_QP_BANK_NUM; i++) { + if (scq) + cqn = to_hr_cq(scq)->cqn; + + for (i = 0; i < HNS_ROCE_QP_BANK_NUM; i++) { + if (scq && (get_affinity_cq_bank(i) != (cqn & CQ_BANKID_MASK))) + continue; + bankcnt = bank[i].inuse; if (bankcnt < least_load) { least_load = bankcnt; @@ -209,7 +224,8 @@ static int alloc_qpn_with_bankid(struct hns_roce_bank *bank, u8 bankid, return 0; } -static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) +static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp, + struct ib_qp_init_attr *init_attr) { struct hns_roce_qp_table *qp_table = &hr_dev->qp_table; unsigned long num = 0; @@ -220,7 +236,7 @@ static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp) num = 1; } else { mutex_lock(&qp_table->bank_mutex); - bankid = get_least_load_bankid_for_qp(qp_table->bank); + bankid = get_least_load_bankid_for_qp(init_attr, qp_table->bank); ret = alloc_qpn_with_bankid(&qp_table->bank[bankid], bankid, &num); @@ -1082,7 +1098,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev, goto err_buf; } - ret = alloc_qpn(hr_dev, hr_qp); + ret = alloc_qpn(hr_dev, hr_qp, init_attr); if (ret) { ibdev_err(ibdev, "failed to alloc QPN, ret = %d.\n", ret); goto err_qpn; diff --git a/drivers/infiniband/hw/hns/hns_roce_restrack.c b/drivers/infiniband/hw/hns/hns_roce_restrack.c index 989a2af2e93829371aff1aa024daae382bcb9f03..081a01de305535cd3bb3d41d585e47249ab5486e 100644 --- a/drivers/infiniband/hw/hns/hns_roce_restrack.c +++ b/drivers/infiniband/hw/hns/hns_roce_restrack.c @@ -9,8 +9,6 @@ #include "hns_roce_device.h" #include "hns_roce_hw_v2.h" -#define MAX_ENTRY_NUM 256 - int hns_roce_fill_res_cq_entry(struct sk_buff *msg, struct ib_cq *ib_cq) { struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq); @@ -47,8 +45,6 @@ int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq) struct hns_roce_dev *hr_dev = to_hr_dev(ib_cq->device); struct hns_roce_cq *hr_cq = to_hr_cq(ib_cq); struct hns_roce_v2_cq_context context; - u32 data[MAX_ENTRY_NUM] = {}; - int offset = 0; int ret; if (!hr_dev->hw->query_cqc) @@ -58,23 +54,7 @@ int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq) if (ret) return -EINVAL; - data[offset++] = hr_reg_read(&context, CQC_CQ_ST); - data[offset++] = hr_reg_read(&context, CQC_SHIFT); - data[offset++] = hr_reg_read(&context, CQC_CQE_SIZE); - data[offset++] = hr_reg_read(&context, CQC_CQE_CNT); - data[offset++] = hr_reg_read(&context, CQC_CQ_PRODUCER_IDX); - data[offset++] = hr_reg_read(&context, CQC_CQ_CONSUMER_IDX); - data[offset++] = hr_reg_read(&context, CQC_DB_RECORD_EN); - data[offset++] = hr_reg_read(&context, CQC_ARM_ST); - data[offset++] = hr_reg_read(&context, CQC_CMD_SN); - data[offset++] = hr_reg_read(&context, CQC_CEQN); - data[offset++] = hr_reg_read(&context, CQC_CQ_MAX_CNT); - data[offset++] = hr_reg_read(&context, CQC_CQ_PERIOD); - data[offset++] = hr_reg_read(&context, CQC_CQE_HOP_NUM); - data[offset++] = hr_reg_read(&context, CQC_CQE_BAR_PG_SZ); - data[offset++] = hr_reg_read(&context, CQC_CQE_BUF_PG_SZ); - - ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, offset * sizeof(u32), data); + ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context); return ret; } @@ -118,8 +98,6 @@ int hns_roce_fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ib_qp) struct hns_roce_dev *hr_dev = to_hr_dev(ib_qp->device); struct hns_roce_qp *hr_qp = to_hr_qp(ib_qp); struct hns_roce_v2_qp_context context; - u32 data[MAX_ENTRY_NUM] = {}; - int offset = 0; int ret; if (!hr_dev->hw->query_qpc) @@ -129,42 +107,7 @@ int hns_roce_fill_res_qp_entry_raw(struct sk_buff *msg, struct ib_qp *ib_qp) if (ret) return -EINVAL; - data[offset++] = hr_reg_read(&context, QPC_QP_ST); - data[offset++] = hr_reg_read(&context, QPC_ERR_TYPE); - data[offset++] = hr_reg_read(&context, QPC_CHECK_FLG); - data[offset++] = hr_reg_read(&context, QPC_SRQ_EN); - data[offset++] = hr_reg_read(&context, QPC_SRQN); - data[offset++] = hr_reg_read(&context, QPC_QKEY_XRCD); - data[offset++] = hr_reg_read(&context, QPC_TX_CQN); - data[offset++] = hr_reg_read(&context, QPC_RX_CQN); - data[offset++] = hr_reg_read(&context, QPC_SQ_PRODUCER_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_CONSUMER_IDX); - data[offset++] = hr_reg_read(&context, QPC_RQ_RECORD_EN); - data[offset++] = hr_reg_read(&context, QPC_RQ_PRODUCER_IDX); - data[offset++] = hr_reg_read(&context, QPC_RQ_CONSUMER_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_SHIFT); - data[offset++] = hr_reg_read(&context, QPC_RQWS); - data[offset++] = hr_reg_read(&context, QPC_RQ_SHIFT); - data[offset++] = hr_reg_read(&context, QPC_SGE_SHIFT); - data[offset++] = hr_reg_read(&context, QPC_SQ_HOP_NUM); - data[offset++] = hr_reg_read(&context, QPC_RQ_HOP_NUM); - data[offset++] = hr_reg_read(&context, QPC_SGE_HOP_NUM); - data[offset++] = hr_reg_read(&context, QPC_WQE_SGE_BA_PG_SZ); - data[offset++] = hr_reg_read(&context, QPC_WQE_SGE_BUF_PG_SZ); - data[offset++] = hr_reg_read(&context, QPC_RETRY_NUM_INIT); - data[offset++] = hr_reg_read(&context, QPC_RETRY_CNT); - data[offset++] = hr_reg_read(&context, QPC_SQ_CUR_PSN); - data[offset++] = hr_reg_read(&context, QPC_SQ_MAX_PSN); - data[offset++] = hr_reg_read(&context, QPC_SQ_FLUSH_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_MAX_IDX); - data[offset++] = hr_reg_read(&context, QPC_SQ_TX_ERR); - data[offset++] = hr_reg_read(&context, QPC_SQ_RX_ERR); - data[offset++] = hr_reg_read(&context, QPC_RQ_RX_ERR); - data[offset++] = hr_reg_read(&context, QPC_RQ_TX_ERR); - data[offset++] = hr_reg_read(&context, QPC_RQ_CQE_IDX); - data[offset++] = hr_reg_read(&context, QPC_RQ_RTY_TX_ERR); - - ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, offset * sizeof(u32), data); + ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context); return ret; } @@ -204,8 +147,6 @@ int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr) struct hns_roce_dev *hr_dev = to_hr_dev(ib_mr->device); struct hns_roce_mr *hr_mr = to_hr_mr(ib_mr); struct hns_roce_v2_mpt_entry context; - u32 data[MAX_ENTRY_NUM] = {}; - int offset = 0; int ret; if (!hr_dev->hw->query_mpt) @@ -215,17 +156,7 @@ int hns_roce_fill_res_mr_entry_raw(struct sk_buff *msg, struct ib_mr *ib_mr) if (ret) return -EINVAL; - data[offset++] = hr_reg_read(&context, MPT_ST); - data[offset++] = hr_reg_read(&context, MPT_PD); - data[offset++] = hr_reg_read(&context, MPT_LKEY); - data[offset++] = hr_reg_read(&context, MPT_LEN_L); - data[offset++] = hr_reg_read(&context, MPT_LEN_H); - data[offset++] = hr_reg_read(&context, MPT_PBL_SIZE); - data[offset++] = hr_reg_read(&context, MPT_PBL_HOP_NUM); - data[offset++] = hr_reg_read(&context, MPT_PBL_BA_PG_SZ); - data[offset++] = hr_reg_read(&context, MPT_PBL_BUF_PG_SZ); - - ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, offset * sizeof(u32), data); + ret = nla_put(msg, RDMA_NLDEV_ATTR_RES_RAW, sizeof(context), &context); return ret; } diff --git a/drivers/infiniband/hw/irdma/cm.c b/drivers/infiniband/hw/irdma/cm.c index 70009b970e08b06f695c9bf95c0bfce2311dc8d7..42d1e977106696af00da93cca1fb0a50119e98b8 100644 --- a/drivers/infiniband/hw/irdma/cm.c +++ b/drivers/infiniband/hw/irdma/cm.c @@ -1555,22 +1555,56 @@ static int irdma_del_multiple_qhash(struct irdma_device *iwdev, return ret; } +static u8 irdma_iw_get_vlan_prio(u32 *loc_addr, u8 prio, bool ipv4) +{ + struct net_device *ndev = NULL; + + rcu_read_lock(); + if (ipv4) { + ndev = ip_dev_find(&init_net, htonl(loc_addr[0])); + } else if (IS_ENABLED(CONFIG_IPV6)) { + struct net_device *ip_dev; + struct in6_addr laddr6; + + irdma_copy_ip_htonl(laddr6.in6_u.u6_addr32, loc_addr); + + for_each_netdev_rcu (&init_net, ip_dev) { + if (ipv6_chk_addr(&init_net, &laddr6, ip_dev, 1)) { + ndev = ip_dev; + break; + } + } + } + + if (!ndev) + goto done; + if (is_vlan_dev(ndev)) + prio = (vlan_dev_get_egress_qos_mask(ndev, prio) & VLAN_PRIO_MASK) + >> VLAN_PRIO_SHIFT; + if (ipv4) + dev_put(ndev); + +done: + rcu_read_unlock(); + + return prio; +} + /** - * irdma_netdev_vlan_ipv6 - Gets the netdev and mac + * irdma_get_vlan_mac_ipv6 - Gets the vlan and mac * @addr: local IPv6 address * @vlan_id: vlan id for the given IPv6 address * @mac: mac address for the given IPv6 address * - * Returns the net_device of the IPv6 address and also sets the - * vlan id and mac for that address. + * Returns the vlan id and mac for an IPv6 address. */ -struct net_device *irdma_netdev_vlan_ipv6(u32 *addr, u16 *vlan_id, u8 *mac) +void irdma_get_vlan_mac_ipv6(u32 *addr, u16 *vlan_id, u8 *mac) { struct net_device *ip_dev = NULL; struct in6_addr laddr6; if (!IS_ENABLED(CONFIG_IPV6)) - return NULL; + return; irdma_copy_ip_htonl(laddr6.in6_u.u6_addr32, addr); if (vlan_id) @@ -1589,8 +1623,6 @@ struct net_device *irdma_netdev_vlan_ipv6(u32 *addr, u16 *vlan_id, u8 *mac) } } rcu_read_unlock(); - - return ip_dev; } /** @@ -1667,6 +1699,12 @@ static int irdma_add_mqh_6(struct irdma_device *iwdev, ifp->addr.in6_u.u6_addr32); memcpy(cm_info->loc_addr, child_listen_node->loc_addr, sizeof(cm_info->loc_addr)); + if (!iwdev->vsi.dscp_mode) + cm_info->user_pri = + irdma_iw_get_vlan_prio(child_listen_node->loc_addr, + cm_info->user_pri, + false); + ret = irdma_manage_qhash(iwdev, cm_info, IRDMA_QHASH_TYPE_TCP_SYN, IRDMA_QHASH_MANAGE_TYPE_ADD, @@ -1751,6 +1789,11 @@ static int irdma_add_mqh_4(struct irdma_device *iwdev, ntohl(ifa->ifa_address); memcpy(cm_info->loc_addr, child_listen_node->loc_addr, sizeof(cm_info->loc_addr)); + if (!iwdev->vsi.dscp_mode) + cm_info->user_pri = + irdma_iw_get_vlan_prio(child_listen_node->loc_addr, + cm_info->user_pri, + true); ret = irdma_manage_qhash(iwdev, cm_info, IRDMA_QHASH_TYPE_TCP_SYN, IRDMA_QHASH_MANAGE_TYPE_ADD, @@ -2219,6 +2262,10 @@ irdma_make_cm_node(struct irdma_cm_core *cm_core, struct irdma_device *iwdev, } else { cm_node->tos = max(listener->tos, cm_info->tos); cm_node->user_pri = rt_tos2priority(cm_node->tos); + cm_node->user_pri = + irdma_iw_get_vlan_prio(cm_info->loc_addr, + cm_node->user_pri, + cm_info->ipv4); } ibdev_dbg(&iwdev->ibdev, "DCB: listener: TOS:[%d] UP:[%d]\n", cm_node->tos, @@ -3577,7 +3624,6 @@ void irdma_free_lsmm_rsrc(struct irdma_qp *iwqp) iwqp->ietf_mem.size, iwqp->ietf_mem.va, iwqp->ietf_mem.pa); iwqp->ietf_mem.va = NULL; - iwqp->ietf_mem.va = NULL; } } @@ -3617,8 +3663,8 @@ int irdma_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) cm_node->vlan_id = irdma_get_vlan_ipv4(cm_node->loc_addr); } else { cm_node->ipv4 = false; - irdma_netdev_vlan_ipv6(cm_node->loc_addr, &cm_node->vlan_id, - NULL); + irdma_get_vlan_mac_ipv6(cm_node->loc_addr, &cm_node->vlan_id, + NULL); } ibdev_dbg(&iwdev->ibdev, "CM: Accept vlan_id=%d\n", cm_node->vlan_id); @@ -3826,17 +3872,21 @@ int irdma_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) raddr6->sin6_addr.in6_u.u6_addr32); cm_info.loc_port = ntohs(laddr6->sin6_port); cm_info.rem_port = ntohs(raddr6->sin6_port); - irdma_netdev_vlan_ipv6(cm_info.loc_addr, &cm_info.vlan_id, - NULL); + irdma_get_vlan_mac_ipv6(cm_info.loc_addr, &cm_info.vlan_id, + NULL); } cm_info.cm_id = cm_id; cm_info.qh_qpid = iwdev->vsi.ilq->qp_id; cm_info.tos = cm_id->tos; - if (iwdev->vsi.dscp_mode) + if (iwdev->vsi.dscp_mode) { cm_info.user_pri = iwqp->sc_qp.vsi->dscp_map[irdma_tos2dscp(cm_info.tos)]; - else + } else { cm_info.user_pri = rt_tos2priority(cm_id->tos); + cm_info.user_pri = irdma_iw_get_vlan_prio(cm_info.loc_addr, + cm_info.user_pri, + cm_info.ipv4); + } if (iwqp->sc_qp.dev->ws_add(iwqp->sc_qp.vsi, cm_info.user_pri)) return -ENOMEM; @@ -3952,8 +4002,8 @@ int irdma_create_listen(struct iw_cm_id *cm_id, int backlog) laddr6->sin6_addr.in6_u.u6_addr32); cm_info.loc_port = ntohs(laddr6->sin6_port); if (ipv6_addr_type(&laddr6->sin6_addr) != IPV6_ADDR_ANY) { - irdma_netdev_vlan_ipv6(cm_info.loc_addr, - &cm_info.vlan_id, NULL); + irdma_get_vlan_mac_ipv6(cm_info.loc_addr, + &cm_info.vlan_id, NULL); } else { cm_info.vlan_id = 0xFFFF; wildcard = true; @@ -3980,7 +4030,7 @@ int irdma_create_listen(struct iw_cm_id *cm_id, int backlog) cm_listen_node->tos = cm_id->tos; if (iwdev->vsi.dscp_mode) cm_listen_node->user_pri = - iwdev->vsi.dscp_map[irdma_tos2dscp(cm_id->tos)]; + iwdev->vsi.dscp_map[irdma_tos2dscp(cm_id->tos)]; else cm_listen_node->user_pri = rt_tos2priority(cm_id->tos); cm_info.user_pri = cm_listen_node->user_pri; @@ -3990,6 +4040,12 @@ int irdma_create_listen(struct iw_cm_id *cm_id, int backlog) if (err) goto error; } else { + if (!iwdev->vsi.dscp_mode) + cm_listen_node->user_pri = + irdma_iw_get_vlan_prio(cm_info.loc_addr, + cm_info.user_pri, + cm_info.ipv4); + cm_info.user_pri = cm_listen_node->user_pri; err = irdma_manage_qhash(iwdev, &cm_info, IRDMA_QHASH_TYPE_TCP_SYN, IRDMA_QHASH_MANAGE_TYPE_ADD, diff --git a/drivers/infiniband/hw/irdma/ctrl.c b/drivers/infiniband/hw/irdma/ctrl.c index 45e3344daa048f123f6f3971566f8cfebf72eb2e..8a6200e55c542808499d92d790d9bbc54140882f 100644 --- a/drivers/infiniband/hw/irdma/ctrl.c +++ b/drivers/infiniband/hw/irdma/ctrl.c @@ -1061,6 +1061,9 @@ static int irdma_sc_alloc_stag(struct irdma_sc_dev *dev, u64 hdr; enum irdma_page_size page_size; + if (!info->total_len && !info->all_memory) + return -EINVAL; + if (info->page_size == 0x40000000) page_size = IRDMA_PAGE_SIZE_1G; else if (info->page_size == 0x200000) @@ -1126,6 +1129,9 @@ static int irdma_sc_mr_reg_non_shared(struct irdma_sc_dev *dev, u8 addr_type; enum irdma_page_size page_size; + if (!info->total_len && !info->all_memory) + return -EINVAL; + if (info->page_size == 0x40000000) page_size = IRDMA_PAGE_SIZE_1G; else if (info->page_size == 0x200000) @@ -1301,7 +1307,6 @@ int irdma_sc_mr_fast_register(struct irdma_sc_qp *qp, sq_info.wr_id = info->wr_id; sq_info.signaled = info->signaled; - sq_info.push_wqe = info->push_wqe; wqe = irdma_qp_get_next_send_wqe(&qp->qp_uk, &wqe_idx, IRDMA_QP_WQE_MIN_QUANTA, 0, &sq_info); @@ -1335,7 +1340,6 @@ int irdma_sc_mr_fast_register(struct irdma_sc_qp *qp, FIELD_PREP(IRDMAQPSQ_HPAGESIZE, page_size) | FIELD_PREP(IRDMAQPSQ_STAGRIGHTS, info->access_rights) | FIELD_PREP(IRDMAQPSQ_VABASEDTO, info->addr_type) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, (sq_info.push_wqe ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_READFENCE, info->read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -1346,13 +1350,9 @@ int irdma_sc_mr_fast_register(struct irdma_sc_qp *qp, print_hex_dump_debug("WQE: FAST_REG WQE", DUMP_PREFIX_OFFSET, 16, 8, wqe, IRDMA_QP_WQE_MIN_SIZE, false); - if (sq_info.push_wqe) { - irdma_qp_push_wqe(&qp->qp_uk, wqe, IRDMA_QP_WQE_MIN_QUANTA, - wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(&qp->qp_uk); - } + + if (post_sq) + irdma_uk_qp_post_wr(&qp->qp_uk); return 0; } @@ -4007,7 +4007,6 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, { u64 temp, compl_ctx; __le64 *aeqe; - u16 wqe_idx; u8 ae_src; u8 polarity; @@ -4027,7 +4026,7 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, aeqe, 16, false); ae_src = (u8)FIELD_GET(IRDMA_AEQE_AESRC, temp); - wqe_idx = (u16)FIELD_GET(IRDMA_AEQE_WQDESCIDX, temp); + info->wqe_idx = (u16)FIELD_GET(IRDMA_AEQE_WQDESCIDX, temp); info->qp_cq_id = (u32)FIELD_GET(IRDMA_AEQE_QPCQID_LOW, temp) | ((u32)FIELD_GET(IRDMA_AEQE_QPCQID_HI, temp) << 18); info->ae_id = (u16)FIELD_GET(IRDMA_AEQE_AECODE, temp); @@ -4110,7 +4109,6 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, case IRDMA_AE_SOURCE_RQ_0011: info->qp = true; info->rq = true; - info->wqe_idx = wqe_idx; info->compl_ctx = compl_ctx; break; case IRDMA_AE_SOURCE_CQ: @@ -4124,7 +4122,6 @@ int irdma_sc_get_next_aeqe(struct irdma_sc_aeq *aeq, case IRDMA_AE_SOURCE_SQ_0111: info->qp = true; info->sq = true; - info->wqe_idx = wqe_idx; info->compl_ctx = compl_ctx; break; case IRDMA_AE_SOURCE_IN_RR_WR: diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c index 457368e324e1038c57d61bb9476147b1950d2232..7cbdd5433dba5264e669fdf5bbe104ae25e2a5cd 100644 --- a/drivers/infiniband/hw/irdma/hw.c +++ b/drivers/infiniband/hw/irdma/hw.c @@ -219,7 +219,6 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) struct irdma_aeqe_info *info = &aeinfo; int ret; struct irdma_qp *iwqp = NULL; - struct irdma_sc_cq *cq = NULL; struct irdma_cq *iwcq = NULL; struct irdma_sc_qp *qp = NULL; struct irdma_qp_host_ctx_info *ctx_info = NULL; @@ -336,10 +335,18 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) ibdev_err(&iwdev->ibdev, "Processing an iWARP related AE for CQ misc = 0x%04X\n", info->ae_id); - cq = (struct irdma_sc_cq *)(unsigned long) - info->compl_ctx; - iwcq = cq->back_cq; + spin_lock_irqsave(&rf->cqtable_lock, flags); + iwcq = rf->cq_table[info->qp_cq_id]; + if (!iwcq) { + spin_unlock_irqrestore(&rf->cqtable_lock, + flags); + ibdev_dbg(to_ibdev(dev), + "cq_id %d is already freed\n", info->qp_cq_id); + continue; + } + irdma_cq_add_ref(&iwcq->ibcq); + spin_unlock_irqrestore(&rf->cqtable_lock, flags); if (iwcq->ibcq.event_handler) { struct ib_event ibevent; @@ -350,6 +357,7 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) iwcq->ibcq.event_handler(&ibevent, iwcq->ibcq.cq_context); } + irdma_cq_rem_ref(&iwcq->ibcq); break; case IRDMA_AE_RESET_NOT_SENT: case IRDMA_AE_LLP_DOUBT_REACHABILITY: @@ -563,12 +571,11 @@ static void irdma_destroy_irq(struct irdma_pci_f *rf, /** * irdma_destroy_cqp - destroy control qp * @rf: RDMA PCI function - * @free_hwcqp: 1 if hw cqp should be freed * * Issue destroy cqp request and * free the resources associated with the cqp */ -static void irdma_destroy_cqp(struct irdma_pci_f *rf, bool free_hwcqp) +static void irdma_destroy_cqp(struct irdma_pci_f *rf) { struct irdma_sc_dev *dev = &rf->sc_dev; struct irdma_cqp *cqp = &rf->cqp; @@ -576,8 +583,8 @@ static void irdma_destroy_cqp(struct irdma_pci_f *rf, bool free_hwcqp) if (rf->cqp_cmpl_wq) destroy_workqueue(rf->cqp_cmpl_wq); - if (free_hwcqp) - status = irdma_sc_cqp_destroy(dev->cqp); + + status = irdma_sc_cqp_destroy(dev->cqp); if (status) ibdev_dbg(to_ibdev(dev), "ERR: Destroy CQP failed %d\n", status); @@ -921,8 +928,8 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) cqp->scratch_array = kcalloc(sqsize, sizeof(*cqp->scratch_array), GFP_KERNEL); if (!cqp->scratch_array) { - kfree(cqp->cqp_requests); - return -ENOMEM; + status = -ENOMEM; + goto err_scratch; } dev->cqp = &cqp->sc_cqp; @@ -932,15 +939,14 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) cqp->sq.va = dma_alloc_coherent(dev->hw->device, cqp->sq.size, &cqp->sq.pa, GFP_KERNEL); if (!cqp->sq.va) { - kfree(cqp->scratch_array); - kfree(cqp->cqp_requests); - return -ENOMEM; + status = -ENOMEM; + goto err_sq; } status = irdma_obj_aligned_mem(rf, &mem, sizeof(struct irdma_cqp_ctx), IRDMA_HOST_CTX_ALIGNMENT_M); if (status) - goto exit; + goto err_ctx; dev->cqp->host_ctx_pa = mem.pa; dev->cqp->host_ctx = mem.va; @@ -966,7 +972,7 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) status = irdma_sc_cqp_init(dev->cqp, &cqp_init_info); if (status) { ibdev_dbg(to_ibdev(dev), "ERR: cqp init status %d\n", status); - goto exit; + goto err_ctx; } spin_lock_init(&cqp->req_lock); @@ -977,7 +983,7 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) ibdev_dbg(to_ibdev(dev), "ERR: cqp create failed - status %d maj_err %d min_err %d\n", status, maj_err, min_err); - goto exit; + goto err_ctx; } INIT_LIST_HEAD(&cqp->cqp_avail_reqs); @@ -991,8 +997,16 @@ static int irdma_create_cqp(struct irdma_pci_f *rf) init_waitqueue_head(&cqp->remove_wq); return 0; -exit: - irdma_destroy_cqp(rf, false); +err_ctx: + dma_free_coherent(dev->hw->device, cqp->sq.size, + cqp->sq.va, cqp->sq.pa); + cqp->sq.va = NULL; +err_sq: + kfree(cqp->scratch_array); + cqp->scratch_array = NULL; +err_scratch: + kfree(cqp->cqp_requests); + cqp->cqp_requests = NULL; return status; } @@ -1549,7 +1563,7 @@ static void irdma_del_init_mem(struct irdma_pci_f *rf) kfree(dev->hmc_info->sd_table.sd_entry); dev->hmc_info->sd_table.sd_entry = NULL; - kfree(rf->mem_rsrc); + vfree(rf->mem_rsrc); rf->mem_rsrc = NULL; dma_free_coherent(rf->hw.device, rf->obj_mem.size, rf->obj_mem.va, rf->obj_mem.pa); @@ -1747,7 +1761,7 @@ void irdma_ctrl_deinit_hw(struct irdma_pci_f *rf) rf->reset, rf->rdma_ver); fallthrough; case CQP_CREATED: - irdma_destroy_cqp(rf, true); + irdma_destroy_cqp(rf); fallthrough; case INITIAL_STATE: irdma_del_init_mem(rf); @@ -1945,10 +1959,12 @@ static void irdma_set_hw_rsrc(struct irdma_pci_f *rf) rf->allocated_arps = &rf->allocated_mcgs[BITS_TO_LONGS(rf->max_mcg)]; rf->qp_table = (struct irdma_qp **) (&rf->allocated_arps[BITS_TO_LONGS(rf->arp_table_size)]); + rf->cq_table = (struct irdma_cq **)(&rf->qp_table[rf->max_qp]); spin_lock_init(&rf->rsrc_lock); spin_lock_init(&rf->arp_lock); spin_lock_init(&rf->qptable_lock); + spin_lock_init(&rf->cqtable_lock); spin_lock_init(&rf->qh_list_lock); } @@ -1969,6 +1985,7 @@ static u32 irdma_calc_mem_rsrc_size(struct irdma_pci_f *rf) rsrc_size += sizeof(unsigned long) * BITS_TO_LONGS(rf->max_ah); rsrc_size += sizeof(unsigned long) * BITS_TO_LONGS(rf->max_mcg); rsrc_size += sizeof(struct irdma_qp **) * rf->max_qp; + rsrc_size += sizeof(struct irdma_cq **) * rf->max_cq; return rsrc_size; } @@ -2002,10 +2019,10 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) rf->max_mcg = rf->max_qp; rsrc_size = irdma_calc_mem_rsrc_size(rf); - rf->mem_rsrc = kzalloc(rsrc_size, GFP_KERNEL); + rf->mem_rsrc = vzalloc(rsrc_size); if (!rf->mem_rsrc) { ret = -ENOMEM; - goto mem_rsrc_kzalloc_fail; + goto mem_rsrc_vzalloc_fail; } rf->arp_table = (struct irdma_arp_entry *)rf->mem_rsrc; @@ -2033,7 +2050,7 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) return 0; -mem_rsrc_kzalloc_fail: +mem_rsrc_vzalloc_fail: bitmap_free(rf->allocated_ws_nodes); rf->allocated_ws_nodes = NULL; diff --git a/drivers/infiniband/hw/irdma/i40iw_hw.c b/drivers/infiniband/hw/irdma/i40iw_hw.c index 37a40fb4d0d7e87deeb724f522c78323c82586a9..638d127fb3e0ee4f6dda8a42e41ceda214ec774e 100644 --- a/drivers/infiniband/hw/irdma/i40iw_hw.c +++ b/drivers/infiniband/hw/irdma/i40iw_hw.c @@ -254,5 +254,6 @@ void i40iw_init_hw(struct irdma_sc_dev *dev) dev->hw_attrs.max_stat_idx = IRDMA_HW_STAT_INDEX_MAX_GEN_1; dev->hw_attrs.max_hw_outbound_msg_size = I40IW_MAX_OUTBOUND_MSG_SIZE; dev->hw_attrs.max_hw_inbound_msg_size = I40IW_MAX_INBOUND_MSG_SIZE; + dev->hw_attrs.uk_attrs.min_hw_wq_size = I40IW_MIN_WQ_SIZE; dev->hw_attrs.max_qp_wr = I40IW_MAX_QP_WRS; } diff --git a/drivers/infiniband/hw/irdma/i40iw_hw.h b/drivers/infiniband/hw/irdma/i40iw_hw.h index 1c438b3593ea8cd82b8b94257de5fabe5c9f03d9..10afc165f5ea97dc2c835a50060853f06403a000 100644 --- a/drivers/infiniband/hw/irdma/i40iw_hw.h +++ b/drivers/infiniband/hw/irdma/i40iw_hw.h @@ -140,11 +140,11 @@ enum i40iw_device_caps_const { I40IW_MAX_CQ_SIZE = 1048575, I40IW_MAX_OUTBOUND_MSG_SIZE = 2147483647, I40IW_MAX_INBOUND_MSG_SIZE = 2147483647, + I40IW_MIN_WQ_SIZE = 4 /* WQEs */, }; #define I40IW_QP_WQE_MIN_SIZE 32 #define I40IW_QP_WQE_MAX_SIZE 128 -#define I40IW_QP_SW_MIN_WQSIZE 4 #define I40IW_MAX_RQ_WQE_SHIFT 2 #define I40IW_MAX_QUANTA_PER_WR 2 diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.c b/drivers/infiniband/hw/irdma/icrdma_hw.c index 298d14905993b9e07d841ba2224e5e07d368cac9..10ccf4bc3f2d28ec673fa09521a220fa5dbec1a2 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.c +++ b/drivers/infiniband/hw/irdma/icrdma_hw.c @@ -195,6 +195,7 @@ void icrdma_init_hw(struct irdma_sc_dev *dev) dev->hw_attrs.max_stat_inst = ICRDMA_MAX_STATS_COUNT; dev->hw_attrs.max_stat_idx = IRDMA_HW_STAT_INDEX_MAX_GEN_2; + dev->hw_attrs.uk_attrs.min_hw_wq_size = ICRDMA_MIN_WQ_SIZE; dev->hw_attrs.uk_attrs.max_hw_sq_chunk = IRDMA_MAX_QUANTA_PER_WR; dev->hw_attrs.uk_attrs.feature_flags |= IRDMA_FEATURE_RTS_AE | IRDMA_FEATURE_CQ_RESIZE; diff --git a/drivers/infiniband/hw/irdma/icrdma_hw.h b/drivers/infiniband/hw/irdma/icrdma_hw.h index b65c463abf0b39d6a724d23d7ece53b8667ea76e..54035a08cc93b6e2130f851bdc253932f01d0d05 100644 --- a/drivers/infiniband/hw/irdma/icrdma_hw.h +++ b/drivers/infiniband/hw/irdma/icrdma_hw.h @@ -64,6 +64,7 @@ enum icrdma_device_caps_const { ICRDMA_MAX_IRD_SIZE = 127, ICRDMA_MAX_ORD_SIZE = 255, + ICRDMA_MIN_WQ_SIZE = 8 /* WQEs */, }; diff --git a/drivers/infiniband/hw/irdma/irdma.h b/drivers/infiniband/hw/irdma/irdma.h index 173e2dc2fc3556408724d580fd8d0078af39ee33..3237fa64bc8f950f5a439d71366786e6d5dae0b0 100644 --- a/drivers/infiniband/hw/irdma/irdma.h +++ b/drivers/infiniband/hw/irdma/irdma.h @@ -119,6 +119,7 @@ struct irdma_uk_attrs { u32 min_hw_cq_size; u32 max_hw_cq_size; u16 max_hw_sq_chunk; + u16 min_hw_wq_size; u8 hw_rev; }; diff --git a/drivers/infiniband/hw/irdma/main.h b/drivers/infiniband/hw/irdma/main.h index 2323962cdeacb60f0c080c5090d1f78d71cfd7a4..82fc5f5b002c04ba1d34b36e4a201368334978ff 100644 --- a/drivers/infiniband/hw/irdma/main.h +++ b/drivers/infiniband/hw/irdma/main.h @@ -239,7 +239,7 @@ struct irdma_qv_info { struct irdma_qvlist_info { u32 num_vectors; - struct irdma_qv_info qv_info[1]; + struct irdma_qv_info qv_info[]; }; struct irdma_gen_ops { @@ -309,7 +309,9 @@ struct irdma_pci_f { spinlock_t arp_lock; /*protect ARP table access*/ spinlock_t rsrc_lock; /* protect HW resource array access */ spinlock_t qptable_lock; /*protect QP table access*/ + spinlock_t cqtable_lock; /*protect CQ table access*/ struct irdma_qp **qp_table; + struct irdma_cq **cq_table; spinlock_t qh_list_lock; /* protect mc_qht_list */ struct mc_table_list mc_qht_list; struct irdma_msix_vector *iw_msixtbl; @@ -500,6 +502,8 @@ int irdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata); int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, struct ib_udata *udata); +void irdma_cq_add_ref(struct ib_cq *ibcq); +void irdma_cq_rem_ref(struct ib_cq *ibcq); void irdma_cq_wq_destroy(struct irdma_pci_f *rf, struct irdma_sc_cq *cq); void irdma_cleanup_pending_cqp_op(struct irdma_pci_f *rf); @@ -529,7 +533,7 @@ void irdma_gen_ae(struct irdma_pci_f *rf, struct irdma_sc_qp *qp, void irdma_copy_ip_ntohl(u32 *dst, __be32 *src); void irdma_copy_ip_htonl(__be32 *dst, u32 *src); u16 irdma_get_vlan_ipv4(u32 *addr); -struct net_device *irdma_netdev_vlan_ipv6(u32 *addr, u16 *vlan_id, u8 *mac); +void irdma_get_vlan_mac_ipv6(u32 *addr, u16 *vlan_id, u8 *mac); struct ib_mr *irdma_reg_phys_mr(struct ib_pd *ib_pd, u64 addr, u64 size, int acc, u64 *iova_start); int irdma_upload_qp_context(struct irdma_qp *iwqp, bool freeze, bool raw); diff --git a/drivers/infiniband/hw/irdma/type.h b/drivers/infiniband/hw/irdma/type.h index a20709577ab0ada080750c4a1c4713fab7cbe939..c84ec4dd8536d3ce39d10e32532c7bf44eedfb5c 100644 --- a/drivers/infiniband/hw/irdma/type.h +++ b/drivers/infiniband/hw/irdma/type.h @@ -971,6 +971,7 @@ struct irdma_allocate_stag_info { bool remote_access:1; bool use_hmc_fcn_index:1; bool use_pf_rid:1; + bool all_memory:1; u8 hmc_fcn_index; }; @@ -998,6 +999,7 @@ struct irdma_reg_ns_stag_info { bool use_hmc_fcn_index:1; u8 hmc_fcn_index; bool use_pf_rid:1; + bool all_memory:1; }; struct irdma_fast_reg_stag_info { @@ -1017,7 +1019,6 @@ struct irdma_fast_reg_stag_info { bool local_fence:1; bool read_fence:1; bool signaled:1; - bool push_wqe:1; bool use_hmc_fcn_index:1; u8 hmc_fcn_index; bool use_pf_rid:1; diff --git a/drivers/infiniband/hw/irdma/uk.c b/drivers/infiniband/hw/irdma/uk.c index 280d633d4ec4f1917d9b416f0ba0111e16da47e5..d8285ca162938a3af90fcaf3d7d03406635b189f 100644 --- a/drivers/infiniband/hw/irdma/uk.c +++ b/drivers/infiniband/hw/irdma/uk.c @@ -127,10 +127,7 @@ void irdma_uk_qp_post_wr(struct irdma_qp_uk *qp) hw_sq_tail = (u32)FIELD_GET(IRDMA_QP_DBSA_HW_SQ_TAIL, temp); sw_sq_head = IRDMA_RING_CURRENT_HEAD(qp->sq_ring); if (sw_sq_head != qp->initial_ring.head) { - if (qp->push_dropped) { - writel(qp->qp_id, qp->wqe_alloc_db); - qp->push_dropped = false; - } else if (sw_sq_head != hw_sq_tail) { + if (sw_sq_head != hw_sq_tail) { if (sw_sq_head > qp->initial_ring.head) { if (hw_sq_tail >= qp->initial_ring.head && hw_sq_tail < sw_sq_head) @@ -146,38 +143,6 @@ void irdma_uk_qp_post_wr(struct irdma_qp_uk *qp) qp->initial_ring.head = qp->sq_ring.head; } -/** - * irdma_qp_ring_push_db - ring qp doorbell - * @qp: hw qp ptr - * @wqe_idx: wqe index - */ -static void irdma_qp_ring_push_db(struct irdma_qp_uk *qp, u32 wqe_idx) -{ - set_32bit_val(qp->push_db, 0, - FIELD_PREP(IRDMA_WQEALLOC_WQE_DESC_INDEX, wqe_idx >> 3) | qp->qp_id); - qp->initial_ring.head = qp->sq_ring.head; - qp->push_mode = true; - qp->push_dropped = false; -} - -void irdma_qp_push_wqe(struct irdma_qp_uk *qp, __le64 *wqe, u16 quanta, - u32 wqe_idx, bool post_sq) -{ - __le64 *push; - - if (IRDMA_RING_CURRENT_HEAD(qp->initial_ring) != - IRDMA_RING_CURRENT_TAIL(qp->sq_ring) && - !qp->push_mode) { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } else { - push = (__le64 *)((uintptr_t)qp->push_wqe + - (wqe_idx & 0x7) * 0x20); - memcpy(push, wqe, quanta * IRDMA_QP_WQE_MIN_SIZE); - irdma_qp_ring_push_db(qp, wqe_idx); - } -} - /** * irdma_qp_get_next_send_wqe - pad with NOP if needed, return where next WR should go * @qp: hw qp ptr @@ -192,7 +157,6 @@ __le64 *irdma_qp_get_next_send_wqe(struct irdma_qp_uk *qp, u32 *wqe_idx, { __le64 *wqe; __le64 *wqe_0 = NULL; - u32 nop_wqe_idx; u16 avail_quanta; u16 i; @@ -209,14 +173,10 @@ __le64 *irdma_qp_get_next_send_wqe(struct irdma_qp_uk *qp, u32 *wqe_idx, IRDMA_SQ_RING_FREE_QUANTA(qp->sq_ring)) return NULL; - nop_wqe_idx = IRDMA_RING_CURRENT_HEAD(qp->sq_ring); for (i = 0; i < avail_quanta; i++) { irdma_nop_1(qp); IRDMA_RING_MOVE_HEAD_NOCHECK(qp->sq_ring); } - if (qp->push_db && info->push_wqe) - irdma_qp_push_wqe(qp, qp->sq_base[nop_wqe_idx].elem, - avail_quanta, nop_wqe_idx, true); } *wqe_idx = IRDMA_RING_CURRENT_HEAD(qp->sq_ring); @@ -282,8 +242,6 @@ int irdma_uk_rdma_write(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, bool read_fence = false; u16 quanta; - info->push_wqe = qp->push_db ? true : false; - op_info = &info->op.rdma_write; if (op_info->num_lo_sges > qp->max_sq_frag_cnt) return -EINVAL; @@ -344,7 +302,6 @@ int irdma_uk_rdma_write(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, FIELD_PREP(IRDMAQPSQ_IMMDATAFLAG, info->imm_data_valid) | FIELD_PREP(IRDMAQPSQ_REPORTRTT, info->report_rtt) | FIELD_PREP(IRDMAQPSQ_ADDFRAGCNT, addl_frag_cnt) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -353,12 +310,9 @@ int irdma_uk_rdma_write(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, dma_wmb(); /* make sure WQE is populated before valid bit is set */ set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -383,8 +337,6 @@ int irdma_uk_rdma_read(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, u16 quanta; u64 hdr; - info->push_wqe = qp->push_db ? true : false; - op_info = &info->op.rdma_read; if (qp->max_sq_frag_cnt < op_info->num_lo_sges) return -EINVAL; @@ -431,7 +383,6 @@ int irdma_uk_rdma_read(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, FIELD_PREP(IRDMAQPSQ_ADDFRAGCNT, addl_frag_cnt) | FIELD_PREP(IRDMAQPSQ_OPCODE, (inv_stag ? IRDMAQP_OP_RDMA_READ_LOC_INV : IRDMAQP_OP_RDMA_READ)) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, info->read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -440,12 +391,9 @@ int irdma_uk_rdma_read(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, dma_wmb(); /* make sure WQE is populated before valid bit is set */ set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -468,8 +416,6 @@ int irdma_uk_send(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, bool read_fence = false; u16 quanta; - info->push_wqe = qp->push_db ? true : false; - op_info = &info->op.send; if (qp->max_sq_frag_cnt < op_info->num_sges) return -EINVAL; @@ -530,7 +476,6 @@ int irdma_uk_send(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, FIELD_PREP(IRDMAQPSQ_REPORTRTT, (info->report_rtt ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_OPCODE, info->op_type) | FIELD_PREP(IRDMAQPSQ_ADDFRAGCNT, addl_frag_cnt) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -541,12 +486,9 @@ int irdma_uk_send(struct irdma_qp_uk *qp, struct irdma_post_sq_info *info, dma_wmb(); /* make sure WQE is populated before valid bit is set */ set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -720,7 +662,6 @@ int irdma_uk_inline_rdma_write(struct irdma_qp_uk *qp, u32 i, total_size = 0; u16 quanta; - info->push_wqe = qp->push_db ? true : false; op_info = &info->op.rdma_write; if (unlikely(qp->max_sq_frag_cnt < op_info->num_lo_sges)) @@ -750,7 +691,6 @@ int irdma_uk_inline_rdma_write(struct irdma_qp_uk *qp, FIELD_PREP(IRDMAQPSQ_REPORTRTT, info->report_rtt ? 1 : 0) | FIELD_PREP(IRDMAQPSQ_INLINEDATAFLAG, 1) | FIELD_PREP(IRDMAQPSQ_IMMDATAFLAG, info->imm_data_valid ? 1 : 0) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe ? 1 : 0) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -767,12 +707,8 @@ int irdma_uk_inline_rdma_write(struct irdma_qp_uk *qp, set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -794,7 +730,6 @@ int irdma_uk_inline_send(struct irdma_qp_uk *qp, u32 i, total_size = 0; u16 quanta; - info->push_wqe = qp->push_db ? true : false; op_info = &info->op.send; if (unlikely(qp->max_sq_frag_cnt < op_info->num_sges)) @@ -827,7 +762,6 @@ int irdma_uk_inline_send(struct irdma_qp_uk *qp, (info->imm_data_valid ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_REPORTRTT, (info->report_rtt ? 1 : 0)) | FIELD_PREP(IRDMAQPSQ_INLINEDATAFLAG, 1) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, info->local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -845,12 +779,8 @@ int irdma_uk_inline_send(struct irdma_qp_uk *qp, set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, quanta, wqe_idx, post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -872,7 +802,6 @@ int irdma_uk_stag_local_invalidate(struct irdma_qp_uk *qp, bool local_fence = false; struct ib_sge sge = {}; - info->push_wqe = qp->push_db ? true : false; op_info = &info->op.inv_local_stag; local_fence = info->local_fence; @@ -889,7 +818,6 @@ int irdma_uk_stag_local_invalidate(struct irdma_qp_uk *qp, set_64bit_val(wqe, 16, 0); hdr = FIELD_PREP(IRDMAQPSQ_OPCODE, IRDMA_OP_TYPE_INV_STAG) | - FIELD_PREP(IRDMAQPSQ_PUSHWQE, info->push_wqe) | FIELD_PREP(IRDMAQPSQ_READFENCE, info->read_fence) | FIELD_PREP(IRDMAQPSQ_LOCALFENCE, local_fence) | FIELD_PREP(IRDMAQPSQ_SIGCOMPL, info->signaled) | @@ -899,13 +827,8 @@ int irdma_uk_stag_local_invalidate(struct irdma_qp_uk *qp, set_64bit_val(wqe, 24, hdr); - if (info->push_wqe) { - irdma_qp_push_wqe(qp, wqe, IRDMA_QP_WQE_MIN_QUANTA, wqe_idx, - post_sq); - } else { - if (post_sq) - irdma_uk_qp_post_wr(qp); - } + if (post_sq) + irdma_uk_qp_post_wr(qp); return 0; } @@ -1124,7 +1047,6 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq, info->q_type = (u8)FIELD_GET(IRDMA_CQ_SQ, qword3); info->error = (bool)FIELD_GET(IRDMA_CQ_ERROR, qword3); - info->push_dropped = (bool)FIELD_GET(IRDMACQ_PSHDROP, qword3); info->ipv4 = (bool)FIELD_GET(IRDMACQ_IPV4, qword3); if (info->error) { info->major_err = FIELD_GET(IRDMA_CQ_MAJERR, qword3); @@ -1213,11 +1135,6 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq, return irdma_uk_cq_poll_cmpl(cq, info); } } - /*cease posting push mode on push drop*/ - if (info->push_dropped) { - qp->push_mode = false; - qp->push_dropped = true; - } if (info->comp_status != IRDMA_COMPL_STATUS_FLUSHED) { info->wr_id = qp->sq_wrtrk_array[wqe_idx].wrid; if (!info->comp_status) @@ -1349,10 +1266,12 @@ void irdma_get_wqe_shift(struct irdma_uk_attrs *uk_attrs, u32 sge, int irdma_get_sqdepth(struct irdma_uk_attrs *uk_attrs, u32 sq_size, u8 shift, u32 *sqdepth) { + u32 min_size = (u32)uk_attrs->min_hw_wq_size << shift; + *sqdepth = irdma_qp_round_up((sq_size << shift) + IRDMA_SQ_RSVD); - if (*sqdepth < (IRDMA_QP_SW_MIN_WQSIZE << shift)) - *sqdepth = IRDMA_QP_SW_MIN_WQSIZE << shift; + if (*sqdepth < min_size) + *sqdepth = min_size; else if (*sqdepth > uk_attrs->max_hw_wq_quanta) return -EINVAL; @@ -1369,10 +1288,12 @@ int irdma_get_sqdepth(struct irdma_uk_attrs *uk_attrs, u32 sq_size, u8 shift, int irdma_get_rqdepth(struct irdma_uk_attrs *uk_attrs, u32 rq_size, u8 shift, u32 *rqdepth) { + u32 min_size = (u32)uk_attrs->min_hw_wq_size << shift; + *rqdepth = irdma_qp_round_up((rq_size << shift) + IRDMA_RQ_RSVD); - if (*rqdepth < (IRDMA_QP_SW_MIN_WQSIZE << shift)) - *rqdepth = IRDMA_QP_SW_MIN_WQSIZE << shift; + if (*rqdepth < min_size) + *rqdepth = min_size; else if (*rqdepth > uk_attrs->max_hw_rq_quanta) return -EINVAL; @@ -1414,6 +1335,78 @@ static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp, IRDMA_RING_MOVE_HEAD_BY_COUNT_NOCHECK(qp->initial_ring, move_cnt); } +/** + * irdma_uk_calc_shift_wq - calculate WQE shift for both SQ and RQ + * @ukinfo: qp initialization info + * @sq_shift: Returns shift of SQ + * @rq_shift: Returns shift of RQ + */ +void irdma_uk_calc_shift_wq(struct irdma_qp_uk_init_info *ukinfo, u8 *sq_shift, + u8 *rq_shift) +{ + bool imm_support = ukinfo->uk_attrs->hw_rev >= IRDMA_GEN_2; + + irdma_get_wqe_shift(ukinfo->uk_attrs, + imm_support ? ukinfo->max_sq_frag_cnt + 1 : + ukinfo->max_sq_frag_cnt, + ukinfo->max_inline_data, sq_shift); + + irdma_get_wqe_shift(ukinfo->uk_attrs, ukinfo->max_rq_frag_cnt, 0, + rq_shift); + + if (ukinfo->uk_attrs->hw_rev == IRDMA_GEN_1) { + if (ukinfo->abi_ver > 4) + *rq_shift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; + } +} + +/** + * irdma_uk_calc_depth_shift_sq - calculate depth and shift for SQ size. + * @ukinfo: qp initialization info + * @sq_depth: Returns depth of SQ + * @sq_shift: Returns shift of SQ + */ +int irdma_uk_calc_depth_shift_sq(struct irdma_qp_uk_init_info *ukinfo, + u32 *sq_depth, u8 *sq_shift) +{ + bool imm_support = ukinfo->uk_attrs->hw_rev >= IRDMA_GEN_2; + int status; + + irdma_get_wqe_shift(ukinfo->uk_attrs, + imm_support ? ukinfo->max_sq_frag_cnt + 1 : + ukinfo->max_sq_frag_cnt, + ukinfo->max_inline_data, sq_shift); + status = irdma_get_sqdepth(ukinfo->uk_attrs, ukinfo->sq_size, + *sq_shift, sq_depth); + + return status; +} + +/** + * irdma_uk_calc_depth_shift_rq - calculate depth and shift for RQ size. + * @ukinfo: qp initialization info + * @rq_depth: Returns depth of RQ + * @rq_shift: Returns shift of RQ + */ +int irdma_uk_calc_depth_shift_rq(struct irdma_qp_uk_init_info *ukinfo, + u32 *rq_depth, u8 *rq_shift) +{ + int status; + + irdma_get_wqe_shift(ukinfo->uk_attrs, ukinfo->max_rq_frag_cnt, 0, + rq_shift); + + if (ukinfo->uk_attrs->hw_rev == IRDMA_GEN_1) { + if (ukinfo->abi_ver > 4) + *rq_shift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; + } + + status = irdma_get_rqdepth(ukinfo->uk_attrs, ukinfo->rq_size, + *rq_shift, rq_depth); + + return status; +} + /** * irdma_uk_qp_init - initialize shared qp * @qp: hw qp (user and kernel) @@ -1428,23 +1421,12 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) { int ret_code = 0; u32 sq_ring_size; - u8 sqshift, rqshift; qp->uk_attrs = info->uk_attrs; if (info->max_sq_frag_cnt > qp->uk_attrs->max_hw_wq_frags || info->max_rq_frag_cnt > qp->uk_attrs->max_hw_wq_frags) return -EINVAL; - irdma_get_wqe_shift(qp->uk_attrs, info->max_rq_frag_cnt, 0, &rqshift); - if (qp->uk_attrs->hw_rev == IRDMA_GEN_1) { - irdma_get_wqe_shift(qp->uk_attrs, info->max_sq_frag_cnt, - info->max_inline_data, &sqshift); - if (info->abi_ver > 4) - rqshift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; - } else { - irdma_get_wqe_shift(qp->uk_attrs, info->max_sq_frag_cnt + 1, - info->max_inline_data, &sqshift); - } qp->qp_caps = info->qp_caps; qp->sq_base = info->sq; qp->rq_base = info->rq; @@ -1456,9 +1438,8 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) qp->wqe_alloc_db = info->wqe_alloc_db; qp->qp_id = info->qp_id; qp->sq_size = info->sq_size; - qp->push_mode = false; qp->max_sq_frag_cnt = info->max_sq_frag_cnt; - sq_ring_size = qp->sq_size << sqshift; + sq_ring_size = qp->sq_size << info->sq_shift; IRDMA_RING_INIT(qp->sq_ring, sq_ring_size); IRDMA_RING_INIT(qp->initial_ring, sq_ring_size); if (info->first_sq_wq) { @@ -1473,9 +1454,9 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info) qp->rq_size = info->rq_size; qp->max_rq_frag_cnt = info->max_rq_frag_cnt; qp->max_inline_data = info->max_inline_data; - qp->rq_wqe_size = rqshift; + qp->rq_wqe_size = info->rq_shift; IRDMA_RING_INIT(qp->rq_ring, qp->rq_size); - qp->rq_wqe_size_multiplier = 1 << rqshift; + qp->rq_wqe_size_multiplier = 1 << info->rq_shift; if (qp->uk_attrs->hw_rev == IRDMA_GEN_1) qp->wqe_ops = iw_wqe_uk_ops_gen_1; else @@ -1554,7 +1535,6 @@ int irdma_nop(struct irdma_qp_uk *qp, u64 wr_id, bool signaled, bool post_sq) u32 wqe_idx; struct irdma_post_sq_info info = {}; - info.push_wqe = false; info.wr_id = wr_id; wqe = irdma_qp_get_next_send_wqe(qp, &wqe_idx, IRDMA_QP_WQE_MIN_QUANTA, 0, &info); diff --git a/drivers/infiniband/hw/irdma/user.h b/drivers/infiniband/hw/irdma/user.h index d0cdf609f5e06aa71c24a5471b63b8f8aab2ba36..36feca57b274bfb5b113b9d5bd9f51d40ebc7976 100644 --- a/drivers/infiniband/hw/irdma/user.h +++ b/drivers/infiniband/hw/irdma/user.h @@ -85,6 +85,7 @@ enum irdma_device_caps_const { IRDMA_Q2_BUF_SIZE = 256, IRDMA_QP_CTX_SIZE = 256, IRDMA_MAX_PDS = 262144, + IRDMA_MIN_WQ_SIZE_GEN2 = 8, }; enum irdma_addressing_type { @@ -215,7 +216,6 @@ struct irdma_post_sq_info { bool local_fence:1; bool inline_data:1; bool imm_data_valid:1; - bool push_wqe:1; bool report_rtt:1; bool udp_hdr:1; bool defer_flag:1; @@ -247,7 +247,6 @@ struct irdma_cq_poll_info { u8 op_type; u8 q_type; bool stag_invalid_set:1; /* or L_R_Key set */ - bool push_dropped:1; bool error:1; bool solicited_event:1; bool ipv4:1; @@ -295,6 +294,12 @@ void irdma_uk_cq_init(struct irdma_cq_uk *cq, struct irdma_cq_uk_init_info *info); int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info); +void irdma_uk_calc_shift_wq(struct irdma_qp_uk_init_info *ukinfo, u8 *sq_shift, + u8 *rq_shift); +int irdma_uk_calc_depth_shift_sq(struct irdma_qp_uk_init_info *ukinfo, + u32 *sq_depth, u8 *sq_shift); +int irdma_uk_calc_depth_shift_rq(struct irdma_qp_uk_init_info *ukinfo, + u32 *rq_depth, u8 *rq_shift); struct irdma_sq_uk_wr_trk_info { u64 wrid; u32 wr_len; @@ -314,8 +319,6 @@ struct irdma_qp_uk { struct irdma_sq_uk_wr_trk_info *sq_wrtrk_array; u64 *rq_wrid_array; __le64 *shadow_area; - __le32 *push_db; - __le64 *push_wqe; struct irdma_ring sq_ring; struct irdma_ring rq_ring; struct irdma_ring initial_ring; @@ -335,8 +338,6 @@ struct irdma_qp_uk { u8 rq_wqe_size; u8 rq_wqe_size_multiplier; bool deferred_flag:1; - bool push_mode:1; /* whether the last post wqe was pushed */ - bool push_dropped:1; bool first_sq_wq:1; bool sq_flush_complete:1; /* Indicates flush was seen and SQ was empty after the flush */ bool rq_flush_complete:1; /* Indicates flush was seen and RQ was empty after the flush */ @@ -374,8 +375,12 @@ struct irdma_qp_uk_init_info { u32 max_sq_frag_cnt; u32 max_rq_frag_cnt; u32 max_inline_data; + u32 sq_depth; + u32 rq_depth; u8 first_sq_wq; u8 type; + u8 sq_shift; + u8 rq_shift; int abi_ver; bool legacy_mode; }; @@ -404,7 +409,5 @@ int irdma_get_sqdepth(struct irdma_uk_attrs *uk_attrs, u32 sq_size, u8 shift, u32 *wqdepth); int irdma_get_rqdepth(struct irdma_uk_attrs *uk_attrs, u32 rq_size, u8 shift, u32 *wqdepth); -void irdma_qp_push_wqe(struct irdma_qp_uk *qp, __le64 *wqe, u16 quanta, - u32 wqe_idx, bool post_sq); void irdma_clr_wqes(struct irdma_qp_uk *qp, u32 qp_wqe_idx); #endif /* IRDMA_USER_H */ diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c index eb083f70b09ffc166516af0ed8ed7586022bbbad..6cd5cb85dafe9fe718a05b4e7f2cffec9200a8d1 100644 --- a/drivers/infiniband/hw/irdma/utils.c +++ b/drivers/infiniband/hw/irdma/utils.c @@ -760,6 +760,31 @@ void irdma_qp_rem_ref(struct ib_qp *ibqp) complete(&iwqp->free_qp); } +void irdma_cq_add_ref(struct ib_cq *ibcq) +{ + struct irdma_cq *iwcq = to_iwcq(ibcq); + + refcount_inc(&iwcq->refcnt); +} + +void irdma_cq_rem_ref(struct ib_cq *ibcq) +{ + struct ib_device *ibdev = ibcq->device; + struct irdma_device *iwdev = to_iwdev(ibdev); + struct irdma_cq *iwcq = to_iwcq(ibcq); + unsigned long flags; + + spin_lock_irqsave(&iwdev->rf->cqtable_lock, flags); + if (!refcount_dec_and_test(&iwcq->refcnt)) { + spin_unlock_irqrestore(&iwdev->rf->cqtable_lock, flags); + return; + } + + iwdev->rf->cq_table[iwcq->cq_num] = NULL; + spin_unlock_irqrestore(&iwdev->rf->cqtable_lock, flags); + complete(&iwcq->free_cq); +} + struct ib_device *to_ibdev(struct irdma_sc_dev *dev) { return &(container_of(dev, struct irdma_pci_f, sc_dev))->iwdev->ibdev; diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 9c4fe4fa90018142f67fd0bd7bd56a9c57b8cdce..3eb7a7a3a975dcb4b16db3c1235cd536d1912ecd 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -277,7 +277,7 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, struct irdma_alloc_ucontext_req req = {}; struct irdma_alloc_ucontext_resp uresp = {}; struct irdma_ucontext *ucontext = to_ucontext(uctx); - struct irdma_uk_attrs *uk_attrs; + struct irdma_uk_attrs *uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; if (udata->inlen < IRDMA_ALLOC_UCTX_MIN_REQ_LEN || udata->outlen < IRDMA_ALLOC_UCTX_MIN_RESP_LEN) @@ -292,7 +292,9 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, ucontext->iwdev = iwdev; ucontext->abi_ver = req.userspace_ver; - uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; + if (req.comp_mask & IRDMA_ALLOC_UCTX_USE_RAW_ATTR) + ucontext->use_raw_attrs = true; + /* GEN_1 legacy support with libi40iw */ if (udata->outlen == IRDMA_ALLOC_UCTX_MIN_RESP_LEN) { if (uk_attrs->hw_rev != IRDMA_GEN_1) @@ -327,6 +329,9 @@ static int irdma_alloc_ucontext(struct ib_ucontext *uctx, uresp.max_hw_cq_size = uk_attrs->max_hw_cq_size; uresp.min_hw_cq_size = uk_attrs->min_hw_cq_size; uresp.hw_rev = uk_attrs->hw_rev; + uresp.comp_mask |= IRDMA_ALLOC_UCTX_USE_RAW_ATTR; + uresp.min_hw_wq_size = uk_attrs->min_hw_wq_size; + uresp.comp_mask |= IRDMA_ALLOC_UCTX_MIN_HW_WQ_SIZE; if (ib_copy_to_udata(udata, &uresp, min(sizeof(uresp), udata->outlen))) { rdma_user_mmap_entry_remove(ucontext->db_mmap_entry); @@ -566,6 +571,87 @@ static void irdma_setup_virt_qp(struct irdma_device *iwdev, } } +/** + * irdma_setup_umode_qp - setup sq and rq size in user mode qp + * @udata: udata + * @iwdev: iwarp device + * @iwqp: qp ptr (user or kernel) + * @info: initialize info to return + * @init_attr: Initial QP create attributes + */ +static int irdma_setup_umode_qp(struct ib_udata *udata, + struct irdma_device *iwdev, + struct irdma_qp *iwqp, + struct irdma_qp_init_info *info, + struct ib_qp_init_attr *init_attr) +{ + struct irdma_ucontext *ucontext = rdma_udata_to_drv_context(udata, + struct irdma_ucontext, ibucontext); + struct irdma_qp_uk_init_info *ukinfo = &info->qp_uk_init_info; + struct irdma_create_qp_req req; + unsigned long flags; + int ret; + + ret = ib_copy_from_udata(&req, udata, + min(sizeof(req), udata->inlen)); + if (ret) { + ibdev_dbg(&iwdev->ibdev, "VERBS: ib_copy_from_data fail\n"); + return ret; + } + + iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; + iwqp->user_mode = 1; + if (req.user_wqe_bufs) { + info->qp_uk_init_info.legacy_mode = ucontext->legacy_mode; + spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); + iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, + &ucontext->qp_reg_mem_list); + spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); + + if (!iwqp->iwpbl) { + ret = -ENODATA; + ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); + return ret; + } + } + + if (!ucontext->use_raw_attrs) { + /** + * Maintain backward compat with older ABI which passes sq and + * rq depth in quanta in cap.max_send_wr and cap.max_recv_wr. + * There is no way to compute the correct value of + * iwqp->max_send_wr/max_recv_wr in the kernel. + */ + iwqp->max_send_wr = init_attr->cap.max_send_wr; + iwqp->max_recv_wr = init_attr->cap.max_recv_wr; + ukinfo->sq_size = init_attr->cap.max_send_wr; + ukinfo->rq_size = init_attr->cap.max_recv_wr; + irdma_uk_calc_shift_wq(ukinfo, &ukinfo->sq_shift, + &ukinfo->rq_shift); + } else { + ret = irdma_uk_calc_depth_shift_sq(ukinfo, &ukinfo->sq_depth, + &ukinfo->sq_shift); + if (ret) + return ret; + + ret = irdma_uk_calc_depth_shift_rq(ukinfo, &ukinfo->rq_depth, + &ukinfo->rq_shift); + if (ret) + return ret; + + iwqp->max_send_wr = + (ukinfo->sq_depth - IRDMA_SQ_RSVD) >> ukinfo->sq_shift; + iwqp->max_recv_wr = + (ukinfo->rq_depth - IRDMA_RQ_RSVD) >> ukinfo->rq_shift; + ukinfo->sq_size = ukinfo->sq_depth >> ukinfo->sq_shift; + ukinfo->rq_size = ukinfo->rq_depth >> ukinfo->rq_shift; + } + + irdma_setup_virt_qp(iwdev, iwqp, info); + + return 0; +} + /** * irdma_setup_kmode_qp - setup initialization for kernel mode qp * @iwdev: iwarp device @@ -579,40 +665,28 @@ static int irdma_setup_kmode_qp(struct irdma_device *iwdev, struct ib_qp_init_attr *init_attr) { struct irdma_dma_mem *mem = &iwqp->kqp.dma_mem; - u32 sqdepth, rqdepth; - u8 sqshift, rqshift; u32 size; int status; struct irdma_qp_uk_init_info *ukinfo = &info->qp_uk_init_info; - struct irdma_uk_attrs *uk_attrs = &iwdev->rf->sc_dev.hw_attrs.uk_attrs; - irdma_get_wqe_shift(uk_attrs, - uk_attrs->hw_rev >= IRDMA_GEN_2 ? ukinfo->max_sq_frag_cnt + 1 : - ukinfo->max_sq_frag_cnt, - ukinfo->max_inline_data, &sqshift); - status = irdma_get_sqdepth(uk_attrs, ukinfo->sq_size, sqshift, - &sqdepth); + status = irdma_uk_calc_depth_shift_sq(ukinfo, &ukinfo->sq_depth, + &ukinfo->sq_shift); if (status) return status; - if (uk_attrs->hw_rev == IRDMA_GEN_1) - rqshift = IRDMA_MAX_RQ_WQE_SHIFT_GEN1; - else - irdma_get_wqe_shift(uk_attrs, ukinfo->max_rq_frag_cnt, 0, - &rqshift); - - status = irdma_get_rqdepth(uk_attrs, ukinfo->rq_size, rqshift, - &rqdepth); + status = irdma_uk_calc_depth_shift_rq(ukinfo, &ukinfo->rq_depth, + &ukinfo->rq_shift); if (status) return status; iwqp->kqp.sq_wrid_mem = - kcalloc(sqdepth, sizeof(*iwqp->kqp.sq_wrid_mem), GFP_KERNEL); + kcalloc(ukinfo->sq_depth, sizeof(*iwqp->kqp.sq_wrid_mem), GFP_KERNEL); if (!iwqp->kqp.sq_wrid_mem) return -ENOMEM; iwqp->kqp.rq_wrid_mem = - kcalloc(rqdepth, sizeof(*iwqp->kqp.rq_wrid_mem), GFP_KERNEL); + kcalloc(ukinfo->rq_depth, sizeof(*iwqp->kqp.rq_wrid_mem), GFP_KERNEL); + if (!iwqp->kqp.rq_wrid_mem) { kfree(iwqp->kqp.sq_wrid_mem); iwqp->kqp.sq_wrid_mem = NULL; @@ -622,7 +696,7 @@ static int irdma_setup_kmode_qp(struct irdma_device *iwdev, ukinfo->sq_wrtrk_array = iwqp->kqp.sq_wrid_mem; ukinfo->rq_wrid_array = iwqp->kqp.rq_wrid_mem; - size = (sqdepth + rqdepth) * IRDMA_QP_WQE_MIN_SIZE; + size = (ukinfo->sq_depth + ukinfo->rq_depth) * IRDMA_QP_WQE_MIN_SIZE; size += (IRDMA_SHADOW_AREA_SIZE << 3); mem->size = ALIGN(size, 256); @@ -638,16 +712,19 @@ static int irdma_setup_kmode_qp(struct irdma_device *iwdev, ukinfo->sq = mem->va; info->sq_pa = mem->pa; - ukinfo->rq = &ukinfo->sq[sqdepth]; - info->rq_pa = info->sq_pa + (sqdepth * IRDMA_QP_WQE_MIN_SIZE); - ukinfo->shadow_area = ukinfo->rq[rqdepth].elem; - info->shadow_area_pa = info->rq_pa + (rqdepth * IRDMA_QP_WQE_MIN_SIZE); - ukinfo->sq_size = sqdepth >> sqshift; - ukinfo->rq_size = rqdepth >> rqshift; + ukinfo->rq = &ukinfo->sq[ukinfo->sq_depth]; + info->rq_pa = info->sq_pa + (ukinfo->sq_depth * IRDMA_QP_WQE_MIN_SIZE); + ukinfo->shadow_area = ukinfo->rq[ukinfo->rq_depth].elem; + info->shadow_area_pa = + info->rq_pa + (ukinfo->rq_depth * IRDMA_QP_WQE_MIN_SIZE); + ukinfo->sq_size = ukinfo->sq_depth >> ukinfo->sq_shift; + ukinfo->rq_size = ukinfo->rq_depth >> ukinfo->rq_shift; ukinfo->qp_id = iwqp->ibqp.qp_num; - init_attr->cap.max_send_wr = (sqdepth - IRDMA_SQ_RSVD) >> sqshift; - init_attr->cap.max_recv_wr = (rqdepth - IRDMA_RQ_RSVD) >> rqshift; + iwqp->max_send_wr = (ukinfo->sq_depth - IRDMA_SQ_RSVD) >> ukinfo->sq_shift; + iwqp->max_recv_wr = (ukinfo->rq_depth - IRDMA_RQ_RSVD) >> ukinfo->rq_shift; + init_attr->cap.max_send_wr = iwqp->max_send_wr; + init_attr->cap.max_recv_wr = iwqp->max_recv_wr; return 0; } @@ -803,18 +880,14 @@ static int irdma_create_qp(struct ib_qp *ibqp, struct irdma_device *iwdev = to_iwdev(ibpd->device); struct irdma_pci_f *rf = iwdev->rf; struct irdma_qp *iwqp = to_iwqp(ibqp); - struct irdma_create_qp_req req = {}; struct irdma_create_qp_resp uresp = {}; u32 qp_num = 0; int err_code; - int sq_size; - int rq_size; struct irdma_sc_qp *qp; struct irdma_sc_dev *dev = &rf->sc_dev; struct irdma_uk_attrs *uk_attrs = &dev->hw_attrs.uk_attrs; struct irdma_qp_init_info init_info = {}; struct irdma_qp_host_ctx_info *ctx_info; - unsigned long flags; err_code = irdma_validate_qp_attrs(init_attr, iwdev); if (err_code) @@ -824,13 +897,10 @@ static int irdma_create_qp(struct ib_qp *ibqp, udata->outlen < IRDMA_CREATE_QP_MIN_RESP_LEN)) return -EINVAL; - sq_size = init_attr->cap.max_send_wr; - rq_size = init_attr->cap.max_recv_wr; - init_info.vsi = &iwdev->vsi; init_info.qp_uk_init_info.uk_attrs = uk_attrs; - init_info.qp_uk_init_info.sq_size = sq_size; - init_info.qp_uk_init_info.rq_size = rq_size; + init_info.qp_uk_init_info.sq_size = init_attr->cap.max_send_wr; + init_info.qp_uk_init_info.rq_size = init_attr->cap.max_recv_wr; init_info.qp_uk_init_info.max_sq_frag_cnt = init_attr->cap.max_send_sge; init_info.qp_uk_init_info.max_rq_frag_cnt = init_attr->cap.max_recv_sge; init_info.qp_uk_init_info.max_inline_data = init_attr->cap.max_inline_data; @@ -880,36 +950,9 @@ static int irdma_create_qp(struct ib_qp *ibqp, init_waitqueue_head(&iwqp->mod_qp_waitq); if (udata) { - err_code = ib_copy_from_udata(&req, udata, - min(sizeof(req), udata->inlen)); - if (err_code) { - ibdev_dbg(&iwdev->ibdev, - "VERBS: ib_copy_from_data fail\n"); - goto error; - } - - iwqp->ctx_info.qp_compl_ctx = req.user_compl_ctx; - iwqp->user_mode = 1; - if (req.user_wqe_bufs) { - struct irdma_ucontext *ucontext = - rdma_udata_to_drv_context(udata, - struct irdma_ucontext, - ibucontext); - - init_info.qp_uk_init_info.legacy_mode = ucontext->legacy_mode; - spin_lock_irqsave(&ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = irdma_get_pbl((unsigned long)req.user_wqe_bufs, - &ucontext->qp_reg_mem_list); - spin_unlock_irqrestore(&ucontext->qp_reg_mem_list_lock, flags); - - if (!iwqp->iwpbl) { - err_code = -ENODATA; - ibdev_dbg(&iwdev->ibdev, "VERBS: no pbl info\n"); - goto error; - } - } init_info.qp_uk_init_info.abi_ver = iwpd->sc_pd.abi_ver; - irdma_setup_virt_qp(iwdev, iwqp, &init_info); + err_code = irdma_setup_umode_qp(udata, iwdev, iwqp, &init_info, + init_attr); } else { INIT_DELAYED_WORK(&iwqp->dwork_flush, irdma_flush_worker); init_info.qp_uk_init_info.abi_ver = IRDMA_ABI_VER; @@ -962,10 +1005,8 @@ static int irdma_create_qp(struct ib_qp *ibqp, refcount_set(&iwqp->refcnt, 1); spin_lock_init(&iwqp->lock); spin_lock_init(&iwqp->sc_qp.pfpdu.lock); - iwqp->sig_all = (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) ? 1 : 0; + iwqp->sig_all = init_attr->sq_sig_type == IB_SIGNAL_ALL_WR; rf->qp_table[qp_num] = iwqp; - iwqp->max_send_wr = sq_size; - iwqp->max_recv_wr = rq_size; if (rdma_protocol_roce(&iwdev->ibdev, 1)) { if (dev->ws_add(&iwdev->vsi, 0)) { @@ -986,8 +1027,8 @@ static int irdma_create_qp(struct ib_qp *ibqp, if (rdma_protocol_iwarp(&iwdev->ibdev, 1)) uresp.lsmm = 1; } - uresp.actual_sq_size = sq_size; - uresp.actual_rq_size = rq_size; + uresp.actual_sq_size = init_info.qp_uk_init_info.sq_size; + uresp.actual_rq_size = init_info.qp_uk_init_info.rq_size; uresp.qp_id = qp_num; uresp.qp_caps = qp->qp_uk.qp_caps; @@ -1098,6 +1139,24 @@ static int irdma_query_pkey(struct ib_device *ibdev, u32 port, u16 index, return 0; } +static u8 irdma_roce_get_vlan_prio(const struct ib_gid_attr *attr, u8 prio) +{ + struct net_device *ndev; + + rcu_read_lock(); + ndev = rcu_dereference(attr->ndev); + if (!ndev) + goto exit; + if (is_vlan_dev(ndev)) { + u16 vlan_qos = vlan_dev_get_egress_qos_mask(ndev, prio); + + prio = (vlan_qos & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + } +exit: + rcu_read_unlock(); + return prio; +} + /** * irdma_modify_qp_roce - modify qp request * @ibqp: qp's pointer for modify @@ -1174,7 +1233,8 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, if (attr_mask & IB_QP_AV) { struct irdma_av *av = &iwqp->roce_ah.av; - const struct ib_gid_attr *sgid_attr; + const struct ib_gid_attr *sgid_attr = + attr->ah_attr.grh.sgid_attr; u16 vlan_id = VLAN_N_VID; u32 local_ip[4]; @@ -1189,17 +1249,22 @@ int irdma_modify_qp_roce(struct ib_qp *ibqp, struct ib_qp_attr *attr, roce_info->dest_qp); irdma_qp_rem_qos(&iwqp->sc_qp); dev->ws_remove(iwqp->sc_qp.vsi, ctx_info->user_pri); - ctx_info->user_pri = rt_tos2priority(udp_info->tos); - iwqp->sc_qp.user_pri = ctx_info->user_pri; - if (dev->ws_add(iwqp->sc_qp.vsi, ctx_info->user_pri)) - return -ENOMEM; - irdma_qp_add_qos(&iwqp->sc_qp); + if (iwqp->sc_qp.vsi->dscp_mode) + ctx_info->user_pri = + iwqp->sc_qp.vsi->dscp_map[irdma_tos2dscp(udp_info->tos)]; + else + ctx_info->user_pri = rt_tos2priority(udp_info->tos); } - sgid_attr = attr->ah_attr.grh.sgid_attr; ret = rdma_read_gid_l2_fields(sgid_attr, &vlan_id, ctx_info->roce_info->mac_addr); if (ret) return ret; + ctx_info->user_pri = irdma_roce_get_vlan_prio(sgid_attr, + ctx_info->user_pri); + if (dev->ws_add(iwqp->sc_qp.vsi, ctx_info->user_pri)) + return -ENOMEM; + iwqp->sc_qp.user_pri = ctx_info->user_pri; + irdma_qp_add_qos(&iwqp->sc_qp); if (vlan_id >= VLAN_N_VID && iwdev->dcb_vlan_mode) vlan_id = 0; @@ -1781,6 +1846,9 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) irdma_process_resize_list(iwcq, iwdev, NULL); spin_unlock_irqrestore(&iwcq->lock, flags); + irdma_cq_rem_ref(ib_cq); + wait_for_completion(&iwcq->free_cq); + irdma_cq_wq_destroy(iwdev->rf, cq); spin_lock_irqsave(&iwceq->ce_lock, flags); @@ -1990,6 +2058,7 @@ static int irdma_create_cq(struct ib_cq *ibcq, cq = &iwcq->sc_cq; cq->back_cq = iwcq; + refcount_set(&iwcq->refcnt, 1); spin_lock_init(&iwcq->lock); INIT_LIST_HEAD(&iwcq->resize_list); INIT_LIST_HEAD(&iwcq->cmpl_generated); @@ -2141,6 +2210,9 @@ static int irdma_create_cq(struct ib_cq *ibcq, goto cq_destroy; } } + rf->cq_table[cq_num] = iwcq; + init_completion(&iwcq->free_cq); + return 0; cq_destroy: irdma_cq_wq_destroy(rf, cq); @@ -2552,7 +2624,8 @@ static int irdma_hw_alloc_stag(struct irdma_device *iwdev, struct irdma_mr *iwmr) { struct irdma_allocate_stag_info *info; - struct irdma_pd *iwpd = to_iwpd(iwmr->ibmr.pd); + struct ib_pd *pd = iwmr->ibmr.pd; + struct irdma_pd *iwpd = to_iwpd(pd); int status; struct irdma_cqp_request *cqp_request; struct cqp_cmds_info *cqp_info; @@ -2568,6 +2641,7 @@ static int irdma_hw_alloc_stag(struct irdma_device *iwdev, info->stag_idx = iwmr->stag >> IRDMA_CQPSQ_STAG_IDX_S; info->pd_id = iwpd->sc_pd.pd_id; info->total_len = iwmr->len; + info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY; info->remote_access = true; cqp_info->cqp_cmd = IRDMA_OP_ALLOC_STAG; cqp_info->post_sq = 1; @@ -2615,6 +2689,8 @@ static struct ib_mr *irdma_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type, iwmr->type = IRDMA_MEMREG_TYPE_MEM; palloc = &iwpbl->pble_alloc; iwmr->page_cnt = max_num_sg; + /* Use system PAGE_SIZE as the sg page sizes are unknown at this point */ + iwmr->len = max_num_sg * PAGE_SIZE; err_code = irdma_get_pble(iwdev->rf->pble_rsrc, palloc, iwmr->page_cnt, false); if (err_code) @@ -2694,7 +2770,8 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr, { struct irdma_pbl *iwpbl = &iwmr->iwpbl; struct irdma_reg_ns_stag_info *stag_info; - struct irdma_pd *iwpd = to_iwpd(iwmr->ibmr.pd); + struct ib_pd *pd = iwmr->ibmr.pd; + struct irdma_pd *iwpd = to_iwpd(pd); struct irdma_pble_alloc *palloc = &iwpbl->pble_alloc; struct irdma_cqp_request *cqp_request; struct cqp_cmds_info *cqp_info; @@ -2713,6 +2790,7 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr, stag_info->total_len = iwmr->len; stag_info->access_rights = irdma_get_mr_access(access); stag_info->pd_id = iwpd->sc_pd.pd_id; + stag_info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY; if (stag_info->access_rights & IRDMA_ACCESS_FLAGS_ZERO_BASED) stag_info->addr_type = IRDMA_ADDR_TYPE_ZERO_BASED; else @@ -2794,8 +2872,8 @@ static struct irdma_mr *irdma_alloc_iwmr(struct ib_umem *region, enum irdma_memreg_type reg_type) { struct irdma_device *iwdev = to_iwdev(pd->device); - struct irdma_pbl *iwpbl = NULL; - struct irdma_mr *iwmr = NULL; + struct irdma_pbl *iwpbl; + struct irdma_mr *iwmr; unsigned long pgsz_bitmap; iwmr = kzalloc(sizeof(*iwmr), GFP_KERNEL); @@ -3476,8 +3554,7 @@ static void irdma_process_cqe(struct ib_wc *entry, set_ib_wc_op_sq(cq_poll_info, entry); } else { set_ib_wc_op_rq(cq_poll_info, entry, - qp->qp_uk.qp_caps & IRDMA_SEND_WITH_IMM ? - true : false); + qp->qp_uk.qp_caps & IRDMA_SEND_WITH_IMM); if (qp->qp_uk.qp_type != IRDMA_QP_TYPE_ROCE_UD && cq_poll_info->stag_invalid_set) { entry->ex.invalidate_rkey = cq_poll_info->inv_stag; @@ -3963,7 +4040,7 @@ static int irdma_attach_mcast(struct ib_qp *ibqp, union ib_gid *ibgid, u16 lid) if (!ipv6_addr_v4mapped((struct in6_addr *)ibgid)) { irdma_copy_ip_ntohl(ip_addr, sgid_addr.saddr_in6.sin6_addr.in6_u.u6_addr32); - irdma_netdev_vlan_ipv6(ip_addr, &vlan_id, NULL); + irdma_get_vlan_mac_ipv6(ip_addr, &vlan_id, NULL); ipv4 = false; ibdev_dbg(&iwdev->ibdev, "VERBS: qp_id=%d, IP6address=%pI6\n", ibqp->qp_num, @@ -4261,9 +4338,12 @@ static int irdma_setup_ah(struct ib_ah *ibah, struct rdma_ah_init_attr *attr) ah_info->vlan_tag = 0; if (ah_info->vlan_tag < VLAN_N_VID) { + u8 prio = rt_tos2priority(ah_info->tc_tos); + + prio = irdma_roce_get_vlan_prio(sgid_attr, prio); + + ah_info->vlan_tag |= (u16)prio << VLAN_PRIO_SHIFT; ah_info->insert_vlan_tag = true; - ah_info->vlan_tag |= - rt_tos2priority(ah_info->tc_tos) << VLAN_PRIO_SHIFT; } return 0; @@ -4424,7 +4504,6 @@ static int irdma_query_ah(struct ib_ah *ibah, struct rdma_ah_attr *ah_attr) ah_attr->grh.traffic_class = ah->sc_ah.ah_info.tc_tos; ah_attr->grh.hop_limit = ah->sc_ah.ah_info.hop_ttl; ah_attr->grh.sgid_index = ah->sgid_index; - ah_attr->grh.sgid_index = ah->sgid_index; memcpy(&ah_attr->grh.dgid, &ah->dgid, sizeof(ah_attr->grh.dgid)); } diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h index a536e9fa85ebf1b84a5865401e787dc3278b9e8e..5d7b983f47a24fc3858312017ac62e735911e9bb 100644 --- a/drivers/infiniband/hw/irdma/verbs.h +++ b/drivers/infiniband/hw/irdma/verbs.h @@ -18,7 +18,8 @@ struct irdma_ucontext { struct list_head qp_reg_mem_list; spinlock_t qp_reg_mem_list_lock; /* protect QP memory list */ int abi_ver; - bool legacy_mode; + u8 legacy_mode : 1; + u8 use_raw_attrs : 1; }; struct irdma_pd { @@ -122,6 +123,8 @@ struct irdma_cq { u32 cq_mem_size; struct irdma_dma_mem kmem; struct irdma_dma_mem kmem_shadow; + struct completion free_cq; + refcount_t refcnt; spinlock_t lock; /* for poll cq */ struct irdma_pbl *iwpbl; struct irdma_pbl *iwpbl_shadow; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 1f8d0d2c5f1736fb61b8092e7230d8cd9a987eb0..529db874d67c698141073b1d99afabdc54562673 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -136,7 +136,7 @@ static struct net_device *mlx4_ib_get_netdev(struct ib_device *device, continue; if (mlx4_is_bonded(ibdev->dev)) { - struct net_device *upper = NULL; + struct net_device *upper; upper = netdev_master_upper_dev_get_rcu(dev); if (upper) { @@ -261,7 +261,7 @@ static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) int ret = 0; int hw_update = 0; int i; - struct gid_entry *gids = NULL; + struct gid_entry *gids; u16 vlan_id = 0xffff; u8 mac[ETH_ALEN]; @@ -300,8 +300,7 @@ static int mlx4_ib_add_gid(const struct ib_gid_attr *attr, void **context) ret = -ENOMEM; } else { *context = port_gid_table->gids[free].ctx; - memcpy(&port_gid_table->gids[free].gid, - &attr->gid, sizeof(attr->gid)); + port_gid_table->gids[free].gid = attr->gid; port_gid_table->gids[free].gid_type = attr->gid_type; port_gid_table->gids[free].vlan_id = vlan_id; port_gid_table->gids[free].ctx->real_index = free; @@ -352,7 +351,7 @@ static int mlx4_ib_del_gid(const struct ib_gid_attr *attr, void **context) struct mlx4_port_gid_table *port_gid_table; int ret = 0; int hw_update = 0; - struct gid_entry *gids = NULL; + struct gid_entry *gids; if (!rdma_cap_roce_gid_table(attr->device, attr->port_num)) return -EINVAL; @@ -438,8 +437,8 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, struct ib_udata *uhw) { struct mlx4_ib_dev *dev = to_mdev(ibdev); - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err; int have_ib_ports; struct mlx4_uverbs_ex_query_device cmd; @@ -656,8 +655,8 @@ mlx4_ib_port_link_layer(struct ib_device *device, u32 port_num) static int ib_link_query_port(struct ib_device *ibdev, u32 port, struct ib_port_attr *props, int netw_view) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int ext_active_speed; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; @@ -834,8 +833,8 @@ static int mlx4_ib_query_port(struct ib_device *ibdev, u32 port, int __mlx4_ib_query_gid(struct ib_device *ibdev, u32 port, int index, union ib_gid *gid, int netw_view) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; struct mlx4_ib_dev *dev = to_mdev(ibdev); int clear = 0; @@ -899,8 +898,8 @@ static int mlx4_ib_query_sl2vl(struct ib_device *ibdev, u32 port, u64 *sl2vl_tbl) { union sl2vl_tbl_to_u64 sl2vl64; - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; int jj; @@ -959,8 +958,8 @@ static void mlx4_init_sl2vl_tbl(struct mlx4_ib_dev *mdev) int __mlx4_ib_query_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey, int netw_view) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; @@ -1975,8 +1974,8 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) static int init_node_data(struct mlx4_ib_dev *dev) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int mad_ifc_flags = MLX4_MAD_IFC_IGNORE_KEYS; int err = -ENOMEM; @@ -2623,7 +2622,7 @@ static int mlx4_ib_probe(struct auxiliary_device *adev, int num_req_counters; int allocated; u32 counter_index; - struct counter_index *new_counter_index = NULL; + struct counter_index *new_counter_index; pr_info_once("%s", mlx4_ib_version); @@ -2946,7 +2945,7 @@ int mlx4_ib_steer_qp_reg(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, { int err; size_t flow_size; - struct ib_flow_attr *flow = NULL; + struct ib_flow_attr *flow; struct ib_flow_spec_ib *ib_spec; if (is_attach) { @@ -2966,11 +2965,11 @@ int mlx4_ib_steer_qp_reg(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, err = __mlx4_ib_create_flow(&mqp->ibqp, flow, MLX4_DOMAIN_NIC, MLX4_FS_REGULAR, &mqp->reg_id); - } else { - err = __mlx4_ib_destroy_flow(mdev->dev, mqp->reg_id); + kfree(flow); + return err; } - kfree(flow); - return err; + + return __mlx4_ib_destroy_flow(mdev->dev, mqp->reg_id); } static void mlx4_ib_remove(struct auxiliary_device *adev) @@ -3019,7 +3018,7 @@ static void mlx4_ib_remove(struct auxiliary_device *adev) static void do_slave_init(struct mlx4_ib_dev *ibdev, int slave, int do_init) { - struct mlx4_ib_demux_work **dm = NULL; + struct mlx4_ib_demux_work **dm; struct mlx4_dev *dev = ibdev->dev; int i; unsigned long flags; diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index 24ee79aa2122ed628fcf6b86c1d52bc071bd4a02..88f534cf690e928bc8d8224bcb280538a2adcd41 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -223,7 +223,7 @@ void del_sysfs_port_mcg_attr(struct mlx4_ib_dev *device, int port_num, static int add_port_entries(struct mlx4_ib_dev *device, int port_num) { int i; - char buff[11]; + char buff[12]; struct mlx4_ib_iov_port *port = NULL; int ret = 0 ; struct ib_port_attr attr; diff --git a/drivers/infiniband/hw/mlx5/counters.c b/drivers/infiniband/hw/mlx5/counters.c index 93257fa5aae8eb6f59b5cf58cda945246933c681..8300ce6228350dde6d4efc6d79d122d843a8cef4 100644 --- a/drivers/infiniband/hw/mlx5/counters.c +++ b/drivers/infiniband/hw/mlx5/counters.c @@ -27,6 +27,7 @@ static const struct mlx5_ib_counter basic_q_cnts[] = { INIT_Q_COUNTER(rx_write_requests), INIT_Q_COUNTER(rx_read_requests), INIT_Q_COUNTER(rx_atomic_requests), + INIT_Q_COUNTER(rx_dct_connect), INIT_Q_COUNTER(out_of_buffer), }; @@ -46,6 +47,7 @@ static const struct mlx5_ib_counter vport_basic_q_cnts[] = { INIT_VPORT_Q_COUNTER(rx_write_requests), INIT_VPORT_Q_COUNTER(rx_read_requests), INIT_VPORT_Q_COUNTER(rx_atomic_requests), + INIT_VPORT_Q_COUNTER(rx_dct_connect), INIT_VPORT_Q_COUNTER(out_of_buffer), }; diff --git a/drivers/infiniband/hw/mlx5/fs.c b/drivers/infiniband/hw/mlx5/fs.c index 1e419e080b5352bbcdf3c7faa4277cca97f6e3ce..520034acf73aac388ae7f66851fb990ea6adec72 100644 --- a/drivers/infiniband/hw/mlx5/fs.c +++ b/drivers/infiniband/hw/mlx5/fs.c @@ -2470,8 +2470,8 @@ static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)( mlx5_steering_anchor_destroy_res(ft_prio); put_flow_table: put_flow_table(dev, ft_prio, true); - mutex_unlock(&dev->flow_db->lock); free_obj: + mutex_unlock(&dev->flow_db->lock); kfree(obj); return err; diff --git a/drivers/infiniband/hw/mlx5/mad.c b/drivers/infiniband/hw/mlx5/mad.c index 9c8a7b206dcf470473fd6eab945a271d58b35c6d..8102ef113b7e08332d317fa43ca21097a27b0120 100644 --- a/drivers/infiniband/hw/mlx5/mad.c +++ b/drivers/infiniband/hw/mlx5/mad.c @@ -308,8 +308,8 @@ int mlx5_ib_process_mad(struct ib_device *ibdev, int mad_flags, u32 port_num, int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, unsigned int port) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; u16 packet_error; @@ -338,8 +338,8 @@ int mlx5_query_ext_port_caps(struct mlx5_ib_dev *dev, unsigned int port) static int mlx5_query_mad_ifc_smp_attr_node_info(struct ib_device *ibdev, struct ib_smp *out_mad) { - struct ib_smp *in_mad = NULL; - int err = -ENOMEM; + struct ib_smp *in_mad; + int err; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); if (!in_mad) @@ -358,8 +358,8 @@ static int mlx5_query_mad_ifc_smp_attr_node_info(struct ib_device *ibdev, int mlx5_query_mad_ifc_system_image_guid(struct ib_device *ibdev, __be64 *sys_image_guid) { - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; + struct ib_smp *out_mad; + int err; out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL); if (!out_mad) @@ -380,8 +380,8 @@ int mlx5_query_mad_ifc_system_image_guid(struct ib_device *ibdev, int mlx5_query_mad_ifc_max_pkeys(struct ib_device *ibdev, u16 *max_pkeys) { - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; + struct ib_smp *out_mad; + int err; out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL); if (!out_mad) @@ -402,8 +402,8 @@ int mlx5_query_mad_ifc_max_pkeys(struct ib_device *ibdev, int mlx5_query_mad_ifc_vendor_id(struct ib_device *ibdev, u32 *vendor_id) { - struct ib_smp *out_mad = NULL; - int err = -ENOMEM; + struct ib_smp *out_mad; + int err; out_mad = kmalloc(sizeof(*out_mad), GFP_KERNEL); if (!out_mad) @@ -423,8 +423,8 @@ int mlx5_query_mad_ifc_vendor_id(struct ib_device *ibdev, int mlx5_query_mad_ifc_node_desc(struct mlx5_ib_dev *dev, char *node_desc) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -448,8 +448,8 @@ int mlx5_query_mad_ifc_node_desc(struct mlx5_ib_dev *dev, char *node_desc) int mlx5_query_mad_ifc_node_guid(struct mlx5_ib_dev *dev, __be64 *node_guid) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -474,8 +474,8 @@ int mlx5_query_mad_ifc_node_guid(struct mlx5_ib_dev *dev, __be64 *node_guid) int mlx5_query_mad_ifc_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -503,8 +503,8 @@ int mlx5_query_mad_ifc_pkey(struct ib_device *ibdev, u32 port, u16 index, int mlx5_query_mad_ifc_gids(struct ib_device *ibdev, u32 port, int index, union ib_gid *gid) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof(*in_mad), GFP_KERNEL); @@ -545,8 +545,8 @@ int mlx5_query_mad_ifc_port(struct ib_device *ibdev, u32 port, { struct mlx5_ib_dev *dev = to_mdev(ibdev); struct mlx5_core_dev *mdev = dev->mdev; - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int ext_active_speed; int err = -ENOMEM; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index aed5cdea50e6c08f35691320a3a50b6fff173973..555629b798b956949fe6c622517986529aa73faa 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -2084,7 +2084,7 @@ static inline char *mmap_cmd2str(enum mlx5_ib_mmap_cmd cmd) case MLX5_IB_MMAP_DEVICE_MEM: return "Device Memory"; default: - return NULL; + return "Unknown"; } } diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 2017ede100a62d793f161761fede1a73d104246f..8a3762d9ff58c1550f3d2b429a47949079a089df 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -301,7 +301,8 @@ static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) static void set_cache_mkc(struct mlx5_cache_ent *ent, void *mkc) { - set_mkc_access_pd_addr_fields(mkc, 0, 0, ent->dev->umrc.pd); + set_mkc_access_pd_addr_fields(mkc, ent->rb_key.access_flags, 0, + ent->dev->umrc.pd); MLX5_SET(mkc, mkc, free, 1); MLX5_SET(mkc, mkc, umr_en, 1); MLX5_SET(mkc, mkc, access_mode_1_0, ent->rb_key.access_mode & 0x3); @@ -1024,19 +1025,26 @@ void mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev) if (!dev->cache.wq) return; - cancel_delayed_work_sync(&dev->cache.remove_ent_dwork); mutex_lock(&dev->cache.rb_lock); for (node = rb_first(root); node; node = rb_next(node)) { ent = rb_entry(node, struct mlx5_cache_ent, node); xa_lock_irq(&ent->mkeys); ent->disabled = true; xa_unlock_irq(&ent->mkeys); - cancel_delayed_work_sync(&ent->dwork); } + mutex_unlock(&dev->cache.rb_lock); + + /* + * After all entries are disabled and will not reschedule on WQ, + * flush it and all async commands. + */ + flush_workqueue(dev->cache.wq); mlx5_mkey_cache_debugfs_cleanup(dev); mlx5_cmd_cleanup_async_ctx(&dev->async_ctx); + /* At this point all entries are disabled and have no concurrent work. */ + mutex_lock(&dev->cache.rb_lock); node = rb_first(root); while (node) { ent = rb_entry(node, struct mlx5_cache_ent, node); @@ -1235,7 +1243,8 @@ static struct mlx5_ib_mr *reg_create(struct ib_pd *pd, struct ib_umem *umem, } /* The pg_access bit allows setting the access flags - * in the page list submitted with the command. */ + * in the page list submitted with the command. + */ MLX5_SET(create_mkey_in, in, pg_access, !!(pg_cap)); mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); @@ -1766,6 +1775,11 @@ mlx5_alloc_priv_descs(struct ib_device *device, int ret; add_size = max_t(int, MLX5_UMR_ALIGN - ARCH_KMALLOC_MINALIGN, 0); + if (is_power_of_2(MLX5_UMR_ALIGN) && add_size) { + int end = max_t(int, MLX5_UMR_ALIGN, roundup_pow_of_two(size)); + + add_size = min_t(int, end - size, add_size); + } mr->descs_alloc = kzalloc(size + add_size, GFP_KERNEL); if (!mr->descs_alloc) diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index c46df53f26cf4acd91aa792e8a3f1f309eb60f7f..e1325f2927d6e3333523ee0c7f322d16ea516b14 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -53,8 +53,8 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *props, struct ib_udata *uhw) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; struct mthca_dev *mdev = to_mdev(ibdev); @@ -121,8 +121,8 @@ static int mthca_query_device(struct ib_device *ibdev, struct ib_device_attr *pr static int mthca_query_port(struct ib_device *ibdev, u32 port, struct ib_port_attr *props) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); @@ -217,8 +217,8 @@ static int mthca_modify_port(struct ib_device *ibdev, static int mthca_query_pkey(struct ib_device *ibdev, u32 port, u16 index, u16 *pkey) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); @@ -246,8 +246,8 @@ static int mthca_query_pkey(struct ib_device *ibdev, static int mthca_query_gid(struct ib_device *ibdev, u32 port, int index, union ib_gid *gid) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); @@ -989,8 +989,8 @@ static const struct attribute_group mthca_attr_group = { static int mthca_init_node_data(struct mthca_dev *dev) { - struct ib_smp *in_mad = NULL; - struct ib_smp *out_mad = NULL; + struct ib_smp *in_mad; + struct ib_smp *out_mad; int err = -ENOMEM; in_mad = kzalloc(sizeof *in_mad, GFP_KERNEL); diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 58f994341e9acd6fdaf211868042d472a72d1c7d..c849fdbd4c994d7d63291c631fb478ac46326e04 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -1277,7 +1277,7 @@ static void ocrdma_set_qp_init_params(struct ocrdma_qp *qp, qp->sq.max_sges = attrs->cap.max_send_sge; qp->rq.max_sges = attrs->cap.max_recv_sge; qp->state = OCRDMA_QPS_RST; - qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false; + qp->signaled = attrs->sq_sig_type == IB_SIGNAL_ALL_WR; } static void ocrdma_store_gsi_qp_cq(struct ocrdma_dev *dev, diff --git a/drivers/infiniband/hw/qedr/qedr_roce_cm.c b/drivers/infiniband/hw/qedr/qedr_roce_cm.c index 05307c1488b8300b15b177ab84eb40f90ab66021..859f66a51bd25d6bd8f04bd9b25430a6e4f99282 100644 --- a/drivers/infiniband/hw/qedr/qedr_roce_cm.c +++ b/drivers/infiniband/hw/qedr/qedr_roce_cm.c @@ -354,7 +354,6 @@ int qedr_create_gsi_qp(struct qedr_dev *dev, struct ib_qp_init_attr *attrs, /* the GSI CQ is handled by the driver so remove it from the FW */ qedr_destroy_gsi_cq(dev, attrs); dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI; - dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI; DP_DEBUG(dev, QEDR_MSG_GSI, "created GSI QP %p\n", qp); diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index d745ce9dc88aa89381c6ee2d1d98de3460ee2933..7887a6786ed43d6917a97b2dfbd8770c49383fbd 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -1358,7 +1358,7 @@ static void qedr_set_common_qp_params(struct qedr_dev *dev, qp->prev_wqe_size = 0; - qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false; + qp->signaled = attrs->sq_sig_type == IB_SIGNAL_ALL_WR; qp->dev = dev; if (qedr_qp_has_sq(qp)) { qedr_reset_qp_hwq_info(&qp->sq); diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index ef85bc8d9384cbfc6552c179e54b16d8ae178f94..152952127f131366cad368ef37d51c8ed9d0eee1 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -2250,7 +2250,9 @@ static ssize_t qib_write_iter(struct kiocb *iocb, struct iov_iter *from) return qib_user_sdma_writev(rcd, pq, iter_iov(from), from->nr_segs); } -static struct class *qib_class; +static const struct class qib_class = { + .name = "ipath", +}; static dev_t qib_dev; int qib_cdev_init(int minor, const char *name, @@ -2281,7 +2283,7 @@ int qib_cdev_init(int minor, const char *name, goto err_cdev; } - device = device_create(qib_class, NULL, dev, NULL, "%s", name); + device = device_create(&qib_class, NULL, dev, NULL, "%s", name); if (!IS_ERR(device)) goto done; ret = PTR_ERR(device); @@ -2325,9 +2327,8 @@ int __init qib_dev_init(void) goto done; } - qib_class = class_create("ipath"); - if (IS_ERR(qib_class)) { - ret = PTR_ERR(qib_class); + ret = class_register(&qib_class); + if (ret) { pr_err("Could not create device class (err %d)\n", -ret); unregister_chrdev_region(qib_dev, QIB_NMINORS); } @@ -2338,10 +2339,8 @@ int __init qib_dev_init(void) void qib_dev_cleanup(void) { - if (qib_class) { - class_destroy(qib_class); - qib_class = NULL; - } + if (class_is_registered(&qib_class)) + class_unregister(&qib_class); unregister_chrdev_region(qib_dev, QIB_NMINORS); } diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c index 5111735aafaed8286a72a4972750a11e2c4cedbc..d0bdc2d8adc82430734a9335aa54dce553728ddd 100644 --- a/drivers/infiniband/sw/rxe/rxe_comp.c +++ b/drivers/infiniband/sw/rxe/rxe_comp.c @@ -597,6 +597,10 @@ static void flush_send_queue(struct rxe_qp *qp, bool notify) struct rxe_queue *q = qp->sq.queue; int err; + /* send queue never got created. nothing to do. */ + if (!qp->sq.queue) + return; + while ((wqe = queue_head(q, q->type))) { if (notify) { err = flush_send_wqe(qp, wqe); diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index 666e06a82bc9e7de16bb3a8f9ca9a677038e39b3..4d2a8ef52c8503ad7225adb01ca001ad369373b4 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -136,12 +136,6 @@ static inline int qp_mtu(struct rxe_qp *qp) return IB_MTU_4096; } -static inline int rcv_wqe_size(int max_sge) -{ - return sizeof(struct rxe_recv_wqe) + - max_sge * sizeof(struct ib_sge); -} - void free_rd_atomic_resource(struct resp_res *res); static inline void rxe_advance_resp_resource(struct rxe_qp *qp) diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index a569b111a9d2a88c79b5a99350e5d261942b45a5..28e379c108bce0f63231a7f80d790b80e3ac83fe 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -183,13 +183,63 @@ static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp, atomic_set(&qp->skb_out, 0); } +static int rxe_init_sq(struct rxe_qp *qp, struct ib_qp_init_attr *init, + struct ib_udata *udata, + struct rxe_create_qp_resp __user *uresp) +{ + struct rxe_dev *rxe = to_rdev(qp->ibqp.device); + int wqe_size; + int err; + + qp->sq.max_wr = init->cap.max_send_wr; + wqe_size = max_t(int, init->cap.max_send_sge * sizeof(struct ib_sge), + init->cap.max_inline_data); + qp->sq.max_sge = wqe_size / sizeof(struct ib_sge); + qp->sq.max_inline = wqe_size; + wqe_size += sizeof(struct rxe_send_wqe); + + qp->sq.queue = rxe_queue_init(rxe, &qp->sq.max_wr, wqe_size, + QUEUE_TYPE_FROM_CLIENT); + if (!qp->sq.queue) { + rxe_err_qp(qp, "Unable to allocate send queue"); + err = -ENOMEM; + goto err_out; + } + + /* prepare info for caller to mmap send queue if user space qp */ + err = do_mmap_info(rxe, uresp ? &uresp->sq_mi : NULL, udata, + qp->sq.queue->buf, qp->sq.queue->buf_size, + &qp->sq.queue->ip); + if (err) { + rxe_err_qp(qp, "do_mmap_info failed, err = %d", err); + goto err_free; + } + + /* return actual capabilities to caller which may be larger + * than requested + */ + init->cap.max_send_wr = qp->sq.max_wr; + init->cap.max_send_sge = qp->sq.max_sge; + init->cap.max_inline_data = qp->sq.max_inline; + + return 0; + +err_free: + vfree(qp->sq.queue->buf); + kfree(qp->sq.queue); + qp->sq.queue = NULL; +err_out: + return err; +} + static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, struct ib_qp_init_attr *init, struct ib_udata *udata, struct rxe_create_qp_resp __user *uresp) { int err; - int wqe_size; - enum queue_type type; + + /* if we don't finish qp create make sure queue is valid */ + skb_queue_head_init(&qp->req_pkts); err = sock_create_kern(&init_net, AF_INET, SOCK_DGRAM, 0, &qp->sk); if (err < 0) @@ -204,32 +254,10 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, * (0xc000 - 0xffff). */ qp->src_port = RXE_ROCE_V2_SPORT + (hash_32(qp_num(qp), 14) & 0x3fff); - qp->sq.max_wr = init->cap.max_send_wr; - - /* These caps are limited by rxe_qp_chk_cap() done by the caller */ - wqe_size = max_t(int, init->cap.max_send_sge * sizeof(struct ib_sge), - init->cap.max_inline_data); - qp->sq.max_sge = init->cap.max_send_sge = - wqe_size / sizeof(struct ib_sge); - qp->sq.max_inline = init->cap.max_inline_data = wqe_size; - wqe_size += sizeof(struct rxe_send_wqe); - type = QUEUE_TYPE_FROM_CLIENT; - qp->sq.queue = rxe_queue_init(rxe, &qp->sq.max_wr, - wqe_size, type); - if (!qp->sq.queue) - return -ENOMEM; - - err = do_mmap_info(rxe, uresp ? &uresp->sq_mi : NULL, udata, - qp->sq.queue->buf, qp->sq.queue->buf_size, - &qp->sq.queue->ip); - - if (err) { - vfree(qp->sq.queue->buf); - kfree(qp->sq.queue); - qp->sq.queue = NULL; + err = rxe_init_sq(qp, init, udata, uresp); + if (err) return err; - } qp->req.wqe_index = queue_get_producer(qp->sq.queue, QUEUE_TYPE_FROM_CLIENT); @@ -248,36 +276,65 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, return 0; } +static int rxe_init_rq(struct rxe_qp *qp, struct ib_qp_init_attr *init, + struct ib_udata *udata, + struct rxe_create_qp_resp __user *uresp) +{ + struct rxe_dev *rxe = to_rdev(qp->ibqp.device); + int wqe_size; + int err; + + qp->rq.max_wr = init->cap.max_recv_wr; + qp->rq.max_sge = init->cap.max_recv_sge; + wqe_size = sizeof(struct rxe_recv_wqe) + + qp->rq.max_sge*sizeof(struct ib_sge); + + qp->rq.queue = rxe_queue_init(rxe, &qp->rq.max_wr, wqe_size, + QUEUE_TYPE_FROM_CLIENT); + if (!qp->rq.queue) { + rxe_err_qp(qp, "Unable to allocate recv queue"); + err = -ENOMEM; + goto err_out; + } + + /* prepare info for caller to mmap recv queue if user space qp */ + err = do_mmap_info(rxe, uresp ? &uresp->rq_mi : NULL, udata, + qp->rq.queue->buf, qp->rq.queue->buf_size, + &qp->rq.queue->ip); + if (err) { + rxe_err_qp(qp, "do_mmap_info failed, err = %d", err); + goto err_free; + } + + /* return actual capabilities to caller which may be larger + * than requested + */ + init->cap.max_recv_wr = qp->rq.max_wr; + + return 0; + +err_free: + vfree(qp->rq.queue->buf); + kfree(qp->rq.queue); + qp->rq.queue = NULL; +err_out: + return err; +} + static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp, struct ib_qp_init_attr *init, struct ib_udata *udata, struct rxe_create_qp_resp __user *uresp) { int err; - int wqe_size; - enum queue_type type; + + /* if we don't finish qp create make sure queue is valid */ + skb_queue_head_init(&qp->resp_pkts); if (!qp->srq) { - qp->rq.max_wr = init->cap.max_recv_wr; - qp->rq.max_sge = init->cap.max_recv_sge; - - wqe_size = rcv_wqe_size(qp->rq.max_sge); - - type = QUEUE_TYPE_FROM_CLIENT; - qp->rq.queue = rxe_queue_init(rxe, &qp->rq.max_wr, - wqe_size, type); - if (!qp->rq.queue) - return -ENOMEM; - - err = do_mmap_info(rxe, uresp ? &uresp->rq_mi : NULL, udata, - qp->rq.queue->buf, qp->rq.queue->buf_size, - &qp->rq.queue->ip); - if (err) { - vfree(qp->rq.queue->buf); - kfree(qp->rq.queue); - qp->rq.queue = NULL; + err = rxe_init_rq(qp, init, udata, uresp); + if (err) return err; - } } rxe_init_task(&qp->resp.task, qp, rxe_responder); @@ -307,10 +364,10 @@ int rxe_qp_from_init(struct rxe_dev *rxe, struct rxe_qp *qp, struct rxe_pd *pd, if (srq) rxe_get(srq); - qp->pd = pd; - qp->rcq = rcq; - qp->scq = scq; - qp->srq = srq; + qp->pd = pd; + qp->rcq = rcq; + qp->scq = scq; + qp->srq = srq; atomic_inc(&rcq->num_wq); atomic_inc(&scq->num_wq); diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index 2171f19494bca131971d3cfaee6dba78281d1276..d8c41fd626a9489220b09b8ebd43c6f1d345713a 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -578,10 +578,11 @@ static void save_state(struct rxe_send_wqe *wqe, struct rxe_send_wqe *rollback_wqe, u32 *rollback_psn) { - rollback_wqe->state = wqe->state; + rollback_wqe->state = wqe->state; rollback_wqe->first_psn = wqe->first_psn; - rollback_wqe->last_psn = wqe->last_psn; - *rollback_psn = qp->req.psn; + rollback_wqe->last_psn = wqe->last_psn; + rollback_wqe->dma = wqe->dma; + *rollback_psn = qp->req.psn; } static void rollback_state(struct rxe_send_wqe *wqe, @@ -589,10 +590,11 @@ static void rollback_state(struct rxe_send_wqe *wqe, struct rxe_send_wqe *rollback_wqe, u32 rollback_psn) { - wqe->state = rollback_wqe->state; + wqe->state = rollback_wqe->state; wqe->first_psn = rollback_wqe->first_psn; - wqe->last_psn = rollback_wqe->last_psn; - qp->req.psn = rollback_psn; + wqe->last_psn = rollback_wqe->last_psn; + wqe->dma = rollback_wqe->dma; + qp->req.psn = rollback_psn; } static void update_state(struct rxe_qp *qp, struct rxe_pkt_info *pkt) @@ -797,6 +799,9 @@ int rxe_requester(struct rxe_qp *qp) pkt.mask = rxe_opcode[opcode].mask; pkt.wqe = wqe; + /* save wqe state before we build and send packet */ + save_state(wqe, qp, &rollback_wqe, &rollback_psn); + av = rxe_get_av(&pkt, &ah); if (unlikely(!av)) { rxe_dbg_qp(qp, "Failed no address vector\n"); @@ -829,29 +834,29 @@ int rxe_requester(struct rxe_qp *qp) if (ah) rxe_put(ah); - /* - * To prevent a race on wqe access between requester and completer, - * wqe members state and psn need to be set before calling - * rxe_xmit_packet(). - * Otherwise, completer might initiate an unjustified retry flow. - */ - save_state(wqe, qp, &rollback_wqe, &rollback_psn); + /* update wqe state as though we had sent it */ update_wqe_state(qp, wqe, &pkt); update_wqe_psn(qp, wqe, &pkt, payload); err = rxe_xmit_packet(qp, &pkt, skb); if (err) { - qp->need_req_skb = 1; + if (err != -EAGAIN) { + wqe->status = IB_WC_LOC_QP_OP_ERR; + goto err; + } + /* the packet was dropped so reset wqe to the state + * before we sent it so we can try to resend + */ rollback_state(wqe, qp, &rollback_wqe, rollback_psn); - if (err == -EAGAIN) { - rxe_sched_task(&qp->req.task); - goto exit; - } + /* force a delay until the dropped packet is freed and + * the send queue is drained below the low water mark + */ + qp->need_req_skb = 1; - wqe->status = IB_WC_LOC_QP_OP_ERR; - goto err; + rxe_sched_task(&qp->req.task); + goto exit; } update_state(qp, &pkt); diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index 64c64f5f36a810addfa84284268fb8699a6be772..da470a925efc7bbbfc051e4855605ae7f4c31eb3 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -1469,6 +1469,10 @@ static void flush_recv_queue(struct rxe_qp *qp, bool notify) return; } + /* recv queue not created. nothing to do. */ + if (!qp->rq.queue) + return; + while ((wqe = queue_head(q, q->type))) { if (notify) { err = flush_recv_wqe(qp, wqe); diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c index 27ca82ec0826b9784546a7c612233c8d2d202fa7..3661cb627d28a9cd28c6ffba9032131c07bf2c9a 100644 --- a/drivers/infiniband/sw/rxe/rxe_srq.c +++ b/drivers/infiniband/sw/rxe/rxe_srq.c @@ -45,40 +45,41 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, struct ib_srq_init_attr *init, struct ib_udata *udata, struct rxe_create_srq_resp __user *uresp) { - int err; - int srq_wqe_size; struct rxe_queue *q; - enum queue_type type; + int wqe_size; + int err; - srq->ibsrq.event_handler = init->event_handler; - srq->ibsrq.srq_context = init->srq_context; - srq->limit = init->attr.srq_limit; - srq->srq_num = srq->elem.index; - srq->rq.max_wr = init->attr.max_wr; - srq->rq.max_sge = init->attr.max_sge; + srq->ibsrq.event_handler = init->event_handler; + srq->ibsrq.srq_context = init->srq_context; + srq->limit = init->attr.srq_limit; + srq->srq_num = srq->elem.index; + srq->rq.max_wr = init->attr.max_wr; + srq->rq.max_sge = init->attr.max_sge; - srq_wqe_size = rcv_wqe_size(srq->rq.max_sge); + wqe_size = sizeof(struct rxe_recv_wqe) + + srq->rq.max_sge*sizeof(struct ib_sge); spin_lock_init(&srq->rq.producer_lock); spin_lock_init(&srq->rq.consumer_lock); - type = QUEUE_TYPE_FROM_CLIENT; - q = rxe_queue_init(rxe, &srq->rq.max_wr, srq_wqe_size, type); + q = rxe_queue_init(rxe, &srq->rq.max_wr, wqe_size, + QUEUE_TYPE_FROM_CLIENT); if (!q) { rxe_dbg_srq(srq, "Unable to allocate queue\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_out; } - srq->rq.queue = q; - err = do_mmap_info(rxe, uresp ? &uresp->mi : NULL, udata, q->buf, q->buf_size, &q->ip); if (err) { - vfree(q->buf); - kfree(q); - return err; + rxe_dbg_srq(srq, "Unable to init mmap info for caller\n"); + goto err_free; } + srq->rq.queue = q; + init->attr.max_wr = srq->rq.max_wr; + if (uresp) { if (copy_to_user(&uresp->srq_num, &srq->srq_num, sizeof(uresp->srq_num))) { @@ -88,6 +89,12 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq, } return 0; + +err_free: + vfree(q->buf); + kfree(q); +err_out: + return err; } int rxe_srq_chk_attr(struct rxe_dev *rxe, struct rxe_srq *srq, @@ -145,9 +152,10 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, struct ib_srq_attr *attr, enum ib_srq_attr_mask mask, struct rxe_modify_srq_cmd *ucmd, struct ib_udata *udata) { - int err; struct rxe_queue *q = srq->rq.queue; struct mminfo __user *mi = NULL; + int wqe_size; + int err; if (mask & IB_SRQ_MAX_WR) { /* @@ -156,12 +164,16 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, */ mi = u64_to_user_ptr(ucmd->mmap_info_addr); - err = rxe_queue_resize(q, &attr->max_wr, - rcv_wqe_size(srq->rq.max_sge), udata, mi, - &srq->rq.producer_lock, + wqe_size = sizeof(struct rxe_recv_wqe) + + srq->rq.max_sge*sizeof(struct ib_sge); + + err = rxe_queue_resize(q, &attr->max_wr, wqe_size, + udata, mi, &srq->rq.producer_lock, &srq->rq.consumer_lock); if (err) - goto err2; + goto err_free; + + srq->rq.max_wr = attr->max_wr; } if (mask & IB_SRQ_LIMIT) @@ -169,7 +181,7 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, return 0; -err2: +err_free: rxe_queue_cleanup(q); srq->rq.queue = NULL; return err; diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 903f0b71447e6d059334962deeb71d4ba3a51819..48f86839d36a8ebba67c5371f9f73d3064b04185 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -798,7 +798,6 @@ static int init_send_wr(struct rxe_qp *qp, struct rxe_send_wr *wr, rxe_err_qp(qp, "unsupported wr opcode %d", wr->opcode); return -EINVAL; - break; } } diff --git a/drivers/infiniband/sw/siw/siw.h b/drivers/infiniband/sw/siw/siw.h index 2f3a9cda3850f61c4b7c0d134c28a995714333c6..58dddb143b9f06a57e984c413b071a47feb448b9 100644 --- a/drivers/infiniband/sw/siw/siw.h +++ b/drivers/infiniband/sw/siw/siw.h @@ -74,6 +74,7 @@ struct siw_device { u32 vendor_part_id; int numa_node; + char raw_gid[ETH_ALEN]; /* physical port state (only one port per device) */ enum ib_port_state state; @@ -530,11 +531,12 @@ void siw_qp_llp_data_ready(struct sock *sk); void siw_qp_llp_write_space(struct sock *sk); /* QP TX path functions */ +int siw_create_tx_threads(void); +void siw_stop_tx_threads(void); int siw_run_sq(void *arg); int siw_qp_sq_process(struct siw_qp *qp); int siw_sq_start(struct siw_qp *qp); int siw_activate_tx(struct siw_qp *qp); -void siw_stop_tx_thread(int nr_cpu); int siw_get_tx_cpu(struct siw_device *sdev); void siw_put_tx_cpu(int cpu); diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index da530c0404da4d7addf185c8802753155d8a76d1..43e776073f49f59d7a1753763c97f3842d68b164 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -976,6 +976,7 @@ static void siw_accept_newconn(struct siw_cep *cep) siw_cep_put(cep); new_cep->listen_cep = NULL; if (rv) { + siw_cancel_mpatimer(new_cep); siw_cep_set_free(new_cep); goto error; } @@ -1100,9 +1101,12 @@ static void siw_cm_work_handler(struct work_struct *w) /* * Socket close before MPA request received. */ - siw_dbg_cep(cep, "no mpareq: drop listener\n"); - siw_cep_put(cep->listen_cep); - cep->listen_cep = NULL; + if (cep->listen_cep) { + siw_dbg_cep(cep, + "no mpareq: drop listener\n"); + siw_cep_put(cep->listen_cep); + cep->listen_cep = NULL; + } } } release_cep = 1; @@ -1227,7 +1231,11 @@ static void siw_cm_llp_data_ready(struct sock *sk) if (!cep) goto out; - siw_dbg_cep(cep, "state: %d\n", cep->state); + siw_dbg_cep(cep, "cep state: %d, socket state %d\n", + cep->state, sk->sk_state); + + if (sk->sk_state != TCP_ESTABLISHED) + goto out; switch (cep->state) { case SIW_EPSTATE_RDMA_MODE: @@ -1501,7 +1509,6 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) cep->cm_id = NULL; id->rem_ref(id); - siw_cep_put(cep); qp->cep = NULL; siw_cep_put(cep); diff --git a/drivers/infiniband/sw/siw/siw_main.c b/drivers/infiniband/sw/siw/siw_main.c index 65b5cda5457baf505b5d763c8a191990c236998d..d4b6e0106851d6e8af6b403eccee0d0774f8316e 100644 --- a/drivers/infiniband/sw/siw/siw_main.c +++ b/drivers/infiniband/sw/siw/siw_main.c @@ -75,8 +75,7 @@ static int siw_device_register(struct siw_device *sdev, const char *name) return rv; } - siw_dbg(base_dev, "HWaddr=%pM\n", sdev->netdev->dev_addr); - + siw_dbg(base_dev, "HWaddr=%pM\n", sdev->raw_gid); return 0; } @@ -88,29 +87,6 @@ static void siw_device_cleanup(struct ib_device *base_dev) xa_destroy(&sdev->mem_xa); } -static int siw_create_tx_threads(void) -{ - int cpu, assigned = 0; - - for_each_online_cpu(cpu) { - /* Skip HT cores */ - if (cpu % cpumask_weight(topology_sibling_cpumask(cpu))) - continue; - - siw_tx_thread[cpu] = - kthread_run_on_cpu(siw_run_sq, - (unsigned long *)(long)cpu, - cpu, "siw_tx/%u"); - if (IS_ERR(siw_tx_thread[cpu])) { - siw_tx_thread[cpu] = NULL; - continue; - } - - assigned++; - } - return assigned; -} - static int siw_dev_qualified(struct net_device *netdev) { /* @@ -313,24 +289,19 @@ static struct siw_device *siw_device_create(struct net_device *netdev) return NULL; base_dev = &sdev->base_dev; - sdev->netdev = netdev; - if (netdev->type != ARPHRD_LOOPBACK && netdev->type != ARPHRD_NONE) { - addrconf_addr_eui48((unsigned char *)&base_dev->node_guid, - netdev->dev_addr); + if (netdev->addr_len) { + memcpy(sdev->raw_gid, netdev->dev_addr, + min_t(unsigned int, netdev->addr_len, ETH_ALEN)); } else { /* - * This device does not have a HW address, - * but connection mangagement lib expects gid != 0 + * This device does not have a HW address, but + * connection mangagement requires a unique gid. */ - size_t len = min_t(size_t, strlen(base_dev->name), 6); - char addr[6] = { }; - - memcpy(addr, base_dev->name, len); - addrconf_addr_eui48((unsigned char *)&base_dev->node_guid, - addr); + eth_random_addr(sdev->raw_gid); } + addrconf_addr_eui48((u8 *)&base_dev->node_guid, sdev->raw_gid); base_dev->uverbs_cmd_mask |= BIT_ULL(IB_USER_VERBS_CMD_POST_SEND); @@ -535,7 +506,6 @@ static struct rdma_link_ops siw_link_ops = { static __init int siw_init_module(void) { int rv; - int nr_cpu; if (SENDPAGE_THRESH < SIW_MAX_INLINE) { pr_info("siw: sendpage threshold too small: %u\n", @@ -580,12 +550,8 @@ static __init int siw_init_module(void) return 0; out_error: - for (nr_cpu = 0; nr_cpu < nr_cpu_ids; nr_cpu++) { - if (siw_tx_thread[nr_cpu]) { - siw_stop_tx_thread(nr_cpu); - siw_tx_thread[nr_cpu] = NULL; - } - } + siw_stop_tx_threads(); + if (siw_crypto_shash) crypto_free_shash(siw_crypto_shash); @@ -599,14 +565,8 @@ static __init int siw_init_module(void) static void __exit siw_exit_module(void) { - int cpu; + siw_stop_tx_threads(); - for_each_possible_cpu(cpu) { - if (siw_tx_thread[cpu]) { - siw_stop_tx_thread(cpu); - siw_tx_thread[cpu] = NULL; - } - } unregister_netdevice_notifier(&siw_netdev_nb); rdma_link_unregister(&siw_link_ops); ib_unregister_driver(RDMA_DRIVER_SIW); diff --git a/drivers/infiniband/sw/siw/siw_qp.c b/drivers/infiniband/sw/siw/siw_qp.c index 81e9bbd9ebdacfed5387e956db8cdd891187f9e4..47d0197db9a10351a9d09a6cb9a8df1515040ead 100644 --- a/drivers/infiniband/sw/siw/siw_qp.c +++ b/drivers/infiniband/sw/siw/siw_qp.c @@ -204,7 +204,7 @@ static int siw_qp_readq_init(struct siw_qp *qp, int irq_size, int orq_size) { if (irq_size) { irq_size = roundup_pow_of_two(irq_size); - qp->irq = vzalloc(irq_size * sizeof(struct siw_sqe)); + qp->irq = vcalloc(irq_size, sizeof(struct siw_sqe)); if (!qp->irq) { qp->attrs.irq_size = 0; return -ENOMEM; @@ -212,7 +212,7 @@ static int siw_qp_readq_init(struct siw_qp *qp, int irq_size, int orq_size) } if (orq_size) { orq_size = roundup_pow_of_two(orq_size); - qp->orq = vzalloc(orq_size * sizeof(struct siw_sqe)); + qp->orq = vcalloc(orq_size, sizeof(struct siw_sqe)); if (!qp->orq) { qp->attrs.orq_size = 0; qp->attrs.irq_size = 0; diff --git a/drivers/infiniband/sw/siw/siw_qp_tx.c b/drivers/infiniband/sw/siw/siw_qp_tx.c index 7c7a51d36d0cda2f41f395143250e0afffef02f9..60b6a413596118a2d4011b8dee55be90566df8e8 100644 --- a/drivers/infiniband/sw/siw/siw_qp_tx.c +++ b/drivers/infiniband/sw/siw/siw_qp_tx.c @@ -1208,10 +1208,45 @@ struct tx_task_t { static DEFINE_PER_CPU(struct tx_task_t, siw_tx_task_g); -void siw_stop_tx_thread(int nr_cpu) +int siw_create_tx_threads(void) { - kthread_stop(siw_tx_thread[nr_cpu]); - wake_up(&per_cpu(siw_tx_task_g, nr_cpu).waiting); + int cpu, assigned = 0; + + for_each_online_cpu(cpu) { + struct tx_task_t *tx_task; + + /* Skip HT cores */ + if (cpu % cpumask_weight(topology_sibling_cpumask(cpu))) + continue; + + tx_task = &per_cpu(siw_tx_task_g, cpu); + init_llist_head(&tx_task->active); + init_waitqueue_head(&tx_task->waiting); + + siw_tx_thread[cpu] = + kthread_run_on_cpu(siw_run_sq, + (unsigned long *)(long)cpu, + cpu, "siw_tx/%u"); + if (IS_ERR(siw_tx_thread[cpu])) { + siw_tx_thread[cpu] = NULL; + continue; + } + assigned++; + } + return assigned; +} + +void siw_stop_tx_threads(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (siw_tx_thread[cpu]) { + kthread_stop(siw_tx_thread[cpu]); + wake_up(&per_cpu(siw_tx_task_g, cpu).waiting); + siw_tx_thread[cpu] = NULL; + } + } } int siw_run_sq(void *data) @@ -1221,9 +1256,6 @@ int siw_run_sq(void *data) struct siw_qp *qp; struct tx_task_t *tx_task = &per_cpu(siw_tx_task_g, nr_cpu); - init_llist_head(&tx_task->active); - init_waitqueue_head(&tx_task->waiting); - while (1) { struct llist_node *fifo_list = NULL; @@ -1239,13 +1271,7 @@ int siw_run_sq(void *data) * llist_del_all returns a list with newest entry first. * Re-order list for fairness among QP's. */ - while (active) { - struct llist_node *tmp = active; - - active = llist_next(active); - tmp->next = fifo_list; - fifo_list = tmp; - } + fifo_list = llist_reverse_order(active); while (fifo_list) { qp = container_of(fifo_list, struct siw_qp, tx_list); fifo_list = llist_next(fifo_list); diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index 398ec13db624819033ee6fd957ea4f806df90649..fdbef3254e308674966e33463c700a259c28e264 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -157,7 +157,7 @@ int siw_query_device(struct ib_device *base_dev, struct ib_device_attr *attr, attr->vendor_part_id = sdev->vendor_part_id; addrconf_addr_eui48((u8 *)&attr->sys_image_guid, - sdev->netdev->dev_addr); + sdev->raw_gid); return 0; } @@ -218,7 +218,7 @@ int siw_query_gid(struct ib_device *base_dev, u32 port, int idx, /* subnet_prefix == interface_id == 0; */ memset(gid, 0, sizeof(*gid)); - memcpy(&gid->raw[0], sdev->netdev->dev_addr, 6); + memcpy(gid->raw, sdev->raw_gid, ETH_ALEN); return 0; } @@ -381,7 +381,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, if (udata) qp->sendq = vmalloc_user(num_sqe * sizeof(struct siw_sqe)); else - qp->sendq = vzalloc(num_sqe * sizeof(struct siw_sqe)); + qp->sendq = vcalloc(num_sqe, sizeof(struct siw_sqe)); if (qp->sendq == NULL) { rv = -ENOMEM; @@ -414,7 +414,7 @@ int siw_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, qp->recvq = vmalloc_user(num_rqe * sizeof(struct siw_rqe)); else - qp->recvq = vzalloc(num_rqe * sizeof(struct siw_rqe)); + qp->recvq = vcalloc(num_rqe, sizeof(struct siw_rqe)); if (qp->recvq == NULL) { rv = -ENOMEM; @@ -1494,7 +1494,7 @@ int siw_map_mr_sg(struct ib_mr *base_mr, struct scatterlist *sl, int num_sle, if (pbl->max_buf < num_sle) { siw_dbg_mem(mem, "too many SGE's: %d > %d\n", - mem->pbl->max_buf, num_sle); + num_sle, pbl->max_buf); return -ENOMEM; } for_each_sg(sl, slp, num_sle, i) { @@ -1624,7 +1624,7 @@ int siw_create_srq(struct ib_srq *base_srq, srq->recvq = vmalloc_user(srq->num_rqe * sizeof(struct siw_rqe)); else - srq->recvq = vzalloc(srq->num_rqe * sizeof(struct siw_rqe)); + srq->recvq = vcalloc(srq->num_rqe, sizeof(struct siw_rqe)); if (srq->recvq == NULL) { rv = -ENOMEM; diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 92e1e7587af8b4131f37e7c6887747f7649b86c1..00a7303c8cc60ef111e7c2e0cbc09c3c2be4ff29 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -2570,6 +2570,8 @@ static void isert_wait_conn(struct iscsit_conn *conn) isert_put_unsol_pending_cmds(conn); isert_wait4cmds(conn); isert_wait4logout(isert_conn); + + queue_work(isert_release_wq, &isert_conn->release_work); } static void isert_free_conn(struct iscsit_conn *conn) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index b32941dd67cb90d5b1260369d227316b418a1ec3..b6ee801fd0ffbf17740d083afa41ef340302fc55 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -45,7 +45,9 @@ static struct rtrs_rdma_dev_pd dev_pd = { }; static struct workqueue_struct *rtrs_wq; -static struct class *rtrs_clt_dev_class; +static const struct class rtrs_clt_dev_class = { + .name = "rtrs-client", +}; static inline bool rtrs_clt_is_connected(const struct rtrs_clt_sess *clt) { @@ -2698,7 +2700,7 @@ static struct rtrs_clt_sess *alloc_clt(const char *sessname, size_t paths_num, return ERR_PTR(-ENOMEM); } - clt->dev.class = rtrs_clt_dev_class; + clt->dev.class = &rtrs_clt_dev_class; clt->dev.release = rtrs_clt_dev_release; uuid_gen(&clt->paths_uuid); INIT_LIST_HEAD_RCU(&clt->paths_list); @@ -3151,16 +3153,17 @@ static const struct rtrs_rdma_dev_pd_ops dev_pd_ops = { static int __init rtrs_client_init(void) { - rtrs_rdma_dev_pd_init(0, &dev_pd); + int ret = 0; - rtrs_clt_dev_class = class_create("rtrs-client"); - if (IS_ERR(rtrs_clt_dev_class)) { + rtrs_rdma_dev_pd_init(0, &dev_pd); + ret = class_register(&rtrs_clt_dev_class); + if (ret) { pr_err("Failed to create rtrs-client dev class\n"); - return PTR_ERR(rtrs_clt_dev_class); + return ret; } rtrs_wq = alloc_workqueue("rtrs_client_wq", 0, 0); if (!rtrs_wq) { - class_destroy(rtrs_clt_dev_class); + class_unregister(&rtrs_clt_dev_class); return -ENOMEM; } @@ -3170,7 +3173,7 @@ static int __init rtrs_client_init(void) static void __exit rtrs_client_exit(void) { destroy_workqueue(rtrs_wq); - class_destroy(rtrs_clt_dev_class); + class_unregister(&rtrs_clt_dev_class); rtrs_rdma_dev_pd_deinit(&dev_pd); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c index 5adba0f754b6b02f28400efe585e99a6c468b2a0..3f305e694fe8cff7c50641f1d4707221368d6bec 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c @@ -164,7 +164,7 @@ static int rtrs_srv_create_once_sysfs_root_folders(struct rtrs_srv_path *srv_pat */ goto unlock; } - srv->dev.class = rtrs_dev_class; + srv->dev.class = &rtrs_dev_class; err = dev_set_name(&srv->dev, "%s", srv_path->s.sessname); if (err) goto unlock; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index c38901e2c8f421e4f0ad54b7e4b725474b87bfe2..75e56604e4628622915340087665bc86e7db83eb 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -27,7 +27,9 @@ MODULE_LICENSE("GPL"); #define MAX_HDR_SIZE PAGE_SIZE static struct rtrs_rdma_dev_pd dev_pd; -struct class *rtrs_dev_class; +const struct class rtrs_dev_class = { + .name = "rtrs-server", +}; static struct rtrs_srv_ib_ctx ib_ctx; static int __read_mostly max_chunk_size = DEFAULT_MAX_CHUNK_SIZE; @@ -2253,11 +2255,10 @@ static int __init rtrs_server_init(void) err); return err; } - rtrs_dev_class = class_create("rtrs-server"); - if (IS_ERR(rtrs_dev_class)) { - err = PTR_ERR(rtrs_dev_class); + err = class_register(&rtrs_dev_class); + if (err) goto out_err; - } + rtrs_wq = alloc_workqueue("rtrs_server_wq", 0, 0); if (!rtrs_wq) { err = -ENOMEM; @@ -2267,7 +2268,7 @@ static int __init rtrs_server_init(void) return 0; out_dev_class: - class_destroy(rtrs_dev_class); + class_unregister(&rtrs_dev_class); out_err: return err; } @@ -2275,7 +2276,7 @@ static int __init rtrs_server_init(void) static void __exit rtrs_server_exit(void) { destroy_workqueue(rtrs_wq); - class_destroy(rtrs_dev_class); + class_unregister(&rtrs_dev_class); rtrs_rdma_dev_pd_deinit(&dev_pd); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.h b/drivers/infiniband/ulp/rtrs/rtrs-srv.h index 2f8a638e36fad48089729f1148215460edfa014a..5e325b82ff336422fbe75b0e4c8c3c91c79a17ab 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.h @@ -129,7 +129,7 @@ struct rtrs_srv_ib_ctx { int ib_dev_count; }; -extern struct class *rtrs_dev_class; +extern const struct class rtrs_dev_class; void close_path(struct rtrs_srv_path *srv_path); diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 0e513a7e5ac808999f3541ef141f43d700fd6150..2916e77f589b813c6f9f0608f4081c719463cfe0 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1979,12 +1979,8 @@ static void srp_process_rsp(struct srp_rdma_ch *ch, struct srp_rsp *rsp) if (unlikely(rsp->flags & SRP_RSP_FLAG_DIUNDER)) scsi_set_resid(scmnd, be32_to_cpu(rsp->data_in_res_cnt)); - else if (unlikely(rsp->flags & SRP_RSP_FLAG_DIOVER)) - scsi_set_resid(scmnd, -be32_to_cpu(rsp->data_in_res_cnt)); else if (unlikely(rsp->flags & SRP_RSP_FLAG_DOUNDER)) scsi_set_resid(scmnd, be32_to_cpu(rsp->data_out_res_cnt)); - else if (unlikely(rsp->flags & SRP_RSP_FLAG_DOOVER)) - scsi_set_resid(scmnd, -be32_to_cpu(rsp->data_out_res_cnt)); srp_free_req(ch, req, scmnd, be32_to_cpu(rsp->req_lim_delta)); @@ -2788,7 +2784,6 @@ static int srp_abort(struct scsi_cmnd *scmnd) u32 tag; u16 ch_idx; struct srp_rdma_ch *ch; - int ret; shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n"); @@ -2802,19 +2797,14 @@ static int srp_abort(struct scsi_cmnd *scmnd) shost_printk(KERN_ERR, target->scsi_host, "Sending SRP abort for tag %#x\n", tag); if (srp_send_tsk_mgmt(ch, tag, scmnd->device->lun, - SRP_TSK_ABORT_TASK, NULL) == 0) - ret = SUCCESS; - else if (target->rport->state == SRP_RPORT_LOST) - ret = FAST_IO_FAIL; - else - ret = FAILED; - if (ret == SUCCESS) { + SRP_TSK_ABORT_TASK, NULL) == 0) { srp_free_req(ch, req, scmnd, 0); - scmnd->result = DID_ABORT << 16; - scsi_done(scmnd); + return SUCCESS; } + if (target->rport->state == SRP_RPORT_LOST) + return FAST_IO_FAIL; - return ret; + return FAILED; } static int srp_reset_device(struct scsi_cmnd *scmnd) diff --git a/drivers/input/gameport/Kconfig b/drivers/input/gameport/Kconfig index 5a2c2fb3217d55ee75f069106fa50789af4a1604..fe73b26e647a17a422edb7b22fee9a786ee5b9fa 100644 --- a/drivers/input/gameport/Kconfig +++ b/drivers/input/gameport/Kconfig @@ -25,6 +25,7 @@ if GAMEPORT config GAMEPORT_NS558 tristate "Classic ISA and PnP gameport support" + depends on ISA help Say Y here if you have an ISA or PnP gameport. @@ -35,6 +36,7 @@ config GAMEPORT_NS558 config GAMEPORT_L4 tristate "PDPI Lightning 4 gamecard support" + depends on ISA help Say Y here if you have a PDPI Lightning 4 gamecard. @@ -53,7 +55,7 @@ config GAMEPORT_EMU10K1 config GAMEPORT_FM801 tristate "ForteMedia FM801 gameport support" - depends on PCI + depends on PCI && HAS_IOPORT help Say Y here if you have ForteMedia FM801 PCI audio controller (Abit AU10, Genius Sound Maker, HP Workstation zx2000, diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index a1443320b419554a6199589e27ff634a28c15e05..34f416a3ebcb7c8af4cf0252e3235f41a97e7892 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -519,12 +519,32 @@ EXPORT_SYMBOL(gameport_set_phys); static void gameport_default_trigger(struct gameport *gameport) { +#ifdef CONFIG_HAS_IOPORT outb(0xff, gameport->io); +#endif } static unsigned char gameport_default_read(struct gameport *gameport) { +#ifdef CONFIG_HAS_IOPORT return inb(gameport->io); +#else + return 0xff; +#endif +} + +static void gameport_setup_default_handlers(struct gameport *gameport) +{ + if ((!gameport->trigger || !gameport->read) && + !IS_ENABLED(CONFIG_HAS_IOPORT)) + dev_err(&gameport->dev, + "I/O port access is required for %s (%s) but is not available\n", + gameport->phys, gameport->name); + + if (!gameport->trigger) + gameport->trigger = gameport_default_trigger; + if (!gameport->read) + gameport->read = gameport_default_read; } /* @@ -545,11 +565,7 @@ static void gameport_init_port(struct gameport *gameport) if (gameport->parent) gameport->dev.parent = &gameport->parent->dev; - if (!gameport->trigger) - gameport->trigger = gameport_default_trigger; - if (!gameport->read) - gameport->read = gameport_default_read; - + gameport_setup_default_handlers(gameport); INIT_LIST_HEAD(&gameport->node); spin_lock_init(&gameport->timer_lock); timer_setup(&gameport->poll_timer, gameport_run_poll_handler, 0); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index cdb193317c3b6825af9b4f84317b4af52fa6684a..ede380551e55ca9f5fe1f9dab02fe9b50c2403e6 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -264,6 +264,7 @@ static const struct xpad_device { { 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE }, { 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0f0d, 0x00c5, "Hori Fighting Commander ONE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, + { 0x0f0d, 0x00dc, "HORIPAD FPS for Nintendo Switch", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX }, { 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX }, { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX }, @@ -365,6 +366,7 @@ static const struct xpad_device { { 0x31e3, 0x1300, "Wooting 60HE (AVR)", 0, XTYPE_XBOX360 }, { 0x31e3, 0x1310, "Wooting 60HE (ARM)", 0, XTYPE_XBOX360 }, { 0x3285, 0x0607, "Nacon GC-100", 0, XTYPE_XBOX360 }, + { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, { 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX }, { 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX }, { 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN } @@ -499,6 +501,8 @@ static const struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x2f24), /* GameSir controllers */ XPAD_XBOX360_VENDOR(0x31e3), /* Wooting Keyboards */ XPAD_XBOX360_VENDOR(0x3285), /* Nacon GC-100 */ + XPAD_XBOX360_VENDOR(0x3537), /* GameSir Controllers */ + XPAD_XBOXONE_VENDOR(0x3537), /* GameSir Controllers */ { } }; @@ -1720,6 +1724,27 @@ static int xpad_start_input(struct usb_xpad *xpad) return error; } } + if (xpad->xtype == XTYPE_XBOX360) { + /* + * Some third-party controllers Xbox 360-style controllers + * require this message to finish initialization. + */ + u8 dummy[20]; + + error = usb_control_msg_recv(xpad->udev, 0, + /* bRequest */ 0x01, + /* bmRequestType */ + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, + /* wValue */ 0x100, + /* wIndex */ 0x00, + dummy, sizeof(dummy), + 25, GFP_KERNEL); + if (error) + dev_warn(&xpad->dev->dev, + "unable to receive magic message: %d\n", + error); + } return 0; } diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 896a5a989ddcecca26dd5348d1742269e32b6b9b..61e8e43e9c2bbd423445a4bff2ce7f15f6070366 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -713,17 +713,11 @@ static int adp5588_fw_parse(struct adp5588_kpad *kpad) return 0; } -static void adp5588_disable_regulator(void *reg) -{ - regulator_disable(reg); -} - static int adp5588_probe(struct i2c_client *client) { struct adp5588_kpad *kpad; struct input_dev *input; struct gpio_desc *gpio; - struct regulator *vcc; unsigned int revid; int ret; int error; @@ -749,16 +743,7 @@ static int adp5588_probe(struct i2c_client *client) if (error) return error; - vcc = devm_regulator_get(&client->dev, "vcc"); - if (IS_ERR(vcc)) - return PTR_ERR(vcc); - - error = regulator_enable(vcc); - if (error) - return error; - - error = devm_add_action_or_reset(&client->dev, - adp5588_disable_regulator, vcc); + error = devm_regulator_get_enable(&client->dev, "vcc"); if (error) return error; diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index a20a4e186639c05bf11004269a40a12b46263b63..e305c44cd0aa9045f4c62f2d3a42db05552ef224 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -196,7 +196,7 @@ static int __init amikbd_probe(struct platform_device *pdev) struct input_dev *dev; int i, err; - dev = input_allocate_device(); + dev = devm_input_allocate_device(&pdev->dev); if (!dev) { dev_err(&pdev->dev, "Not enough memory for input device\n"); return -ENOMEM; @@ -208,7 +208,6 @@ static int __init amikbd_probe(struct platform_device *pdev) dev->id.vendor = 0x0001; dev->id.product = 0x0001; dev->id.version = 0x0100; - dev->dev.parent = &pdev->dev; dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); @@ -218,35 +217,21 @@ static int __init amikbd_probe(struct platform_device *pdev) amikbd_init_console_keymaps(); ciaa.cra &= ~0x41; /* serial data in, turn off TA */ - err = request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd", - dev); + err = devm_request_irq(&pdev->dev, IRQ_AMIGA_CIAA_SP, amikbd_interrupt, + 0, "amikbd", dev); if (err) - goto fail2; + return err; err = input_register_device(dev); if (err) - goto fail3; + return err; platform_set_drvdata(pdev, dev); return 0; - - fail3: free_irq(IRQ_AMIGA_CIAA_SP, dev); - fail2: input_free_device(dev); - return err; -} - -static int __exit amikbd_remove(struct platform_device *pdev) -{ - struct input_dev *dev = platform_get_drvdata(pdev); - - free_irq(IRQ_AMIGA_CIAA_SP, dev); - input_unregister_device(dev); - return 0; } static struct platform_driver amikbd_driver = { - .remove = __exit_p(amikbd_remove), .driver = { .name = "amiga-keyboard", }, diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c index 56a919ec23b529149adfec91c268075fd53e0380..f3c3746acd4cf96a0ecdc905550bafb608cda4d9 100644 --- a/drivers/input/keyboard/bcm-keypad.c +++ b/drivers/input/keyboard/bcm-keypad.c @@ -307,7 +307,6 @@ static int bcm_kp_probe(struct platform_device *pdev) { struct bcm_kp *kp; struct input_dev *input_dev; - struct resource *res; int error; kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); @@ -353,29 +352,16 @@ static int bcm_kp_probe(struct platform_device *pdev) return error; } - /* Get the KEYPAD base address */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Missing keypad base address resource\n"); - return -ENODEV; - } - - kp->base = devm_ioremap_resource(&pdev->dev, res); + kp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kp->base)) return PTR_ERR(kp->base); /* Enable clock */ - kp->clk = devm_clk_get(&pdev->dev, "peri_clk"); + kp->clk = devm_clk_get_optional(&pdev->dev, "peri_clk"); if (IS_ERR(kp->clk)) { - error = PTR_ERR(kp->clk); - if (error != -ENOENT) { - if (error != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to get clock\n"); - return error; - } - dev_dbg(&pdev->dev, - "No clock specified. Assuming it's enabled\n"); - kp->clk = NULL; + return dev_err_probe(&pdev->dev, PTR_ERR(kp->clk), "Failed to get clock\n"); + } else if (!kp->clk) { + dev_dbg(&pdev->dev, "No clock specified. Assuming it's enabled\n"); } else { unsigned int desired_rate; long actual_rate; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index c928829a8b0cea53b35899f2e6f99dec9b7773f7..2e7c2c046e675f8ae21a6fe668e9b7dfa23c1a9a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -523,18 +523,15 @@ static int gpio_keys_setup_key(struct platform_device *pdev, NULL, GPIOD_IN, desc); if (IS_ERR(bdata->gpiod)) { error = PTR_ERR(bdata->gpiod); - if (error == -ENOENT) { - /* - * GPIO is optional, we may be dealing with - * purely interrupt-driven setup. - */ - bdata->gpiod = NULL; - } else { - if (error != -EPROBE_DEFER) - dev_err(dev, "failed to get gpio: %d\n", - error); - return error; - } + if (error != -ENOENT) + return dev_err_probe(dev, error, + "failed to get gpio\n"); + + /* + * GPIO is optional, we may be dealing with + * purely interrupt-driven setup. + */ + bdata->gpiod = NULL; } } else if (gpio_is_valid(button->gpio)) { /* diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index c3937d2fc7446e8b68e517cb4e5491809407fb06..ba00ecfbd343bc796fd48f77db92659a1c36bccc 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -299,13 +299,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) NULL, GPIOD_IN, button->desc); if (IS_ERR(bdata->gpiod)) { - error = PTR_ERR(bdata->gpiod); - if (error != -EPROBE_DEFER) - dev_err(dev, - "failed to get gpio: %d\n", - error); fwnode_handle_put(child); - return error; + return dev_err_probe(dev, PTR_ERR(bdata->gpiod), + "failed to get gpio\n"); } } else if (gpio_is_valid(button->gpio)) { /* diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 3964f6e0f6afad9b59147a95035ea850ed760e5b..7bee93e9b0f5e8cfb78049916f8883ef7f399f10 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -556,6 +556,7 @@ static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, const char *name) { struct lm8323_pwm *pwm; + int err; BUG_ON(id > 3); @@ -575,9 +576,11 @@ static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, pwm->cdev.name = name; pwm->cdev.brightness_set = lm8323_pwm_set_brightness; pwm->cdev.groups = lm8323_pwm_groups; - if (led_classdev_register(dev, &pwm->cdev) < 0) { - dev_err(dev, "couldn't register PWM %d\n", id); - return -1; + + err = devm_led_classdev_register(dev, &pwm->cdev); + if (err) { + dev_err(dev, "couldn't register PWM %d: %d\n", id, err); + return err; } pwm->enabled = true; } @@ -585,8 +588,6 @@ static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev, return 0; } -static struct i2c_driver lm8323_i2c_driver; - static ssize_t lm8323_show_disable(struct device *dev, struct device_attribute *attr, char *buf) { @@ -615,6 +616,12 @@ static ssize_t lm8323_set_disable(struct device *dev, } static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable); +static struct attribute *lm8323_attrs[] = { + &dev_attr_disable_kp.attr, + NULL, +}; +ATTRIBUTE_GROUPS(lm8323); + static int lm8323_probe(struct i2c_client *client) { struct lm8323_platform_data *pdata = dev_get_platdata(&client->dev); @@ -642,12 +649,13 @@ static int lm8323_probe(struct i2c_client *client) return -EINVAL; } - lm = kzalloc(sizeof *lm, GFP_KERNEL); - idev = input_allocate_device(); - if (!lm || !idev) { - err = -ENOMEM; - goto fail1; - } + lm = devm_kzalloc(&client->dev, sizeof(*lm), GFP_KERNEL); + if (!lm) + return -ENOMEM; + + idev = devm_input_allocate_device(&client->dev); + if (!idev) + return -ENOMEM; lm->client = client; lm->idev = idev; @@ -663,8 +671,10 @@ static int lm8323_probe(struct i2c_client *client) lm8323_reset(lm); - /* Nothing's set up to service the IRQ yet, so just spin for max. - * 100ms until we can configure. */ + /* + * Nothing's set up to service the IRQ yet, so just spin for max. + * 100ms until we can configure. + */ tmo = jiffies + msecs_to_jiffies(100); while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) { if (data[0] & INT_NOINIT) @@ -684,21 +694,17 @@ static int lm8323_probe(struct i2c_client *client) /* If a true probe check the device */ if (lm8323_read_id(lm, data) != 0) { dev_err(&client->dev, "device not found\n"); - err = -ENODEV; - goto fail1; + return -ENODEV; } for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) { err = init_pwm(lm, pwm + 1, &client->dev, pdata->pwm_names[pwm]); - if (err < 0) - goto fail2; + if (err) + return err; } lm->kp_enabled = true; - err = device_create_file(&client->dev, &dev_attr_disable_kp); - if (err < 0) - goto fail2; idev->name = pdata->name ? : "LM8323 keypad"; snprintf(lm->phys, sizeof(lm->phys), @@ -719,14 +725,16 @@ static int lm8323_probe(struct i2c_client *client) err = input_register_device(idev); if (err) { dev_dbg(&client->dev, "error registering input device\n"); - goto fail3; + return err; } - err = request_threaded_irq(client->irq, NULL, lm8323_irq, - IRQF_TRIGGER_LOW|IRQF_ONESHOT, "lm8323", lm); + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, lm8323_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "lm8323", lm); if (err) { dev_err(&client->dev, "could not get IRQ %d\n", client->irq); - goto fail4; + return err; } i2c_set_clientdata(client, lm); @@ -735,39 +743,6 @@ static int lm8323_probe(struct i2c_client *client) enable_irq_wake(client->irq); return 0; - -fail4: - input_unregister_device(idev); - idev = NULL; -fail3: - device_remove_file(&client->dev, &dev_attr_disable_kp); -fail2: - while (--pwm >= 0) - if (lm->pwm[pwm].enabled) - led_classdev_unregister(&lm->pwm[pwm].cdev); -fail1: - input_free_device(idev); - kfree(lm); - return err; -} - -static void lm8323_remove(struct i2c_client *client) -{ - struct lm8323_chip *lm = i2c_get_clientdata(client); - int i; - - disable_irq_wake(client->irq); - free_irq(client->irq, lm); - - input_unregister_device(lm->idev); - - device_remove_file(&lm->client->dev, &dev_attr_disable_kp); - - for (i = 0; i < 3; i++) - if (lm->pwm[i].enabled) - led_classdev_unregister(&lm->pwm[i].cdev); - - kfree(lm); } /* @@ -823,11 +798,11 @@ static const struct i2c_device_id lm8323_id[] = { static struct i2c_driver lm8323_i2c_driver = { .driver = { - .name = "lm8323", - .pm = pm_sleep_ptr(&lm8323_pm_ops), + .name = "lm8323", + .pm = pm_sleep_ptr(&lm8323_pm_ops), + .dev_groups = lm8323_groups, }, .probe = lm8323_probe, - .remove = lm8323_remove, .id_table = lm8323_id, }; MODULE_DEVICE_TABLE(i2c, lm8323_id); diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index c9f05764e36df1049331fec9903639235085afb2..1c070c499c85ae9521a65c54869a318026b7db67 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -142,18 +142,18 @@ static int lm8333_probe(struct i2c_client *client) return -EINVAL; } - lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL); - input = input_allocate_device(); - if (!lm8333 || !input) { - err = -ENOMEM; - goto free_mem; - } + lm8333 = devm_kzalloc(&client->dev, sizeof(*lm8333), GFP_KERNEL); + if (!lm8333) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; lm8333->client = client; lm8333->input = input; input->name = client->name; - input->dev.parent = &client->dev; input->id.bustype = BUS_I2C; input_set_capability(input, EV_MSC, MSC_SCAN); @@ -162,7 +162,7 @@ static int lm8333_probe(struct i2c_client *client) LM8333_NUM_ROWS, LM8333_NUM_COLS, lm8333->keycodes, input); if (err) - goto free_mem; + return err; if (pdata->debounce_time) { err = lm8333_write8(lm8333, LM8333_DEBOUNCE, @@ -178,34 +178,19 @@ static int lm8333_probe(struct i2c_client *client) dev_warn(&client->dev, "Unable to set active time\n"); } - err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "lm8333", lm8333); + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, lm8333_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "lm8333", lm8333); if (err) - goto free_mem; + return err; err = input_register_device(input); if (err) - goto free_irq; + return err; i2c_set_clientdata(client, lm8333); return 0; - - free_irq: - free_irq(client->irq, lm8333); - free_mem: - input_free_device(input); - kfree(lm8333); - return err; -} - -static void lm8333_remove(struct i2c_client *client) -{ - struct lm8333 *lm8333 = i2c_get_clientdata(client); - - free_irq(client->irq, lm8333); - input_unregister_device(lm8333->input); - kfree(lm8333); } static const struct i2c_device_id lm8333_id[] = { @@ -219,7 +204,6 @@ static struct i2c_driver lm8333_driver = { .name = "lm8333", }, .probe = lm8333_probe, - .remove = lm8333_remove, .id_table = lm8333_id, }; module_i2c_driver(lm8333_driver); diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 911e1181cd6fb7f55cfba8b51b91a983bec5904d..322a878071591dc11a470f9afc4b38f8c9cc3cde 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -160,17 +160,10 @@ static int lpc32xx_kscan_probe(struct platform_device *pdev) { struct lpc32xx_kscan_drv *kscandat; struct input_dev *input; - struct resource *res; size_t keymap_size; int error; int irq; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to get platform I/O memory\n"); - return -EINVAL; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) return -EINVAL; @@ -221,7 +214,7 @@ static int lpc32xx_kscan_probe(struct platform_device *pdev) input_set_drvdata(kscandat->input, kscandat); - kscandat->kscan_base = devm_ioremap_resource(&pdev->dev, res); + kscandat->kscan_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kscandat->kscan_base)) return PTR_ERR(kscandat->kscan_base); diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index de312d8eb974d543b44eb1a18ce60e908182c590..2410f676c7f95e9074afb7252cd86af6350a3259 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -92,6 +92,13 @@ static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void mcs_touchkey_poweroff(void *data) +{ + struct mcs_touchkey_data *touchkey = data; + + touchkey->poweron(false); +} + static int mcs_touchkey_probe(struct i2c_client *client) { const struct i2c_device_id *id = i2c_client_get_device_id(client); @@ -109,13 +116,16 @@ static int mcs_touchkey_probe(struct i2c_client *client) return -EINVAL; } - data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1), - GFP_KERNEL); - input_dev = input_allocate_device(); - if (!data || !input_dev) { - dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; + data = devm_kzalloc(&client->dev, + struct_size(data, keycodes, pdata->key_maxval + 1), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + return -ENOMEM; } data->client = client; @@ -136,15 +146,13 @@ static int mcs_touchkey_probe(struct i2c_client *client) fw_ver = i2c_smbus_read_byte_data(client, fw_reg); if (fw_ver < 0) { - error = fw_ver; - dev_err(&client->dev, "i2c read error[%d]\n", error); - goto err_free_mem; + dev_err(&client->dev, "i2c read error[%d]\n", fw_ver); + return fw_ver; } dev_info(&client->dev, "Firmware version: %d\n", fw_ver); input_dev->name = "MELFAS MCS Touchkey"; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY); if (!pdata->no_autorepeat) input_dev->evbit[0] |= BIT_MASK(EV_REP); @@ -169,40 +177,28 @@ static int mcs_touchkey_probe(struct i2c_client *client) if (pdata->poweron) { data->poweron = pdata->poweron; data->poweron(true); + + error = devm_add_action_or_reset(&client->dev, + mcs_touchkey_poweroff, data); + if (error) + return error; } - error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->dev.driver->name, data); + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mcs_touchkey_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_free_mem; + return error; } error = input_register_device(input_dev); if (error) - goto err_free_irq; + return error; i2c_set_clientdata(client, data); return 0; - -err_free_irq: - free_irq(client->irq, data); -err_free_mem: - input_free_device(input_dev); - kfree(data); - return error; -} - -static void mcs_touchkey_remove(struct i2c_client *client) -{ - struct mcs_touchkey_data *data = i2c_get_clientdata(client); - - free_irq(client->irq, data); - if (data->poweron) - data->poweron(false); - input_unregister_device(data->input_dev); - kfree(data); } static void mcs_touchkey_shutdown(struct i2c_client *client) @@ -259,7 +255,6 @@ static struct i2c_driver mcs_touchkey_driver = { .pm = pm_sleep_ptr(&mcs_touchkey_pm_ops), }, .probe = mcs_touchkey_probe, - .remove = mcs_touchkey_remove, .shutdown = mcs_touchkey_shutdown, .id_table = mcs_touchkey_id, }; diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 970f2a671c2e6d8dfa7414e4666fad89ace994ea..b3ccc97f61e1fb2d21e3651bbc1490789ce00d51 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -221,13 +221,20 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static void ske_keypad_board_exit(void *data) +{ + struct ske_keypad *keypad = data; + + keypad->board->exit(); +} + static int __init ske_keypad_probe(struct platform_device *pdev) { const struct ske_keypad_platform_data *plat = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; struct ske_keypad *keypad; struct input_dev *input; - struct resource *res; int irq; int error; @@ -238,20 +245,14 @@ static int __init ske_keypad_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -EINVAL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "missing platform resources\n"); - return -EINVAL; - } + return irq; - keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); - input = input_allocate_device(); + keypad = devm_kzalloc(dev, sizeof(struct ske_keypad), + GFP_KERNEL); + input = devm_input_allocate_device(dev); if (!keypad || !input) { dev_err(&pdev->dev, "failed to allocate keypad memory\n"); - error = -ENOMEM; - goto err_free_mem; + return -ENOMEM; } keypad->irq = irq; @@ -259,31 +260,20 @@ static int __init ske_keypad_probe(struct platform_device *pdev) keypad->input = input; spin_lock_init(&keypad->ske_keypad_lock); - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto err_free_mem; - } - - keypad->reg_base = ioremap(res->start, resource_size(res)); - if (!keypad->reg_base) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto err_free_mem_region; - } + keypad->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(keypad->reg_base)) + return PTR_ERR(keypad->reg_base); - keypad->pclk = clk_get(&pdev->dev, "apb_pclk"); + keypad->pclk = devm_clk_get_enabled(dev, "apb_pclk"); if (IS_ERR(keypad->pclk)) { dev_err(&pdev->dev, "failed to get pclk\n"); - error = PTR_ERR(keypad->pclk); - goto err_iounmap; + return PTR_ERR(keypad->pclk); } - keypad->clk = clk_get(&pdev->dev, NULL); + keypad->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(keypad->clk)) { dev_err(&pdev->dev, "failed to get clk\n"); - error = PTR_ERR(keypad->clk); - goto err_pclk; + return PTR_ERR(keypad->clk); } input->id.bustype = BUS_HOST; @@ -295,48 +285,43 @@ static int __init ske_keypad_probe(struct platform_device *pdev) keypad->keymap, input); if (error) { dev_err(&pdev->dev, "Failed to build keymap\n"); - goto err_clk; + return error; } input_set_capability(input, EV_MSC, MSC_SCAN); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - error = clk_prepare_enable(keypad->pclk); - if (error) { - dev_err(&pdev->dev, "Failed to prepare/enable pclk\n"); - goto err_clk; - } - - error = clk_prepare_enable(keypad->clk); - if (error) { - dev_err(&pdev->dev, "Failed to prepare/enable clk\n"); - goto err_pclk_disable; - } - - /* go through board initialization helpers */ if (keypad->board->init) keypad->board->init(); + if (keypad->board->exit) { + error = devm_add_action_or_reset(dev, ske_keypad_board_exit, + keypad); + if (error) + return error; + } + error = ske_keypad_chip_init(keypad); if (error) { dev_err(&pdev->dev, "unable to init keypad hardware\n"); - goto err_clk_disable; + return error; } - error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, - IRQF_ONESHOT, "ske-keypad", keypad); + error = devm_request_threaded_irq(dev, keypad->irq, + NULL, ske_keypad_irq, + IRQF_ONESHOT, "ske-keypad", keypad); if (error) { dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); - goto err_clk_disable; + return error; } error = input_register_device(input); if (error) { dev_err(&pdev->dev, - "unable to register input device: %d\n", error); - goto err_free_irq; + "unable to register input device: %d\n", error); + return error; } if (plat->wakeup_enable) @@ -344,47 +329,6 @@ static int __init ske_keypad_probe(struct platform_device *pdev) platform_set_drvdata(pdev, keypad); - return 0; - -err_free_irq: - free_irq(keypad->irq, keypad); -err_clk_disable: - clk_disable_unprepare(keypad->clk); -err_pclk_disable: - clk_disable_unprepare(keypad->pclk); -err_clk: - clk_put(keypad->clk); -err_pclk: - clk_put(keypad->pclk); -err_iounmap: - iounmap(keypad->reg_base); -err_free_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: - input_free_device(input); - kfree(keypad); - return error; -} - -static int ske_keypad_remove(struct platform_device *pdev) -{ - struct ske_keypad *keypad = platform_get_drvdata(pdev); - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - free_irq(keypad->irq, keypad); - - input_unregister_device(keypad->input); - - clk_disable_unprepare(keypad->clk); - clk_put(keypad->clk); - - if (keypad->board->exit) - keypad->board->exit(); - - iounmap(keypad->reg_base); - release_mem_region(res->start, resource_size(res)); - kfree(keypad); - return 0; } @@ -424,7 +368,6 @@ static struct platform_driver ske_keypad_driver = { .name = "nmk-ske-keypad", .pm = pm_sleep_ptr(&ske_keypad_dev_pm_ops), }, - .remove = ske_keypad_remove, }; module_platform_driver_probe(ske_keypad_driver, ske_keypad_probe); diff --git a/drivers/input/keyboard/nspire-keypad.c b/drivers/input/keyboard/nspire-keypad.c index e9fa1423f136090ebeabafb4e74810c56a558f92..096c18d7bca1158ce9abefb7292927274288ae91 100644 --- a/drivers/input/keyboard/nspire-keypad.c +++ b/drivers/input/keyboard/nspire-keypad.c @@ -186,8 +186,7 @@ static int nspire_keypad_probe(struct platform_device *pdev) return PTR_ERR(keypad->clk); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - keypad->reg_base = devm_ioremap_resource(&pdev->dev, res); + keypad->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(keypad->reg_base)) return PTR_ERR(keypad->reg_base); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 9f085d5679dbbccb5dcf6b38e955e887add6ecea..773e55eed88b18353742107689d5c93e2aae4c16 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -341,17 +341,10 @@ static int omap4_keypad_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct omap4_keypad *keypad_data; struct input_dev *input_dev; - struct resource *res; unsigned int max_keys; int irq; int error; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no base address specified\n"); - return -EINVAL; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -370,7 +363,7 @@ static int omap4_keypad_probe(struct platform_device *pdev) if (error) return error; - keypad_data->base = devm_ioremap_resource(dev, res); + keypad_data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(keypad_data->base)) return PTR_ERR(keypad_data->base); diff --git a/drivers/input/keyboard/opencores-kbd.c b/drivers/input/keyboard/opencores-kbd.c index b0ea387414c1e2069585842b21293151bbeba389..7ffe1a70c8566404b6ad14d23b9cb56d68850ad0 100644 --- a/drivers/input/keyboard/opencores-kbd.c +++ b/drivers/input/keyboard/opencores-kbd.c @@ -39,15 +39,8 @@ static int opencores_kbd_probe(struct platform_device *pdev) { struct input_dev *input; struct opencores_kbd *opencores_kbd; - struct resource *res; int irq, i, error; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "missing board memory resource\n"); - return -EINVAL; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) return -EINVAL; @@ -65,7 +58,7 @@ static int opencores_kbd_probe(struct platform_device *pdev) opencores_kbd->input = input; - opencores_kbd->addr = devm_ioremap_resource(&pdev->dev, res); + opencores_kbd->addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(opencores_kbd->addr)) return PTR_ERR(opencores_kbd->addr); diff --git a/drivers/input/keyboard/pinephone-keyboard.c b/drivers/input/keyboard/pinephone-keyboard.c index 038ff3549a7ac999e99cf6a9dfb3c83de0ce4a1c..147b1f288a33443c609086e9ae8cd37ef743fc9d 100644 --- a/drivers/input/keyboard/pinephone-keyboard.c +++ b/drivers/input/keyboard/pinephone-keyboard.c @@ -318,40 +318,22 @@ static void ppkb_close(struct input_dev *input) ppkb_set_scan(client, false); } -static void ppkb_regulator_disable(void *regulator) -{ - regulator_disable(regulator); -} - static int ppkb_probe(struct i2c_client *client) { struct device *dev = &client->dev; unsigned int phys_rows, phys_cols; struct pinephone_keyboard *ppkb; - struct regulator *vbat_supply; u8 info[PPKB_MATRIX_SIZE + 1]; struct device_node *i2c_bus; int ret; int error; - vbat_supply = devm_regulator_get(dev, "vbat"); - error = PTR_ERR_OR_ZERO(vbat_supply); + error = devm_regulator_get_enable(dev, "vbat"); if (error) { dev_err(dev, "Failed to get VBAT supply: %d\n", error); return error; } - error = regulator_enable(vbat_supply); - if (error) { - dev_err(dev, "Failed to enable VBAT: %d\n", error); - return error; - } - - error = devm_add_action_or_reset(dev, ppkb_regulator_disable, - vbat_supply); - if (error) - return error; - ret = i2c_smbus_read_i2c_block_data(client, 0, sizeof(info), info); if (ret != sizeof(info)) { error = ret < 0 ? ret : -EIO; diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 871f858d0ba78a5bb79b6f8aa9d78bcf7ebc0b44..3724363d140e5f99163dbe97da8fa4900700011d 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -717,7 +717,6 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct pxa27x_keypad *keypad; struct input_dev *input_dev; - struct resource *res; int irq, error; /* Driver need build keycode from device tree or pdata */ @@ -728,12 +727,6 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) if (irq < 0) return -ENXIO; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get I/O memory\n"); - return -ENXIO; - } - keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); if (!keypad) @@ -747,7 +740,7 @@ static int pxa27x_keypad_probe(struct platform_device *pdev) keypad->input_dev = input_dev; keypad->irq = irq; - keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res); + keypad->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(keypad->mmio_base)) return PTR_ERR(keypad->mmio_base); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index 91aaa9fc43a4a5774bfeea1b7079407c936d7a85..9b093b042bf1900ed5a99a09382a026f9468ce0a 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -149,20 +149,20 @@ static int qt1070_probe(struct i2c_client *client) if (!qt1070_identify(client)) return -ENODEV; - data = kzalloc(sizeof(struct qt1070_data), GFP_KERNEL); - input = input_allocate_device(); - if (!data || !input) { - dev_err(&client->dev, "insufficient memory\n"); - err = -ENOMEM; - goto err_free_mem; - } + data = devm_kzalloc(&client->dev, sizeof(struct qt1070_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; data->client = client; data->input = input; data->irq = client->irq; input->name = "AT42QT1070 QTouch Sensor"; - input->dev.parent = &client->dev; input->id.bustype = BUS_I2C; /* Add the keycode */ @@ -185,19 +185,20 @@ static int qt1070_probe(struct i2c_client *client) qt1070_write(client, RESET, 1); msleep(QT1070_RESET_TIME); - err = request_threaded_irq(client->irq, NULL, qt1070_interrupt, - IRQF_TRIGGER_NONE | IRQF_ONESHOT, - client->dev.driver->name, data); + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, qt1070_interrupt, + IRQF_TRIGGER_NONE | IRQF_ONESHOT, + client->dev.driver->name, data); if (err) { dev_err(&client->dev, "fail to request irq\n"); - goto err_free_mem; + return err; } /* Register the input device */ err = input_register_device(data->input); if (err) { dev_err(&client->dev, "Failed to register input device\n"); - goto err_free_irq; + return err; } i2c_set_clientdata(client, data); @@ -206,24 +207,6 @@ static int qt1070_probe(struct i2c_client *client) qt1070_read(client, DET_STATUS); return 0; - -err_free_irq: - free_irq(client->irq, data); -err_free_mem: - input_free_device(input); - kfree(data); - return err; -} - -static void qt1070_remove(struct i2c_client *client) -{ - struct qt1070_data *data = i2c_get_clientdata(client); - - /* Release IRQ */ - free_irq(client->irq, data); - - input_unregister_device(data->input); - kfree(data); } static int qt1070_suspend(struct device *dev) @@ -272,7 +255,6 @@ static struct i2c_driver qt1070_driver = { }, .id_table = qt1070_id, .probe = qt1070_probe, - .remove = qt1070_remove, }; module_i2c_driver(qt1070_driver); diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index 599ea85cfd30c7c0f95def09593288e7f7bc5fb5..7e3b09642ab754db16952076bfd3dd1971b07e67 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -32,7 +32,7 @@ #define QT2160_NUM_LEDS_X 8 -#define QT2160_CYCLE_INTERVAL (2*HZ) +#define QT2160_CYCLE_INTERVAL 2000 /* msec - 2 sec */ static unsigned char qt2160_key2code[] = { KEY_0, KEY_1, KEY_2, KEY_3, @@ -54,7 +54,6 @@ struct qt2160_led { struct qt2160_data { struct i2c_client *client; struct input_dev *input; - struct delayed_work dwork; unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; u16 key_matrix; #ifdef CONFIG_LEDS_CLASS @@ -155,10 +154,10 @@ static int qt2160_read_block(struct i2c_client *client, return 0; } -static int qt2160_get_key_matrix(struct qt2160_data *qt2160) +static void qt2160_get_key_matrix(struct input_dev *input) { + struct qt2160_data *qt2160 = input_get_drvdata(input); struct i2c_client *client = qt2160->client; - struct input_dev *input = qt2160->input; u8 regs[6]; u16 old_matrix, new_matrix; int ret, i, mask; @@ -173,7 +172,7 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160) if (ret) { dev_err(&client->dev, "could not perform chip read.\n"); - return ret; + return; } old_matrix = qt2160->key_matrix; @@ -191,37 +190,17 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160) } input_sync(input); - - return 0; } -static irqreturn_t qt2160_irq(int irq, void *_qt2160) +static irqreturn_t qt2160_irq(int irq, void *data) { - struct qt2160_data *qt2160 = _qt2160; + struct input_dev *input = data; - mod_delayed_work(system_wq, &qt2160->dwork, 0); + qt2160_get_key_matrix(input); return IRQ_HANDLED; } -static void qt2160_schedule_read(struct qt2160_data *qt2160) -{ - schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL); -} - -static void qt2160_worker(struct work_struct *work) -{ - struct qt2160_data *qt2160 = - container_of(work, struct qt2160_data, dwork.work); - - dev_dbg(&qt2160->client->dev, "worker\n"); - - qt2160_get_key_matrix(qt2160); - - /* Avoid device lock up by checking every so often */ - qt2160_schedule_read(qt2160); -} - static int qt2160_read(struct i2c_client *client, u8 reg) { int ret; @@ -260,7 +239,7 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data) static int qt2160_register_leds(struct qt2160_data *qt2160) { struct i2c_client *client = qt2160->client; - int ret; + int error; int i; for (i = 0; i < QT2160_NUM_LEDS_X; i++) { @@ -273,9 +252,9 @@ static int qt2160_register_leds(struct qt2160_data *qt2160) led->id = i; led->qt2160 = qt2160; - ret = led_classdev_register(&client->dev, &led->cdev); - if (ret < 0) - return ret; + error = devm_led_classdev_register(&client->dev, &led->cdev); + if (error) + return error; } /* Tur off LEDs */ @@ -286,14 +265,6 @@ static int qt2160_register_leds(struct qt2160_data *qt2160) return 0; } -static void qt2160_unregister_leds(struct qt2160_data *qt2160) -{ - int i; - - for (i = 0; i < QT2160_NUM_LEDS_X; i++) - led_classdev_unregister(&qt2160->leds[i].cdev); -} - #else static inline int qt2160_register_leds(struct qt2160_data *qt2160) @@ -301,10 +272,6 @@ static inline int qt2160_register_leds(struct qt2160_data *qt2160) return 0; } -static inline void qt2160_unregister_leds(struct qt2160_data *qt2160) -{ -} - #endif static bool qt2160_identify(struct i2c_client *client) @@ -345,12 +312,9 @@ static int qt2160_probe(struct i2c_client *client) int i; int error; - /* Check functionality */ - error = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE); - if (!error) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { dev_err(&client->dev, "%s adapter not supported\n", - dev_driver_string(&client->adapter->dev)); + dev_driver_string(&client->adapter->dev)); return -ENODEV; } @@ -358,17 +322,16 @@ static int qt2160_probe(struct i2c_client *client) return -ENODEV; /* Chip is valid and active. Allocate structure */ - qt2160 = kzalloc(sizeof(struct qt2160_data), GFP_KERNEL); - input = input_allocate_device(); - if (!qt2160 || !input) { - dev_err(&client->dev, "insufficient memory\n"); - error = -ENOMEM; - goto err_free_mem; - } + qt2160 = devm_kzalloc(&client->dev, sizeof(*qt2160), GFP_KERNEL); + if (!qt2160) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; qt2160->client = client; qt2160->input = input; - INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker); input->name = "AT42QT2160 Touch Sense Keyboard"; input->id.bustype = BUS_I2C; @@ -385,66 +348,48 @@ static int qt2160_probe(struct i2c_client *client) } __clear_bit(KEY_RESERVED, input->keybit); + input_set_drvdata(input, qt2160); + /* Calibrate device */ error = qt2160_write(client, QT2160_CMD_CALIBRATE, 1); if (error) { dev_err(&client->dev, "failed to calibrate device\n"); - goto err_free_mem; + return error; } if (client->irq) { - error = request_irq(client->irq, qt2160_irq, - IRQF_TRIGGER_FALLING, "qt2160", qt2160); + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, qt2160_irq, + IRQF_ONESHOT, + "qt2160", input); if (error) { dev_err(&client->dev, "failed to allocate irq %d\n", client->irq); - goto err_free_mem; + return error; + } + } else { + error = input_setup_polling(input, qt2160_get_key_matrix); + if (error) { + dev_err(&client->dev, "Failed to setup polling\n"); + return error; } + input_set_poll_interval(input, QT2160_CYCLE_INTERVAL); } error = qt2160_register_leds(qt2160); if (error) { dev_err(&client->dev, "Failed to register leds\n"); - goto err_free_irq; + return error; } error = input_register_device(qt2160->input); if (error) { dev_err(&client->dev, "Failed to register input device\n"); - goto err_unregister_leds; + return error; } - i2c_set_clientdata(client, qt2160); - qt2160_schedule_read(qt2160); - return 0; - -err_unregister_leds: - qt2160_unregister_leds(qt2160); -err_free_irq: - if (client->irq) - free_irq(client->irq, qt2160); -err_free_mem: - input_free_device(input); - kfree(qt2160); - return error; -} - -static void qt2160_remove(struct i2c_client *client) -{ - struct qt2160_data *qt2160 = i2c_get_clientdata(client); - - qt2160_unregister_leds(qt2160); - - /* Release IRQ so no queue will be scheduled */ - if (client->irq) - free_irq(client->irq, qt2160); - - cancel_delayed_work_sync(&qt2160->dwork); - - input_unregister_device(qt2160->input); - kfree(qt2160); } static const struct i2c_device_id qt2160_idtable[] = { @@ -461,7 +406,6 @@ static struct i2c_driver qt2160_driver = { .id_table = qt2160_idtable, .probe = qt2160_probe, - .remove = qt2160_remove, }; module_i2c_driver(qt2160_driver); diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c index 15c15c0958b00be87bce9ce0ea2f9fffe58f247e..f304cab0ebdb837484b4ce41bfb4227fc577ff76 100644 --- a/drivers/input/keyboard/sun4i-lradc-keys.c +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -21,10 +21,11 @@ #include #include #include -#include +#include #include #include #include +#include #include #include #include @@ -307,8 +308,7 @@ static int sun4i_lradc_probe(struct platform_device *pdev) input_set_drvdata(lradc->input, lradc); - lradc->base = devm_ioremap_resource(dev, - platform_get_resource(pdev, IORESOURCE_MEM, 0)); + lradc->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(lradc->base)) return PTR_ERR(lradc->base); diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 2f745cabf4f248aec628ef4422e1e1cd8d373254..8af59ced1ec2eda1365f5636ac91d48984da29da 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -24,6 +24,8 @@ #define TCA6416_INVERT 2 #define TCA6416_DIRECTION 3 +#define TCA6416_POLL_INTERVAL 100 /* msec */ + static const struct i2c_device_id tca6416_id[] = { { "tca6416-keys", 16, }, { "tca6408-keys", 8, }, @@ -43,7 +45,6 @@ struct tca6416_keypad_chip { struct i2c_client *client; struct input_dev *input; - struct delayed_work dwork; int io_size; int irqnum; u16 pinmask; @@ -85,9 +86,9 @@ static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) return 0; } -static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) +static void tca6416_keys_scan(struct input_dev *input) { - struct input_dev *input = chip->input; + struct tca6416_keypad_chip *chip = input_get_drvdata(input); u16 reg_val, val; int error, i, pin_index; @@ -122,33 +123,20 @@ static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) */ static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) { - struct tca6416_keypad_chip *chip = dev_id; - - tca6416_keys_scan(chip); + tca6416_keys_scan(dev_id); return IRQ_HANDLED; } -static void tca6416_keys_work_func(struct work_struct *work) -{ - struct tca6416_keypad_chip *chip = - container_of(work, struct tca6416_keypad_chip, dwork.work); - - tca6416_keys_scan(chip); - schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); -} - static int tca6416_keys_open(struct input_dev *dev) { struct tca6416_keypad_chip *chip = input_get_drvdata(dev); - /* Get initial device state in case it has switches */ - tca6416_keys_scan(chip); - - if (chip->use_polling) - schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); - else - enable_irq(chip->irqnum); + if (!chip->use_polling) { + /* Get initial device state in case it has switches */ + tca6416_keys_scan(dev); + enable_irq(chip->client->irq); + } return 0; } @@ -157,10 +145,8 @@ static void tca6416_keys_close(struct input_dev *dev) { struct tca6416_keypad_chip *chip = input_get_drvdata(dev); - if (chip->use_polling) - cancel_delayed_work_sync(&chip->dwork); - else - disable_irq(chip->irqnum); + if (!chip->use_polling) + disable_irq(chip->client->irq); } static int tca6416_setup_registers(struct tca6416_keypad_chip *chip) @@ -216,12 +202,15 @@ static int tca6416_keypad_probe(struct i2c_client *client) return -EINVAL; } - chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL); - input = input_allocate_device(); - if (!chip || !input) { - error = -ENOMEM; - goto fail1; - } + chip = devm_kzalloc(&client->dev, + struct_size(chip, buttons, pdata->nbuttons), + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; chip->client = client; chip->input = input; @@ -229,11 +218,8 @@ static int tca6416_keypad_probe(struct i2c_client *client) chip->pinmask = pdata->pinmask; chip->use_polling = pdata->use_polling; - INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); - input->phys = "tca6416-keys/input0"; input->name = client->name; - input->dev.parent = &client->dev; input->open = tca6416_keys_open; input->close = tca6416_keys_close; @@ -263,24 +249,28 @@ static int tca6416_keypad_probe(struct i2c_client *client) */ error = tca6416_setup_registers(chip); if (error) - goto fail1; + return error; - if (!chip->use_polling) { - if (pdata->irq_is_gpio) - chip->irqnum = gpio_to_irq(client->irq); - else - chip->irqnum = client->irq; - - error = request_threaded_irq(chip->irqnum, NULL, - tca6416_keys_isr, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT | IRQF_NO_AUTOEN, - "tca6416-keypad", chip); + if (chip->use_polling) { + error = input_setup_polling(input, tca6416_keys_scan); + if (error) { + dev_err(&client->dev, "Failed to setup polling\n"); + return error; + } + + input_set_poll_interval(input, TCA6416_POLL_INTERVAL); + } else { + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, tca6416_keys_isr, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT | + IRQF_NO_AUTOEN, + "tca6416-keypad", input); if (error) { dev_dbg(&client->dev, "Unable to claim irq %d; error %d\n", - chip->irqnum, error); - goto fail1; + client->irq, error); + return error; } } @@ -288,70 +278,19 @@ static int tca6416_keypad_probe(struct i2c_client *client) if (error) { dev_dbg(&client->dev, "Unable to register input device, error: %d\n", error); - goto fail2; + return error; } i2c_set_clientdata(client, chip); - device_init_wakeup(&client->dev, 1); return 0; - -fail2: - if (!chip->use_polling) { - free_irq(chip->irqnum, chip); - enable_irq(chip->irqnum); - } -fail1: - input_free_device(input); - kfree(chip); - return error; } -static void tca6416_keypad_remove(struct i2c_client *client) -{ - struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); - - if (!chip->use_polling) { - free_irq(chip->irqnum, chip); - enable_irq(chip->irqnum); - } - - input_unregister_device(chip->input); - kfree(chip); -} - -static int tca6416_keypad_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); - - if (device_may_wakeup(dev)) - enable_irq_wake(chip->irqnum); - - return 0; -} - -static int tca6416_keypad_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); - - if (device_may_wakeup(dev)) - disable_irq_wake(chip->irqnum); - - return 0; -} - -static DEFINE_SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops, - tca6416_keypad_suspend, tca6416_keypad_resume); - static struct i2c_driver tca6416_keypad_driver = { .driver = { .name = "tca6416-keypad", - .pm = pm_sleep_ptr(&tca6416_keypad_dev_pm_ops), }, .probe = tca6416_keypad_probe, - .remove = tca6416_keypad_remove, .id_table = tca6416_id, }; diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index d5a6c7d8eb25d0f57f40096dabc707fb01105c50..c9a823ea45d02965275f44318a7e8c5af3790887 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -640,7 +640,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) timer_setup(&kbc->timer, tegra_kbc_keypress_timer, 0); - kbc->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + kbc->mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kbc->mmio)) return PTR_ERR(kbc->mmio); diff --git a/drivers/input/keyboard/tm2-touchkey.c b/drivers/input/keyboard/tm2-touchkey.c index 75bd3ea5119490da8ca83c20809a33ab5e4f9c6a..0fd761ae052f10c47bbfa8300680fe787ba2a1e0 100644 --- a/drivers/input/keyboard/tm2-touchkey.c +++ b/drivers/input/keyboard/tm2-touchkey.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 8a320e6218e372a36b385bc04a8fa0bfb455cbd2..6ba984d7f0b18694f48160c4628575af1c8ea252 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -791,10 +791,10 @@ config INPUT_IQS626A module will be called iqs626a. config INPUT_IQS7222 - tristate "Azoteq IQS7222A/B/C capacitive touch controller" + tristate "Azoteq IQS7222A/B/C/D capacitive touch controller" depends on I2C help - Say Y to enable support for the Azoteq IQS7222A/B/C family + Say Y to enable support for the Azoteq IQS7222A/B/C/D family of capacitive touch controllers. To compile this driver as a module, choose M here: the diff --git a/drivers/input/misc/cpcap-pwrbutton.c b/drivers/input/misc/cpcap-pwrbutton.c index 879790bbf9fe23953cada9e1fb6b4d43f614a623..85cddb84717aeaa6b08856454b609ead0c62daae 100644 --- a/drivers/input/misc/cpcap-pwrbutton.c +++ b/drivers/input/misc/cpcap-pwrbutton.c @@ -1,16 +1,8 @@ -/** +// SPDX-License-Identifier: GPL-2.0-only +/* * CPCAP Power Button Input Driver * * Copyright (C) 2017 Sebastian Reichel - * - * This file is subject to the terms and conditions of the GNU General - * Public License. See the file "COPYING" in the main directory of this - * archive for more details. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/input/misc/da9063_onkey.c b/drivers/input/misc/da9063_onkey.c index b14a389600c9eab7c356d6a85bea78aaf1e46289..74808bae326a710779cdfa455ff5781739bf6948 100644 --- a/drivers/input/misc/da9063_onkey.c +++ b/drivers/input/misc/da9063_onkey.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -251,6 +252,14 @@ static int da9063_onkey_probe(struct platform_device *pdev) return error; } + error = dev_pm_set_wake_irq(&pdev->dev, irq); + if (error) + dev_warn(&pdev->dev, + "Failed to set IRQ %d as a wake IRQ: %d\n", + irq, error); + else + device_init_wakeup(&pdev->dev, true); + error = input_register_device(onkey->input); if (error) { dev_err(&pdev->dev, diff --git a/drivers/input/misc/gpio-vibra.c b/drivers/input/misc/gpio-vibra.c index 134a1309ba927bcba83ae23f7244e0aec2c48938..ad44b4d18a2a16ace7420597a812287c5b6e4d39 100644 --- a/drivers/input/misc/gpio-vibra.c +++ b/drivers/input/misc/gpio-vibra.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -113,22 +113,14 @@ static int gpio_vibrator_probe(struct platform_device *pdev) return -ENOMEM; vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc"); - err = PTR_ERR_OR_ZERO(vibrator->vcc); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to request regulator: %d\n", - err); - return err; - } + if (IS_ERR(vibrator->vcc)) + return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->vcc), + "Failed to request regulator\n"); vibrator->gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW); - err = PTR_ERR_OR_ZERO(vibrator->gpio); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to request main gpio: %d\n", - err); - return err; - } + if (IS_ERR(vibrator->gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->gpio), + "Failed to request main gpio\n"); INIT_WORK(&vibrator->play_work, gpio_vibrator_play_work); diff --git a/drivers/input/misc/iqs269a.c b/drivers/input/misc/iqs269a.c index 1272ef7b579494dab0b15cdc75dad90cfe5df931..c0a08563987053effad40b29e51d75a8839320d0 100644 --- a/drivers/input/misc/iqs269a.c +++ b/drivers/input/misc/iqs269a.c @@ -17,9 +17,9 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/drivers/input/misc/iqs626a.c b/drivers/input/misc/iqs626a.c index 50035c25c3f70aed98ce186bee08a6b9504fe94c..0dab54d3a0603b53e914df38f53104863ff3fbf3 100644 --- a/drivers/input/misc/iqs626a.c +++ b/drivers/input/misc/iqs626a.c @@ -19,8 +19,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/input/misc/iqs7222.c b/drivers/input/misc/iqs7222.c index 096b0925f41bab10819c900e014708c1eca4c990..36aeeae7761101de277ffbfcbc93fba402c51590 100644 --- a/drivers/input/misc/iqs7222.c +++ b/drivers/input/misc/iqs7222.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Azoteq IQS7222A/B/C Capacitive Touch Controller + * Azoteq IQS7222A/B/C/D Capacitive Touch Controller * * Copyright (C) 2022 Jeff LaBundy */ @@ -12,11 +12,12 @@ #include #include #include +#include #include #include #include +#include #include -#include #include #include #include @@ -25,6 +26,7 @@ #define IQS7222_PROD_NUM_A 840 #define IQS7222_PROD_NUM_B 698 #define IQS7222_PROD_NUM_C 863 +#define IQS7222_PROD_NUM_D 1046 #define IQS7222_SYS_STATUS 0x10 #define IQS7222_SYS_STATUS_RESET BIT(3) @@ -54,6 +56,7 @@ #define IQS7222_EVENT_MASK_ATI BIT(12) #define IQS7222_EVENT_MASK_SLDR BIT(10) +#define IQS7222_EVENT_MASK_TPAD IQS7222_EVENT_MASK_SLDR #define IQS7222_EVENT_MASK_TOUCH BIT(1) #define IQS7222_EVENT_MASK_PROX BIT(0) @@ -71,6 +74,7 @@ #define IQS7222_MAX_COLS_CHAN 6 #define IQS7222_MAX_COLS_FILT 2 #define IQS7222_MAX_COLS_SLDR 11 +#define IQS7222_MAX_COLS_TPAD 24 #define IQS7222_MAX_COLS_GPIO 3 #define IQS7222_MAX_COLS_SYS 13 @@ -102,16 +106,18 @@ enum iqs7222_reg_grp_id { IQS7222_REG_GRP_BTN, IQS7222_REG_GRP_CHAN, IQS7222_REG_GRP_SLDR, + IQS7222_REG_GRP_TPAD, IQS7222_REG_GRP_GPIO, IQS7222_REG_GRP_SYS, IQS7222_NUM_REG_GRPS }; static const char * const iqs7222_reg_grp_names[IQS7222_NUM_REG_GRPS] = { - [IQS7222_REG_GRP_CYCLE] = "cycle", - [IQS7222_REG_GRP_CHAN] = "channel", - [IQS7222_REG_GRP_SLDR] = "slider", - [IQS7222_REG_GRP_GPIO] = "gpio", + [IQS7222_REG_GRP_CYCLE] = "cycle-%d", + [IQS7222_REG_GRP_CHAN] = "channel-%d", + [IQS7222_REG_GRP_SLDR] = "slider-%d", + [IQS7222_REG_GRP_TPAD] = "trackpad", + [IQS7222_REG_GRP_GPIO] = "gpio-%d", }; static const unsigned int iqs7222_max_cols[IQS7222_NUM_REG_GRPS] = { @@ -122,6 +128,7 @@ static const unsigned int iqs7222_max_cols[IQS7222_NUM_REG_GRPS] = { [IQS7222_REG_GRP_CHAN] = IQS7222_MAX_COLS_CHAN, [IQS7222_REG_GRP_FILT] = IQS7222_MAX_COLS_FILT, [IQS7222_REG_GRP_SLDR] = IQS7222_MAX_COLS_SLDR, + [IQS7222_REG_GRP_TPAD] = IQS7222_MAX_COLS_TPAD, [IQS7222_REG_GRP_GPIO] = IQS7222_MAX_COLS_GPIO, [IQS7222_REG_GRP_SYS] = IQS7222_MAX_COLS_SYS, }; @@ -130,8 +137,10 @@ static const unsigned int iqs7222_gpio_links[] = { 2, 5, 6, }; struct iqs7222_event_desc { const char *name; + u16 link; u16 mask; u16 val; + u16 strict; u16 enable; enum iqs7222_reg_key_id reg_key; }; @@ -188,6 +197,93 @@ static const struct iqs7222_event_desc iqs7222_sl_events[] = { }, }; +static const struct iqs7222_event_desc iqs7222_tp_events[] = { + { + .name = "event-press", + .link = BIT(7), + }, + { + .name = "event-tap", + .link = BIT(0), + .mask = BIT(0), + .val = BIT(0), + .enable = BIT(0), + .reg_key = IQS7222_REG_KEY_TAP, + }, + { + .name = "event-swipe-x-pos", + .link = BIT(2), + .mask = BIT(2) | BIT(1), + .val = BIT(2), + .strict = BIT(4), + .enable = BIT(1), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-swipe-y-pos", + .link = BIT(3), + .mask = BIT(3) | BIT(1), + .val = BIT(3), + .strict = BIT(3), + .enable = BIT(1), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-swipe-x-neg", + .link = BIT(4), + .mask = BIT(4) | BIT(1), + .val = BIT(4), + .strict = BIT(4), + .enable = BIT(1), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-swipe-y-neg", + .link = BIT(5), + .mask = BIT(5) | BIT(1), + .val = BIT(5), + .strict = BIT(3), + .enable = BIT(1), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-flick-x-pos", + .link = BIT(2), + .mask = BIT(2) | BIT(1), + .val = BIT(2) | BIT(1), + .strict = BIT(4), + .enable = BIT(2), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-flick-y-pos", + .link = BIT(3), + .mask = BIT(3) | BIT(1), + .val = BIT(3) | BIT(1), + .strict = BIT(3), + .enable = BIT(2), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-flick-x-neg", + .link = BIT(4), + .mask = BIT(4) | BIT(1), + .val = BIT(4) | BIT(1), + .strict = BIT(4), + .enable = BIT(2), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, + { + .name = "event-flick-y-neg", + .link = BIT(5), + .mask = BIT(5) | BIT(1), + .val = BIT(5) | BIT(1), + .strict = BIT(3), + .enable = BIT(2), + .reg_key = IQS7222_REG_KEY_AXIAL, + }, +}; + struct iqs7222_reg_grp_desc { u16 base; int num_row; @@ -524,6 +620,62 @@ static const struct iqs7222_dev_desc iqs7222_devs[] = { }, }, }, + { + .prod_num = IQS7222_PROD_NUM_D, + .fw_major = 0, + .fw_minor = 37, + .touch_link = 1770, + .allow_offset = 9, + .event_offset = 10, + .comms_offset = 11, + .reg_grps = { + [IQS7222_REG_GRP_STAT] = { + .base = IQS7222_SYS_STATUS, + .num_row = 1, + .num_col = 7, + }, + [IQS7222_REG_GRP_CYCLE] = { + .base = 0x8000, + .num_row = 7, + .num_col = 2, + }, + [IQS7222_REG_GRP_GLBL] = { + .base = 0x8700, + .num_row = 1, + .num_col = 3, + }, + [IQS7222_REG_GRP_BTN] = { + .base = 0x9000, + .num_row = 14, + .num_col = 3, + }, + [IQS7222_REG_GRP_CHAN] = { + .base = 0xA000, + .num_row = 14, + .num_col = 4, + }, + [IQS7222_REG_GRP_FILT] = { + .base = 0xAE00, + .num_row = 1, + .num_col = 2, + }, + [IQS7222_REG_GRP_TPAD] = { + .base = 0xB000, + .num_row = 1, + .num_col = 24, + }, + [IQS7222_REG_GRP_GPIO] = { + .base = 0xC000, + .num_row = 3, + .num_col = 3, + }, + [IQS7222_REG_GRP_SYS] = { + .base = IQS7222_SYS_SETUP, + .num_row = 1, + .num_col = 12, + }, + }, + }, }; struct iqs7222_prop_desc { @@ -1008,6 +1160,123 @@ static const struct iqs7222_prop_desc iqs7222_props[] = { .val_pitch = 4, .label = "maximum gesture time", }, + { + .name = "azoteq,num-rows", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 0, + .reg_shift = 4, + .reg_width = 4, + .val_min = 1, + .val_max = 12, + .label = "number of rows", + }, + { + .name = "azoteq,num-cols", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 0, + .reg_shift = 0, + .reg_width = 4, + .val_min = 1, + .val_max = 12, + .label = "number of columns", + }, + { + .name = "azoteq,lower-cal-y", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 1, + .reg_shift = 8, + .reg_width = 8, + .label = "lower vertical calibration", + }, + { + .name = "azoteq,lower-cal-x", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 1, + .reg_shift = 0, + .reg_width = 8, + .label = "lower horizontal calibration", + }, + { + .name = "azoteq,upper-cal-y", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 2, + .reg_shift = 8, + .reg_width = 8, + .label = "upper vertical calibration", + }, + { + .name = "azoteq,upper-cal-x", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 2, + .reg_shift = 0, + .reg_width = 8, + .label = "upper horizontal calibration", + }, + { + .name = "azoteq,top-speed", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 3, + .reg_shift = 8, + .reg_width = 8, + .val_pitch = 4, + .label = "top speed", + }, + { + .name = "azoteq,bottom-speed", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_offset = 3, + .reg_shift = 0, + .reg_width = 8, + .label = "bottom speed", + }, + { + .name = "azoteq,gesture-min-ms", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_key = IQS7222_REG_KEY_TAP, + .reg_offset = 20, + .reg_shift = 8, + .reg_width = 8, + .val_pitch = 16, + .label = "minimum gesture time", + }, + { + .name = "azoteq,gesture-max-ms", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_key = IQS7222_REG_KEY_AXIAL, + .reg_offset = 21, + .reg_shift = 8, + .reg_width = 8, + .val_pitch = 16, + .label = "maximum gesture time", + }, + { + .name = "azoteq,gesture-max-ms", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_key = IQS7222_REG_KEY_TAP, + .reg_offset = 21, + .reg_shift = 0, + .reg_width = 8, + .val_pitch = 16, + .label = "maximum gesture time", + }, + { + .name = "azoteq,gesture-dist", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_key = IQS7222_REG_KEY_TAP, + .reg_offset = 22, + .reg_shift = 0, + .reg_width = 16, + .label = "gesture distance", + }, + { + .name = "azoteq,gesture-dist", + .reg_grp = IQS7222_REG_GRP_TPAD, + .reg_key = IQS7222_REG_KEY_AXIAL, + .reg_offset = 23, + .reg_shift = 0, + .reg_width = 16, + .label = "gesture distance", + }, { .name = "drive-open-drain", .reg_grp = IQS7222_REG_GRP_GPIO, @@ -1091,16 +1360,19 @@ struct iqs7222_private { struct gpio_desc *irq_gpio; struct i2c_client *client; struct input_dev *keypad; + struct touchscreen_properties prop; unsigned int kp_type[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)]; unsigned int kp_code[IQS7222_MAX_CHAN][ARRAY_SIZE(iqs7222_kp_events)]; unsigned int sl_code[IQS7222_MAX_SLDR][ARRAY_SIZE(iqs7222_sl_events)]; unsigned int sl_axis[IQS7222_MAX_SLDR]; + unsigned int tp_code[ARRAY_SIZE(iqs7222_tp_events)]; u16 cycle_setup[IQS7222_MAX_CHAN / 2][IQS7222_MAX_COLS_CYCLE]; u16 glbl_setup[IQS7222_MAX_COLS_GLBL]; u16 btn_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_BTN]; u16 chan_setup[IQS7222_MAX_CHAN][IQS7222_MAX_COLS_CHAN]; u16 filt_setup[IQS7222_MAX_COLS_FILT]; u16 sldr_setup[IQS7222_MAX_SLDR][IQS7222_MAX_COLS_SLDR]; + u16 tpad_setup[IQS7222_MAX_COLS_TPAD]; u16 gpio_setup[ARRAY_SIZE(iqs7222_gpio_links)][IQS7222_MAX_COLS_GPIO]; u16 sys_setup[IQS7222_MAX_COLS_SYS]; }; @@ -1127,6 +1399,9 @@ static u16 *iqs7222_setup(struct iqs7222_private *iqs7222, case IQS7222_REG_GRP_SLDR: return iqs7222->sldr_setup[row]; + case IQS7222_REG_GRP_TPAD: + return iqs7222->tpad_setup; + case IQS7222_REG_GRP_GPIO: return iqs7222->gpio_setup[row]; @@ -1381,9 +1656,6 @@ static int iqs7222_ati_trigger(struct iqs7222_private *iqs7222) if (error) return error; - sys_setup &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; - sys_setup &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; - for (i = 0; i < IQS7222_NUM_RETRIES; i++) { /* * Trigger ATI from streaming and normal-power modes so that @@ -1561,8 +1833,11 @@ static int iqs7222_dev_init(struct iqs7222_private *iqs7222, int dir) return error; } - if (dir == READ) + if (dir == READ) { + iqs7222->sys_setup[0] &= ~IQS7222_SYS_SETUP_INTF_MODE_MASK; + iqs7222->sys_setup[0] &= ~IQS7222_SYS_SETUP_PWR_MODE_MASK; return 0; + } return iqs7222_ati_trigger(iqs7222); } @@ -1936,6 +2211,14 @@ static int iqs7222_parse_chan(struct iqs7222_private *iqs7222, ref_setup[4] = dev_desc->touch_link; if (fwnode_property_present(chan_node, "azoteq,use-prox")) ref_setup[4] -= 2; + } else if (dev_desc->reg_grps[IQS7222_REG_GRP_TPAD].num_row && + fwnode_property_present(chan_node, + "azoteq,counts-filt-enable")) { + /* + * In the case of IQS7222D, however, the reference mode field + * is partially repurposed as a counts filter enable control. + */ + chan_setup[0] |= IQS7222_CHAN_SETUP_0_REF_MODE_REF; } if (fwnode_property_present(chan_node, "azoteq,rx-enable")) { @@ -2278,6 +2561,136 @@ static int iqs7222_parse_sldr(struct iqs7222_private *iqs7222, IQS7222_REG_KEY_NO_WHEEL); } +static int iqs7222_parse_tpad(struct iqs7222_private *iqs7222, + struct fwnode_handle *tpad_node, int tpad_index) +{ + const struct iqs7222_dev_desc *dev_desc = iqs7222->dev_desc; + struct touchscreen_properties *prop = &iqs7222->prop; + struct i2c_client *client = iqs7222->client; + int num_chan = dev_desc->reg_grps[IQS7222_REG_GRP_CHAN].num_row; + int count, error, i; + u16 *event_mask = &iqs7222->sys_setup[dev_desc->event_offset]; + u16 *tpad_setup = iqs7222->tpad_setup; + unsigned int chan_sel[12]; + + error = iqs7222_parse_props(iqs7222, tpad_node, tpad_index, + IQS7222_REG_GRP_TPAD, + IQS7222_REG_KEY_NONE); + if (error) + return error; + + count = fwnode_property_count_u32(tpad_node, "azoteq,channel-select"); + if (count < 0) { + dev_err(&client->dev, "Failed to count %s channels: %d\n", + fwnode_get_name(tpad_node), count); + return count; + } else if (!count || count > ARRAY_SIZE(chan_sel)) { + dev_err(&client->dev, "Invalid number of %s channels\n", + fwnode_get_name(tpad_node)); + return -EINVAL; + } + + error = fwnode_property_read_u32_array(tpad_node, + "azoteq,channel-select", + chan_sel, count); + if (error) { + dev_err(&client->dev, "Failed to read %s channels: %d\n", + fwnode_get_name(tpad_node), error); + return error; + } + + tpad_setup[6] &= ~GENMASK(num_chan - 1, 0); + + for (i = 0; i < ARRAY_SIZE(chan_sel); i++) { + tpad_setup[8 + i] = 0; + if (i >= count || chan_sel[i] == U8_MAX) + continue; + + if (chan_sel[i] >= num_chan) { + dev_err(&client->dev, "Invalid %s channel: %u\n", + fwnode_get_name(tpad_node), chan_sel[i]); + return -EINVAL; + } + + /* + * The following fields indicate which channels participate in + * the trackpad, as well as each channel's relative placement. + */ + tpad_setup[6] |= BIT(chan_sel[i]); + tpad_setup[8 + i] = chan_sel[i] * 34 + 1072; + } + + tpad_setup[7] = dev_desc->touch_link; + if (fwnode_property_present(tpad_node, "azoteq,use-prox")) + tpad_setup[7] -= 2; + + for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++) + tpad_setup[20] &= ~(iqs7222_tp_events[i].strict | + iqs7222_tp_events[i].enable); + + for (i = 0; i < ARRAY_SIZE(iqs7222_tp_events); i++) { + const char *event_name = iqs7222_tp_events[i].name; + struct fwnode_handle *event_node; + + event_node = fwnode_get_named_child_node(tpad_node, event_name); + if (!event_node) + continue; + + if (fwnode_property_present(event_node, + "azoteq,gesture-angle-tighten")) + tpad_setup[20] |= iqs7222_tp_events[i].strict; + + tpad_setup[20] |= iqs7222_tp_events[i].enable; + + error = iqs7222_parse_event(iqs7222, event_node, tpad_index, + IQS7222_REG_GRP_TPAD, + iqs7222_tp_events[i].reg_key, + iqs7222_tp_events[i].link, 1566, + NULL, + &iqs7222->tp_code[i]); + fwnode_handle_put(event_node); + if (error) + return error; + + if (!dev_desc->event_offset) + continue; + + /* + * The press/release event is determined based on whether the + * coordinate fields report 0xFFFF and solely relies on touch + * or proximity interrupts to be unmasked. + */ + if (i) + *event_mask |= IQS7222_EVENT_MASK_TPAD; + else if (tpad_setup[7] == dev_desc->touch_link) + *event_mask |= IQS7222_EVENT_MASK_TOUCH; + else + *event_mask |= IQS7222_EVENT_MASK_PROX; + } + + if (!iqs7222->tp_code[0]) + return 0; + + input_set_abs_params(iqs7222->keypad, ABS_X, + 0, (tpad_setup[4] ? : 1) - 1, 0, 0); + + input_set_abs_params(iqs7222->keypad, ABS_Y, + 0, (tpad_setup[5] ? : 1) - 1, 0, 0); + + touchscreen_parse_properties(iqs7222->keypad, false, prop); + + if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) { + dev_err(&client->dev, "Invalid trackpad size: %u*%u\n", + prop->max_x, prop->max_y); + return -EINVAL; + } + + tpad_setup[4] = prop->max_x + 1; + tpad_setup[5] = prop->max_y + 1; + + return 0; +} + static int (*iqs7222_parse_extra[IQS7222_NUM_REG_GRPS]) (struct iqs7222_private *iqs7222, struct fwnode_handle *reg_grp_node, @@ -2285,6 +2698,7 @@ static int (*iqs7222_parse_extra[IQS7222_NUM_REG_GRPS]) [IQS7222_REG_GRP_CYCLE] = iqs7222_parse_cycle, [IQS7222_REG_GRP_CHAN] = iqs7222_parse_chan, [IQS7222_REG_GRP_SLDR] = iqs7222_parse_sldr, + [IQS7222_REG_GRP_TPAD] = iqs7222_parse_tpad, }; static int iqs7222_parse_reg_grp(struct iqs7222_private *iqs7222, @@ -2298,7 +2712,7 @@ static int iqs7222_parse_reg_grp(struct iqs7222_private *iqs7222, if (iqs7222_reg_grp_names[reg_grp]) { char reg_grp_name[16]; - snprintf(reg_grp_name, sizeof(reg_grp_name), "%s-%d", + snprintf(reg_grp_name, sizeof(reg_grp_name), iqs7222_reg_grp_names[reg_grp], reg_grp_index); reg_grp_node = device_get_named_child_node(&client->dev, @@ -2346,8 +2760,8 @@ static int iqs7222_parse_all(struct iqs7222_private *iqs7222) continue; /* - * The IQS7222C exposes multiple GPIO and must be informed - * as to which GPIO this group represents. + * The IQS7222C and IQS7222D expose multiple GPIO and must be + * informed as to which GPIO this group represents. */ for (j = 0; j < ARRAY_SIZE(iqs7222_gpio_links); j++) gpio_setup[0] &= ~BIT(iqs7222_gpio_links[j]); @@ -2480,6 +2894,41 @@ static int iqs7222_report(struct iqs7222_private *iqs7222) iqs7222->sl_code[i][j], 0); } + for (i = 0; i < dev_desc->reg_grps[IQS7222_REG_GRP_TPAD].num_row; i++) { + u16 tpad_pos_x = le16_to_cpu(status[4]); + u16 tpad_pos_y = le16_to_cpu(status[5]); + u16 state = le16_to_cpu(status[6]); + + input_report_key(iqs7222->keypad, iqs7222->tp_code[0], + tpad_pos_x < U16_MAX); + + if (tpad_pos_x < U16_MAX) + touchscreen_report_pos(iqs7222->keypad, &iqs7222->prop, + tpad_pos_x, tpad_pos_y, false); + + if (!(le16_to_cpu(status[1]) & IQS7222_EVENT_MASK_TPAD)) + continue; + + /* + * Skip the press/release event, as it does not have separate + * status fields and is handled separately. + */ + for (j = 1; j < ARRAY_SIZE(iqs7222_tp_events); j++) { + u16 mask = iqs7222_tp_events[j].mask; + u16 val = iqs7222_tp_events[j].val; + + input_report_key(iqs7222->keypad, + iqs7222->tp_code[j], + (state & mask) == val); + } + + input_sync(iqs7222->keypad); + + for (j = 1; j < ARRAY_SIZE(iqs7222_tp_events); j++) + input_report_key(iqs7222->keypad, + iqs7222->tp_code[j], 0); + } + input_sync(iqs7222->keypad); return 0; @@ -2584,6 +3033,7 @@ static const struct of_device_id iqs7222_of_match[] = { { .compatible = "azoteq,iqs7222a" }, { .compatible = "azoteq,iqs7222b" }, { .compatible = "azoteq,iqs7222c" }, + { .compatible = "azoteq,iqs7222d" }, { } }; MODULE_DEVICE_TABLE(of, iqs7222_of_match); @@ -2598,5 +3048,5 @@ static struct i2c_driver iqs7222_i2c_driver = { module_i2c_driver(iqs7222_i2c_driver); MODULE_AUTHOR("Jeff LaBundy "); -MODULE_DESCRIPTION("Azoteq IQS7222A/B/C Capacitive Touch Controller"); +MODULE_DESCRIPTION("Azoteq IQS7222A/B/C/D Capacitive Touch Controller"); MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 76a190b2220bd465016071118440b5fc90708116..662b436d765bb958c5e4d0030a1e27fed389fa8f 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #define MMA8450_DRV_NAME "mma8450" diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c index 74d77d8aaeff204d3b9cfca4aa531dc4431d78bb..ba747c5b2b5fc7620fbd8b2acf2bac46cf92fd47 100644 --- a/drivers/input/misc/pm8941-pwrkey.c +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index 04cb87efd7991cc075fb38172a347924001664ad..5c288fe7accf1fbc231923d6444721eff0fcccf5 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 89fb137e3715774b3fc081fda978b470a47cc363..c406a1cca5c443cb4b7cd6919231e1139e6ab504 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -12,7 +12,6 @@ #include #include #include -#include #define PON_CNTL_1 0x1C #define PON_CNTL_PULL_UP BIT(7) diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index 3cf1812384e6ad97009aa594bbd5a96c40c0c3c0..1e731d8397c6f525c283b8858d45fac7c34eab6d 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -132,13 +132,8 @@ static int pwm_beeper_probe(struct platform_device *pdev) return -ENOMEM; beeper->pwm = devm_pwm_get(dev, NULL); - if (IS_ERR(beeper->pwm)) { - error = PTR_ERR(beeper->pwm); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to request PWM device: %d\n", - error); - return error; - } + if (IS_ERR(beeper->pwm)) + return dev_err_probe(dev, PTR_ERR(beeper->pwm), "Failed to request PWM device\n"); /* Sync up PWM state and ensure it is off. */ pwm_init_state(beeper->pwm, &state); @@ -151,13 +146,9 @@ static int pwm_beeper_probe(struct platform_device *pdev) } beeper->amplifier = devm_regulator_get(dev, "amp"); - if (IS_ERR(beeper->amplifier)) { - error = PTR_ERR(beeper->amplifier); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'amp' regulator: %d\n", - error); - return error; - } + if (IS_ERR(beeper->amplifier)) + return dev_err_probe(dev, PTR_ERR(beeper->amplifier), + "Failed to get 'amp' regulator\n"); INIT_WORK(&beeper->work, pwm_beeper_work); diff --git a/drivers/input/misc/pwm-vibra.c b/drivers/input/misc/pwm-vibra.c index 2ba035299db879b224010004b6b018e2fecd4b60..acac79c488aa1531fa421ea6d677469652d4c406 100644 --- a/drivers/input/misc/pwm-vibra.c +++ b/drivers/input/misc/pwm-vibra.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -140,32 +140,20 @@ static int pwm_vibrator_probe(struct platform_device *pdev) return -ENOMEM; vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc"); - err = PTR_ERR_OR_ZERO(vibrator->vcc); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to request regulator: %d\n", - err); - return err; - } + if (IS_ERR(vibrator->vcc)) + return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->vcc), + "Failed to request regulator\n"); vibrator->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); - err = PTR_ERR_OR_ZERO(vibrator->enable_gpio); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to request enable gpio: %d\n", - err); - return err; - } + if (IS_ERR(vibrator->enable_gpio)) + return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->enable_gpio), + "Failed to request enable gpio\n"); vibrator->pwm = devm_pwm_get(&pdev->dev, "enable"); - err = PTR_ERR_OR_ZERO(vibrator->pwm); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Failed to request main pwm: %d\n", - err); - return err; - } + if (IS_ERR(vibrator->pwm)) + return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->pwm), + "Failed to request main pwm\n"); INIT_WORK(&vibrator->play_work, pwm_vibrator_play_work); diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 22ec620830653dd73505843a871874555ebd6f36..e94cab8133becb9b5970789a1658cb38c59ca5a2 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -236,12 +236,8 @@ static int rotary_encoder_probe(struct platform_device *pdev) device_property_read_bool(dev, "rotary-encoder,relative-axis"); encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); - if (IS_ERR(encoder->gpios)) { - err = PTR_ERR(encoder->gpios); - if (err != -EPROBE_DEFER) - dev_err(dev, "unable to get gpios: %d\n", err); - return err; - } + if (IS_ERR(encoder->gpios)) + return dev_err_probe(dev, PTR_ERR(encoder->gpios), "unable to get gpios\n"); if (encoder->gpios->ndescs < 2) { dev_err(dev, "not enough gpios found\n"); return -EINVAL; @@ -255,7 +251,6 @@ static int rotary_encoder_probe(struct platform_device *pdev) input->name = pdev->name; input->id.bustype = BUS_HOST; - input->dev.parent = dev; if (encoder->relative_axis) input_set_capability(input, EV_REL, encoder->axis); diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index cdcb7737c46aa50a91d5205d8728f13f9d8215a1..e5dd84725c6e74737ca4a080d6ac4b1129330623 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -9,7 +9,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 0cff742302a9fca607db01ee4496b88186748d37..148a601396f92cd5293405fe0355f57672154e4e 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1221,13 +1221,8 @@ static int elan_probe(struct i2c_client *client) mutex_init(&data->sysfs_mutex); data->vcc = devm_regulator_get(dev, "vcc"); - if (IS_ERR(data->vcc)) { - error = PTR_ERR(data->vcc); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get 'vcc' regulator: %d\n", - error); - return error; - } + if (IS_ERR(data->vcc)) + return dev_err_probe(dev, PTR_ERR(data->vcc), "Failed to get 'vcc' regulator\n"); error = regulator_enable(data->vcc); if (error) { diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c index 2a2459b1b4f2cb91b0465f77637f22a0b71bcf62..7b13de9799089fcea428738cb3f59fb9939b5abb 100644 --- a/drivers/input/mouse/psmouse-smbus.c +++ b/drivers/input/mouse/psmouse-smbus.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -118,13 +119,18 @@ static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse) return PSMOUSE_FULL_PACKET; } -static int psmouse_smbus_reconnect(struct psmouse *psmouse) +static void psmouse_activate_smbus_mode(struct psmouse_smbus_dev *smbdev) { - struct psmouse_smbus_dev *smbdev = psmouse->private; - - if (smbdev->need_deactivate) - psmouse_deactivate(psmouse); + if (smbdev->need_deactivate) { + psmouse_deactivate(smbdev->psmouse); + /* Give the device time to switch into SMBus mode */ + msleep(30); + } +} +static int psmouse_smbus_reconnect(struct psmouse *psmouse) +{ + psmouse_activate_smbus_mode(psmouse->private); return 0; } @@ -257,8 +263,7 @@ int psmouse_smbus_init(struct psmouse *psmouse, } } - if (need_deactivate) - psmouse_deactivate(psmouse); + psmouse_activate_smbus_mode(smbdev); psmouse->private = smbdev; psmouse->protocol_handler = psmouse_smbus_process_byte; diff --git a/drivers/input/serio/apbps2.c b/drivers/input/serio/apbps2.c index 513d96e40e0efacbac3236f392cacd310ead80ff..3f6866d39b862454fa903981016c3df03b252c8f 100644 --- a/drivers/input/serio/apbps2.c +++ b/drivers/input/serio/apbps2.c @@ -14,11 +14,11 @@ * Contributors: Daniel Hellstrom */ #include -#include #include #include #include #include +#include #include #include #include diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h index 028e45bd050bf3f151cee382b666bfce86775ee7..1724d6cb8649d6094a84ec91e9e09211e0307e21 100644 --- a/drivers/input/serio/i8042-acpipnpio.h +++ b/drivers/input/serio/i8042-acpipnpio.h @@ -1281,6 +1281,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = { .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP) }, + /* See comment on TUXEDO InfinityBook S17 Gen6 / Clevo NS70MU above */ + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "PD5x_7xPNP_PNR_PNN_PNT"), + }, + .driver_data = (void *)(SERIO_QUIRK_NOAUX) + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "X170SM"), diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index c712c1fe060533045d5f10bf80f5f6bc00636632..b68793bf05c8cefb9f7bb59da7748147d611da7f 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -2,7 +2,9 @@ #ifndef _I8042_SPARCIO_H #define _I8042_SPARCIO_H -#include +#include +#include +#include #include #include diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index ce420eb1f51bec22d377e6043318a3c816e48263..e8a9709f32ebbb68ab1d09f8bc56860f1751b63c 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -101,12 +101,12 @@ static int rpckbd_probe(struct platform_device *dev) int tx_irq, rx_irq; rx_irq = platform_get_irq(dev, 0); - if (rx_irq <= 0) - return rx_irq < 0 ? rx_irq : -ENXIO; + if (rx_irq < 0) + return rx_irq; tx_irq = platform_get_irq(dev, 1); - if (tx_irq <= 0) - return tx_irq < 0 ? tx_irq : -ENXIO; + if (tx_irq < 0) + return tx_irq; serio = kzalloc(sizeof(struct serio), GFP_KERNEL); rpckbd = kzalloc(sizeof(*rpckbd), GFP_KERNEL); diff --git a/drivers/input/serio/xilinx_ps2.c b/drivers/input/serio/xilinx_ps2.c index 960d7601fbc825dc22a7d64ac42cda1c95113070..f3d28da70b75c4478becad35f757e972fc7a054d 100644 --- a/drivers/input/serio/xilinx_ps2.c +++ b/drivers/input/serio/xilinx_ps2.c @@ -14,10 +14,10 @@ #include #include #include +#include #include -#include #include -#include +#include #define DRIVER_NAME "xilinx_ps2" diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index c2cbd332af1dfabd17ad339a75efd5b76c9c943b..e3e2324547b909d0dc1f0e57a3b32db0c519ddbc 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -655,10 +655,10 @@ config TOUCHSCREEN_MTOUCH module will be called mtouch. config TOUCHSCREEN_NOVATEK_NVT_TS - tristate "Novatek NVT-ts touchscreen support" + tristate "Novatek NT11205 touchscreen support" depends on I2C help - Say Y here if you have a Novatek NVT-ts touchscreen. + Say Y here if you have a Novatek NT11205 touchscreen. If unsure, say N. To compile this driver as a module, choose M here: the @@ -1365,6 +1365,16 @@ config TOUCHSCREEN_IQS5XX To compile this driver as a module, choose M here: the module will be called iqs5xx. +config TOUCHSCREEN_IQS7211 + tristate "Azoteq IQS7210A/7211A/E trackpad/touchscreen controller" + depends on I2C + help + Say Y to enable support for the Azoteq IQS7210A/7211A/E + family of trackpad/touchscreen controllers. + + To compile this driver as a module, choose M here: the + module will be called iqs7211. + config TOUCHSCREEN_ZINITIX tristate "Zinitix touchscreen support" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 159cd5136fdbca6938d31e2772296c6b73953ec2..62bd24f3ac8e0d382d041325c0de3c53e1cbc118 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -115,5 +115,6 @@ obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o +obj-$(CONFIG_TOUCHSCREEN_IQS7211) += iqs7211.o obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX83112B) += himax_hx83112b.o diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 85332cfaa29dc92718a90c812ff30e8368e79da2..652439a79e216d8e67ffae5033fd5dd9929af992 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -410,31 +410,32 @@ static int bu21013_probe(struct i2c_client *client) struct input_dev *in_dev; struct input_absinfo *info; u32 max_x = 0, max_y = 0; + struct device *dev = &client->dev; int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, "i2c smbus byte data not supported\n"); + dev_err(dev, "i2c smbus byte data not supported\n"); return -EIO; } if (!client->irq) { - dev_err(&client->dev, "No IRQ set up\n"); + dev_err(dev, "No IRQ set up\n"); return -EINVAL; } - ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); if (!ts) return -ENOMEM; ts->client = client; - ts->x_flip = device_property_read_bool(&client->dev, "rohm,flip-x"); - ts->y_flip = device_property_read_bool(&client->dev, "rohm,flip-y"); + ts->x_flip = device_property_read_bool(dev, "rohm,flip-x"); + ts->y_flip = device_property_read_bool(dev, "rohm,flip-y"); - in_dev = devm_input_allocate_device(&client->dev); + in_dev = devm_input_allocate_device(dev); if (!in_dev) { - dev_err(&client->dev, "device memory alloc failed\n"); + dev_err(dev, "device memory alloc failed\n"); return -ENOMEM; } ts->in_dev = in_dev; @@ -444,8 +445,8 @@ static int bu21013_probe(struct i2c_client *client) in_dev->name = DRIVER_TP; in_dev->id.bustype = BUS_I2C; - device_property_read_u32(&client->dev, "rohm,touch-max-x", &max_x); - device_property_read_u32(&client->dev, "rohm,touch-max-y", &max_y); + device_property_read_u32(dev, "rohm,touch-max-x", &max_x); + device_property_read_u32(dev, "rohm,touch-max-y", &max_y); input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, max_x, 0, 0); input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, max_y, 0, 0); @@ -454,14 +455,14 @@ static int bu21013_probe(struct i2c_client *client) /* Adjust for the legacy "flip" properties, if present */ if (!ts->props.invert_x && - device_property_read_bool(&client->dev, "rohm,flip-x")) { + device_property_read_bool(dev, "rohm,flip-x")) { info = &in_dev->absinfo[ABS_MT_POSITION_X]; info->maximum -= info->minimum; info->minimum = 0; } if (!ts->props.invert_y && - device_property_read_bool(&client->dev, "rohm,flip-y")) { + device_property_read_bool(dev, "rohm,flip-y")) { info = &in_dev->absinfo[ABS_MT_POSITION_Y]; info->maximum -= info->minimum; info->minimum = 0; @@ -471,55 +472,46 @@ static int bu21013_probe(struct i2c_client *client) INPUT_MT_DIRECT | INPUT_MT_TRACK | INPUT_MT_DROP_UNUSED); if (error) { - dev_err(&client->dev, "failed to initialize MT slots"); + dev_err(dev, "failed to initialize MT slots"); return error; } - ts->regulator = devm_regulator_get(&client->dev, "avdd"); + ts->regulator = devm_regulator_get(dev, "avdd"); if (IS_ERR(ts->regulator)) { - dev_err(&client->dev, "regulator_get failed\n"); + dev_err(dev, "regulator_get failed\n"); return PTR_ERR(ts->regulator); } error = regulator_enable(ts->regulator); if (error) { - dev_err(&client->dev, "regulator enable failed\n"); + dev_err(dev, "regulator enable failed\n"); return error; } - error = devm_add_action_or_reset(&client->dev, bu21013_power_off, ts); + error = devm_add_action_or_reset(dev, bu21013_power_off, ts); if (error) { - dev_err(&client->dev, "failed to install power off handler\n"); + dev_err(dev, "failed to install power off handler\n"); return error; } /* Named "CS" on the chip, DT binding is "reset" */ - ts->cs_gpiod = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); - error = PTR_ERR_OR_ZERO(ts->cs_gpiod); - if (error) { - if (error != -EPROBE_DEFER) - dev_err(&client->dev, "failed to get CS GPIO\n"); - return error; - } + ts->cs_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ts->cs_gpiod)) + return dev_err_probe(dev, PTR_ERR(ts->cs_gpiod), "failed to get CS GPIO\n"); + gpiod_set_consumer_name(ts->cs_gpiod, "BU21013 CS"); - error = devm_add_action_or_reset(&client->dev, - bu21013_disable_chip, ts); + error = devm_add_action_or_reset(dev, bu21013_disable_chip, ts); if (error) { - dev_err(&client->dev, - "failed to install chip disable handler\n"); + dev_err(dev, "failed to install chip disable handler\n"); return error; } /* Named "INT" on the chip, DT binding is "touch" */ - ts->int_gpiod = devm_gpiod_get_optional(&client->dev, - "touch", GPIOD_IN); + ts->int_gpiod = devm_gpiod_get_optional(dev, "touch", GPIOD_IN); error = PTR_ERR_OR_ZERO(ts->int_gpiod); - if (error) { - if (error != -EPROBE_DEFER) - dev_err(&client->dev, "failed to get INT GPIO\n"); - return error; - } + if (error) + return dev_err_probe(dev, error, "failed to get INT GPIO\n"); if (ts->int_gpiod) gpiod_set_consumer_name(ts->int_gpiod, "BU21013 INT"); @@ -527,22 +519,20 @@ static int bu21013_probe(struct i2c_client *client) /* configure the touch panel controller */ error = bu21013_init_chip(ts); if (error) { - dev_err(&client->dev, "error in bu21013 config\n"); + dev_err(dev, "error in bu21013 config\n"); return error; } - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, bu21013_gpio_irq, + error = devm_request_threaded_irq(dev, client->irq, NULL, bu21013_gpio_irq, IRQF_ONESHOT, DRIVER_TP, ts); if (error) { - dev_err(&client->dev, "request irq %d failed\n", - client->irq); + dev_err(dev, "request irq %d failed\n", client->irq); return error; } error = input_register_device(in_dev); if (error) { - dev_err(&client->dev, "failed to register input device\n"); + dev_err(dev, "failed to register input device\n"); return error; } diff --git a/drivers/input/touchscreen/bu21029_ts.c b/drivers/input/touchscreen/bu21029_ts.c index c8126d2efe95a9c4c6ed8e8d819d516a65e2aa81..e1dfbd92ab648e47dc517a6604c19e8123d8c120 100644 --- a/drivers/input/touchscreen/bu21029_ts.c +++ b/drivers/input/touchscreen/bu21029_ts.c @@ -333,6 +333,7 @@ static void bu21029_stop_chip(struct input_dev *dev) static int bu21029_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct bu21029_ts_data *bu21029; struct input_dev *in_dev; int error; @@ -341,45 +342,33 @@ static int bu21029_probe(struct i2c_client *client) I2C_FUNC_SMBUS_WRITE_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { - dev_err(&client->dev, - "i2c functionality support is not sufficient\n"); + dev_err(dev, "i2c functionality support is not sufficient\n"); return -EIO; } - bu21029 = devm_kzalloc(&client->dev, sizeof(*bu21029), GFP_KERNEL); + bu21029 = devm_kzalloc(dev, sizeof(*bu21029), GFP_KERNEL); if (!bu21029) return -ENOMEM; - error = device_property_read_u32(&client->dev, "rohm,x-plate-ohms", - &bu21029->x_plate_ohms); + error = device_property_read_u32(dev, "rohm,x-plate-ohms", &bu21029->x_plate_ohms); if (error) { - dev_err(&client->dev, - "invalid 'x-plate-ohms' supplied: %d\n", error); + dev_err(dev, "invalid 'x-plate-ohms' supplied: %d\n", error); return error; } - bu21029->vdd = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(bu21029->vdd)) { - error = PTR_ERR(bu21029->vdd); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "failed to acquire 'vdd' supply: %d\n", error); - return error; - } + bu21029->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(bu21029->vdd)) + return dev_err_probe(dev, PTR_ERR(bu21029->vdd), + "failed to acquire 'vdd' supply\n"); - bu21029->reset_gpios = devm_gpiod_get_optional(&client->dev, - "reset", GPIOD_OUT_HIGH); - if (IS_ERR(bu21029->reset_gpios)) { - error = PTR_ERR(bu21029->reset_gpios); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "failed to acquire 'reset' gpio: %d\n", error); - return error; - } + bu21029->reset_gpios = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(bu21029->reset_gpios)) + return dev_err_probe(dev, PTR_ERR(bu21029->reset_gpios), + "failed to acquire 'reset' gpio\n"); - in_dev = devm_input_allocate_device(&client->dev); + in_dev = devm_input_allocate_device(dev); if (!in_dev) { - dev_err(&client->dev, "unable to allocate input device\n"); + dev_err(dev, "unable to allocate input device\n"); return -ENOMEM; } @@ -400,20 +389,18 @@ static int bu21029_probe(struct i2c_client *client) input_set_drvdata(in_dev, bu21029); - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, bu21029_touch_soft_irq, + error = devm_request_threaded_irq(dev, client->irq, NULL, + bu21029_touch_soft_irq, IRQF_ONESHOT | IRQF_NO_AUTOEN, DRIVER_NAME, bu21029); if (error) { - dev_err(&client->dev, - "unable to request touch irq: %d\n", error); + dev_err(dev, "unable to request touch irq: %d\n", error); return error; } error = input_register_device(in_dev); if (error) { - dev_err(&client->dev, - "unable to register input device: %d\n", error); + dev_err(dev, "unable to register input device: %d\n", error); return error; } diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c index 9fbeaf17f00b70ea2ae604f18a578459b3220924..d6876d10b25283d2e2447c069edb5f00bb51331d 100644 --- a/drivers/input/touchscreen/chipone_icn8318.c +++ b/drivers/input/touchscreen/chipone_icn8318.c @@ -191,12 +191,8 @@ static int icn8318_probe(struct i2c_client *client) return -ENOMEM; data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW); - if (IS_ERR(data->wake_gpio)) { - error = PTR_ERR(data->wake_gpio); - if (error != -EPROBE_DEFER) - dev_err(dev, "Error getting wake gpio: %d\n", error); - return error; - } + if (IS_ERR(data->wake_gpio)) + return dev_err_probe(dev, PTR_ERR(data->wake_gpio), "Error getting wake gpio\n"); input = devm_input_allocate_device(dev); if (!input) diff --git a/drivers/input/touchscreen/cy8ctma140.c b/drivers/input/touchscreen/cy8ctma140.c index 967ecde23e83ff53cc6c17c40baddec88b7be138..ea3895167b82e529fc0d7f6d05e989f7022f6ef3 100644 --- a/drivers/input/touchscreen/cy8ctma140.c +++ b/drivers/input/touchscreen/cy8ctma140.c @@ -258,12 +258,8 @@ static int cy8ctma140_probe(struct i2c_client *client) ts->regulators[1].supply = "vdd"; error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators), ts->regulators); - if (error) { - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get regulators %d\n", - error); - return error; - } + if (error) + return dev_err_probe(dev, error, "Failed to get regulators\n"); error = cy8ctma140_power_up(ts); if (error) diff --git a/drivers/input/touchscreen/cyttsp5.c b/drivers/input/touchscreen/cyttsp5.c index b461ded946fc892bbc5c08eac54983d2d5fd8b9e..db5a885ecd728573edc566eb2d57f10d26e7e33a 100644 --- a/drivers/input/touchscreen/cyttsp5.c +++ b/drivers/input/touchscreen/cyttsp5.c @@ -18,8 +18,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 795c7dad22bfb04f5fb58ff0c4cf33572ff1f4ad..457d53337fbb399191efd37f7f822a53e204b981 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1168,13 +1168,9 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client) tsdata->max_support_points = chip_data->max_support_points; tsdata->vcc = devm_regulator_get(&client->dev, "vcc"); - if (IS_ERR(tsdata->vcc)) { - error = PTR_ERR(tsdata->vcc); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "failed to request regulator: %d\n", error); - return error; - } + if (IS_ERR(tsdata->vcc)) + return dev_err_probe(&client->dev, PTR_ERR(tsdata->vcc), + "failed to request regulator\n"); tsdata->iovcc = devm_regulator_get(&client->dev, "iovcc"); if (IS_ERR(tsdata->iovcc)) { diff --git a/drivers/input/touchscreen/ektf2127.c b/drivers/input/touchscreen/ektf2127.c index fd8724a3c19fd3e7eb751b122f0bee8b1643f8b2..cc3103b9cbfba4690db992bbf9de54f0f65e0e5d 100644 --- a/drivers/input/touchscreen/ektf2127.c +++ b/drivers/input/touchscreen/ektf2127.c @@ -264,12 +264,8 @@ static int ektf2127_probe(struct i2c_client *client) /* This requests the gpio *and* turns on the touchscreen controller */ ts->power_gpios = devm_gpiod_get(dev, "power", GPIOD_OUT_HIGH); - if (IS_ERR(ts->power_gpios)) { - error = PTR_ERR(ts->power_gpios); - if (error != -EPROBE_DEFER) - dev_err(dev, "Error getting power gpio: %d\n", error); - return error; - } + if (IS_ERR(ts->power_gpios)) + return dev_err_probe(dev, PTR_ERR(ts->power_gpios), "Error getting power gpio\n"); input = devm_input_allocate_device(dev); if (!input) diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 2da1db64126d19fc49860598d410dd9af11c52f7..a1af3de9f3109ed6c6887d6b6575a33facf44669 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -1438,24 +1438,14 @@ static int elants_i2c_probe(struct i2c_client *client) i2c_set_clientdata(client, ts); ts->vcc33 = devm_regulator_get(&client->dev, "vcc33"); - if (IS_ERR(ts->vcc33)) { - error = PTR_ERR(ts->vcc33); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get 'vcc33' regulator: %d\n", - error); - return error; - } + if (IS_ERR(ts->vcc33)) + return dev_err_probe(&client->dev, PTR_ERR(ts->vcc33), + "Failed to get 'vcc33' regulator\n"); ts->vccio = devm_regulator_get(&client->dev, "vccio"); - if (IS_ERR(ts->vccio)) { - error = PTR_ERR(ts->vccio); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get 'vccio' regulator: %d\n", - error); - return error; - } + if (IS_ERR(ts->vccio)) + return dev_err_probe(&client->dev, PTR_ERR(ts->vccio), + "Failed to get 'vccio' regulator\n"); ts->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(ts->reset_gpio)) { diff --git a/drivers/input/touchscreen/exc3000.c b/drivers/input/touchscreen/exc3000.c index 4af4c1e5d0da25458f98439d09306b32bf114abe..4c0d99aae9e0346be665ac679c17925e2291e332 100644 --- a/drivers/input/touchscreen/exc3000.c +++ b/drivers/input/touchscreen/exc3000.c @@ -7,6 +7,7 @@ * minimal implementation based on egalax_ts.c and egalax_i2c.c */ +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -360,6 +362,12 @@ static int exc3000_probe(struct i2c_client *client) if (IS_ERR(data->reset)) return PTR_ERR(data->reset); + /* For proper reset sequence, enable power while reset asserted */ + error = devm_regulator_get_enable(&client->dev, "vdd"); + if (error && error != -ENODEV) + return dev_err_probe(&client->dev, error, + "failed to request vdd regulator\n"); + if (data->reset) { msleep(EXC3000_RESET_MS); gpiod_set_value_cansleep(data->reset, 0); @@ -454,10 +462,19 @@ static const struct of_device_id exc3000_of_match[] = { MODULE_DEVICE_TABLE(of, exc3000_of_match); #endif +#ifdef CONFIG_ACPI +static const struct acpi_device_id exc3000_acpi_match[] = { + { "EGA00001", .driver_data = (kernel_ulong_t)&exc3000_info[EETI_EXC80H60] }, + { } +}; +MODULE_DEVICE_TABLE(acpi, exc3000_acpi_match); +#endif + static struct i2c_driver exc3000_driver = { .driver = { .name = "exc3000", .of_match_table = of_match_ptr(exc3000_of_match), + .acpi_match_table = ACPI_PTR(exc3000_acpi_match), }, .id_table = exc3000_id, .probe = exc3000_probe, diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index f5aa240739f9709dab5640b87a6120d977f47c32..da9954d6df44c19b45032ba862e13acfee267832 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -935,7 +935,6 @@ static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts) */ static int goodix_get_gpio_config(struct goodix_ts_data *ts) { - int error; struct device *dev; struct gpio_desc *gpiod; bool added_acpi_mappings = false; @@ -951,33 +950,20 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) ts->gpiod_rst_flags = GPIOD_IN; ts->avdd28 = devm_regulator_get(dev, "AVDD28"); - if (IS_ERR(ts->avdd28)) { - error = PTR_ERR(ts->avdd28); - if (error != -EPROBE_DEFER) - dev_err(dev, - "Failed to get AVDD28 regulator: %d\n", error); - return error; - } + if (IS_ERR(ts->avdd28)) + return dev_err_probe(dev, PTR_ERR(ts->avdd28), "Failed to get AVDD28 regulator\n"); ts->vddio = devm_regulator_get(dev, "VDDIO"); - if (IS_ERR(ts->vddio)) { - error = PTR_ERR(ts->vddio); - if (error != -EPROBE_DEFER) - dev_err(dev, - "Failed to get VDDIO regulator: %d\n", error); - return error; - } + if (IS_ERR(ts->vddio)) + return dev_err_probe(dev, PTR_ERR(ts->vddio), "Failed to get VDDIO regulator\n"); retry_get_irq_gpio: /* Get the interrupt GPIO pin number */ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); - if (IS_ERR(gpiod)) { - error = PTR_ERR(gpiod); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get %s GPIO: %d\n", - GOODIX_GPIO_INT_NAME, error); - return error; - } + if (IS_ERR(gpiod)) + return dev_err_probe(dev, PTR_ERR(gpiod), "Failed to get %s GPIO\n", + GOODIX_GPIO_INT_NAME); + if (!gpiod && has_acpi_companion(dev) && !added_acpi_mappings) { added_acpi_mappings = true; if (goodix_add_acpi_gpio_mappings(ts) == 0) @@ -988,13 +974,9 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) /* Get the reset line GPIO pin number */ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, ts->gpiod_rst_flags); - if (IS_ERR(gpiod)) { - error = PTR_ERR(gpiod); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get %s GPIO: %d\n", - GOODIX_GPIO_RST_NAME, error); - return error; - } + if (IS_ERR(gpiod)) + return dev_err_probe(dev, PTR_ERR(gpiod), "Failed to get %s GPIO\n", + GOODIX_GPIO_RST_NAME); ts->gpiod_rst = gpiod; @@ -1517,6 +1499,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id); static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, { "GDIX1002", 0 }, + { "GDX9110", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index f7cd773f7292a1a3d582391a83d0f6a843c63034..ad6828e4f2e2df579b3f084c17405324eee662d4 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/input/touchscreen/iqs5xx.c b/drivers/input/touchscreen/iqs5xx.c index 0aa9d6492df8f8584f93a4cf743ee79253aeab17..b4768b66eb1012d6c669d7eaeb8848e1f91fc969 100644 --- a/drivers/input/touchscreen/iqs5xx.c +++ b/drivers/input/touchscreen/iqs5xx.c @@ -23,8 +23,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/drivers/input/touchscreen/iqs7211.c b/drivers/input/touchscreen/iqs7211.c new file mode 100644 index 0000000000000000000000000000000000000000..dc084f873762005b3c129cdf154f1feaa5b6758d --- /dev/null +++ b/drivers/input/touchscreen/iqs7211.c @@ -0,0 +1,2557 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller + * + * Copyright (C) 2023 Jeff LaBundy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IQS7211_PROD_NUM 0x00 + +#define IQS7211_EVENT_MASK_ALL GENMASK(14, 8) +#define IQS7211_EVENT_MASK_ALP BIT(13) +#define IQS7211_EVENT_MASK_BTN BIT(12) +#define IQS7211_EVENT_MASK_ATI BIT(11) +#define IQS7211_EVENT_MASK_MOVE BIT(10) +#define IQS7211_EVENT_MASK_GSTR BIT(9) +#define IQS7211_EVENT_MODE BIT(8) + +#define IQS7211_COMMS_ERROR 0xEEEE +#define IQS7211_COMMS_RETRY_MS 50 +#define IQS7211_COMMS_SLEEP_US 100 +#define IQS7211_COMMS_TIMEOUT_US (100 * USEC_PER_MSEC) +#define IQS7211_RESET_TIMEOUT_MS 150 +#define IQS7211_START_TIMEOUT_US (1 * USEC_PER_SEC) + +#define IQS7211_NUM_RETRIES 5 +#define IQS7211_NUM_CRX 8 +#define IQS7211_MAX_CTX 13 + +#define IQS7211_MAX_CONTACTS 2 +#define IQS7211_MAX_CYCLES 21 + +/* + * The following delay is used during instances that must wait for the open- + * drain RDY pin to settle. Its value is calculated as 5*R*C, where R and C + * represent typical datasheet values of 4.7k and 100 nF, respectively. + */ +#define iqs7211_irq_wait() usleep_range(2500, 2600) + +enum iqs7211_dev_id { + IQS7210A, + IQS7211A, + IQS7211E, +}; + +enum iqs7211_comms_mode { + IQS7211_COMMS_MODE_WAIT, + IQS7211_COMMS_MODE_FREE, + IQS7211_COMMS_MODE_FORCE, +}; + +struct iqs7211_reg_field_desc { + struct list_head list; + u8 addr; + u16 mask; + u16 val; +}; + +enum iqs7211_reg_key_id { + IQS7211_REG_KEY_NONE, + IQS7211_REG_KEY_PROX, + IQS7211_REG_KEY_TOUCH, + IQS7211_REG_KEY_TAP, + IQS7211_REG_KEY_HOLD, + IQS7211_REG_KEY_PALM, + IQS7211_REG_KEY_AXIAL_X, + IQS7211_REG_KEY_AXIAL_Y, + IQS7211_REG_KEY_RESERVED +}; + +enum iqs7211_reg_grp_id { + IQS7211_REG_GRP_TP, + IQS7211_REG_GRP_BTN, + IQS7211_REG_GRP_ALP, + IQS7211_REG_GRP_SYS, + IQS7211_NUM_REG_GRPS +}; + +static const char * const iqs7211_reg_grp_names[IQS7211_NUM_REG_GRPS] = { + [IQS7211_REG_GRP_TP] = "trackpad", + [IQS7211_REG_GRP_BTN] = "button", + [IQS7211_REG_GRP_ALP] = "alp", +}; + +static const u16 iqs7211_reg_grp_masks[IQS7211_NUM_REG_GRPS] = { + [IQS7211_REG_GRP_TP] = IQS7211_EVENT_MASK_GSTR, + [IQS7211_REG_GRP_BTN] = IQS7211_EVENT_MASK_BTN, + [IQS7211_REG_GRP_ALP] = IQS7211_EVENT_MASK_ALP, +}; + +struct iqs7211_event_desc { + const char *name; + u16 mask; + u16 enable; + enum iqs7211_reg_grp_id reg_grp; + enum iqs7211_reg_key_id reg_key; +}; + +static const struct iqs7211_event_desc iqs7210a_kp_events[] = { + { + .mask = BIT(10), + .enable = BIT(13) | BIT(12), + .reg_grp = IQS7211_REG_GRP_ALP, + }, + { + .name = "event-prox", + .mask = BIT(2), + .enable = BIT(5) | BIT(4), + .reg_grp = IQS7211_REG_GRP_BTN, + .reg_key = IQS7211_REG_KEY_PROX, + }, + { + .name = "event-touch", + .mask = BIT(3), + .enable = BIT(5) | BIT(4), + .reg_grp = IQS7211_REG_GRP_BTN, + .reg_key = IQS7211_REG_KEY_TOUCH, + }, + { + .name = "event-tap", + .mask = BIT(0), + .enable = BIT(0), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_TAP, + }, + { + .name = "event-hold", + .mask = BIT(1), + .enable = BIT(1), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, + { + .name = "event-swipe-x-neg", + .mask = BIT(2), + .enable = BIT(2), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_X, + }, + { + .name = "event-swipe-x-pos", + .mask = BIT(3), + .enable = BIT(3), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_X, + }, + { + .name = "event-swipe-y-pos", + .mask = BIT(4), + .enable = BIT(4), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + }, + { + .name = "event-swipe-y-neg", + .mask = BIT(5), + .enable = BIT(5), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + }, +}; + +static const struct iqs7211_event_desc iqs7211a_kp_events[] = { + { + .mask = BIT(14), + .reg_grp = IQS7211_REG_GRP_ALP, + }, + { + .name = "event-tap", + .mask = BIT(0), + .enable = BIT(0), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_TAP, + }, + { + .name = "event-hold", + .mask = BIT(1), + .enable = BIT(1), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, + { + .name = "event-swipe-x-neg", + .mask = BIT(2), + .enable = BIT(2), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_X, + }, + { + .name = "event-swipe-x-pos", + .mask = BIT(3), + .enable = BIT(3), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_X, + }, + { + .name = "event-swipe-y-pos", + .mask = BIT(4), + .enable = BIT(4), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + }, + { + .name = "event-swipe-y-neg", + .mask = BIT(5), + .enable = BIT(5), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + }, +}; + +static const struct iqs7211_event_desc iqs7211e_kp_events[] = { + { + .mask = BIT(14), + .reg_grp = IQS7211_REG_GRP_ALP, + }, + { + .name = "event-tap", + .mask = BIT(0), + .enable = BIT(0), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_TAP, + }, + { + .name = "event-tap-double", + .mask = BIT(1), + .enable = BIT(1), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_TAP, + }, + { + .name = "event-tap-triple", + .mask = BIT(2), + .enable = BIT(2), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_TAP, + }, + { + .name = "event-hold", + .mask = BIT(3), + .enable = BIT(3), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, + { + .name = "event-palm", + .mask = BIT(4), + .enable = BIT(4), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_PALM, + }, + { + .name = "event-swipe-x-pos", + .mask = BIT(8), + .enable = BIT(8), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_X, + }, + { + .name = "event-swipe-x-neg", + .mask = BIT(9), + .enable = BIT(9), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_X, + }, + { + .name = "event-swipe-y-pos", + .mask = BIT(10), + .enable = BIT(10), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + }, + { + .name = "event-swipe-y-neg", + .mask = BIT(11), + .enable = BIT(11), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + }, + { + .name = "event-swipe-x-pos-hold", + .mask = BIT(12), + .enable = BIT(12), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, + { + .name = "event-swipe-x-neg-hold", + .mask = BIT(13), + .enable = BIT(13), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, + { + .name = "event-swipe-y-pos-hold", + .mask = BIT(14), + .enable = BIT(14), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, + { + .name = "event-swipe-y-neg-hold", + .mask = BIT(15), + .enable = BIT(15), + .reg_grp = IQS7211_REG_GRP_TP, + .reg_key = IQS7211_REG_KEY_HOLD, + }, +}; + +struct iqs7211_dev_desc { + const char *tp_name; + const char *kp_name; + u16 prod_num; + u16 show_reset; + u16 ati_error[IQS7211_NUM_REG_GRPS]; + u16 ati_start[IQS7211_NUM_REG_GRPS]; + u16 suspend; + u16 ack_reset; + u16 comms_end; + u16 comms_req; + int charge_shift; + int info_offs; + int gesture_offs; + int contact_offs; + u8 sys_stat; + u8 sys_ctrl; + u8 alp_config; + u8 tp_config; + u8 exp_file; + u8 kp_enable[IQS7211_NUM_REG_GRPS]; + u8 gesture_angle; + u8 rx_tx_map; + u8 cycle_alloc[2]; + u8 cycle_limit[2]; + const struct iqs7211_event_desc *kp_events; + int num_kp_events; + int min_crx_alp; + int num_ctx; +}; + +static const struct iqs7211_dev_desc iqs7211_devs[] = { + [IQS7210A] = { + .tp_name = "iqs7210a_trackpad", + .kp_name = "iqs7210a_keys", + .prod_num = 944, + .show_reset = BIT(15), + .ati_error = { + [IQS7211_REG_GRP_TP] = BIT(12), + [IQS7211_REG_GRP_BTN] = BIT(0), + [IQS7211_REG_GRP_ALP] = BIT(8), + }, + .ati_start = { + [IQS7211_REG_GRP_TP] = BIT(13), + [IQS7211_REG_GRP_BTN] = BIT(1), + [IQS7211_REG_GRP_ALP] = BIT(9), + }, + .suspend = BIT(11), + .ack_reset = BIT(7), + .comms_end = BIT(2), + .comms_req = BIT(1), + .charge_shift = 4, + .info_offs = 0, + .gesture_offs = 1, + .contact_offs = 4, + .sys_stat = 0x0A, + .sys_ctrl = 0x35, + .alp_config = 0x39, + .tp_config = 0x4E, + .exp_file = 0x57, + .kp_enable = { + [IQS7211_REG_GRP_TP] = 0x58, + [IQS7211_REG_GRP_BTN] = 0x37, + [IQS7211_REG_GRP_ALP] = 0x37, + }, + .gesture_angle = 0x5F, + .rx_tx_map = 0x60, + .cycle_alloc = { 0x66, 0x75, }, + .cycle_limit = { 10, 6, }, + .kp_events = iqs7210a_kp_events, + .num_kp_events = ARRAY_SIZE(iqs7210a_kp_events), + .min_crx_alp = 4, + .num_ctx = IQS7211_MAX_CTX - 1, + }, + [IQS7211A] = { + .tp_name = "iqs7211a_trackpad", + .kp_name = "iqs7211a_keys", + .prod_num = 763, + .show_reset = BIT(7), + .ati_error = { + [IQS7211_REG_GRP_TP] = BIT(3), + [IQS7211_REG_GRP_ALP] = BIT(5), + }, + .ati_start = { + [IQS7211_REG_GRP_TP] = BIT(5), + [IQS7211_REG_GRP_ALP] = BIT(6), + }, + .ack_reset = BIT(7), + .comms_req = BIT(4), + .charge_shift = 0, + .info_offs = 0, + .gesture_offs = 1, + .contact_offs = 4, + .sys_stat = 0x10, + .sys_ctrl = 0x50, + .tp_config = 0x60, + .alp_config = 0x72, + .exp_file = 0x74, + .kp_enable = { + [IQS7211_REG_GRP_TP] = 0x80, + }, + .gesture_angle = 0x87, + .rx_tx_map = 0x90, + .cycle_alloc = { 0xA0, 0xB0, }, + .cycle_limit = { 10, 8, }, + .kp_events = iqs7211a_kp_events, + .num_kp_events = ARRAY_SIZE(iqs7211a_kp_events), + .num_ctx = IQS7211_MAX_CTX - 1, + }, + [IQS7211E] = { + .tp_name = "iqs7211e_trackpad", + .kp_name = "iqs7211e_keys", + .prod_num = 1112, + .show_reset = BIT(7), + .ati_error = { + [IQS7211_REG_GRP_TP] = BIT(3), + [IQS7211_REG_GRP_ALP] = BIT(5), + }, + .ati_start = { + [IQS7211_REG_GRP_TP] = BIT(5), + [IQS7211_REG_GRP_ALP] = BIT(6), + }, + .suspend = BIT(11), + .ack_reset = BIT(7), + .comms_end = BIT(6), + .comms_req = BIT(4), + .charge_shift = 0, + .info_offs = 1, + .gesture_offs = 0, + .contact_offs = 2, + .sys_stat = 0x0E, + .sys_ctrl = 0x33, + .tp_config = 0x41, + .alp_config = 0x36, + .exp_file = 0x4A, + .kp_enable = { + [IQS7211_REG_GRP_TP] = 0x4B, + }, + .gesture_angle = 0x55, + .rx_tx_map = 0x56, + .cycle_alloc = { 0x5D, 0x6C, }, + .cycle_limit = { 10, 11, }, + .kp_events = iqs7211e_kp_events, + .num_kp_events = ARRAY_SIZE(iqs7211e_kp_events), + .num_ctx = IQS7211_MAX_CTX, + }, +}; + +struct iqs7211_prop_desc { + const char *name; + enum iqs7211_reg_key_id reg_key; + u8 reg_addr[IQS7211_NUM_REG_GRPS][ARRAY_SIZE(iqs7211_devs)]; + int reg_shift; + int reg_width; + int val_pitch; + int val_min; + int val_max; + const char *label; +}; + +static const struct iqs7211_prop_desc iqs7211_props[] = { + { + .name = "azoteq,ati-frac-div-fine", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x1E, + [IQS7211A] = 0x30, + [IQS7211E] = 0x21, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x22, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x23, + [IQS7211A] = 0x36, + [IQS7211E] = 0x25, + }, + }, + .reg_shift = 9, + .reg_width = 5, + .label = "ATI fine fractional divider", + }, + { + .name = "azoteq,ati-frac-mult-coarse", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x1E, + [IQS7211A] = 0x30, + [IQS7211E] = 0x21, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x22, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x23, + [IQS7211A] = 0x36, + [IQS7211E] = 0x25, + }, + }, + .reg_shift = 5, + .reg_width = 4, + .label = "ATI coarse fractional multiplier", + }, + { + .name = "azoteq,ati-frac-div-coarse", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x1E, + [IQS7211A] = 0x30, + [IQS7211E] = 0x21, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x22, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x23, + [IQS7211A] = 0x36, + [IQS7211E] = 0x25, + }, + }, + .reg_shift = 0, + .reg_width = 5, + .label = "ATI coarse fractional divider", + }, + { + .name = "azoteq,ati-comp-div", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x1F, + [IQS7211E] = 0x22, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x24, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7211E] = 0x26, + }, + }, + .reg_shift = 0, + .reg_width = 8, + .val_max = 31, + .label = "ATI compensation divider", + }, + { + .name = "azoteq,ati-comp-div", + .reg_addr = { + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x24, + }, + }, + .reg_shift = 8, + .reg_width = 8, + .val_max = 31, + .label = "ATI compensation divider", + }, + { + .name = "azoteq,ati-comp-div", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7211A] = 0x31, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7211A] = 0x37, + }, + }, + .val_max = 31, + .label = "ATI compensation divider", + }, + { + .name = "azoteq,ati-target", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x20, + [IQS7211A] = 0x32, + [IQS7211E] = 0x23, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x27, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x28, + [IQS7211A] = 0x38, + [IQS7211E] = 0x27, + }, + }, + .label = "ATI target", + }, + { + .name = "azoteq,ati-base", + .reg_addr[IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x26, + }, + .reg_shift = 8, + .reg_width = 8, + .val_pitch = 8, + .label = "ATI base", + }, + { + .name = "azoteq,ati-base", + .reg_addr[IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x26, + }, + .reg_shift = 0, + .reg_width = 8, + .val_pitch = 8, + .label = "ATI base", + }, + { + .name = "azoteq,rate-active-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x29, + [IQS7211A] = 0x40, + [IQS7211E] = 0x28, + }, + .label = "active mode report rate", + }, + { + .name = "azoteq,rate-touch-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x2A, + [IQS7211A] = 0x41, + [IQS7211E] = 0x29, + }, + .label = "idle-touch mode report rate", + }, + { + .name = "azoteq,rate-idle-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x2B, + [IQS7211A] = 0x42, + [IQS7211E] = 0x2A, + }, + .label = "idle mode report rate", + }, + { + .name = "azoteq,rate-lp1-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x2C, + [IQS7211A] = 0x43, + [IQS7211E] = 0x2B, + }, + .label = "low-power mode 1 report rate", + }, + { + .name = "azoteq,rate-lp2-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x2D, + [IQS7211A] = 0x44, + [IQS7211E] = 0x2C, + }, + .label = "low-power mode 2 report rate", + }, + { + .name = "azoteq,timeout-active-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x2E, + [IQS7211A] = 0x45, + [IQS7211E] = 0x2D, + }, + .val_pitch = 1000, + .label = "active mode timeout", + }, + { + .name = "azoteq,timeout-touch-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x2F, + [IQS7211A] = 0x46, + [IQS7211E] = 0x2E, + }, + .val_pitch = 1000, + .label = "idle-touch mode timeout", + }, + { + .name = "azoteq,timeout-idle-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x30, + [IQS7211A] = 0x47, + [IQS7211E] = 0x2F, + }, + .val_pitch = 1000, + .label = "idle mode timeout", + }, + { + .name = "azoteq,timeout-lp1-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x31, + [IQS7211A] = 0x48, + [IQS7211E] = 0x30, + }, + .val_pitch = 1000, + .label = "low-power mode 1 timeout", + }, + { + .name = "azoteq,timeout-lp2-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x32, + [IQS7211E] = 0x31, + }, + .reg_shift = 8, + .reg_width = 8, + .val_pitch = 1000, + .val_max = 60000, + .label = "trackpad reference value update rate", + }, + { + .name = "azoteq,timeout-lp2-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7211A] = 0x49, + }, + .val_pitch = 1000, + .val_max = 60000, + .label = "trackpad reference value update rate", + }, + { + .name = "azoteq,timeout-ati-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x32, + [IQS7211E] = 0x31, + }, + .reg_width = 8, + .val_pitch = 1000, + .val_max = 60000, + .label = "ATI error timeout", + }, + { + .name = "azoteq,timeout-ati-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7211A] = 0x35, + }, + .val_pitch = 1000, + .val_max = 60000, + .label = "ATI error timeout", + }, + { + .name = "azoteq,timeout-comms-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x33, + [IQS7211A] = 0x4A, + [IQS7211E] = 0x32, + }, + .label = "communication timeout", + }, + { + .name = "azoteq,timeout-press-ms", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x34, + }, + .reg_width = 8, + .val_pitch = 1000, + .val_max = 60000, + .label = "press timeout", + }, + { + .name = "azoteq,ati-mode", + .reg_addr[IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x37, + }, + .reg_shift = 15, + .reg_width = 1, + .label = "ATI mode", + }, + { + .name = "azoteq,ati-mode", + .reg_addr[IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x37, + }, + .reg_shift = 7, + .reg_width = 1, + .label = "ATI mode", + }, + { + .name = "azoteq,sense-mode", + .reg_addr[IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x37, + [IQS7211A] = 0x72, + [IQS7211E] = 0x36, + }, + .reg_shift = 8, + .reg_width = 1, + .label = "sensing mode", + }, + { + .name = "azoteq,sense-mode", + .reg_addr[IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x37, + }, + .reg_shift = 0, + .reg_width = 2, + .val_max = 2, + .label = "sensing mode", + }, + { + .name = "azoteq,fosc-freq", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x38, + [IQS7211A] = 0x52, + [IQS7211E] = 0x35, + }, + .reg_shift = 4, + .reg_width = 1, + .label = "core clock frequency selection", + }, + { + .name = "azoteq,fosc-trim", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x38, + [IQS7211A] = 0x52, + [IQS7211E] = 0x35, + }, + .reg_shift = 0, + .reg_width = 4, + .label = "core clock frequency trim", + }, + { + .name = "azoteq,touch-exit", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x3B, + [IQS7211A] = 0x53, + [IQS7211E] = 0x38, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x3E, + }, + }, + .reg_shift = 8, + .reg_width = 8, + .label = "touch exit factor", + }, + { + .name = "azoteq,touch-enter", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x3B, + [IQS7211A] = 0x53, + [IQS7211E] = 0x38, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x3E, + }, + }, + .reg_shift = 0, + .reg_width = 8, + .label = "touch entrance factor", + }, + { + .name = "azoteq,thresh", + .reg_addr = { + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x3C, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x3D, + [IQS7211A] = 0x54, + [IQS7211E] = 0x39, + }, + }, + .label = "threshold", + }, + { + .name = "azoteq,debounce-exit", + .reg_addr = { + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x3F, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x40, + [IQS7211A] = 0x56, + [IQS7211E] = 0x3A, + }, + }, + .reg_shift = 8, + .reg_width = 8, + .label = "debounce exit factor", + }, + { + .name = "azoteq,debounce-enter", + .reg_addr = { + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x3F, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x40, + [IQS7211A] = 0x56, + [IQS7211E] = 0x3A, + }, + }, + .reg_shift = 0, + .reg_width = 8, + .label = "debounce entrance factor", + }, + { + .name = "azoteq,conv-frac", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x48, + [IQS7211A] = 0x58, + [IQS7211E] = 0x3D, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x49, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x4A, + [IQS7211A] = 0x59, + [IQS7211E] = 0x3E, + }, + }, + .reg_shift = 8, + .reg_width = 8, + .label = "conversion frequency fractional divider", + }, + { + .name = "azoteq,conv-period", + .reg_addr = { + [IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x48, + [IQS7211A] = 0x58, + [IQS7211E] = 0x3D, + }, + [IQS7211_REG_GRP_BTN] = { + [IQS7210A] = 0x49, + }, + [IQS7211_REG_GRP_ALP] = { + [IQS7210A] = 0x4A, + [IQS7211A] = 0x59, + [IQS7211E] = 0x3E, + }, + }, + .reg_shift = 0, + .reg_width = 8, + .label = "conversion period", + }, + { + .name = "azoteq,thresh", + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x55, + [IQS7211A] = 0x67, + [IQS7211E] = 0x48, + }, + .reg_shift = 0, + .reg_width = 8, + .label = "threshold", + }, + { + .name = "azoteq,contact-split", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x55, + [IQS7211A] = 0x67, + [IQS7211E] = 0x48, + }, + .reg_shift = 8, + .reg_width = 8, + .label = "contact split factor", + }, + { + .name = "azoteq,trim-x", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x56, + [IQS7211E] = 0x49, + }, + .reg_shift = 0, + .reg_width = 8, + .label = "horizontal trim width", + }, + { + .name = "azoteq,trim-x", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7211A] = 0x68, + }, + .label = "horizontal trim width", + }, + { + .name = "azoteq,trim-y", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7210A] = 0x56, + [IQS7211E] = 0x49, + }, + .reg_shift = 8, + .reg_width = 8, + .label = "vertical trim height", + }, + { + .name = "azoteq,trim-y", + .reg_addr[IQS7211_REG_GRP_SYS] = { + [IQS7211A] = 0x69, + }, + .label = "vertical trim height", + }, + { + .name = "azoteq,gesture-max-ms", + .reg_key = IQS7211_REG_KEY_TAP, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x59, + [IQS7211A] = 0x81, + [IQS7211E] = 0x4C, + }, + .label = "maximum gesture time", + }, + { + .name = "azoteq,gesture-mid-ms", + .reg_key = IQS7211_REG_KEY_TAP, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7211E] = 0x4D, + }, + .label = "repeated gesture time", + }, + { + .name = "azoteq,gesture-dist", + .reg_key = IQS7211_REG_KEY_TAP, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5A, + [IQS7211A] = 0x82, + [IQS7211E] = 0x4E, + }, + .label = "gesture distance", + }, + { + .name = "azoteq,gesture-dist", + .reg_key = IQS7211_REG_KEY_HOLD, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5A, + [IQS7211A] = 0x82, + [IQS7211E] = 0x4E, + }, + .label = "gesture distance", + }, + { + .name = "azoteq,gesture-min-ms", + .reg_key = IQS7211_REG_KEY_HOLD, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5B, + [IQS7211A] = 0x83, + [IQS7211E] = 0x4F, + }, + .label = "minimum gesture time", + }, + { + .name = "azoteq,gesture-max-ms", + .reg_key = IQS7211_REG_KEY_AXIAL_X, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5C, + [IQS7211A] = 0x84, + [IQS7211E] = 0x50, + }, + .label = "maximum gesture time", + }, + { + .name = "azoteq,gesture-max-ms", + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5C, + [IQS7211A] = 0x84, + [IQS7211E] = 0x50, + }, + .label = "maximum gesture time", + }, + { + .name = "azoteq,gesture-dist", + .reg_key = IQS7211_REG_KEY_AXIAL_X, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5D, + [IQS7211A] = 0x85, + [IQS7211E] = 0x51, + }, + .label = "gesture distance", + }, + { + .name = "azoteq,gesture-dist", + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7210A] = 0x5E, + [IQS7211A] = 0x86, + [IQS7211E] = 0x52, + }, + .label = "gesture distance", + }, + { + .name = "azoteq,gesture-dist-rep", + .reg_key = IQS7211_REG_KEY_AXIAL_X, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7211E] = 0x53, + }, + .label = "repeated gesture distance", + }, + { + .name = "azoteq,gesture-dist-rep", + .reg_key = IQS7211_REG_KEY_AXIAL_Y, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7211E] = 0x54, + }, + .label = "repeated gesture distance", + }, + { + .name = "azoteq,thresh", + .reg_key = IQS7211_REG_KEY_PALM, + .reg_addr[IQS7211_REG_GRP_TP] = { + [IQS7211E] = 0x55, + }, + .reg_shift = 8, + .reg_width = 8, + .val_max = 42, + .label = "threshold", + }, +}; + +static const u8 iqs7211_gesture_angle[] = { + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, + 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x14, 0x15, 0x16, + 0x17, 0x19, 0x1A, 0x1B, + 0x1C, 0x1E, 0x1F, 0x21, + 0x22, 0x23, 0x25, 0x26, + 0x28, 0x2A, 0x2B, 0x2D, + 0x2E, 0x30, 0x32, 0x34, + 0x36, 0x38, 0x3A, 0x3C, + 0x3E, 0x40, 0x42, 0x45, + 0x47, 0x4A, 0x4C, 0x4F, + 0x52, 0x55, 0x58, 0x5B, + 0x5F, 0x63, 0x66, 0x6B, + 0x6F, 0x73, 0x78, 0x7E, + 0x83, 0x89, 0x90, 0x97, + 0x9E, 0xA7, 0xB0, 0xBA, + 0xC5, 0xD1, 0xDF, 0xEF, +}; + +struct iqs7211_ver_info { + __le16 prod_num; + __le16 major; + __le16 minor; + __le32 patch; +} __packed; + +struct iqs7211_touch_data { + __le16 abs_x; + __le16 abs_y; + __le16 pressure; + __le16 area; +} __packed; + +struct iqs7211_tp_config { + u8 tp_settings; + u8 total_rx; + u8 total_tx; + u8 num_contacts; + __le16 max_x; + __le16 max_y; +} __packed; + +struct iqs7211_private { + const struct iqs7211_dev_desc *dev_desc; + struct gpio_desc *reset_gpio; + struct gpio_desc *irq_gpio; + struct i2c_client *client; + struct input_dev *tp_idev; + struct input_dev *kp_idev; + struct iqs7211_ver_info ver_info; + struct iqs7211_tp_config tp_config; + struct touchscreen_properties prop; + struct list_head reg_field_head; + enum iqs7211_comms_mode comms_init; + enum iqs7211_comms_mode comms_mode; + unsigned int num_contacts; + unsigned int kp_code[ARRAY_SIZE(iqs7211e_kp_events)]; + u8 rx_tx_map[IQS7211_MAX_CTX + 1]; + u8 cycle_alloc[2][33]; + u8 exp_file[2]; + u16 event_mask; + u16 ati_start; + u16 gesture_cache; +}; + +static int iqs7211_irq_poll(struct iqs7211_private *iqs7211, u64 timeout_us) +{ + int error, val; + + error = readx_poll_timeout(gpiod_get_value_cansleep, iqs7211->irq_gpio, + val, val, IQS7211_COMMS_SLEEP_US, timeout_us); + + return val < 0 ? val : error; +} + +static int iqs7211_hard_reset(struct iqs7211_private *iqs7211) +{ + if (!iqs7211->reset_gpio) + return 0; + + gpiod_set_value_cansleep(iqs7211->reset_gpio, 1); + + /* + * The following delay ensures the shared RDY/MCLR pin is sampled in + * between periodic assertions by the device and assumes the default + * communication timeout has not been overwritten in OTP memory. + */ + if (iqs7211->reset_gpio == iqs7211->irq_gpio) + msleep(IQS7211_RESET_TIMEOUT_MS); + else + usleep_range(1000, 1100); + + gpiod_set_value_cansleep(iqs7211->reset_gpio, 0); + if (iqs7211->reset_gpio == iqs7211->irq_gpio) + iqs7211_irq_wait(); + + return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US); +} + +static int iqs7211_force_comms(struct iqs7211_private *iqs7211) +{ + u8 msg_buf[] = { 0xFF, }; + int ret; + + switch (iqs7211->comms_mode) { + case IQS7211_COMMS_MODE_WAIT: + return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US); + + case IQS7211_COMMS_MODE_FREE: + return 0; + + case IQS7211_COMMS_MODE_FORCE: + break; + + default: + return -EINVAL; + } + + /* + * The device cannot communicate until it asserts its interrupt (RDY) + * pin. Attempts to do so while RDY is deasserted return an ACK; how- + * ever all write data is ignored, and all read data returns 0xEE. + * + * Unsolicited communication must be preceded by a special force com- + * munication command, after which the device eventually asserts its + * RDY pin and agrees to communicate. + * + * Regardless of whether communication is forced or the result of an + * interrupt, the device automatically deasserts its RDY pin once it + * detects an I2C stop condition, or a timeout expires. + */ + ret = gpiod_get_value_cansleep(iqs7211->irq_gpio); + if (ret < 0) + return ret; + else if (ret > 0) + return 0; + + ret = i2c_master_send(iqs7211->client, msg_buf, sizeof(msg_buf)); + if (ret < (int)sizeof(msg_buf)) { + if (ret >= 0) + ret = -EIO; + + msleep(IQS7211_COMMS_RETRY_MS); + return ret; + } + + iqs7211_irq_wait(); + + return iqs7211_irq_poll(iqs7211, IQS7211_COMMS_TIMEOUT_US); +} + +static int iqs7211_read_burst(struct iqs7211_private *iqs7211, + u8 reg, void *val, u16 val_len) +{ + int ret, i; + struct i2c_client *client = iqs7211->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(reg), + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = val_len, + .buf = (u8 *)val, + }, + }; + + /* + * The following loop protects against an edge case in which the RDY + * pin is automatically deasserted just as the read is initiated. In + * that case, the read must be retried using forced communication. + */ + for (i = 0; i < IQS7211_NUM_RETRIES; i++) { + ret = iqs7211_force_comms(iqs7211); + if (ret < 0) + continue; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret < (int)ARRAY_SIZE(msg)) { + if (ret >= 0) + ret = -EIO; + + msleep(IQS7211_COMMS_RETRY_MS); + continue; + } + + if (get_unaligned_le16(msg[1].buf) == IQS7211_COMMS_ERROR) { + ret = -ENODATA; + continue; + } + + ret = 0; + break; + } + + iqs7211_irq_wait(); + + if (ret < 0) + dev_err(&client->dev, + "Failed to read from address 0x%02X: %d\n", reg, ret); + + return ret; +} + +static int iqs7211_read_word(struct iqs7211_private *iqs7211, u8 reg, u16 *val) +{ + __le16 val_buf; + int error; + + error = iqs7211_read_burst(iqs7211, reg, &val_buf, sizeof(val_buf)); + if (error) + return error; + + *val = le16_to_cpu(val_buf); + + return 0; +} + +static int iqs7211_write_burst(struct iqs7211_private *iqs7211, + u8 reg, const void *val, u16 val_len) +{ + int msg_len = sizeof(reg) + val_len; + int ret, i; + struct i2c_client *client = iqs7211->client; + u8 *msg_buf; + + msg_buf = kzalloc(msg_len, GFP_KERNEL); + if (!msg_buf) + return -ENOMEM; + + *msg_buf = reg; + memcpy(msg_buf + sizeof(reg), val, val_len); + + /* + * The following loop protects against an edge case in which the RDY + * pin is automatically asserted just before the force communication + * command is sent. + * + * In that case, the subsequent I2C stop condition tricks the device + * into preemptively deasserting the RDY pin and the command must be + * sent again. + */ + for (i = 0; i < IQS7211_NUM_RETRIES; i++) { + ret = iqs7211_force_comms(iqs7211); + if (ret < 0) + continue; + + ret = i2c_master_send(client, msg_buf, msg_len); + if (ret < msg_len) { + if (ret >= 0) + ret = -EIO; + + msleep(IQS7211_COMMS_RETRY_MS); + continue; + } + + ret = 0; + break; + } + + kfree(msg_buf); + + iqs7211_irq_wait(); + + if (ret < 0) + dev_err(&client->dev, + "Failed to write to address 0x%02X: %d\n", reg, ret); + + return ret; +} + +static int iqs7211_write_word(struct iqs7211_private *iqs7211, u8 reg, u16 val) +{ + __le16 val_buf = cpu_to_le16(val); + + return iqs7211_write_burst(iqs7211, reg, &val_buf, sizeof(val_buf)); +} + +static int iqs7211_start_comms(struct iqs7211_private *iqs7211) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct i2c_client *client = iqs7211->client; + bool forced_comms; + unsigned int val; + u16 comms_setup; + int error; + + /* + * Until forced communication can be enabled, the host must wait for a + * communication window each time it intends to elicit a response from + * the device. + * + * Forced communication is not necessary, however, if the host adapter + * can support clock stretching. In that case, the device freely clock + * stretches until all pending conversions are complete. + */ + forced_comms = device_property_present(&client->dev, + "azoteq,forced-comms"); + + error = device_property_read_u32(&client->dev, + "azoteq,forced-comms-default", &val); + if (error == -EINVAL) { + iqs7211->comms_init = IQS7211_COMMS_MODE_WAIT; + } else if (error) { + dev_err(&client->dev, + "Failed to read default communication mode: %d\n", + error); + return error; + } else if (val) { + iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_FORCE + : IQS7211_COMMS_MODE_WAIT; + } else { + iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_WAIT + : IQS7211_COMMS_MODE_FREE; + } + + iqs7211->comms_mode = iqs7211->comms_init; + + error = iqs7211_hard_reset(iqs7211); + if (error) { + dev_err(&client->dev, "Failed to reset device: %d\n", error); + return error; + } + + error = iqs7211_read_burst(iqs7211, IQS7211_PROD_NUM, + &iqs7211->ver_info, + sizeof(iqs7211->ver_info)); + if (error) + return error; + + if (le16_to_cpu(iqs7211->ver_info.prod_num) != dev_desc->prod_num) { + dev_err(&client->dev, "Invalid product number: %u\n", + le16_to_cpu(iqs7211->ver_info.prod_num)); + return -EINVAL; + } + + error = iqs7211_read_word(iqs7211, dev_desc->sys_ctrl + 1, + &comms_setup); + if (error) + return error; + + if (forced_comms) + comms_setup |= dev_desc->comms_req; + else + comms_setup &= ~dev_desc->comms_req; + + error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1, + comms_setup | dev_desc->comms_end); + if (error) + return error; + + if (forced_comms) + iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE; + else + iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE; + + error = iqs7211_read_burst(iqs7211, dev_desc->exp_file, + iqs7211->exp_file, + sizeof(iqs7211->exp_file)); + if (error) + return error; + + error = iqs7211_read_burst(iqs7211, dev_desc->tp_config, + &iqs7211->tp_config, + sizeof(iqs7211->tp_config)); + if (error) + return error; + + error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1, + comms_setup); + if (error) + return error; + + iqs7211->event_mask = comms_setup & ~IQS7211_EVENT_MASK_ALL; + iqs7211->event_mask |= (IQS7211_EVENT_MASK_ATI | IQS7211_EVENT_MODE); + + return 0; +} + +static int iqs7211_init_device(struct iqs7211_private *iqs7211) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct iqs7211_reg_field_desc *reg_field; + __le16 sys_ctrl[] = { + cpu_to_le16(dev_desc->ack_reset), + cpu_to_le16(iqs7211->event_mask), + }; + int error, i; + + /* + * Acknowledge reset before writing any registers in case the device + * suffers a spurious reset during initialization. The communication + * mode is configured at this time as well. + */ + error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl, + sizeof(sys_ctrl)); + if (error) + return error; + + if (iqs7211->event_mask & dev_desc->comms_req) + iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE; + else + iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE; + + /* + * Take advantage of the stop-bit disable function, if available, to + * save the trouble of having to reopen a communication window after + * each read or write. + */ + error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1, + iqs7211->event_mask | dev_desc->comms_end); + if (error) + return error; + + list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) { + u16 new_val = reg_field->val; + + if (reg_field->mask < U16_MAX) { + u16 old_val; + + error = iqs7211_read_word(iqs7211, reg_field->addr, + &old_val); + if (error) + return error; + + new_val = old_val & ~reg_field->mask; + new_val |= reg_field->val; + + if (new_val == old_val) + continue; + } + + error = iqs7211_write_word(iqs7211, reg_field->addr, new_val); + if (error) + return error; + } + + error = iqs7211_write_burst(iqs7211, dev_desc->tp_config, + &iqs7211->tp_config, + sizeof(iqs7211->tp_config)); + if (error) + return error; + + if (**iqs7211->cycle_alloc) { + error = iqs7211_write_burst(iqs7211, dev_desc->rx_tx_map, + &iqs7211->rx_tx_map, + dev_desc->num_ctx); + if (error) + return error; + + for (i = 0; i < sizeof(dev_desc->cycle_limit); i++) { + error = iqs7211_write_burst(iqs7211, + dev_desc->cycle_alloc[i], + iqs7211->cycle_alloc[i], + dev_desc->cycle_limit[i] * 3); + if (error) + return error; + } + } + + *sys_ctrl = cpu_to_le16(iqs7211->ati_start); + + return iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl, + sizeof(sys_ctrl)); +} + +static int iqs7211_add_field(struct iqs7211_private *iqs7211, + struct iqs7211_reg_field_desc new_field) +{ + struct i2c_client *client = iqs7211->client; + struct iqs7211_reg_field_desc *reg_field; + + if (!new_field.addr) + return 0; + + list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) { + if (reg_field->addr != new_field.addr) + continue; + + reg_field->mask |= new_field.mask; + reg_field->val |= new_field.val; + return 0; + } + + reg_field = devm_kzalloc(&client->dev, sizeof(*reg_field), GFP_KERNEL); + if (!reg_field) + return -ENOMEM; + + reg_field->addr = new_field.addr; + reg_field->mask = new_field.mask; + reg_field->val = new_field.val; + + list_add(®_field->list, &iqs7211->reg_field_head); + + return 0; +} + +static int iqs7211_parse_props(struct iqs7211_private *iqs7211, + struct fwnode_handle *reg_grp_node, + enum iqs7211_reg_grp_id reg_grp, + enum iqs7211_reg_key_id reg_key) +{ + struct i2c_client *client = iqs7211->client; + int i; + + for (i = 0; i < ARRAY_SIZE(iqs7211_props); i++) { + const char *name = iqs7211_props[i].name; + u8 reg_addr = iqs7211_props[i].reg_addr[reg_grp] + [iqs7211->dev_desc - + iqs7211_devs]; + int reg_shift = iqs7211_props[i].reg_shift; + int reg_width = iqs7211_props[i].reg_width ? : 16; + int val_pitch = iqs7211_props[i].val_pitch ? : 1; + int val_min = iqs7211_props[i].val_min; + int val_max = iqs7211_props[i].val_max; + const char *label = iqs7211_props[i].label ? : name; + struct iqs7211_reg_field_desc reg_field; + unsigned int val; + int error; + + if (iqs7211_props[i].reg_key != reg_key) + continue; + + if (!reg_addr) + continue; + + error = fwnode_property_read_u32(reg_grp_node, name, &val); + if (error == -EINVAL) { + continue; + } else if (error) { + dev_err(&client->dev, "Failed to read %s %s: %d\n", + fwnode_get_name(reg_grp_node), label, error); + return error; + } + + if (!val_max) + val_max = GENMASK(reg_width - 1, 0) * val_pitch; + + if (val < val_min || val > val_max) { + dev_err(&client->dev, "Invalid %s: %u\n", label, val); + return -EINVAL; + } + + reg_field.addr = reg_addr; + reg_field.mask = GENMASK(reg_shift + reg_width - 1, reg_shift); + reg_field.val = val / val_pitch << reg_shift; + + error = iqs7211_add_field(iqs7211, reg_field); + if (error) + return error; + } + + return 0; +} + +static int iqs7211_parse_event(struct iqs7211_private *iqs7211, + struct fwnode_handle *event_node, + enum iqs7211_reg_grp_id reg_grp, + enum iqs7211_reg_key_id reg_key, + unsigned int *event_code) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct i2c_client *client = iqs7211->client; + struct iqs7211_reg_field_desc reg_field; + unsigned int val; + int error; + + error = iqs7211_parse_props(iqs7211, event_node, reg_grp, reg_key); + if (error) + return error; + + if (reg_key == IQS7211_REG_KEY_AXIAL_X || + reg_key == IQS7211_REG_KEY_AXIAL_Y) { + error = fwnode_property_read_u32(event_node, + "azoteq,gesture-angle", &val); + if (!error) { + if (val >= ARRAY_SIZE(iqs7211_gesture_angle)) { + dev_err(&client->dev, + "Invalid %s gesture angle: %u\n", + fwnode_get_name(event_node), val); + return -EINVAL; + } + + reg_field.addr = dev_desc->gesture_angle; + reg_field.mask = U8_MAX; + reg_field.val = iqs7211_gesture_angle[val]; + + error = iqs7211_add_field(iqs7211, reg_field); + if (error) + return error; + } else if (error != -EINVAL) { + dev_err(&client->dev, + "Failed to read %s gesture angle: %d\n", + fwnode_get_name(event_node), error); + return error; + } + } + + error = fwnode_property_read_u32(event_node, "linux,code", event_code); + if (error == -EINVAL) + error = 0; + else if (error) + dev_err(&client->dev, "Failed to read %s code: %d\n", + fwnode_get_name(event_node), error); + + return error; +} + +static int iqs7211_parse_cycles(struct iqs7211_private *iqs7211, + struct fwnode_handle *tp_node) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct i2c_client *client = iqs7211->client; + int num_cycles = dev_desc->cycle_limit[0] + dev_desc->cycle_limit[1]; + int error, count, i, j, k, cycle_start; + unsigned int cycle_alloc[IQS7211_MAX_CYCLES][2]; + u8 total_rx = iqs7211->tp_config.total_rx; + u8 total_tx = iqs7211->tp_config.total_tx; + + for (i = 0; i < IQS7211_MAX_CYCLES * 2; i++) + *(cycle_alloc[0] + i) = U8_MAX; + + count = fwnode_property_count_u32(tp_node, "azoteq,channel-select"); + if (count == -EINVAL) { + /* + * Assign each sensing cycle's slots (0 and 1) to a channel, + * defined as the intersection between two CRx and CTx pins. + * A channel assignment of 255 means the slot is unused. + */ + for (i = 0, cycle_start = 0; i < total_tx; i++) { + int cycle_stop = 0; + + for (j = 0; j < total_rx; j++) { + /* + * Channels formed by CRx0-3 and CRx4-7 are + * bound to slots 0 and 1, respectively. + */ + int slot = iqs7211->rx_tx_map[j] < 4 ? 0 : 1; + int chan = i * total_rx + j; + + for (k = cycle_start; k < num_cycles; k++) { + if (cycle_alloc[k][slot] < U8_MAX) + continue; + + cycle_alloc[k][slot] = chan; + break; + } + + if (k < num_cycles) { + cycle_stop = max(k, cycle_stop); + continue; + } + + dev_err(&client->dev, + "Insufficient number of cycles\n"); + return -EINVAL; + } + + /* + * Sensing cycles cannot straddle more than one CTx + * pin. As such, the next row's starting cycle must + * be greater than the previous row's highest cycle. + */ + cycle_start = cycle_stop + 1; + } + } else if (count < 0) { + dev_err(&client->dev, "Failed to count channels: %d\n", count); + return count; + } else if (count > num_cycles * 2) { + dev_err(&client->dev, "Insufficient number of cycles\n"); + return -EINVAL; + } else if (count > 0) { + error = fwnode_property_read_u32_array(tp_node, + "azoteq,channel-select", + cycle_alloc[0], count); + if (error) { + dev_err(&client->dev, "Failed to read channels: %d\n", + error); + return error; + } + + for (i = 0; i < count; i++) { + int chan = *(cycle_alloc[0] + i); + + if (chan == U8_MAX) + continue; + + if (chan >= total_rx * total_tx) { + dev_err(&client->dev, "Invalid channel: %d\n", + chan); + return -EINVAL; + } + + for (j = 0; j < count; j++) { + if (j == i || *(cycle_alloc[0] + j) != chan) + continue; + + dev_err(&client->dev, "Duplicate channel: %d\n", + chan); + return -EINVAL; + } + } + } + + /* + * Once the raw channel assignments have been derived, they must be + * packed according to the device's register map. + */ + for (i = 0, cycle_start = 0; i < sizeof(dev_desc->cycle_limit); i++) { + int offs = 0; + + for (j = cycle_start; + j < cycle_start + dev_desc->cycle_limit[i]; j++) { + iqs7211->cycle_alloc[i][offs++] = 0x05; + iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][0]; + iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][1]; + } + + cycle_start += dev_desc->cycle_limit[i]; + } + + return 0; +} + +static int iqs7211_parse_tp(struct iqs7211_private *iqs7211, + struct fwnode_handle *tp_node) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct i2c_client *client = iqs7211->client; + unsigned int pins[IQS7211_MAX_CTX]; + int error, count, i, j; + + count = fwnode_property_count_u32(tp_node, "azoteq,rx-enable"); + if (count == -EINVAL) { + return 0; + } else if (count < 0) { + dev_err(&client->dev, "Failed to count CRx pins: %d\n", count); + return count; + } else if (count > IQS7211_NUM_CRX) { + dev_err(&client->dev, "Invalid number of CRx pins\n"); + return -EINVAL; + } + + error = fwnode_property_read_u32_array(tp_node, "azoteq,rx-enable", + pins, count); + if (error) { + dev_err(&client->dev, "Failed to read CRx pins: %d\n", error); + return error; + } + + for (i = 0; i < count; i++) { + if (pins[i] >= IQS7211_NUM_CRX) { + dev_err(&client->dev, "Invalid CRx pin: %u\n", pins[i]); + return -EINVAL; + } + + iqs7211->rx_tx_map[i] = pins[i]; + } + + iqs7211->tp_config.total_rx = count; + + count = fwnode_property_count_u32(tp_node, "azoteq,tx-enable"); + if (count < 0) { + dev_err(&client->dev, "Failed to count CTx pins: %d\n", count); + return count; + } else if (count > dev_desc->num_ctx) { + dev_err(&client->dev, "Invalid number of CTx pins\n"); + return -EINVAL; + } + + error = fwnode_property_read_u32_array(tp_node, "azoteq,tx-enable", + pins, count); + if (error) { + dev_err(&client->dev, "Failed to read CTx pins: %d\n", error); + return error; + } + + for (i = 0; i < count; i++) { + if (pins[i] >= dev_desc->num_ctx) { + dev_err(&client->dev, "Invalid CTx pin: %u\n", pins[i]); + return -EINVAL; + } + + for (j = 0; j < iqs7211->tp_config.total_rx; j++) { + if (iqs7211->rx_tx_map[j] != pins[i]) + continue; + + dev_err(&client->dev, "Conflicting CTx pin: %u\n", + pins[i]); + return -EINVAL; + } + + iqs7211->rx_tx_map[iqs7211->tp_config.total_rx + i] = pins[i]; + } + + iqs7211->tp_config.total_tx = count; + + return iqs7211_parse_cycles(iqs7211, tp_node); +} + +static int iqs7211_parse_alp(struct iqs7211_private *iqs7211, + struct fwnode_handle *alp_node) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct i2c_client *client = iqs7211->client; + struct iqs7211_reg_field_desc reg_field; + int error, count, i; + + count = fwnode_property_count_u32(alp_node, "azoteq,rx-enable"); + if (count < 0 && count != -EINVAL) { + dev_err(&client->dev, "Failed to count CRx pins: %d\n", count); + return count; + } else if (count > IQS7211_NUM_CRX) { + dev_err(&client->dev, "Invalid number of CRx pins\n"); + return -EINVAL; + } else if (count >= 0) { + unsigned int pins[IQS7211_NUM_CRX]; + + error = fwnode_property_read_u32_array(alp_node, + "azoteq,rx-enable", + pins, count); + if (error) { + dev_err(&client->dev, "Failed to read CRx pins: %d\n", + error); + return error; + } + + reg_field.addr = dev_desc->alp_config; + reg_field.mask = GENMASK(IQS7211_NUM_CRX - 1, 0); + reg_field.val = 0; + + for (i = 0; i < count; i++) { + if (pins[i] < dev_desc->min_crx_alp || + pins[i] >= IQS7211_NUM_CRX) { + dev_err(&client->dev, "Invalid CRx pin: %u\n", + pins[i]); + return -EINVAL; + } + + reg_field.val |= BIT(pins[i]); + } + + error = iqs7211_add_field(iqs7211, reg_field); + if (error) + return error; + } + + count = fwnode_property_count_u32(alp_node, "azoteq,tx-enable"); + if (count < 0 && count != -EINVAL) { + dev_err(&client->dev, "Failed to count CTx pins: %d\n", count); + return count; + } else if (count > dev_desc->num_ctx) { + dev_err(&client->dev, "Invalid number of CTx pins\n"); + return -EINVAL; + } else if (count >= 0) { + unsigned int pins[IQS7211_MAX_CTX]; + + error = fwnode_property_read_u32_array(alp_node, + "azoteq,tx-enable", + pins, count); + if (error) { + dev_err(&client->dev, "Failed to read CTx pins: %d\n", + error); + return error; + } + + reg_field.addr = dev_desc->alp_config + 1; + reg_field.mask = GENMASK(dev_desc->num_ctx - 1, 0); + reg_field.val = 0; + + for (i = 0; i < count; i++) { + if (pins[i] >= dev_desc->num_ctx) { + dev_err(&client->dev, "Invalid CTx pin: %u\n", + pins[i]); + return -EINVAL; + } + + reg_field.val |= BIT(pins[i]); + } + + error = iqs7211_add_field(iqs7211, reg_field); + if (error) + return error; + } + + return 0; +} + +static int (*iqs7211_parse_extra[IQS7211_NUM_REG_GRPS]) + (struct iqs7211_private *iqs7211, + struct fwnode_handle *reg_grp_node) = { + [IQS7211_REG_GRP_TP] = iqs7211_parse_tp, + [IQS7211_REG_GRP_ALP] = iqs7211_parse_alp, +}; + +static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211, + struct fwnode_handle *reg_grp_node, + enum iqs7211_reg_grp_id reg_grp) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct iqs7211_reg_field_desc reg_field; + int error, i; + + error = iqs7211_parse_props(iqs7211, reg_grp_node, reg_grp, + IQS7211_REG_KEY_NONE); + if (error) + return error; + + if (iqs7211_parse_extra[reg_grp]) { + error = iqs7211_parse_extra[reg_grp](iqs7211, reg_grp_node); + if (error) + return error; + } + + iqs7211->ati_start |= dev_desc->ati_start[reg_grp]; + + reg_field.addr = dev_desc->kp_enable[reg_grp]; + reg_field.mask = 0; + reg_field.val = 0; + + for (i = 0; i < dev_desc->num_kp_events; i++) { + const char *event_name = dev_desc->kp_events[i].name; + struct fwnode_handle *event_node; + + if (dev_desc->kp_events[i].reg_grp != reg_grp) + continue; + + reg_field.mask |= dev_desc->kp_events[i].enable; + + if (event_name) + event_node = fwnode_get_named_child_node(reg_grp_node, + event_name); + else + event_node = fwnode_handle_get(reg_grp_node); + + if (!event_node) + continue; + + error = iqs7211_parse_event(iqs7211, event_node, + dev_desc->kp_events[i].reg_grp, + dev_desc->kp_events[i].reg_key, + &iqs7211->kp_code[i]); + fwnode_handle_put(event_node); + if (error) + return error; + + reg_field.val |= dev_desc->kp_events[i].enable; + + iqs7211->event_mask |= iqs7211_reg_grp_masks[reg_grp]; + } + + return iqs7211_add_field(iqs7211, reg_field); +} + +static int iqs7211_register_kp(struct iqs7211_private *iqs7211) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct input_dev *kp_idev = iqs7211->kp_idev; + struct i2c_client *client = iqs7211->client; + int error, i; + + for (i = 0; i < dev_desc->num_kp_events; i++) + if (iqs7211->kp_code[i]) + break; + + if (i == dev_desc->num_kp_events) + return 0; + + kp_idev = devm_input_allocate_device(&client->dev); + if (!kp_idev) + return -ENOMEM; + + iqs7211->kp_idev = kp_idev; + + kp_idev->name = dev_desc->kp_name; + kp_idev->id.bustype = BUS_I2C; + + for (i = 0; i < dev_desc->num_kp_events; i++) + if (iqs7211->kp_code[i]) + input_set_capability(iqs7211->kp_idev, EV_KEY, + iqs7211->kp_code[i]); + + error = input_register_device(kp_idev); + if (error) + dev_err(&client->dev, "Failed to register %s: %d\n", + kp_idev->name, error); + + return error; +} + +static int iqs7211_register_tp(struct iqs7211_private *iqs7211) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct touchscreen_properties *prop = &iqs7211->prop; + struct input_dev *tp_idev = iqs7211->tp_idev; + struct i2c_client *client = iqs7211->client; + int error; + + error = device_property_read_u32(&client->dev, "azoteq,num-contacts", + &iqs7211->num_contacts); + if (error == -EINVAL) { + return 0; + } else if (error) { + dev_err(&client->dev, "Failed to read number of contacts: %d\n", + error); + return error; + } else if (iqs7211->num_contacts > IQS7211_MAX_CONTACTS) { + dev_err(&client->dev, "Invalid number of contacts: %u\n", + iqs7211->num_contacts); + return -EINVAL; + } + + iqs7211->tp_config.num_contacts = iqs7211->num_contacts ? : 1; + + if (!iqs7211->num_contacts) + return 0; + + iqs7211->event_mask |= IQS7211_EVENT_MASK_MOVE; + + tp_idev = devm_input_allocate_device(&client->dev); + if (!tp_idev) + return -ENOMEM; + + iqs7211->tp_idev = tp_idev; + + tp_idev->name = dev_desc->tp_name; + tp_idev->id.bustype = BUS_I2C; + + input_set_abs_params(tp_idev, ABS_MT_POSITION_X, + 0, le16_to_cpu(iqs7211->tp_config.max_x), 0, 0); + + input_set_abs_params(tp_idev, ABS_MT_POSITION_Y, + 0, le16_to_cpu(iqs7211->tp_config.max_y), 0, 0); + + input_set_abs_params(tp_idev, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0); + + touchscreen_parse_properties(tp_idev, true, prop); + + /* + * The device reserves 0xFFFF for coordinates that correspond to slots + * which are not in a state of touch. + */ + if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) { + dev_err(&client->dev, "Invalid trackpad size: %u*%u\n", + prop->max_x, prop->max_y); + return -EINVAL; + } + + iqs7211->tp_config.max_x = cpu_to_le16(prop->max_x); + iqs7211->tp_config.max_y = cpu_to_le16(prop->max_y); + + error = input_mt_init_slots(tp_idev, iqs7211->num_contacts, + INPUT_MT_DIRECT); + if (error) { + dev_err(&client->dev, "Failed to initialize slots: %d\n", + error); + return error; + } + + error = input_register_device(tp_idev); + if (error) + dev_err(&client->dev, "Failed to register %s: %d\n", + tp_idev->name, error); + + return error; +} + +static int iqs7211_report(struct iqs7211_private *iqs7211) +{ + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + struct i2c_client *client = iqs7211->client; + struct iqs7211_touch_data *touch_data; + u16 info_flags, charge_mode, gesture_flags; + __le16 status[12]; + int error, i; + + error = iqs7211_read_burst(iqs7211, dev_desc->sys_stat, status, + dev_desc->contact_offs * sizeof(__le16) + + iqs7211->num_contacts * sizeof(*touch_data)); + if (error) + return error; + + info_flags = le16_to_cpu(status[dev_desc->info_offs]); + + if (info_flags & dev_desc->show_reset) { + dev_err(&client->dev, "Unexpected device reset\n"); + + /* + * The device may or may not expect forced communication after + * it exits hardware reset, so the corresponding state machine + * must be reset as well. + */ + iqs7211->comms_mode = iqs7211->comms_init; + + return iqs7211_init_device(iqs7211); + } + + for (i = 0; i < ARRAY_SIZE(dev_desc->ati_error); i++) { + if (!(info_flags & dev_desc->ati_error[i])) + continue; + + dev_err(&client->dev, "Unexpected %s ATI error\n", + iqs7211_reg_grp_names[i]); + return 0; + } + + for (i = 0; i < iqs7211->num_contacts; i++) { + u16 pressure; + + touch_data = (struct iqs7211_touch_data *) + &status[dev_desc->contact_offs] + i; + pressure = le16_to_cpu(touch_data->pressure); + + input_mt_slot(iqs7211->tp_idev, i); + if (input_mt_report_slot_state(iqs7211->tp_idev, MT_TOOL_FINGER, + pressure != 0)) { + touchscreen_report_pos(iqs7211->tp_idev, &iqs7211->prop, + le16_to_cpu(touch_data->abs_x), + le16_to_cpu(touch_data->abs_y), + true); + input_report_abs(iqs7211->tp_idev, ABS_MT_PRESSURE, + pressure); + } + } + + if (iqs7211->num_contacts) { + input_mt_sync_frame(iqs7211->tp_idev); + input_sync(iqs7211->tp_idev); + } + + if (!iqs7211->kp_idev) + return 0; + + charge_mode = info_flags & GENMASK(dev_desc->charge_shift + 2, + dev_desc->charge_shift); + charge_mode >>= dev_desc->charge_shift; + + /* + * A charging mode higher than 2 (idle mode) indicates the device last + * operated in low-power mode and intends to express an ALP event. + */ + if (info_flags & dev_desc->kp_events->mask && charge_mode > 2) { + input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 1); + input_sync(iqs7211->kp_idev); + + input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 0); + } + + for (i = 0; i < dev_desc->num_kp_events; i++) { + if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_BTN) + continue; + + input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i], + info_flags & dev_desc->kp_events[i].mask); + } + + gesture_flags = le16_to_cpu(status[dev_desc->gesture_offs]); + + for (i = 0; i < dev_desc->num_kp_events; i++) { + enum iqs7211_reg_key_id reg_key = dev_desc->kp_events[i].reg_key; + u16 mask = dev_desc->kp_events[i].mask; + + if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_TP) + continue; + + if ((gesture_flags ^ iqs7211->gesture_cache) & mask) + input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i], + gesture_flags & mask); + + iqs7211->gesture_cache &= ~mask; + + /* + * Hold and palm gestures persist while the contact remains in + * place; all others are momentary and hence are followed by a + * complementary release event. + */ + if (reg_key == IQS7211_REG_KEY_HOLD || + reg_key == IQS7211_REG_KEY_PALM) { + iqs7211->gesture_cache |= gesture_flags & mask; + gesture_flags &= ~mask; + } + } + + if (gesture_flags) { + input_sync(iqs7211->kp_idev); + + for (i = 0; i < dev_desc->num_kp_events; i++) + if (dev_desc->kp_events[i].reg_grp == IQS7211_REG_GRP_TP && + gesture_flags & dev_desc->kp_events[i].mask) + input_report_key(iqs7211->kp_idev, + iqs7211->kp_code[i], 0); + } + + input_sync(iqs7211->kp_idev); + + return 0; +} + +static irqreturn_t iqs7211_irq(int irq, void *context) +{ + struct iqs7211_private *iqs7211 = context; + + return iqs7211_report(iqs7211) ? IRQ_NONE : IRQ_HANDLED; +} + +static int iqs7211_suspend(struct device *dev) +{ + struct iqs7211_private *iqs7211 = dev_get_drvdata(dev); + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + int error; + + if (!dev_desc->suspend || device_may_wakeup(dev)) + return 0; + + /* + * I2C communication prompts the device to assert its RDY pin if it is + * not already asserted. As such, the interrupt must be disabled so as + * to prevent reentrant interrupts. + */ + disable_irq(gpiod_to_irq(iqs7211->irq_gpio)); + + error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl, + dev_desc->suspend); + + enable_irq(gpiod_to_irq(iqs7211->irq_gpio)); + + return error; +} + +static int iqs7211_resume(struct device *dev) +{ + struct iqs7211_private *iqs7211 = dev_get_drvdata(dev); + const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc; + __le16 sys_ctrl[] = { + 0, + cpu_to_le16(iqs7211->event_mask), + }; + int error; + + if (!dev_desc->suspend || device_may_wakeup(dev)) + return 0; + + disable_irq(gpiod_to_irq(iqs7211->irq_gpio)); + + /* + * Forced communication, if in use, must be explicitly enabled as part + * of the wake-up command. + */ + error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl, + sizeof(sys_ctrl)); + + enable_irq(gpiod_to_irq(iqs7211->irq_gpio)); + + return error; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(iqs7211_pm, iqs7211_suspend, iqs7211_resume); + +static ssize_t fw_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iqs7211_private *iqs7211 = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u.%u.%u.%u:%u.%u\n", + le16_to_cpu(iqs7211->ver_info.prod_num), + le32_to_cpu(iqs7211->ver_info.patch), + le16_to_cpu(iqs7211->ver_info.major), + le16_to_cpu(iqs7211->ver_info.minor), + iqs7211->exp_file[1], iqs7211->exp_file[0]); +} + +static DEVICE_ATTR_RO(fw_info); + +static struct attribute *iqs7211_attrs[] = { + &dev_attr_fw_info.attr, + NULL +}; +ATTRIBUTE_GROUPS(iqs7211); + +static const struct of_device_id iqs7211_of_match[] = { + { + .compatible = "azoteq,iqs7210a", + .data = &iqs7211_devs[IQS7210A], + }, + { + .compatible = "azoteq,iqs7211a", + .data = &iqs7211_devs[IQS7211A], + }, + { + .compatible = "azoteq,iqs7211e", + .data = &iqs7211_devs[IQS7211E], + }, + { } +}; +MODULE_DEVICE_TABLE(of, iqs7211_of_match); + +static int iqs7211_probe(struct i2c_client *client) +{ + struct iqs7211_private *iqs7211; + enum iqs7211_reg_grp_id reg_grp; + unsigned long irq_flags; + bool shared_irq; + int error, irq; + + iqs7211 = devm_kzalloc(&client->dev, sizeof(*iqs7211), GFP_KERNEL); + if (!iqs7211) + return -ENOMEM; + + i2c_set_clientdata(client, iqs7211); + iqs7211->client = client; + + INIT_LIST_HEAD(&iqs7211->reg_field_head); + + iqs7211->dev_desc = device_get_match_data(&client->dev); + if (!iqs7211->dev_desc) + return -ENODEV; + + shared_irq = iqs7211->dev_desc->num_ctx == IQS7211_MAX_CTX; + + /* + * The RDY pin behaves as an interrupt, but must also be polled ahead + * of unsolicited I2C communication. As such, it is first opened as a + * GPIO and then passed to gpiod_to_irq() to register the interrupt. + * + * If an extra CTx pin is present, the RDY and MCLR pins are combined + * into a single bidirectional pin. In that case, the platform's GPIO + * must be configured as an open-drain output. + */ + iqs7211->irq_gpio = devm_gpiod_get(&client->dev, "irq", + shared_irq ? GPIOD_OUT_LOW + : GPIOD_IN); + if (IS_ERR(iqs7211->irq_gpio)) { + error = PTR_ERR(iqs7211->irq_gpio); + dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n", + error); + return error; + } + + if (shared_irq) { + iqs7211->reset_gpio = iqs7211->irq_gpio; + } else { + iqs7211->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(iqs7211->reset_gpio)) { + error = PTR_ERR(iqs7211->reset_gpio); + dev_err(&client->dev, + "Failed to request reset GPIO: %d\n", error); + return error; + } + } + + error = iqs7211_start_comms(iqs7211); + if (error) + return error; + + for (reg_grp = 0; reg_grp < IQS7211_NUM_REG_GRPS; reg_grp++) { + const char *reg_grp_name = iqs7211_reg_grp_names[reg_grp]; + struct fwnode_handle *reg_grp_node; + + if (reg_grp_name) + reg_grp_node = device_get_named_child_node(&client->dev, + reg_grp_name); + else + reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev)); + + if (!reg_grp_node) + continue; + + error = iqs7211_parse_reg_grp(iqs7211, reg_grp_node, reg_grp); + fwnode_handle_put(reg_grp_node); + if (error) + return error; + } + + error = iqs7211_register_kp(iqs7211); + if (error) + return error; + + error = iqs7211_register_tp(iqs7211); + if (error) + return error; + + error = iqs7211_init_device(iqs7211); + if (error) + return error; + + irq = gpiod_to_irq(iqs7211->irq_gpio); + if (irq < 0) + return irq; + + irq_flags = gpiod_is_active_low(iqs7211->irq_gpio) ? IRQF_TRIGGER_LOW + : IRQF_TRIGGER_HIGH; + irq_flags |= IRQF_ONESHOT; + + error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7211_irq, + irq_flags, client->name, iqs7211); + if (error) + dev_err(&client->dev, "Failed to request IRQ: %d\n", error); + + return error; +} + +static struct i2c_driver iqs7211_i2c_driver = { + .probe = iqs7211_probe, + .driver = { + .name = "iqs7211", + .of_match_table = iqs7211_of_match, + .dev_groups = iqs7211_groups, + .pm = pm_sleep_ptr(&iqs7211_pm), + }, +}; +module_i2c_driver(iqs7211_i2c_driver); + +MODULE_AUTHOR("Jeff LaBundy "); +MODULE_DESCRIPTION("Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c index 15b5cb763526a56d24d8c12949823e31f28bc7e6..9bad8b93c0395bd1435f216eae9ac68fa6b24e42 100644 --- a/drivers/input/touchscreen/lpc32xx_ts.c +++ b/drivers/input/touchscreen/lpc32xx_ts.c @@ -198,54 +198,36 @@ static void lpc32xx_ts_close(struct input_dev *dev) static int lpc32xx_ts_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct lpc32xx_tsc *tsc; struct input_dev *input; - struct resource *res; - resource_size_t size; int irq; int error; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Can't get memory resource\n"); - return -ENOENT; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; - tsc = kzalloc(sizeof(*tsc), GFP_KERNEL); - input = input_allocate_device(); - if (!tsc || !input) { - dev_err(&pdev->dev, "failed allocating memory\n"); - error = -ENOMEM; - goto err_free_mem; - } + tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); + if (!tsc) + return -ENOMEM; - tsc->dev = input; tsc->irq = irq; - size = resource_size(res); - - if (!request_mem_region(res->start, size, pdev->name)) { - dev_err(&pdev->dev, "TSC registers are not free\n"); - error = -EBUSY; - goto err_free_mem; - } + tsc->tsc_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tsc->tsc_base)) + return PTR_ERR(tsc->tsc_base); - tsc->tsc_base = ioremap(res->start, size); - if (!tsc->tsc_base) { - dev_err(&pdev->dev, "Can't map memory\n"); - error = -ENOMEM; - goto err_release_mem; - } - - tsc->clk = clk_get(&pdev->dev, NULL); + tsc->clk = devm_clk_get(dev, NULL); if (IS_ERR(tsc->clk)) { dev_err(&pdev->dev, "failed getting clock\n"); - error = PTR_ERR(tsc->clk); - goto err_unmap; + return PTR_ERR(tsc->clk); + } + + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(&pdev->dev, "failed allocating input device\n"); + return -ENOMEM; } input->name = MOD_NAME; @@ -254,68 +236,33 @@ static int lpc32xx_ts_probe(struct platform_device *pdev) input->id.vendor = 0x0001; input->id.product = 0x0002; input->id.version = 0x0100; - input->dev.parent = &pdev->dev; input->open = lpc32xx_ts_open; input->close = lpc32xx_ts_close; - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_capability(input, EV_KEY, BTN_TOUCH); input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL, LPC32XX_TSC_MAX_XY_VAL, 0, 0); input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL, LPC32XX_TSC_MAX_XY_VAL, 0, 0); input_set_drvdata(input, tsc); + tsc->dev = input; - error = request_irq(tsc->irq, lpc32xx_ts_interrupt, - 0, pdev->name, tsc); + error = devm_request_irq(dev, tsc->irq, lpc32xx_ts_interrupt, + 0, pdev->name, tsc); if (error) { dev_err(&pdev->dev, "failed requesting interrupt\n"); - goto err_put_clock; + return error; } error = input_register_device(input); if (error) { dev_err(&pdev->dev, "failed registering input device\n"); - goto err_free_irq; + return error; } platform_set_drvdata(pdev, tsc); - device_init_wakeup(&pdev->dev, 1); - - return 0; - -err_free_irq: - free_irq(tsc->irq, tsc); -err_put_clock: - clk_put(tsc->clk); -err_unmap: - iounmap(tsc->tsc_base); -err_release_mem: - release_mem_region(res->start, size); -err_free_mem: - input_free_device(input); - kfree(tsc); - - return error; -} - -static int lpc32xx_ts_remove(struct platform_device *pdev) -{ - struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev); - struct resource *res; - - free_irq(tsc->irq, tsc); - - input_unregister_device(tsc->dev); - - clk_put(tsc->clk); - - iounmap(tsc->tsc_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - kfree(tsc); + device_init_wakeup(&pdev->dev, true); return 0; } @@ -384,7 +331,6 @@ MODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match); static struct platform_driver lpc32xx_ts_driver = { .probe = lpc32xx_ts_probe, - .remove = lpc32xx_ts_remove, .driver = { .name = MOD_NAME, .pm = LPC32XX_TS_PM_OPS, diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c index 32896e5085bdcfb7090856e1d78f7523b40704d8..2ac4483fbc2588b552ef9c76f40caa7f51978036 100644 --- a/drivers/input/touchscreen/melfas_mip4.c +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -1451,13 +1451,8 @@ static int mip4_probe(struct i2c_client *client) ts->gpio_ce = devm_gpiod_get_optional(&client->dev, "ce", GPIOD_OUT_LOW); - if (IS_ERR(ts->gpio_ce)) { - error = PTR_ERR(ts->gpio_ce); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get gpio: %d\n", error); - return error; - } + if (IS_ERR(ts->gpio_ce)) + return dev_err_probe(&client->dev, PTR_ERR(ts->gpio_ce), "Failed to get gpio\n"); error = mip4_power_on(ts); if (error) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index ac12494c7930d679af17e3a0c77df03fe107c50e..af233b6a16d9abf780285a71b543a952fac1711a 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +42,7 @@ /* Touchscreen absolute values */ #define MMS114_MAX_AREA 0xff +#define MMS114_MAX_TOUCHKEYS 15 #define MMS114_MAX_TOUCH 10 #define MMS114_EVENT_SIZE 8 #define MMS136_EVENT_SIZE 6 @@ -70,6 +70,9 @@ struct mms114_data { unsigned int contact_threshold; unsigned int moving_threshold; + u32 keycodes[MMS114_MAX_TOUCHKEYS]; + int num_keycodes; + /* Use cache data for mode control register(write only) */ u8 cache_mode_control; }; @@ -167,11 +170,6 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou return; } - if (touch->type != MMS114_TYPE_TOUCHSCREEN) { - dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type); - return; - } - id = touch->id - 1; x = touch->x_lo | touch->x_hi << 8; y = touch->y_lo | touch->y_hi << 8; @@ -191,9 +189,33 @@ static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *tou } } +static void mms114_process_touchkey(struct mms114_data *data, + struct mms114_touch *touch) +{ + struct i2c_client *client = data->client; + struct input_dev *input_dev = data->input_dev; + unsigned int keycode_id; + + if (touch->id == 0) + return; + + if (touch->id > data->num_keycodes) { + dev_err(&client->dev, "Wrong touch id for touchkey (%d)\n", + touch->id); + return; + } + + keycode_id = touch->id - 1; + dev_dbg(&client->dev, "keycode id: %d, pressed: %d\n", keycode_id, + touch->pressed); + + input_report_key(input_dev, data->keycodes[keycode_id], touch->pressed); +} + static irqreturn_t mms114_interrupt(int irq, void *dev_id) { struct mms114_data *data = dev_id; + struct i2c_client *client = data->client; struct input_dev *input_dev = data->input_dev; struct mms114_touch touch[MMS114_MAX_TOUCH]; int packet_size; @@ -223,8 +245,22 @@ static irqreturn_t mms114_interrupt(int irq, void *dev_id) if (error < 0) goto out; - for (index = 0; index < touch_size; index++) - mms114_process_mt(data, touch + index); + for (index = 0; index < touch_size; index++) { + switch (touch[index].type) { + case MMS114_TYPE_TOUCHSCREEN: + mms114_process_mt(data, touch + index); + break; + + case MMS114_TYPE_TOUCHKEY: + mms114_process_touchkey(data, touch + index); + break; + + default: + dev_err(&client->dev, "Wrong touch type (%d)\n", + touch[index].type); + break; + } + } input_mt_report_pointer_emulation(data->input_dev, true); input_sync(data->input_dev); @@ -446,6 +482,7 @@ static int mms114_probe(struct i2c_client *client) struct input_dev *input_dev; const void *match_data; int error; + int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "Not supported I2C adapter\n"); @@ -469,6 +506,42 @@ static int mms114_probe(struct i2c_client *client) data->type = (enum mms_type)match_data; + data->num_keycodes = device_property_count_u32(&client->dev, + "linux,keycodes"); + if (data->num_keycodes == -EINVAL) { + data->num_keycodes = 0; + } else if (data->num_keycodes < 0) { + dev_err(&client->dev, + "Unable to parse linux,keycodes property: %d\n", + data->num_keycodes); + return data->num_keycodes; + } else if (data->num_keycodes > MMS114_MAX_TOUCHKEYS) { + dev_warn(&client->dev, + "Found %d linux,keycodes but max is %d, ignoring the rest\n", + data->num_keycodes, MMS114_MAX_TOUCHKEYS); + data->num_keycodes = MMS114_MAX_TOUCHKEYS; + } + + if (data->num_keycodes > 0) { + error = device_property_read_u32_array(&client->dev, + "linux,keycodes", + data->keycodes, + data->num_keycodes); + if (error) { + dev_err(&client->dev, + "Unable to read linux,keycodes values: %d\n", + error); + return error; + } + + input_dev->keycode = data->keycodes; + input_dev->keycodemax = data->num_keycodes; + input_dev->keycodesize = sizeof(data->keycodes[0]); + for (i = 0; i < data->num_keycodes; i++) + input_set_capability(input_dev, + EV_KEY, data->keycodes[i]); + } + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); diff --git a/drivers/input/touchscreen/novatek-nvt-ts.c b/drivers/input/touchscreen/novatek-nvt-ts.c index 7f7d879aac6d55b6a265bce39faee4b8228c8398..1a797e410a3faeb324869a1620946627fc7335ef 100644 --- a/drivers/input/touchscreen/novatek-nvt-ts.c +++ b/drivers/input/touchscreen/novatek-nvt-ts.c @@ -1,9 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Driver for Novatek i2c touchscreen controller as found on - * the Acer Iconia One 7 B1-750 tablet. The Touchscreen controller - * model-number is unknown. Android calls this a "NVT-ts" touchscreen, - * but that may apply to other Novatek controller models too. + * Driver for Novatek NT11205 i2c touchscreen controller as found + * on the Acer Iconia One 7 B1-750 tablet. * * Copyright (c) 2023 Hans de Goede */ @@ -272,7 +270,7 @@ static int nvt_ts_probe(struct i2c_client *client) error = input_register_device(input); if (error) { - dev_err(dev, "failed to request irq: %d\n", error); + dev_err(dev, "failed to register input device: %d\n", error); return error; } @@ -296,6 +294,6 @@ static struct i2c_driver nvt_ts_driver = { module_i2c_driver(nvt_ts_driver); -MODULE_DESCRIPTION("Novatek NVT-ts touchscreen driver"); +MODULE_DESCRIPTION("Novatek NT11205 touchscreen driver"); MODULE_AUTHOR("Hans de Goede "); MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 554e179c2e487dbd0709b051e73cea7e32837868..4ede0687beb096565a88bd60fe3e36b55e258f43 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -13,8 +13,8 @@ #include #include #include -#include #include +#include #include #define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ @@ -515,41 +515,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client) input_set_drvdata(input, tsdata); tsdata->gpio_attb = devm_gpiod_get(dev, "attb", GPIOD_IN); - if (IS_ERR(tsdata->gpio_attb)) { - error = PTR_ERR(tsdata->gpio_attb); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to request ATTB gpio: %d\n", - error); - return error; - } + if (IS_ERR(tsdata->gpio_attb)) + return dev_err_probe(dev, PTR_ERR(tsdata->gpio_attb), + "Failed to request ATTB gpio\n"); tsdata->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(tsdata->gpio_reset)) { - error = PTR_ERR(tsdata->gpio_reset); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to request RESET gpio: %d\n", - error); - return error; - } + if (IS_ERR(tsdata->gpio_reset)) + return dev_err_probe(dev, PTR_ERR(tsdata->gpio_reset), + "Failed to request RESET gpio\n"); tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_HIGH); - if (IS_ERR(tsdata->gpio_wake)) { - error = PTR_ERR(tsdata->gpio_wake); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get wake gpio: %d\n", error); - return error; - } + if (IS_ERR(tsdata->gpio_wake)) + return dev_err_probe(dev, PTR_ERR(tsdata->gpio_wake), + "Failed to get wake gpio\n"); tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); - if (IS_ERR(tsdata->gpio_enable)) { - error = PTR_ERR(tsdata->gpio_enable); - if (error != -EPROBE_DEFER) - dev_err(dev, "Failed to get enable gpio: %d\n", error); - return error; - } + if (IS_ERR(tsdata->gpio_enable)) + return dev_err_probe(dev, PTR_ERR(tsdata->gpio_enable), + "Failed to get enable gpio\n"); if (tsdata->gpio_enable) msleep(100); diff --git a/drivers/input/touchscreen/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_i2c_ts.c index 76e7d62d587023c682edca7b73225f93de4e8b12..78dd3059d585ac21ecfd5d1fe502466b111ed984 100644 --- a/drivers/input/touchscreen/raydium_i2c_ts.c +++ b/drivers/input/touchscreen/raydium_i2c_ts.c @@ -1087,32 +1087,20 @@ static int raydium_i2c_probe(struct i2c_client *client) i2c_set_clientdata(client, ts); ts->avdd = devm_regulator_get(&client->dev, "avdd"); - if (IS_ERR(ts->avdd)) { - error = PTR_ERR(ts->avdd); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get 'avdd' regulator: %d\n", error); - return error; - } + if (IS_ERR(ts->avdd)) + return dev_err_probe(&client->dev, PTR_ERR(ts->avdd), + "Failed to get 'avdd' regulator\n"); ts->vccio = devm_regulator_get(&client->dev, "vccio"); - if (IS_ERR(ts->vccio)) { - error = PTR_ERR(ts->vccio); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get 'vccio' regulator: %d\n", error); - return error; - } + if (IS_ERR(ts->vccio)) + return dev_err_probe(&client->dev, PTR_ERR(ts->vccio), + "Failed to get 'vccio' regulator\n"); ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ts->reset_gpio)) { - error = PTR_ERR(ts->reset_gpio); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "failed to get reset gpio: %d\n", error); - return error; - } + if (IS_ERR(ts->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(ts->reset_gpio), + "Failed to get reset gpio\n"); error = raydium_i2c_power_on(ts); if (error) diff --git a/drivers/input/touchscreen/resistive-adc-touch.c b/drivers/input/touchscreen/resistive-adc-touch.c index 6f754a8d30b11776381b3fe78427c09226b98385..7e761ec73273b270ac49c0229d6fb7f6b81d1bf7 100644 --- a/drivers/input/touchscreen/resistive-adc-touch.c +++ b/drivers/input/touchscreen/resistive-adc-touch.c @@ -210,12 +210,8 @@ static int grts_probe(struct platform_device *pdev) /* get the channels from IIO device */ st->iio_chans = devm_iio_channel_get_all(dev); - if (IS_ERR(st->iio_chans)) { - error = PTR_ERR(st->iio_chans); - if (error != -EPROBE_DEFER) - dev_err(dev, "can't get iio channels.\n"); - return error; - } + if (IS_ERR(st->iio_chans)) + return dev_err_probe(dev, PTR_ERR(st->iio_chans), "can't get iio channels\n"); if (!device_property_present(dev, "io-channel-names")) return -ENODEV; diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index 9e28f962e059d43231a47e7c505a116d0f584e53..62f562ad50263c64af43303e03d2bb75b6b04d66 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -706,11 +706,9 @@ static int silead_ts_probe(struct i2c_client *client) /* Power GPIO pin */ data->gpio_power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); - if (IS_ERR(data->gpio_power)) { - if (PTR_ERR(data->gpio_power) != -EPROBE_DEFER) - dev_err(dev, "Shutdown GPIO request failed\n"); - return PTR_ERR(data->gpio_power); - } + if (IS_ERR(data->gpio_power)) + return dev_err_probe(dev, PTR_ERR(data->gpio_power), + "Shutdown GPIO request failed\n"); error = silead_ts_setup(client); if (error) diff --git a/drivers/input/touchscreen/sis_i2c.c b/drivers/input/touchscreen/sis_i2c.c index 426564d0fc39aed0b4cc10e5d6649cbc942fa24b..ed56cb546f393b37d0ed9b75716401a15981ae71 100644 --- a/drivers/input/touchscreen/sis_i2c.c +++ b/drivers/input/touchscreen/sis_i2c.c @@ -310,23 +310,15 @@ static int sis_ts_probe(struct i2c_client *client) ts->attn_gpio = devm_gpiod_get_optional(&client->dev, "attn", GPIOD_IN); - if (IS_ERR(ts->attn_gpio)) { - error = PTR_ERR(ts->attn_gpio); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get attention GPIO: %d\n", error); - return error; - } + if (IS_ERR(ts->attn_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(ts->attn_gpio), + "Failed to get attention GPIO\n"); ts->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ts->reset_gpio)) { - error = PTR_ERR(ts->reset_gpio); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, - "Failed to get reset GPIO: %d\n", error); - return error; - } + if (IS_ERR(ts->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(ts->reset_gpio), + "Failed to get reset GPIO\n"); sis_ts_reset(ts); diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c index 31d140248f2e96ee70d2c7900537c9b6678aaee8..7efbcd0fde4fc32021cefc9b3557e7c8ded25866 100644 --- a/drivers/input/touchscreen/surface3_spi.c +++ b/drivers/input/touchscreen/surface3_spi.c @@ -221,7 +221,6 @@ static void surface3_spi_power(struct surface3_ts_data *data, bool on) */ static int surface3_spi_get_gpio_config(struct surface3_ts_data *data) { - int error; struct device *dev; struct gpio_desc *gpiod; int i; @@ -231,15 +230,9 @@ static int surface3_spi_get_gpio_config(struct surface3_ts_data *data) /* Get the reset lines GPIO pin number */ for (i = 0; i < 2; i++) { gpiod = devm_gpiod_get_index(dev, NULL, i, GPIOD_OUT_LOW); - if (IS_ERR(gpiod)) { - error = PTR_ERR(gpiod); - if (error != -EPROBE_DEFER) - dev_err(dev, - "Failed to get power GPIO %d: %d\n", - i, - error); - return error; - } + if (IS_ERR(gpiod)) + return dev_err_probe(dev, PTR_ERR(gpiod), + "Failed to get power GPIO %d\n", i); data->gpiod_rst[i] = gpiod; } diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 0293c493bc79f0184111307aa4b8fd44fdc750a4..f5c5881cef6bc7600d2f6d4913da05f015c35ea1 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c @@ -323,13 +323,9 @@ static int sx8654_probe(struct i2c_client *client) sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(sx8654->gpio_reset)) { - error = PTR_ERR(sx8654->gpio_reset); - if (error != -EPROBE_DEFER) - dev_err(&client->dev, "unable to get reset-gpio: %d\n", - error); - return error; - } + if (IS_ERR(sx8654->gpio_reset)) + return dev_err_probe(&client->dev, PTR_ERR(sx8654->gpio_reset), + "unable to get reset-gpio\n"); dev_dbg(&client->dev, "got GPIO reset pin\n"); sx8654->data = device_get_match_data(&client->dev); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index decf2d24a11554eb96f6170337f4f867163a5457..9aa4e35fb4f5aaf083341f93703457ead0d1deb2 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h index 0c35018239cecfa2308a4e10c737f751289149ce..e2857109e966015e94efb919f28723c8ed151fa4 100644 --- a/drivers/iommu/amd/amd_iommu.h +++ b/drivers/iommu/amd/amd_iommu.h @@ -12,13 +12,14 @@ #include "amd_iommu_types.h" irqreturn_t amd_iommu_int_thread(int irq, void *data); +irqreturn_t amd_iommu_int_thread_evtlog(int irq, void *data); +irqreturn_t amd_iommu_int_thread_pprlog(int irq, void *data); +irqreturn_t amd_iommu_int_thread_galog(int irq, void *data); irqreturn_t amd_iommu_int_handler(int irq, void *data); void amd_iommu_apply_erratum_63(struct amd_iommu *iommu, u16 devid); void amd_iommu_restart_event_logging(struct amd_iommu *iommu); void amd_iommu_restart_ga_log(struct amd_iommu *iommu); -int amd_iommu_init_devices(void); -void amd_iommu_uninit_devices(void); -void amd_iommu_init_notifier(void); +void amd_iommu_restart_ppr_log(struct amd_iommu *iommu); void amd_iommu_set_rlookup_table(struct amd_iommu *iommu, u16 devid); #ifdef CONFIG_AMD_IOMMU_DEBUGFS diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h index dc1db6167927910939cdf9d5d749fdbeedf9e5bd..7dc30c2b56b302d8bd3cd129f410610d536059e6 100644 --- a/drivers/iommu/amd/amd_iommu_types.h +++ b/drivers/iommu/amd/amd_iommu_types.h @@ -120,10 +120,13 @@ #define PASID_MASK 0x0000ffff /* MMIO status bits */ -#define MMIO_STATUS_EVT_OVERFLOW_INT_MASK BIT(0) +#define MMIO_STATUS_EVT_OVERFLOW_MASK BIT(0) #define MMIO_STATUS_EVT_INT_MASK BIT(1) #define MMIO_STATUS_COM_WAIT_INT_MASK BIT(2) +#define MMIO_STATUS_EVT_RUN_MASK BIT(3) +#define MMIO_STATUS_PPR_OVERFLOW_MASK BIT(5) #define MMIO_STATUS_PPR_INT_MASK BIT(6) +#define MMIO_STATUS_PPR_RUN_MASK BIT(7) #define MMIO_STATUS_GALOG_RUN_MASK BIT(8) #define MMIO_STATUS_GALOG_OVERFLOW_MASK BIT(9) #define MMIO_STATUS_GALOG_INT_MASK BIT(10) @@ -381,15 +384,15 @@ */ #define DTE_FLAG_V BIT_ULL(0) #define DTE_FLAG_TV BIT_ULL(1) +#define DTE_FLAG_GIOV BIT_ULL(54) +#define DTE_FLAG_GV BIT_ULL(55) +#define DTE_GLX_SHIFT (56) +#define DTE_GLX_MASK (3) #define DTE_FLAG_IR BIT_ULL(61) #define DTE_FLAG_IW BIT_ULL(62) #define DTE_FLAG_IOTLB BIT_ULL(32) -#define DTE_FLAG_GIOV BIT_ULL(54) -#define DTE_FLAG_GV BIT_ULL(55) #define DTE_FLAG_MASK (0x3ffULL << 32) -#define DTE_GLX_SHIFT (56) -#define DTE_GLX_MASK (3) #define DEV_DOMID_MASK 0xffffULL #define DTE_GCR3_VAL_A(x) (((x) >> 12) & 0x00007ULL) @@ -702,12 +705,21 @@ struct amd_iommu { /* event buffer virtual address */ u8 *evt_buf; + /* Name for event log interrupt */ + unsigned char evt_irq_name[16]; + /* Base of the PPR log, if present */ u8 *ppr_log; + /* Name for PPR log interrupt */ + unsigned char ppr_irq_name[16]; + /* Base of the GA log, if present */ u8 *ga_log; + /* Name for GA log interrupt */ + unsigned char ga_irq_name[16]; + /* Tail of the GA log, if present */ u8 *ga_log_tail; diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c index ea0f1ab94178373094f9eff87a4512c7781b3e22..45efb7e5d725460b39de534c67b5fb5be0d31d1d 100644 --- a/drivers/iommu/amd/init.c +++ b/drivers/iommu/amd/init.c @@ -483,6 +483,10 @@ static void iommu_disable(struct amd_iommu *iommu) iommu_feature_disable(iommu, CONTROL_GALOG_EN); iommu_feature_disable(iommu, CONTROL_GAINT_EN); + /* Disable IOMMU PPR logging */ + iommu_feature_disable(iommu, CONTROL_PPRLOG_EN); + iommu_feature_disable(iommu, CONTROL_PPRINT_EN); + /* Disable IOMMU hardware itself */ iommu_feature_disable(iommu, CONTROL_IOMMU_EN); @@ -752,38 +756,62 @@ static int __init alloc_command_buffer(struct amd_iommu *iommu) return iommu->cmd_buf ? 0 : -ENOMEM; } +/* + * Interrupt handler has processed all pending events and adjusted head + * and tail pointer. Reset overflow mask and restart logging again. + */ +static void amd_iommu_restart_log(struct amd_iommu *iommu, const char *evt_type, + u8 cntrl_intr, u8 cntrl_log, + u32 status_run_mask, u32 status_overflow_mask) +{ + u32 status; + + status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); + if (status & status_run_mask) + return; + + pr_info_ratelimited("IOMMU %s log restarting\n", evt_type); + + iommu_feature_disable(iommu, cntrl_log); + iommu_feature_disable(iommu, cntrl_intr); + + writel(status_overflow_mask, iommu->mmio_base + MMIO_STATUS_OFFSET); + + iommu_feature_enable(iommu, cntrl_intr); + iommu_feature_enable(iommu, cntrl_log); +} + /* * This function restarts event logging in case the IOMMU experienced * an event log buffer overflow. */ void amd_iommu_restart_event_logging(struct amd_iommu *iommu) { - iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN); - iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN); + amd_iommu_restart_log(iommu, "Event", CONTROL_EVT_INT_EN, + CONTROL_EVT_LOG_EN, MMIO_STATUS_EVT_RUN_MASK, + MMIO_STATUS_EVT_OVERFLOW_MASK); } /* * This function restarts event logging in case the IOMMU experienced - * an GA log overflow. + * GA log overflow. */ void amd_iommu_restart_ga_log(struct amd_iommu *iommu) { - u32 status; - - status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); - if (status & MMIO_STATUS_GALOG_RUN_MASK) - return; - - pr_info_ratelimited("IOMMU GA Log restarting\n"); - - iommu_feature_disable(iommu, CONTROL_GALOG_EN); - iommu_feature_disable(iommu, CONTROL_GAINT_EN); - - writel(MMIO_STATUS_GALOG_OVERFLOW_MASK, - iommu->mmio_base + MMIO_STATUS_OFFSET); + amd_iommu_restart_log(iommu, "GA", CONTROL_GAINT_EN, + CONTROL_GALOG_EN, MMIO_STATUS_GALOG_RUN_MASK, + MMIO_STATUS_GALOG_OVERFLOW_MASK); +} - iommu_feature_enable(iommu, CONTROL_GAINT_EN); - iommu_feature_enable(iommu, CONTROL_GALOG_EN); +/* + * This function restarts ppr logging in case the IOMMU experienced + * PPR log overflow. + */ +void amd_iommu_restart_ppr_log(struct amd_iommu *iommu) +{ + amd_iommu_restart_log(iommu, "PPR", CONTROL_PPRINT_EN, + CONTROL_PPRLOG_EN, MMIO_STATUS_PPR_RUN_MASK, + MMIO_STATUS_PPR_OVERFLOW_MASK); } /* @@ -906,6 +934,8 @@ static void iommu_enable_ppr_log(struct amd_iommu *iommu) if (iommu->ppr_log == NULL) return; + iommu_feature_enable(iommu, CONTROL_PPR_EN); + entry = iommu_virt_to_phys(iommu->ppr_log) | PPR_LOG_SIZE_512; memcpy_toio(iommu->mmio_base + MMIO_PPR_LOG_OFFSET, @@ -916,7 +946,7 @@ static void iommu_enable_ppr_log(struct amd_iommu *iommu) writel(0x00, iommu->mmio_base + MMIO_PPR_TAIL_OFFSET); iommu_feature_enable(iommu, CONTROL_PPRLOG_EN); - iommu_feature_enable(iommu, CONTROL_PPR_EN); + iommu_feature_enable(iommu, CONTROL_PPRINT_EN); } static void __init free_ppr_log(struct amd_iommu *iommu) @@ -2311,6 +2341,7 @@ static int intcapxt_irqdomain_alloc(struct irq_domain *domain, unsigned int virq struct irq_data *irqd = irq_domain_get_irq_data(domain, i); irqd->chip = &intcapxt_controller; + irqd->hwirq = info->hwirq; irqd->chip_data = info->data; __irq_set_handler(i, handle_edge_irq, 0, "edge"); } @@ -2337,22 +2368,14 @@ static void intcapxt_unmask_irq(struct irq_data *irqd) xt.destid_0_23 = cfg->dest_apicid & GENMASK(23, 0); xt.destid_24_31 = cfg->dest_apicid >> 24; - /** - * Current IOMMU implementation uses the same IRQ for all - * 3 IOMMU interrupts. - */ - writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_EVT_OFFSET); - writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_PPR_OFFSET); - writeq(xt.capxt, iommu->mmio_base + MMIO_INTCAPXT_GALOG_OFFSET); + writeq(xt.capxt, iommu->mmio_base + irqd->hwirq); } static void intcapxt_mask_irq(struct irq_data *irqd) { struct amd_iommu *iommu = irqd->chip_data; - writeq(0, iommu->mmio_base + MMIO_INTCAPXT_EVT_OFFSET); - writeq(0, iommu->mmio_base + MMIO_INTCAPXT_PPR_OFFSET); - writeq(0, iommu->mmio_base + MMIO_INTCAPXT_GALOG_OFFSET); + writeq(0, iommu->mmio_base + irqd->hwirq); } @@ -2415,7 +2438,8 @@ static struct irq_domain *iommu_get_irqdomain(void) return iommu_irqdomain; } -static int iommu_setup_intcapxt(struct amd_iommu *iommu) +static int __iommu_setup_intcapxt(struct amd_iommu *iommu, const char *devname, + int hwirq, irq_handler_t thread_fn) { struct irq_domain *domain; struct irq_alloc_info info; @@ -2429,6 +2453,7 @@ static int iommu_setup_intcapxt(struct amd_iommu *iommu) init_irq_alloc_info(&info, NULL); info.type = X86_IRQ_ALLOC_TYPE_AMDVI; info.data = iommu; + info.hwirq = hwirq; irq = irq_domain_alloc_irqs(domain, 1, node, &info); if (irq < 0) { @@ -2437,7 +2462,7 @@ static int iommu_setup_intcapxt(struct amd_iommu *iommu) } ret = request_threaded_irq(irq, amd_iommu_int_handler, - amd_iommu_int_thread, 0, "AMD-Vi", iommu); + thread_fn, 0, devname, iommu); if (ret) { irq_domain_free_irqs(irq, 1); irq_domain_remove(domain); @@ -2447,6 +2472,37 @@ static int iommu_setup_intcapxt(struct amd_iommu *iommu) return 0; } +static int iommu_setup_intcapxt(struct amd_iommu *iommu) +{ + int ret; + + snprintf(iommu->evt_irq_name, sizeof(iommu->evt_irq_name), + "AMD-Vi%d-Evt", iommu->index); + ret = __iommu_setup_intcapxt(iommu, iommu->evt_irq_name, + MMIO_INTCAPXT_EVT_OFFSET, + amd_iommu_int_thread_evtlog); + if (ret) + return ret; + + snprintf(iommu->ppr_irq_name, sizeof(iommu->ppr_irq_name), + "AMD-Vi%d-PPR", iommu->index); + ret = __iommu_setup_intcapxt(iommu, iommu->ppr_irq_name, + MMIO_INTCAPXT_PPR_OFFSET, + amd_iommu_int_thread_pprlog); + if (ret) + return ret; + +#ifdef CONFIG_IRQ_REMAP + snprintf(iommu->ga_irq_name, sizeof(iommu->ga_irq_name), + "AMD-Vi%d-GA", iommu->index); + ret = __iommu_setup_intcapxt(iommu, iommu->ga_irq_name, + MMIO_INTCAPXT_GALOG_OFFSET, + amd_iommu_int_thread_galog); +#endif + + return ret; +} + static int iommu_init_irq(struct amd_iommu *iommu) { int ret; @@ -2472,8 +2528,6 @@ static int iommu_init_irq(struct amd_iommu *iommu) iommu_feature_enable(iommu, CONTROL_EVT_INT_EN); - if (iommu->ppr_log != NULL) - iommu_feature_enable(iommu, CONTROL_PPRINT_EN); return 0; } @@ -2889,8 +2943,6 @@ static void enable_iommus_vapic(void) static void enable_iommus(void) { early_enable_iommus(); - enable_iommus_vapic(); - enable_iommus_v2(); } static void disable_iommus(void) @@ -3154,6 +3206,13 @@ static int amd_iommu_enable_interrupts(void) goto out; } + /* + * Interrupt handler is ready to process interrupts. Enable + * PPR and GA log interrupt for all IOMMUs. + */ + enable_iommus_vapic(); + enable_iommus_v2(); + out: return ret; } @@ -3233,8 +3292,6 @@ static int __init state_next(void) register_syscore_ops(&amd_iommu_syscore_ops); ret = amd_iommu_init_pci(); init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT; - enable_iommus_vapic(); - enable_iommus_v2(); break; case IOMMU_PCI_INIT: ret = amd_iommu_enable_interrupts(); diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c index 7d7d3799199aed3a78219cef39f52f4ad0f3d06c..95bd7c25ba6f366b5db2582e8cb5318491cbb523 100644 --- a/drivers/iommu/amd/iommu.c +++ b/drivers/iommu/amd/iommu.c @@ -841,50 +841,27 @@ static inline void amd_iommu_set_pci_msi_domain(struct device *dev, struct amd_iommu *iommu) { } #endif /* !CONFIG_IRQ_REMAP */ -#define AMD_IOMMU_INT_MASK \ - (MMIO_STATUS_EVT_OVERFLOW_INT_MASK | \ - MMIO_STATUS_EVT_INT_MASK | \ - MMIO_STATUS_PPR_INT_MASK | \ - MMIO_STATUS_GALOG_OVERFLOW_MASK | \ - MMIO_STATUS_GALOG_INT_MASK) - -irqreturn_t amd_iommu_int_thread(int irq, void *data) +static void amd_iommu_handle_irq(void *data, const char *evt_type, + u32 int_mask, u32 overflow_mask, + void (*int_handler)(struct amd_iommu *), + void (*overflow_handler)(struct amd_iommu *)) { struct amd_iommu *iommu = (struct amd_iommu *) data; u32 status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); + u32 mask = int_mask | overflow_mask; - while (status & AMD_IOMMU_INT_MASK) { + while (status & mask) { /* Enable interrupt sources again */ - writel(AMD_IOMMU_INT_MASK, - iommu->mmio_base + MMIO_STATUS_OFFSET); + writel(mask, iommu->mmio_base + MMIO_STATUS_OFFSET); - if (status & MMIO_STATUS_EVT_INT_MASK) { - pr_devel("Processing IOMMU Event Log\n"); - iommu_poll_events(iommu); + if (int_handler) { + pr_devel("Processing IOMMU (ivhd%d) %s Log\n", + iommu->index, evt_type); + int_handler(iommu); } - if (status & MMIO_STATUS_PPR_INT_MASK) { - pr_devel("Processing IOMMU PPR Log\n"); - iommu_poll_ppr_log(iommu); - } - -#ifdef CONFIG_IRQ_REMAP - if (status & (MMIO_STATUS_GALOG_INT_MASK | - MMIO_STATUS_GALOG_OVERFLOW_MASK)) { - pr_devel("Processing IOMMU GA Log\n"); - iommu_poll_ga_log(iommu); - } - - if (status & MMIO_STATUS_GALOG_OVERFLOW_MASK) { - pr_info_ratelimited("IOMMU GA Log overflow\n"); - amd_iommu_restart_ga_log(iommu); - } -#endif - - if (status & MMIO_STATUS_EVT_OVERFLOW_INT_MASK) { - pr_info_ratelimited("IOMMU event log overflow\n"); - amd_iommu_restart_event_logging(iommu); - } + if ((status & overflow_mask) && overflow_handler) + overflow_handler(iommu); /* * Hardware bug: ERBT1312 @@ -901,6 +878,43 @@ irqreturn_t amd_iommu_int_thread(int irq, void *data) */ status = readl(iommu->mmio_base + MMIO_STATUS_OFFSET); } +} + +irqreturn_t amd_iommu_int_thread_evtlog(int irq, void *data) +{ + amd_iommu_handle_irq(data, "Evt", MMIO_STATUS_EVT_INT_MASK, + MMIO_STATUS_EVT_OVERFLOW_MASK, + iommu_poll_events, amd_iommu_restart_event_logging); + + return IRQ_HANDLED; +} + +irqreturn_t amd_iommu_int_thread_pprlog(int irq, void *data) +{ + amd_iommu_handle_irq(data, "PPR", MMIO_STATUS_PPR_INT_MASK, + MMIO_STATUS_PPR_OVERFLOW_MASK, + iommu_poll_ppr_log, amd_iommu_restart_ppr_log); + + return IRQ_HANDLED; +} + +irqreturn_t amd_iommu_int_thread_galog(int irq, void *data) +{ +#ifdef CONFIG_IRQ_REMAP + amd_iommu_handle_irq(data, "GA", MMIO_STATUS_GALOG_INT_MASK, + MMIO_STATUS_GALOG_OVERFLOW_MASK, + iommu_poll_ga_log, amd_iommu_restart_ga_log); +#endif + + return IRQ_HANDLED; +} + +irqreturn_t amd_iommu_int_thread(int irq, void *data) +{ + amd_iommu_int_thread_evtlog(irq, data); + amd_iommu_int_thread_pprlog(irq, data); + amd_iommu_int_thread_galog(irq, data); + return IRQ_HANDLED; } diff --git a/drivers/iommu/amd/iommu_v2.c b/drivers/iommu/amd/iommu_v2.c index 2596466cd5a60a346b36c0a0406626bd2a510a30..57c2fb1146e25f113ce7f782e8cdf1e63abd31c9 100644 --- a/drivers/iommu/amd/iommu_v2.c +++ b/drivers/iommu/amd/iommu_v2.c @@ -262,8 +262,8 @@ static void put_pasid_state(struct pasid_state *pasid_state) static void put_pasid_state_wait(struct pasid_state *pasid_state) { - refcount_dec(&pasid_state->count); - wait_event(pasid_state->wq, !refcount_read(&pasid_state->count)); + if (!refcount_dec_and_test(&pasid_state->count)) + wait_event(pasid_state->wq, !refcount_read(&pasid_state->count)); free_pasid_state(pasid_state); } @@ -327,6 +327,9 @@ static void free_pasid_states(struct device_state *dev_state) put_pasid_state(pasid_state); + /* Clear the pasid state so that the pasid can be re-used */ + clear_pasid_state(dev_state, pasid_state->pasid); + /* * This will call the mn_release function and * unbind the PASID diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index 8af64b57f0483e29d231bd5c6c47b647bb2ed814..0b89275084274659fb91f8dbd2567693bdf963dd 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -671,8 +671,7 @@ static int apple_dart_attach_dev(struct iommu_domain *domain, return ret; switch (domain->type) { - case IOMMU_DOMAIN_DMA: - case IOMMU_DOMAIN_UNMANAGED: + default: ret = apple_dart_domain_add_streams(dart_domain, cfg); if (ret) return ret; @@ -1276,7 +1275,7 @@ static __maybe_unused int apple_dart_resume(struct device *dev) return 0; } -DEFINE_SIMPLE_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dart_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dart_resume); static const struct of_device_id apple_dart_of_match[] = { { .compatible = "apple,t8103-dart", .data = &apple_dart_hw_t8103 }, diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c index dbc812a0e57e970b6f12e797adf1d93883681c76..8a16cd3ef487cad4d63e8ebfae6f775b7bef3825 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c @@ -80,7 +80,7 @@ arm_smmu_share_asid(struct mm_struct *mm, u16 asid) * be some overlap between use of both ASIDs, until we invalidate the * TLB. */ - arm_smmu_write_ctx_desc(smmu_domain, 0, cd); + arm_smmu_write_ctx_desc(smmu_domain, IOMMU_NO_PASID, cd); /* Invalidate TLB entries previously associated with that context */ arm_smmu_tlb_inv_asid(smmu, asid); @@ -186,6 +186,15 @@ static void arm_smmu_free_shared_cd(struct arm_smmu_ctx_desc *cd) } } +/* + * Cloned from the MAX_TLBI_OPS in arch/arm64/include/asm/tlbflush.h, this + * is used as a threshold to replace per-page TLBI commands to issue in the + * command queue with an address-space TLBI command, when SMMU w/o a range + * invalidation feature handles too many per-page TLBI commands, which will + * otherwise result in a soft lockup. + */ +#define CMDQ_MAX_TLBI_OPS (1 << (PAGE_SHIFT - 3)) + static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn, struct mm_struct *mm, unsigned long start, @@ -201,8 +210,13 @@ static void arm_smmu_mm_arch_invalidate_secondary_tlbs(struct mmu_notifier *mn, * range. So do a simple translation here by calculating size correctly. */ size = end - start; - if (size == ULONG_MAX) - size = 0; + if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_RANGE_INV)) { + if (size >= CMDQ_MAX_TLBI_OPS * PAGE_SIZE) + size = 0; + } else { + if (size == ULONG_MAX) + size = 0; + } if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_BTM)) { if (!size) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 9b0dc35056019e0fe095efeedbfc9eb7e320fa08..bd0a596f9863a32eb11316822d424ec6349de0ef 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -1059,7 +1059,7 @@ int arm_smmu_write_ctx_desc(struct arm_smmu_domain *smmu_domain, int ssid, /* * This function handles the following cases: * - * (1) Install primary CD, for normal DMA traffic (SSID = 0). + * (1) Install primary CD, for normal DMA traffic (SSID = IOMMU_NO_PASID = 0). * (2) Install a secondary CD, for SID+SSID traffic. * (3) Update ASID of a CD. Atomically write the first 64 bits of the * CD, then invalidate the old entry and mappings. @@ -1607,7 +1607,7 @@ static void arm_smmu_handle_ppr(struct arm_smmu_device *smmu, u64 *evt) sid = FIELD_GET(PRIQ_0_SID, evt[0]); ssv = FIELD_GET(PRIQ_0_SSID_V, evt[0]); - ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : 0; + ssid = ssv ? FIELD_GET(PRIQ_0_SSID, evt[0]) : IOMMU_NO_PASID; last = FIELD_GET(PRIQ_0_PRG_LAST, evt[0]); grpid = FIELD_GET(PRIQ_1_PRG_IDX, evt[1]); @@ -1748,7 +1748,7 @@ arm_smmu_atc_inv_to_cmd(int ssid, unsigned long iova, size_t size, */ *cmd = (struct arm_smmu_cmdq_ent) { .opcode = CMDQ_OP_ATC_INV, - .substream_valid = !!ssid, + .substream_valid = (ssid != IOMMU_NO_PASID), .atc.ssid = ssid, }; @@ -1795,7 +1795,7 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master) struct arm_smmu_cmdq_ent cmd; struct arm_smmu_cmdq_batch cmds; - arm_smmu_atc_inv_to_cmd(0, 0, 0, &cmd); + arm_smmu_atc_inv_to_cmd(IOMMU_NO_PASID, 0, 0, &cmd); cmds.num = 0; for (i = 0; i < master->num_streams; i++) { @@ -1875,7 +1875,7 @@ static void arm_smmu_tlb_inv_context(void *cookie) cmd.tlbi.vmid = smmu_domain->s2_cfg.vmid; arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd); } - arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0); + arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, 0, 0); } static void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd, @@ -1895,18 +1895,23 @@ static void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd, /* Get the leaf page size */ tg = __ffs(smmu_domain->domain.pgsize_bitmap); + num_pages = size >> tg; + /* Convert page size of 12,14,16 (log2) to 1,2,3 */ cmd->tlbi.tg = (tg - 10) / 2; /* - * Determine what level the granule is at. For non-leaf, io-pgtable - * assumes .tlb_flush_walk can invalidate multiple levels at once, - * so ignore the nominal last-level granule and leave TTL=0. + * Determine what level the granule is at. For non-leaf, both + * io-pgtable and SVA pass a nominal last-level granule because + * they don't know what level(s) actually apply, so ignore that + * and leave TTL=0. However for various errata reasons we still + * want to use a range command, so avoid the SVA corner case + * where both scale and num could be 0 as well. */ if (cmd->tlbi.leaf) cmd->tlbi.ttl = 4 - ((ilog2(granule) - 3) / (tg - 3)); - - num_pages = size >> tg; + else if ((num_pages & CMDQ_TLBI_RANGE_NUM_MAX) == 1) + num_pages++; } cmds.num = 0; @@ -1968,7 +1973,7 @@ static void arm_smmu_tlb_inv_range_domain(unsigned long iova, size_t size, * Unfortunately, this can't be leaf-only since we may have * zapped an entire table. */ - arm_smmu_atc_inv_domain(smmu_domain, 0, iova, size); + arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, iova, size); } void arm_smmu_tlb_inv_range_asid(unsigned long iova, size_t size, int asid, @@ -2055,24 +2060,6 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type) return &smmu_domain->domain; } -static int arm_smmu_bitmap_alloc(unsigned long *map, int span) -{ - int idx, size = 1 << span; - - do { - idx = find_first_zero_bit(map, size); - if (idx == size) - return -ENOSPC; - } while (test_and_set_bit(idx, map)); - - return idx; -} - -static void arm_smmu_bitmap_free(unsigned long *map, int idx) -{ - clear_bit(idx, map); -} - static void arm_smmu_domain_free(struct iommu_domain *domain) { struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); @@ -2093,7 +2080,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain) } else { struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg; if (cfg->vmid) - arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid); + ida_free(&smmu->vmid_map, cfg->vmid); } kfree(smmu_domain); @@ -2142,7 +2129,7 @@ static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain, * the master has been added to the devices list for this domain. * This isn't an issue because the STE hasn't been installed yet. */ - ret = arm_smmu_write_ctx_desc(smmu_domain, 0, &cfg->cd); + ret = arm_smmu_write_ctx_desc(smmu_domain, IOMMU_NO_PASID, &cfg->cd); if (ret) goto out_free_cd_tables; @@ -2167,7 +2154,9 @@ static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain, struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg; typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr; - vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits); + /* Reserve VMID 0 for stage-2 bypass STEs */ + vmid = ida_alloc_range(&smmu->vmid_map, 1, (1 << smmu->vmid_bits) - 1, + GFP_KERNEL); if (vmid < 0) return vmid; @@ -2328,7 +2317,7 @@ static void arm_smmu_enable_ats(struct arm_smmu_master *master) pdev = to_pci_dev(master->dev); atomic_inc(&smmu_domain->nr_ats_masters); - arm_smmu_atc_inv_domain(smmu_domain, 0, 0, 0); + arm_smmu_atc_inv_domain(smmu_domain, IOMMU_NO_PASID, 0, 0); if (pci_enable_ats(pdev, stu)) dev_err(master->dev, "Failed to enable ATS (STU %zu)\n", stu); } @@ -3098,8 +3087,8 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu) reg |= STRTAB_BASE_RA; smmu->strtab_cfg.strtab_base = reg; - /* Allocate the first VMID for stage-2 bypass STEs */ - set_bit(0, smmu->vmid_map); + ida_init(&smmu->vmid_map); + return 0; } @@ -3923,6 +3912,7 @@ static void arm_smmu_device_remove(struct platform_device *pdev) iommu_device_sysfs_remove(&smmu->iommu); arm_smmu_device_disable(smmu); iopf_queue_free(smmu->evtq.iopf); + ida_destroy(&smmu->vmid_map); } static void arm_smmu_device_shutdown(struct platform_device *pdev) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index dcab85698a4e257ae0ab07534727769797e1e0c3..9915850dd4dbf771981cb378155ef8fee47ab4d6 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -670,7 +670,7 @@ struct arm_smmu_device { #define ARM_SMMU_MAX_VMIDS (1 << 16) unsigned int vmid_bits; - DECLARE_BITMAP(vmid_map, ARM_SMMU_MAX_VMIDS); + struct ida vmid_map; unsigned int ssid_bits; unsigned int sid_bits; diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c index b5b14108e086a9ac107ea265ec90dcdad91b5d85..bb89d49adf8d2164a03d29be6f94c6252346f0ea 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom-debug.c @@ -3,7 +3,7 @@ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ -#include +#include #include #include diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c index c71afda79d6442f57026b945fd7ff8fe08cd35a4..7f52ac67495fd10467095b5e395782238d5368b9 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c @@ -251,10 +251,12 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { { .compatible = "qcom,sc7280-mss-pil" }, { .compatible = "qcom,sc8180x-mdss" }, { .compatible = "qcom,sc8280xp-mdss" }, - { .compatible = "qcom,sm8150-mdss" }, - { .compatible = "qcom,sm8250-mdss" }, { .compatible = "qcom,sdm845-mdss" }, { .compatible = "qcom,sdm845-mss-pil" }, + { .compatible = "qcom,sm6350-mdss" }, + { .compatible = "qcom,sm6375-mdss" }, + { .compatible = "qcom,sm8150-mdss" }, + { .compatible = "qcom,sm8250-mdss" }, { } }; @@ -528,6 +530,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = { { .compatible = "qcom,sm6125-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm6350-smmu-v2", .data = &qcom_smmu_v2_data }, { .compatible = "qcom,sm6350-smmu-500", .data = &qcom_smmu_500_impl0_data }, + { .compatible = "qcom,sm6375-smmu-v2", .data = &qcom_smmu_v2_data }, { .compatible = "qcom,sm6375-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm8150-smmu-500", .data = &qcom_smmu_500_impl0_data }, { .compatible = "qcom,sm8250-smmu-500", .data = &qcom_smmu_500_impl0_data }, diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c index a86acd76c1dfb43b3475a11d632011feedadfac4..d6d1a2a55cc0692fb02f0f58b901ac438c71604c 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c index a503ed758ec3029ed75c74ab7ef8c55e846991c1..775a3cbaff4ed0522fadb2db80fd2e00f6dc1acc 100644 --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c @@ -22,8 +22,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -51,14 +50,15 @@ struct qcom_iommu_dev { struct clk_bulk_data clks[CLK_NUM]; void __iomem *local_base; u32 sec_id; - u8 num_ctxs; - struct qcom_iommu_ctx *ctxs[]; /* indexed by asid-1 */ + u8 max_asid; + struct qcom_iommu_ctx *ctxs[]; /* indexed by asid */ }; struct qcom_iommu_ctx { struct device *dev; void __iomem *base; bool secure_init; + bool secured_ctx; u8 asid; /* asid and ctx bank # are 1:1 */ struct iommu_domain *domain; }; @@ -94,7 +94,7 @@ static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid struct qcom_iommu_dev *qcom_iommu = d->iommu; if (!qcom_iommu) return NULL; - return qcom_iommu->ctxs[asid - 1]; + return qcom_iommu->ctxs[asid]; } static inline void @@ -273,6 +273,19 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain, ctx->secure_init = true; } + /* Secured QSMMU-500/QSMMU-v2 contexts cannot be programmed */ + if (ctx->secured_ctx) { + ctx->domain = domain; + continue; + } + + /* Disable context bank before programming */ + iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0); + + /* Clear context bank fault address fault status registers */ + iommu_writel(ctx, ARM_SMMU_CB_FAR, 0); + iommu_writel(ctx, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT); + /* TTBRs */ iommu_writeq(ctx, ARM_SMMU_CB_TTBR0, pgtbl_cfg.arm_lpae_s1_cfg.ttbr | @@ -527,11 +540,10 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args) qcom_iommu = platform_get_drvdata(iommu_pdev); /* make sure the asid specified in dt is valid, so we don't have - * to sanity check this elsewhere, since 'asid - 1' is used to - * index into qcom_iommu->ctxs: + * to sanity check this elsewhere: */ - if (WARN_ON(asid < 1) || - WARN_ON(asid > qcom_iommu->num_ctxs)) { + if (WARN_ON(asid > qcom_iommu->max_asid) || + WARN_ON(qcom_iommu->ctxs[asid] == NULL)) { put_device(&iommu_pdev->dev); return -EINVAL; } @@ -617,7 +629,8 @@ static int qcom_iommu_sec_ptbl_init(struct device *dev) static int get_asid(const struct device_node *np) { - u32 reg; + u32 reg, val; + int asid; /* read the "reg" property directly to get the relative address * of the context bank, and calculate the asid from that: @@ -625,7 +638,17 @@ static int get_asid(const struct device_node *np) if (of_property_read_u32_index(np, "reg", 0, ®)) return -ENODEV; - return reg / 0x1000; /* context banks are 0x1000 apart */ + /* + * Context banks are 0x1000 apart but, in some cases, the ASID + * number doesn't match to this logic and needs to be passed + * from the DT configuration explicitly. + */ + if (!of_property_read_u32(np, "qcom,ctx-asid", &val)) + asid = val; + else + asid = reg / 0x1000; + + return asid; } static int qcom_iommu_ctx_probe(struct platform_device *pdev) @@ -633,7 +656,6 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev) struct qcom_iommu_ctx *ctx; struct device *dev = &pdev->dev; struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev->parent); - struct resource *res; int ret, irq; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); @@ -643,19 +665,22 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev) ctx->dev = dev; platform_set_drvdata(pdev, ctx); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->base = devm_ioremap_resource(dev, res); + ctx->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->base)) return PTR_ERR(ctx->base); irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENODEV; + return irq; + + if (of_device_is_compatible(dev->of_node, "qcom,msm-iommu-v2-sec")) + ctx->secured_ctx = true; /* clear IRQs before registering fault handler, just in case the * boot-loader left us a surprise: */ - iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR)); + if (!ctx->secured_ctx) + iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR)); ret = devm_request_irq(dev, irq, qcom_iommu_fault, @@ -677,7 +702,7 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev) dev_dbg(dev, "found asid %u\n", ctx->asid); - qcom_iommu->ctxs[ctx->asid - 1] = ctx; + qcom_iommu->ctxs[ctx->asid] = ctx; return 0; } @@ -689,12 +714,14 @@ static void qcom_iommu_ctx_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - qcom_iommu->ctxs[ctx->asid - 1] = NULL; + qcom_iommu->ctxs[ctx->asid] = NULL; } static const struct of_device_id ctx_of_match[] = { { .compatible = "qcom,msm-iommu-v1-ns" }, { .compatible = "qcom,msm-iommu-v1-sec" }, + { .compatible = "qcom,msm-iommu-v2-ns" }, + { .compatible = "qcom,msm-iommu-v2-sec" }, { /* sentinel */ } }; @@ -712,7 +739,8 @@ static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu) struct device_node *child; for_each_child_of_node(qcom_iommu->dev->of_node, child) { - if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec")) { + if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec") || + of_device_is_compatible(child, "qcom,msm-iommu-v2-sec")) { of_node_put(child); return true; } @@ -736,11 +764,11 @@ static int qcom_iommu_device_probe(struct platform_device *pdev) for_each_child_of_node(dev->of_node, child) max_asid = max(max_asid, get_asid(child)); - qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid), + qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid + 1), GFP_KERNEL); if (!qcom_iommu) return -ENOMEM; - qcom_iommu->num_ctxs = max_asid; + qcom_iommu->max_asid = max_asid; qcom_iommu->dev = dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -856,6 +884,7 @@ static const struct dev_pm_ops qcom_iommu_pm_ops = { static const struct of_device_id qcom_iommu_of_match[] = { { .compatible = "qcom,msm-iommu-v1" }, + { .compatible = "qcom,msm-iommu-v2" }, { /* sentinel */ } }; diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index e577241638350c2e7fd0f4b3eefedc631a4aeace..4b1a88f514c9c06151e54036b295df6d48e2d8ba 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -660,7 +660,7 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, { struct iommu_dma_cookie *cookie = domain->iova_cookie; struct iova_domain *iovad = &cookie->iovad; - unsigned long shift, iova_len, iova = 0; + unsigned long shift, iova_len, iova; if (cookie->type == IOMMU_DMA_MSI_COOKIE) { cookie->msi_iova += size; @@ -675,15 +675,29 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, if (domain->geometry.force_aperture) dma_limit = min(dma_limit, (u64)domain->geometry.aperture_end); - /* Try to get PCI devices a SAC address */ - if (dma_limit > DMA_BIT_MASK(32) && !iommu_dma_forcedac && dev_is_pci(dev)) + /* + * Try to use all the 32-bit PCI addresses first. The original SAC vs. + * DAC reasoning loses relevance with PCIe, but enough hardware and + * firmware bugs are still lurking out there that it's safest not to + * venture into the 64-bit space until necessary. + * + * If your device goes wrong after seeing the notice then likely either + * its driver is not setting DMA masks accurately, the hardware has + * some inherent bug in handling >32-bit addresses, or not all the + * expected address bits are wired up between the device and the IOMMU. + */ + if (dma_limit > DMA_BIT_MASK(32) && dev->iommu->pci_32bit_workaround) { iova = alloc_iova_fast(iovad, iova_len, DMA_BIT_MASK(32) >> shift, false); + if (iova) + goto done; - if (!iova) - iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift, - true); + dev->iommu->pci_32bit_workaround = false; + dev_notice(dev, "Using %d-bit DMA addresses\n", bits_per(dma_limit)); + } + iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift, true); +done: return (dma_addr_t)iova << shift; } diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h index 9427900092922c3f3faa02e5bdeb67b63708d9b6..c829f1f82a991c63d8901f65f25c73c2af2f1bb2 100644 --- a/drivers/iommu/dma-iommu.h +++ b/drivers/iommu/dma-iommu.h @@ -17,6 +17,10 @@ int iommu_dma_init_fq(struct iommu_domain *domain); void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list); extern bool iommu_dma_forcedac; +static inline void iommu_dma_set_pci_32bit_workaround(struct device *dev) +{ + dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac; +} #else /* CONFIG_IOMMU_DMA */ @@ -38,5 +42,9 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he { } +static inline void iommu_dma_set_pci_32bit_workaround(struct device *dev) +{ +} + #endif /* CONFIG_IOMMU_DMA */ #endif /* __DMA_IOMMU_H */ diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 9e6f78830ececa186949f0376dcfac3e64730d5b..3685ba90ec88e81baac849f1693f507e005f4a21 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -114,13 +114,17 @@ static inline unsigned long lvl_to_nr_pages(unsigned int lvl) /* VT-d pages must always be _smaller_ than MM pages. Otherwise things are never going to work. */ -static inline unsigned long mm_to_dma_pfn(unsigned long mm_pfn) +static inline unsigned long mm_to_dma_pfn_start(unsigned long mm_pfn) { return mm_pfn << (PAGE_SHIFT - VTD_PAGE_SHIFT); } +static inline unsigned long mm_to_dma_pfn_end(unsigned long mm_pfn) +{ + return ((mm_pfn + 1) << (PAGE_SHIFT - VTD_PAGE_SHIFT)) - 1; +} static inline unsigned long page_to_dma_pfn(struct page *pg) { - return mm_to_dma_pfn(page_to_pfn(pg)); + return mm_to_dma_pfn_start(page_to_pfn(pg)); } static inline unsigned long virt_to_dma_pfn(void *p) { @@ -878,7 +882,7 @@ void dmar_fault_dump_ptes(struct intel_iommu *iommu, u16 source_id, } /* For request-without-pasid, get the pasid from context entry */ if (intel_iommu_sm && pasid == IOMMU_PASID_INVALID) - pasid = PASID_RID2PASID; + pasid = IOMMU_NO_PASID; dir_index = pasid >> PASID_PDE_SHIFT; pde = &dir[dir_index]; @@ -1360,6 +1364,7 @@ domain_lookup_dev_info(struct dmar_domain *domain, static void domain_update_iotlb(struct dmar_domain *domain) { + struct dev_pasid_info *dev_pasid; struct device_domain_info *info; bool has_iotlb_device = false; unsigned long flags; @@ -1371,6 +1376,14 @@ static void domain_update_iotlb(struct dmar_domain *domain) break; } } + + list_for_each_entry(dev_pasid, &domain->dev_pasids, link_domain) { + info = dev_iommu_priv_get(dev_pasid->dev); + if (info->ats_enabled) { + has_iotlb_device = true; + break; + } + } domain->has_iotlb_device = has_iotlb_device; spin_unlock_irqrestore(&domain->lock, flags); } @@ -1450,12 +1463,13 @@ static void __iommu_flush_dev_iotlb(struct device_domain_info *info, qdep = info->ats_qdep; qi_flush_dev_iotlb(info->iommu, sid, info->pfsid, qdep, addr, mask); - quirk_extra_dev_tlb_flush(info, addr, mask, PASID_RID2PASID, qdep); + quirk_extra_dev_tlb_flush(info, addr, mask, IOMMU_NO_PASID, qdep); } static void iommu_flush_dev_iotlb(struct dmar_domain *domain, u64 addr, unsigned mask) { + struct dev_pasid_info *dev_pasid; struct device_domain_info *info; unsigned long flags; @@ -1465,6 +1479,36 @@ static void iommu_flush_dev_iotlb(struct dmar_domain *domain, spin_lock_irqsave(&domain->lock, flags); list_for_each_entry(info, &domain->devices, link) __iommu_flush_dev_iotlb(info, addr, mask); + + list_for_each_entry(dev_pasid, &domain->dev_pasids, link_domain) { + info = dev_iommu_priv_get(dev_pasid->dev); + + if (!info->ats_enabled) + continue; + + qi_flush_dev_iotlb_pasid(info->iommu, + PCI_DEVID(info->bus, info->devfn), + info->pfsid, dev_pasid->pasid, + info->ats_qdep, addr, + mask); + } + spin_unlock_irqrestore(&domain->lock, flags); +} + +static void domain_flush_pasid_iotlb(struct intel_iommu *iommu, + struct dmar_domain *domain, u64 addr, + unsigned long npages, bool ih) +{ + u16 did = domain_id_iommu(domain, iommu); + struct dev_pasid_info *dev_pasid; + unsigned long flags; + + spin_lock_irqsave(&domain->lock, flags); + list_for_each_entry(dev_pasid, &domain->dev_pasids, link_domain) + qi_flush_piotlb(iommu, did, dev_pasid->pasid, addr, npages, ih); + + if (!list_empty(&domain->devices)) + qi_flush_piotlb(iommu, did, IOMMU_NO_PASID, addr, npages, ih); spin_unlock_irqrestore(&domain->lock, flags); } @@ -1485,7 +1529,7 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, ih = 1 << 6; if (domain->use_first_level) { - qi_flush_piotlb(iommu, did, PASID_RID2PASID, addr, pages, ih); + domain_flush_pasid_iotlb(iommu, domain, addr, pages, ih); } else { unsigned long bitmask = aligned_pages - 1; @@ -1555,7 +1599,7 @@ static void intel_flush_iotlb_all(struct iommu_domain *domain) u16 did = domain_id_iommu(dmar_domain, iommu); if (dmar_domain->use_first_level) - qi_flush_piotlb(iommu, did, PASID_RID2PASID, 0, -1, 0); + domain_flush_pasid_iotlb(iommu, dmar_domain, 0, -1, 0); else iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH); @@ -1727,6 +1771,7 @@ static struct dmar_domain *alloc_domain(unsigned int type) domain->use_first_level = true; domain->has_iotlb_device = false; INIT_LIST_HEAD(&domain->devices); + INIT_LIST_HEAD(&domain->dev_pasids); spin_lock_init(&domain->lock); xa_init(&domain->iommu_array); @@ -1941,7 +1986,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, context_pdts(pds); /* Setup the RID_PASID field: */ - context_set_sm_rid2pasid(context, PASID_RID2PASID); + context_set_sm_rid2pasid(context, IOMMU_NO_PASID); /* * Setup the Device-TLB enable bit and Page request @@ -2363,8 +2408,8 @@ static int __init si_domain_init(int hw) for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, NULL) { ret = iommu_domain_identity_map(si_domain, - mm_to_dma_pfn(start_pfn), - mm_to_dma_pfn(end_pfn)); + mm_to_dma_pfn_start(start_pfn), + mm_to_dma_pfn_end(end_pfn)); if (ret) return ret; } @@ -2385,8 +2430,8 @@ static int __init si_domain_init(int hw) continue; ret = iommu_domain_identity_map(si_domain, - mm_to_dma_pfn(start >> PAGE_SHIFT), - mm_to_dma_pfn(end >> PAGE_SHIFT)); + mm_to_dma_pfn_start(start >> PAGE_SHIFT), + mm_to_dma_pfn_end(end >> PAGE_SHIFT)); if (ret) return ret; } @@ -2421,13 +2466,13 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, /* Setup the PASID entry for requests without PASID: */ if (hw_pass_through && domain_type_is_si(domain)) ret = intel_pasid_setup_pass_through(iommu, domain, - dev, PASID_RID2PASID); + dev, IOMMU_NO_PASID); else if (domain->use_first_level) ret = domain_setup_first_level(iommu, domain, dev, - PASID_RID2PASID); + IOMMU_NO_PASID); else ret = intel_pasid_setup_second_level(iommu, domain, - dev, PASID_RID2PASID); + dev, IOMMU_NO_PASID); if (ret) { dev_err(dev, "Setup RID2PASID failed\n"); device_block_translation(dev); @@ -2447,30 +2492,6 @@ static int dmar_domain_attach_device(struct dmar_domain *domain, return 0; } -static bool device_has_rmrr(struct device *dev) -{ - struct dmar_rmrr_unit *rmrr; - struct device *tmp; - int i; - - rcu_read_lock(); - for_each_rmrr_units(rmrr) { - /* - * Return TRUE if this RMRR contains the device that - * is passed in. - */ - for_each_active_dev_scope(rmrr->devices, - rmrr->devices_cnt, i, tmp) - if (tmp == dev || - is_downstream_to_pci_bridge(dev, tmp)) { - rcu_read_unlock(); - return true; - } - } - rcu_read_unlock(); - return false; -} - /** * device_rmrr_is_relaxable - Test whether the RMRR of this device * is relaxable (ie. is allowed to be not enforced under some conditions) @@ -2500,34 +2521,6 @@ static bool device_rmrr_is_relaxable(struct device *dev) return false; } -/* - * There are a couple cases where we need to restrict the functionality of - * devices associated with RMRRs. The first is when evaluating a device for - * identity mapping because problems exist when devices are moved in and out - * of domains and their respective RMRR information is lost. This means that - * a device with associated RMRRs will never be in a "passthrough" domain. - * The second is use of the device through the IOMMU API. This interface - * expects to have full control of the IOVA space for the device. We cannot - * satisfy both the requirement that RMRR access is maintained and have an - * unencumbered IOVA space. We also have no ability to quiesce the device's - * use of the RMRR space or even inform the IOMMU API user of the restriction. - * We therefore prevent devices associated with an RMRR from participating in - * the IOMMU API, which eliminates them from device assignment. - * - * In both cases, devices which have relaxable RMRRs are not concerned by this - * restriction. See device_rmrr_is_relaxable comment. - */ -static bool device_is_rmrr_locked(struct device *dev) -{ - if (!device_has_rmrr(dev)) - return false; - - if (device_rmrr_is_relaxable(dev)) - return false; - - return true; -} - /* * Return the required default domain type for a specific device. * @@ -3005,13 +2998,6 @@ static int iommu_suspend(void) struct intel_iommu *iommu = NULL; unsigned long flag; - for_each_active_iommu(iommu, drhd) { - iommu->iommu_state = kcalloc(MAX_SR_DMAR_REGS, sizeof(u32), - GFP_KERNEL); - if (!iommu->iommu_state) - goto nomem; - } - iommu_flush_all(); for_each_active_iommu(iommu, drhd) { @@ -3031,12 +3017,6 @@ static int iommu_suspend(void) raw_spin_unlock_irqrestore(&iommu->register_lock, flag); } return 0; - -nomem: - for_each_active_iommu(iommu, drhd) - kfree(iommu->iommu_state); - - return -ENOMEM; } static void iommu_resume(void) @@ -3068,9 +3048,6 @@ static void iommu_resume(void) raw_spin_unlock_irqrestore(&iommu->register_lock, flag); } - - for_each_active_iommu(iommu, drhd) - kfree(iommu->iommu_state); } static struct syscore_ops iommu_syscore_ops = { @@ -3561,8 +3538,8 @@ static int intel_iommu_memory_notifier(struct notifier_block *nb, unsigned long val, void *v) { struct memory_notify *mhp = v; - unsigned long start_vpfn = mm_to_dma_pfn(mhp->start_pfn); - unsigned long last_vpfn = mm_to_dma_pfn(mhp->start_pfn + + unsigned long start_vpfn = mm_to_dma_pfn_start(mhp->start_pfn); + unsigned long last_vpfn = mm_to_dma_pfn_end(mhp->start_pfn + mhp->nr_pages - 1); switch (val) { @@ -3757,7 +3734,6 @@ static int __init probe_acpi_namespace_devices(void) for_each_active_dev_scope(drhd->devices, drhd->devices_cnt, i, dev) { struct acpi_device_physical_node *pn; - struct iommu_group *group; struct acpi_device *adev; if (dev->bus != &acpi_bus_type) @@ -3767,12 +3743,6 @@ static int __init probe_acpi_namespace_devices(void) mutex_lock(&adev->physical_node_lock); list_for_each_entry(pn, &adev->physical_node_list, node) { - group = iommu_group_get(pn->dev); - if (group) { - iommu_group_put(group); - continue; - } - ret = iommu_probe_device(pn->dev); if (ret) break; @@ -3969,7 +3939,7 @@ static void dmar_remove_one_dev_info(struct device *dev) if (!dev_is_real_dma_subdevice(info->dev)) { if (dev_is_pci(info->dev) && sm_supported(iommu)) intel_pasid_tear_down_entry(iommu, info->dev, - PASID_RID2PASID, false); + IOMMU_NO_PASID, false); iommu_disable_pci_caps(info); domain_context_clear(info); @@ -3998,7 +3968,7 @@ static void device_block_translation(struct device *dev) if (!dev_is_real_dma_subdevice(dev)) { if (sm_supported(iommu)) intel_pasid_tear_down_entry(iommu, dev, - PASID_RID2PASID, false); + IOMMU_NO_PASID, false); else domain_context_clear(info); } @@ -4140,12 +4110,6 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, struct device_domain_info *info = dev_iommu_priv_get(dev); int ret; - if (domain->type == IOMMU_DOMAIN_UNMANAGED && - device_is_rmrr_locked(dev)) { - dev_warn(dev, "Device is ineligible for IOMMU domain attach due to platform RMRR requirement. Contact your platform vendor.\n"); - return -EPERM; - } - if (info->domain) device_block_translation(dev); @@ -4272,7 +4236,7 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain, unsigned long i; nrpages = aligned_nrpages(gather->start, size); - start_pfn = mm_to_dma_pfn(iova_pfn); + start_pfn = mm_to_dma_pfn_start(iova_pfn); xa_for_each(&dmar_domain->iommu_array, i, info) iommu_flush_iotlb_psi(info->iommu, dmar_domain, @@ -4332,7 +4296,7 @@ static void domain_set_force_snooping(struct dmar_domain *domain) list_for_each_entry(info, &domain->devices, link) intel_pasid_setup_page_snoop_control(info->iommu, info->dev, - PASID_RID2PASID); + IOMMU_NO_PASID); } static bool intel_iommu_enforce_cache_coherency(struct iommu_domain *domain) @@ -4714,23 +4678,96 @@ static void intel_iommu_iotlb_sync_map(struct iommu_domain *domain, static void intel_iommu_remove_dev_pasid(struct device *dev, ioasid_t pasid) { struct intel_iommu *iommu = device_to_iommu(dev, NULL, NULL); + struct dev_pasid_info *curr, *dev_pasid = NULL; + struct dmar_domain *dmar_domain; struct iommu_domain *domain; + unsigned long flags; - /* Domain type specific cleanup: */ domain = iommu_get_domain_for_dev_pasid(dev, pasid, 0); - if (domain) { - switch (domain->type) { - case IOMMU_DOMAIN_SVA: - intel_svm_remove_dev_pasid(dev, pasid); - break; - default: - /* should never reach here */ - WARN_ON(1); + if (WARN_ON_ONCE(!domain)) + goto out_tear_down; + + /* + * The SVA implementation needs to handle its own stuffs like the mm + * notification. Before consolidating that code into iommu core, let + * the intel sva code handle it. + */ + if (domain->type == IOMMU_DOMAIN_SVA) { + intel_svm_remove_dev_pasid(dev, pasid); + goto out_tear_down; + } + + dmar_domain = to_dmar_domain(domain); + spin_lock_irqsave(&dmar_domain->lock, flags); + list_for_each_entry(curr, &dmar_domain->dev_pasids, link_domain) { + if (curr->dev == dev && curr->pasid == pasid) { + list_del(&curr->link_domain); + dev_pasid = curr; break; } } + WARN_ON_ONCE(!dev_pasid); + spin_unlock_irqrestore(&dmar_domain->lock, flags); + domain_detach_iommu(dmar_domain, iommu); + kfree(dev_pasid); +out_tear_down: intel_pasid_tear_down_entry(iommu, dev, pasid, false); + intel_drain_pasid_prq(dev, pasid); +} + +static int intel_iommu_set_dev_pasid(struct iommu_domain *domain, + struct device *dev, ioasid_t pasid) +{ + struct device_domain_info *info = dev_iommu_priv_get(dev); + struct dmar_domain *dmar_domain = to_dmar_domain(domain); + struct intel_iommu *iommu = info->iommu; + struct dev_pasid_info *dev_pasid; + unsigned long flags; + int ret; + + if (!pasid_supported(iommu) || dev_is_real_dma_subdevice(dev)) + return -EOPNOTSUPP; + + if (context_copied(iommu, info->bus, info->devfn)) + return -EBUSY; + + ret = prepare_domain_attach_device(domain, dev); + if (ret) + return ret; + + dev_pasid = kzalloc(sizeof(*dev_pasid), GFP_KERNEL); + if (!dev_pasid) + return -ENOMEM; + + ret = domain_attach_iommu(dmar_domain, iommu); + if (ret) + goto out_free; + + if (domain_type_is_si(dmar_domain)) + ret = intel_pasid_setup_pass_through(iommu, dmar_domain, + dev, pasid); + else if (dmar_domain->use_first_level) + ret = domain_setup_first_level(iommu, dmar_domain, + dev, pasid); + else + ret = intel_pasid_setup_second_level(iommu, dmar_domain, + dev, pasid); + if (ret) + goto out_detach_iommu; + + dev_pasid->dev = dev; + dev_pasid->pasid = pasid; + spin_lock_irqsave(&dmar_domain->lock, flags); + list_add(&dev_pasid->link_domain, &dmar_domain->dev_pasids); + spin_unlock_irqrestore(&dmar_domain->lock, flags); + + return 0; +out_detach_iommu: + domain_detach_iommu(dmar_domain, iommu); +out_free: + kfree(dev_pasid); + return ret; } static void *intel_iommu_hw_info(struct device *dev, u32 *length, u32 *type) @@ -4770,6 +4807,7 @@ const struct iommu_ops intel_iommu_ops = { #endif .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = intel_iommu_attach_device, + .set_dev_pasid = intel_iommu_set_dev_pasid, .map_pages = intel_iommu_map_pages, .unmap_pages = intel_iommu_unmap_pages, .iotlb_sync_map = intel_iommu_iotlb_sync_map, @@ -5006,7 +5044,7 @@ void quirk_extra_dev_tlb_flush(struct device_domain_info *info, return; sid = PCI_DEVID(info->bus, info->devfn); - if (pasid == PASID_RID2PASID) { + if (pasid == IOMMU_NO_PASID) { qi_flush_dev_iotlb(info->iommu, sid, info->pfsid, qdep, address, mask); } else { diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h index 1c5e1d88862ba5feb122f87aac09a9749168f559..7dac94f62b4ec661af7030b475103ef4ac184fee 100644 --- a/drivers/iommu/intel/iommu.h +++ b/drivers/iommu/intel/iommu.h @@ -595,6 +595,7 @@ struct dmar_domain { spinlock_t lock; /* Protect device tracking lists */ struct list_head devices; /* all devices' list */ + struct list_head dev_pasids; /* all attached pasids */ struct dma_pte *pgd; /* virtual address */ int gaw; /* max guest address width */ @@ -680,7 +681,7 @@ struct intel_iommu { struct iopf_queue *iopf_queue; unsigned char iopfq_name[16]; struct q_inval *qi; /* Queued invalidation info */ - u32 *iommu_state; /* Store iommu states between suspend and resume.*/ + u32 iommu_state[MAX_SR_DMAR_REGS]; /* Store iommu states between suspend and resume.*/ #ifdef CONFIG_IRQ_REMAP struct ir_table *ir_table; /* Interrupt remapping info */ @@ -717,6 +718,12 @@ struct device_domain_info { struct pasid_table *pasid_table; /* pasid table */ }; +struct dev_pasid_info { + struct list_head link_domain; /* link to domain siblings */ + struct device *dev; + ioasid_t pasid; +}; + static inline void __iommu_flush_cache( struct intel_iommu *iommu, void *addr, int size) { @@ -844,6 +851,7 @@ int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt, struct iommu_page_response *msg); struct iommu_domain *intel_svm_domain_alloc(void); void intel_svm_remove_dev_pasid(struct device *dev, ioasid_t pasid); +void intel_drain_pasid_prq(struct device *dev, u32 pasid); struct intel_svm_dev { struct list_head list; @@ -862,6 +870,7 @@ struct intel_svm { }; #else static inline void intel_svm_check(struct intel_iommu *iommu) {} +static inline void intel_drain_pasid_prq(struct device *dev, u32 pasid) {} static inline struct iommu_domain *intel_svm_domain_alloc(void) { return NULL; diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c index c5d479770e12e8247a23732a332e9ab633bc58df..8f92b92f3d2aba5ce2455fcf8d3604ea4eeee4ae 100644 --- a/drivers/iommu/intel/pasid.c +++ b/drivers/iommu/intel/pasid.c @@ -129,7 +129,7 @@ int intel_pasid_alloc_table(struct device *dev) info->pasid_table = pasid_table; if (!ecap_coherent(info->iommu->ecap)) - clflush_cache_range(pasid_table->table, size); + clflush_cache_range(pasid_table->table, (1 << order) * PAGE_SIZE); return 0; } @@ -438,7 +438,7 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu, * SVA usage, device could do DMA with multiple PASIDs. It is more * efficient to flush devTLB specific to the PASID. */ - if (pasid == PASID_RID2PASID) + if (pasid == IOMMU_NO_PASID) qi_flush_dev_iotlb(iommu, sid, pfsid, qdep, 0, 64 - VTD_PAGE_SHIFT); else qi_flush_dev_iotlb_pasid(iommu, sid, pfsid, pasid, qdep, 0, 64 - VTD_PAGE_SHIFT); diff --git a/drivers/iommu/intel/pasid.h b/drivers/iommu/intel/pasid.h index d6b7d21244b118d2529c1a060875f0c19fb91113..4e9e68c3c3888f6acd4c3ecff8ebc90f1db39955 100644 --- a/drivers/iommu/intel/pasid.h +++ b/drivers/iommu/intel/pasid.h @@ -10,8 +10,6 @@ #ifndef __INTEL_PASID_H #define __INTEL_PASID_H -#define PASID_RID2PASID 0x0 -#define PASID_MIN 0x1 #define PASID_MAX 0x100000 #define PASID_PTE_MASK 0x3F #define PASID_PTE_PRESENT 1 diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c index 8f6d68006ab6a9dae13b2d63b1b2ca8602de4f60..50a481c895b867202a4317afb7ef19a5666eafe5 100644 --- a/drivers/iommu/intel/svm.c +++ b/drivers/iommu/intel/svm.c @@ -26,8 +26,6 @@ #include "trace.h" static irqreturn_t prq_event_thread(int irq, void *d); -static void intel_svm_drain_prq(struct device *dev, u32 pasid); -#define to_intel_svm_dev(handle) container_of(handle, struct intel_svm_dev, sva) static DEFINE_XARRAY_ALLOC(pasid_private_array); static int pasid_private_add(ioasid_t pasid, void *priv) @@ -259,8 +257,6 @@ static const struct mmu_notifier_ops intel_mmuops = { .arch_invalidate_secondary_tlbs = intel_arch_invalidate_secondary_tlbs, }; -static DEFINE_MUTEX(pasid_mutex); - static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid, struct intel_svm **rsvm, struct intel_svm_dev **rsdev) @@ -268,10 +264,6 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid, struct intel_svm_dev *sdev = NULL; struct intel_svm *svm; - /* The caller should hold the pasid_mutex lock */ - if (WARN_ON(!mutex_is_locked(&pasid_mutex))) - return -EINVAL; - if (pasid == IOMMU_PASID_INVALID || pasid >= PASID_MAX) return -EINVAL; @@ -371,37 +363,23 @@ static int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev, return ret; } -/* Caller must hold pasid_mutex */ -static int intel_svm_unbind_mm(struct device *dev, u32 pasid) +void intel_svm_remove_dev_pasid(struct device *dev, u32 pasid) { struct intel_svm_dev *sdev; struct intel_iommu *iommu; struct intel_svm *svm; struct mm_struct *mm; - int ret = -EINVAL; iommu = device_to_iommu(dev, NULL, NULL); if (!iommu) - goto out; + return; - ret = pasid_to_svm_sdev(dev, pasid, &svm, &sdev); - if (ret) - goto out; + if (pasid_to_svm_sdev(dev, pasid, &svm, &sdev)) + return; mm = svm->mm; if (sdev) { list_del_rcu(&sdev->list); - /* - * Flush the PASID cache and IOTLB for this device. - * Note that we do depend on the hardware *not* using - * the PASID any more. Just as we depend on other - * devices never using PASIDs that they have no right - * to use. We have a *shared* PASID table, because it's - * large and has to be physically contiguous. So it's - * hard to be as defensive as we might like. - */ - intel_pasid_tear_down_entry(iommu, dev, svm->pasid, false); - intel_svm_drain_prq(dev, svm->pasid); kfree_rcu(sdev, rcu); if (list_empty(&svm->devs)) { @@ -418,8 +396,6 @@ static int intel_svm_unbind_mm(struct device *dev, u32 pasid) kfree(svm); } } -out: - return ret; } /* Page request queue descriptor */ @@ -460,7 +436,7 @@ static bool is_canonical_address(u64 addr) } /** - * intel_svm_drain_prq - Drain page requests and responses for a pasid + * intel_drain_pasid_prq - Drain page requests and responses for a pasid * @dev: target device * @pasid: pasid for draining * @@ -474,7 +450,7 @@ static bool is_canonical_address(u64 addr) * described in VT-d spec CH7.10 to drain all page requests and page * responses pending in the hardware. */ -static void intel_svm_drain_prq(struct device *dev, u32 pasid) +void intel_drain_pasid_prq(struct device *dev, u32 pasid) { struct device_domain_info *info; struct dmar_domain *domain; @@ -520,19 +496,7 @@ static void intel_svm_drain_prq(struct device *dev, u32 pasid) goto prq_retry; } - /* - * A work in IO page fault workqueue may try to lock pasid_mutex now. - * Holding pasid_mutex while waiting in iopf_queue_flush_dev() for - * all works in the workqueue to finish may cause deadlock. - * - * It's unnecessary to hold pasid_mutex in iopf_queue_flush_dev(). - * Unlock it to allow the works to be handled while waiting for - * them to finish. - */ - lockdep_assert_held(&pasid_mutex); - mutex_unlock(&pasid_mutex); iopf_queue_flush_dev(dev); - mutex_lock(&pasid_mutex); /* * Perform steps described in VT-d spec CH7.10 to drain page @@ -827,26 +791,14 @@ int intel_svm_page_response(struct device *dev, return ret; } -void intel_svm_remove_dev_pasid(struct device *dev, ioasid_t pasid) -{ - mutex_lock(&pasid_mutex); - intel_svm_unbind_mm(dev, pasid); - mutex_unlock(&pasid_mutex); -} - static int intel_svm_set_dev_pasid(struct iommu_domain *domain, struct device *dev, ioasid_t pasid) { struct device_domain_info *info = dev_iommu_priv_get(dev); struct intel_iommu *iommu = info->iommu; struct mm_struct *mm = domain->mm; - int ret; - mutex_lock(&pasid_mutex); - ret = intel_svm_bind_mm(iommu, dev, mm); - mutex_unlock(&pasid_mutex); - - return ret; + return intel_svm_bind_mm(iommu, dev, mm); } static void intel_svm_domain_free(struct iommu_domain *domain) diff --git a/drivers/iommu/iommu-sva.c b/drivers/iommu/iommu-sva.c index 05c0fb2acbc447f94ed3209adbdc893d1b334583..b78671a8a9143fe0b961c69d0ad81fa3224e5a83 100644 --- a/drivers/iommu/iommu-sva.c +++ b/drivers/iommu/iommu-sva.c @@ -10,34 +10,30 @@ #include "iommu-sva.h" static DEFINE_MUTEX(iommu_sva_lock); -static DEFINE_IDA(iommu_global_pasid_ida); /* Allocate a PASID for the mm within range (inclusive) */ -static int iommu_sva_alloc_pasid(struct mm_struct *mm, ioasid_t min, ioasid_t max) +static int iommu_sva_alloc_pasid(struct mm_struct *mm, struct device *dev) { + ioasid_t pasid; int ret = 0; - if (min == IOMMU_PASID_INVALID || - max == IOMMU_PASID_INVALID || - min == 0 || max < min) - return -EINVAL; - if (!arch_pgtable_dma_compat(mm)) return -EBUSY; mutex_lock(&iommu_sva_lock); /* Is a PASID already associated with this mm? */ if (mm_valid_pasid(mm)) { - if (mm->pasid < min || mm->pasid > max) + if (mm->pasid >= dev->iommu->max_pasids) ret = -EOVERFLOW; goto out; } - ret = ida_alloc_range(&iommu_global_pasid_ida, min, max, GFP_KERNEL); - if (ret < 0) + pasid = iommu_alloc_global_pasid(dev); + if (pasid == IOMMU_PASID_INVALID) { + ret = -ENOSPC; goto out; - - mm->pasid = ret; + } + mm->pasid = pasid; ret = 0; out: mutex_unlock(&iommu_sva_lock); @@ -64,15 +60,10 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev, struct mm_struct *mm { struct iommu_domain *domain; struct iommu_sva *handle; - ioasid_t max_pasids; int ret; - max_pasids = dev->iommu->max_pasids; - if (!max_pasids) - return ERR_PTR(-EOPNOTSUPP); - /* Allocate mm->pasid if necessary. */ - ret = iommu_sva_alloc_pasid(mm, 1, max_pasids - 1); + ret = iommu_sva_alloc_pasid(mm, dev); if (ret) return ERR_PTR(ret); @@ -217,5 +208,5 @@ void mm_pasid_drop(struct mm_struct *mm) if (likely(!mm_valid_pasid(mm))) return; - ida_free(&iommu_global_pasid_ida, mm->pasid); + iommu_free_global_pasid(mm->pasid); } diff --git a/drivers/iommu/iommu-sysfs.c b/drivers/iommu/iommu-sysfs.c index 99869217fbec7d862af4bba0e0a6facbe34a7168..cbe378c34ba3ebe1bd9e8ab22acdb7d5c976159e 100644 --- a/drivers/iommu/iommu-sysfs.c +++ b/drivers/iommu/iommu-sysfs.c @@ -107,9 +107,6 @@ int iommu_device_link(struct iommu_device *iommu, struct device *link) { int ret; - if (!iommu || IS_ERR(iommu)) - return -ENODEV; - ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices", &link->kobj, dev_name(link)); if (ret) @@ -122,14 +119,9 @@ int iommu_device_link(struct iommu_device *iommu, struct device *link) return ret; } -EXPORT_SYMBOL_GPL(iommu_device_link); void iommu_device_unlink(struct iommu_device *iommu, struct device *link) { - if (!iommu || IS_ERR(iommu)) - return; - sysfs_remove_link(&link->kobj, "iommu"); sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link)); } -EXPORT_SYMBOL_GPL(iommu_device_unlink); diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index c9c370de9d3d54e9a024147064fd2f39d48c38c8..3bfc56df4f781cbb15f6cad090681f8327b8dde4 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -41,6 +41,7 @@ static struct kset *iommu_group_kset; static DEFINE_IDA(iommu_group_ida); +static DEFINE_IDA(iommu_global_pasid_ida); static unsigned int iommu_def_domain_type __read_mostly; static bool iommu_dma_strict __read_mostly = IS_ENABLED(CONFIG_IOMMU_DEFAULT_DMA_STRICT); @@ -129,9 +130,12 @@ static int iommu_setup_default_domain(struct iommu_group *group, int target_type); static int iommu_create_device_direct_mappings(struct iommu_domain *domain, struct device *dev); -static struct iommu_group *iommu_group_get_for_dev(struct device *dev); static ssize_t iommu_group_store_type(struct iommu_group *group, const char *buf, size_t count); +static struct group_device *iommu_group_alloc_device(struct iommu_group *group, + struct device *dev); +static void __iommu_group_free_device(struct iommu_group *group, + struct group_device *grp_dev); #define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \ struct iommu_group_attribute iommu_group_attr_##_name = \ @@ -377,28 +381,18 @@ static u32 dev_iommu_get_max_pasids(struct device *dev) return min_t(u32, max_pasids, dev->iommu->iommu_dev->max_pasids); } -static int __iommu_probe_device(struct device *dev, struct list_head *group_list) +/* + * Init the dev->iommu and dev->iommu_group in the struct device and get the + * driver probed + */ +static int iommu_init_device(struct device *dev, const struct iommu_ops *ops) { - const struct iommu_ops *ops = dev->bus->iommu_ops; struct iommu_device *iommu_dev; struct iommu_group *group; - static DEFINE_MUTEX(iommu_probe_device_lock); int ret; - if (!ops) - return -ENODEV; - /* - * Serialise to avoid races between IOMMU drivers registering in - * parallel and/or the "replay" calls from ACPI/OF code via client - * driver probe. Once the latter have been cleaned up we should - * probably be able to use device_lock() here to minimise the scope, - * but for now enforcing a simple global ordering is fine. - */ - mutex_lock(&iommu_probe_device_lock); - if (!dev_iommu_get(dev)) { - ret = -ENOMEM; - goto err_unlock; - } + if (!dev_iommu_get(dev)) + return -ENOMEM; if (!try_module_get(ops->owner)) { ret = -EINVAL; @@ -408,124 +402,184 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list iommu_dev = ops->probe_device(dev); if (IS_ERR(iommu_dev)) { ret = PTR_ERR(iommu_dev); - goto out_module_put; + goto err_module_put; } - dev->iommu->iommu_dev = iommu_dev; - dev->iommu->max_pasids = dev_iommu_get_max_pasids(dev); - if (ops->is_attach_deferred) - dev->iommu->attach_deferred = ops->is_attach_deferred(dev); + ret = iommu_device_link(iommu_dev, dev); + if (ret) + goto err_release; - group = iommu_group_get_for_dev(dev); + group = ops->device_group(dev); + if (WARN_ON_ONCE(group == NULL)) + group = ERR_PTR(-EINVAL); if (IS_ERR(group)) { ret = PTR_ERR(group); - goto out_release; + goto err_unlink; } + dev->iommu_group = group; - mutex_lock(&group->mutex); - if (group_list && !group->default_domain && list_empty(&group->entry)) - list_add_tail(&group->entry, group_list); - mutex_unlock(&group->mutex); - iommu_group_put(group); - - mutex_unlock(&iommu_probe_device_lock); - iommu_device_link(iommu_dev, dev); - + dev->iommu->iommu_dev = iommu_dev; + dev->iommu->max_pasids = dev_iommu_get_max_pasids(dev); + if (ops->is_attach_deferred) + dev->iommu->attach_deferred = ops->is_attach_deferred(dev); return 0; -out_release: +err_unlink: + iommu_device_unlink(iommu_dev, dev); +err_release: if (ops->release_device) ops->release_device(dev); - -out_module_put: +err_module_put: module_put(ops->owner); - err_free: dev_iommu_free(dev); + return ret; +} -err_unlock: - mutex_unlock(&iommu_probe_device_lock); +static void iommu_deinit_device(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + const struct iommu_ops *ops = dev_iommu_ops(dev); - return ret; + lockdep_assert_held(&group->mutex); + + iommu_device_unlink(dev->iommu->iommu_dev, dev); + + /* + * release_device() must stop using any attached domain on the device. + * If there are still other devices in the group they are not effected + * by this callback. + * + * The IOMMU driver must set the device to either an identity or + * blocking translation and stop using any domain pointer, as it is + * going to be freed. + */ + if (ops->release_device) + ops->release_device(dev); + + /* + * If this is the last driver to use the group then we must free the + * domains before we do the module_put(). + */ + if (list_empty(&group->devices)) { + if (group->default_domain) { + iommu_domain_free(group->default_domain); + group->default_domain = NULL; + } + if (group->blocking_domain) { + iommu_domain_free(group->blocking_domain); + group->blocking_domain = NULL; + } + group->domain = NULL; + } + + /* Caller must put iommu_group */ + dev->iommu_group = NULL; + module_put(ops->owner); + dev_iommu_free(dev); } -int iommu_probe_device(struct device *dev) +static int __iommu_probe_device(struct device *dev, struct list_head *group_list) { - const struct iommu_ops *ops; + const struct iommu_ops *ops = dev->bus->iommu_ops; struct iommu_group *group; + static DEFINE_MUTEX(iommu_probe_device_lock); + struct group_device *gdev; int ret; - ret = __iommu_probe_device(dev, NULL); - if (ret) - goto err_out; + if (!ops) + return -ENODEV; + /* + * Serialise to avoid races between IOMMU drivers registering in + * parallel and/or the "replay" calls from ACPI/OF code via client + * driver probe. Once the latter have been cleaned up we should + * probably be able to use device_lock() here to minimise the scope, + * but for now enforcing a simple global ordering is fine. + */ + mutex_lock(&iommu_probe_device_lock); - group = iommu_group_get(dev); - if (!group) { - ret = -ENODEV; - goto err_release; + /* Device is probed already if in a group */ + if (dev->iommu_group) { + ret = 0; + goto out_unlock; } + ret = iommu_init_device(dev, ops); + if (ret) + goto out_unlock; + + group = dev->iommu_group; + gdev = iommu_group_alloc_device(group, dev); mutex_lock(&group->mutex); + if (IS_ERR(gdev)) { + ret = PTR_ERR(gdev); + goto err_put_group; + } + /* + * The gdev must be in the list before calling + * iommu_setup_default_domain() + */ + list_add_tail(&gdev->list, &group->devices); + WARN_ON(group->default_domain && !group->domain); if (group->default_domain) iommu_create_device_direct_mappings(group->default_domain, dev); - if (group->domain) { ret = __iommu_device_set_domain(group, dev, group->domain, 0); if (ret) - goto err_unlock; - } else if (!group->default_domain) { + goto err_remove_gdev; + } else if (!group->default_domain && !group_list) { ret = iommu_setup_default_domain(group, 0); if (ret) - goto err_unlock; + goto err_remove_gdev; + } else if (!group->default_domain) { + /* + * With a group_list argument we defer the default_domain setup + * to the caller by providing a de-duplicated list of groups + * that need further setup. + */ + if (list_empty(&group->entry)) + list_add_tail(&group->entry, group_list); } - mutex_unlock(&group->mutex); - iommu_group_put(group); + mutex_unlock(&iommu_probe_device_lock); - ops = dev_iommu_ops(dev); - if (ops->probe_finalize) - ops->probe_finalize(dev); + if (dev_is_pci(dev)) + iommu_dma_set_pci_32bit_workaround(dev); return 0; -err_unlock: +err_remove_gdev: + list_del(&gdev->list); + __iommu_group_free_device(group, gdev); +err_put_group: + iommu_deinit_device(dev); mutex_unlock(&group->mutex); iommu_group_put(group); -err_release: - iommu_release_device(dev); +out_unlock: + mutex_unlock(&iommu_probe_device_lock); -err_out: return ret; - } -/* - * Remove a device from a group's device list and return the group device - * if successful. - */ -static struct group_device * -__iommu_group_remove_device(struct iommu_group *group, struct device *dev) +int iommu_probe_device(struct device *dev) { - struct group_device *device; + const struct iommu_ops *ops; + int ret; - lockdep_assert_held(&group->mutex); - for_each_group_device(group, device) { - if (device->dev == dev) { - list_del(&device->list); - return device; - } - } + ret = __iommu_probe_device(dev, NULL); + if (ret) + return ret; - return NULL; + ops = dev_iommu_ops(dev); + if (ops->probe_finalize) + ops->probe_finalize(dev); + + return 0; } -/* - * Release a device from its group and decrements the iommu group reference - * count. - */ -static void __iommu_group_release_device(struct iommu_group *group, - struct group_device *grp_dev) +static void __iommu_group_free_device(struct iommu_group *group, + struct group_device *grp_dev) { struct device *dev = grp_dev->dev; @@ -534,54 +588,57 @@ static void __iommu_group_release_device(struct iommu_group *group, trace_remove_device_from_group(group->id, dev); + /* + * If the group has become empty then ownership must have been + * released, and the current domain must be set back to NULL or + * the default domain. + */ + if (list_empty(&group->devices)) + WARN_ON(group->owner_cnt || + group->domain != group->default_domain); + kfree(grp_dev->name); kfree(grp_dev); - dev->iommu_group = NULL; - kobject_put(group->devices_kobj); } -static void iommu_release_device(struct device *dev) +/* Remove the iommu_group from the struct device. */ +static void __iommu_group_remove_device(struct device *dev) { struct iommu_group *group = dev->iommu_group; struct group_device *device; - const struct iommu_ops *ops; - - if (!dev->iommu || !group) - return; - - iommu_device_unlink(dev->iommu->iommu_dev, dev); mutex_lock(&group->mutex); - device = __iommu_group_remove_device(group, dev); + for_each_group_device(group, device) { + if (device->dev != dev) + continue; - /* - * If the group has become empty then ownership must have been released, - * and the current domain must be set back to NULL or the default - * domain. - */ - if (list_empty(&group->devices)) - WARN_ON(group->owner_cnt || - group->domain != group->default_domain); + list_del(&device->list); + __iommu_group_free_device(group, device); + if (dev->iommu && dev->iommu->iommu_dev) + iommu_deinit_device(dev); + else + dev->iommu_group = NULL; + break; + } + mutex_unlock(&group->mutex); /* - * release_device() must stop using any attached domain on the device. - * If there are still other devices in the group they are not effected - * by this callback. - * - * The IOMMU driver must set the device to either an identity or - * blocking translation and stop using any domain pointer, as it is - * going to be freed. + * Pairs with the get in iommu_init_device() or + * iommu_group_add_device() */ - ops = dev_iommu_ops(dev); - if (ops->release_device) - ops->release_device(dev); - mutex_unlock(&group->mutex); + iommu_group_put(group); +} - if (device) - __iommu_group_release_device(group, device); +static void iommu_release_device(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; - module_put(ops->owner); - dev_iommu_free(dev); + if (group) + __iommu_group_remove_device(dev); + + /* Free any fwspec if no iommu_driver was ever attached */ + if (dev->iommu) + dev_iommu_free(dev); } static int __init iommu_set_def_domain_type(char *str) @@ -842,10 +899,9 @@ static void iommu_group_release(struct kobject *kobj) ida_free(&iommu_group_ida, group->id); - if (group->default_domain) - iommu_domain_free(group->default_domain); - if (group->blocking_domain) - iommu_domain_free(group->blocking_domain); + /* Domains are free'd by iommu_deinit_device() */ + WARN_ON(group->default_domain); + WARN_ON(group->blocking_domain); kfree(group->name); kfree(group); @@ -1003,14 +1059,12 @@ static int iommu_create_device_direct_mappings(struct iommu_domain *domain, unsigned long pg_size; int ret = 0; - if (!iommu_is_dma_domain(domain)) - return 0; - - BUG_ON(!domain->pgsize_bitmap); - - pg_size = 1UL << __ffs(domain->pgsize_bitmap); + pg_size = domain->pgsize_bitmap ? 1UL << __ffs(domain->pgsize_bitmap) : 0; INIT_LIST_HEAD(&mappings); + if (WARN_ON_ONCE(iommu_is_dma_domain(domain) && !pg_size)) + return -EINVAL; + iommu_get_resv_regions(dev, &mappings); /* We need to consider overlapping regions for different devices */ @@ -1018,13 +1072,17 @@ static int iommu_create_device_direct_mappings(struct iommu_domain *domain, dma_addr_t start, end, addr; size_t map_size = 0; - start = ALIGN(entry->start, pg_size); - end = ALIGN(entry->start + entry->length, pg_size); + if (entry->type == IOMMU_RESV_DIRECT) + dev->iommu->require_direct = 1; - if (entry->type != IOMMU_RESV_DIRECT && - entry->type != IOMMU_RESV_DIRECT_RELAXABLE) + if ((entry->type != IOMMU_RESV_DIRECT && + entry->type != IOMMU_RESV_DIRECT_RELAXABLE) || + !iommu_is_dma_domain(domain)) continue; + start = ALIGN(entry->start, pg_size); + end = ALIGN(entry->start + entry->length, pg_size); + for (addr = start; addr <= end; addr += pg_size) { phys_addr_t phys_addr; @@ -1058,22 +1116,16 @@ static int iommu_create_device_direct_mappings(struct iommu_domain *domain, return ret; } -/** - * iommu_group_add_device - add a device to an iommu group - * @group: the group into which to add the device (reference should be held) - * @dev: the device - * - * This function is called by an iommu driver to add a device into a - * group. Adding a device increments the group reference count. - */ -int iommu_group_add_device(struct iommu_group *group, struct device *dev) +/* This is undone by __iommu_group_free_device() */ +static struct group_device *iommu_group_alloc_device(struct iommu_group *group, + struct device *dev) { int ret, i = 0; struct group_device *device; device = kzalloc(sizeof(*device), GFP_KERNEL); if (!device) - return -ENOMEM; + return ERR_PTR(-ENOMEM); device->dev = dev; @@ -1104,18 +1156,11 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev) goto err_free_name; } - kobject_get(group->devices_kobj); - - dev->iommu_group = group; - - mutex_lock(&group->mutex); - list_add_tail(&device->list, &group->devices); - mutex_unlock(&group->mutex); trace_add_device_to_group(group->id, dev); dev_info(dev, "Adding to iommu group %d\n", group->id); - return 0; + return device; err_free_name: kfree(device->name); @@ -1124,7 +1169,32 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev) err_free_device: kfree(device); dev_err(dev, "Failed to add to iommu group %d: %d\n", group->id, ret); - return ret; + return ERR_PTR(ret); +} + +/** + * iommu_group_add_device - add a device to an iommu group + * @group: the group into which to add the device (reference should be held) + * @dev: the device + * + * This function is called by an iommu driver to add a device into a + * group. Adding a device increments the group reference count. + */ +int iommu_group_add_device(struct iommu_group *group, struct device *dev) +{ + struct group_device *gdev; + + gdev = iommu_group_alloc_device(group, dev); + if (IS_ERR(gdev)) + return PTR_ERR(gdev); + + iommu_group_ref_get(group); + dev->iommu_group = group; + + mutex_lock(&group->mutex); + list_add_tail(&gdev->list, &group->devices); + mutex_unlock(&group->mutex); + return 0; } EXPORT_SYMBOL_GPL(iommu_group_add_device); @@ -1138,19 +1208,13 @@ EXPORT_SYMBOL_GPL(iommu_group_add_device); void iommu_group_remove_device(struct device *dev) { struct iommu_group *group = dev->iommu_group; - struct group_device *device; if (!group) return; dev_info(dev, "Removing from iommu group %d\n", group->id); - mutex_lock(&group->mutex); - device = __iommu_group_remove_device(group, dev); - mutex_unlock(&group->mutex); - - if (device) - __iommu_group_release_device(group, device); + __iommu_group_remove_device(dev); } EXPORT_SYMBOL_GPL(iommu_group_remove_device); @@ -1708,45 +1772,6 @@ iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) return dom; } -/** - * iommu_group_get_for_dev - Find or create the IOMMU group for a device - * @dev: target device - * - * This function is intended to be called by IOMMU drivers and extended to - * support common, bus-defined algorithms when determining or creating the - * IOMMU group for a device. On success, the caller will hold a reference - * to the returned IOMMU group, which will already include the provided - * device. The reference should be released with iommu_group_put(). - */ -static struct iommu_group *iommu_group_get_for_dev(struct device *dev) -{ - const struct iommu_ops *ops = dev_iommu_ops(dev); - struct iommu_group *group; - int ret; - - group = iommu_group_get(dev); - if (group) - return group; - - group = ops->device_group(dev); - if (WARN_ON_ONCE(group == NULL)) - return ERR_PTR(-EINVAL); - - if (IS_ERR(group)) - return group; - - ret = iommu_group_add_device(group, dev); - if (ret) - goto out_put_group; - - return group; - -out_put_group: - iommu_group_put(group); - - return ERR_PTR(ret); -} - struct iommu_domain *iommu_group_default_domain(struct iommu_group *group) { return group->default_domain; @@ -1755,16 +1780,8 @@ struct iommu_domain *iommu_group_default_domain(struct iommu_group *group) static int probe_iommu_group(struct device *dev, void *data) { struct list_head *group_list = data; - struct iommu_group *group; int ret; - /* Device is probed already if in a group */ - group = iommu_group_get(dev); - if (group) { - iommu_group_put(group); - return 0; - } - ret = __iommu_probe_device(dev, group_list); if (ret == -ENODEV) ret = 0; @@ -1840,11 +1857,6 @@ int bus_iommu_probe(const struct bus_type *bus) LIST_HEAD(group_list); int ret; - /* - * This code-path does not allocate the default domain when - * creating the iommu group, so do it after the groups are - * created. - */ ret = bus_for_each_dev(bus, NULL, &group_list, probe_iommu_group); if (ret) return ret; @@ -1857,6 +1869,11 @@ int bus_iommu_probe(const struct bus_type *bus) /* Remove item from the list */ list_del_init(&group->entry); + /* + * We go to the trouble of deferred default domain creation so + * that the cross-group default domain type and the setup of the + * IOMMU_RESV_DIRECT will work correctly in non-hotpug scenarios. + */ ret = iommu_setup_default_domain(group, 0); if (ret) { mutex_unlock(&group->mutex); @@ -2191,6 +2208,21 @@ static int __iommu_device_set_domain(struct iommu_group *group, { int ret; + /* + * If the device requires IOMMU_RESV_DIRECT then we cannot allow + * the blocking domain to be attached as it does not contain the + * required 1:1 mapping. This test effectively excludes the device + * being used with iommu_group_claim_dma_owner() which will block + * vfio and iommufd as well. + */ + if (dev->iommu->require_direct && + (new_domain->type == IOMMU_DOMAIN_BLOCKED || + new_domain == group->blocking_domain)) { + dev_warn(dev, + "Firmware has requested this device have a 1:1 IOMMU mapping, rejecting configuring the device without a 1:1 mapping. Contact your platform vendor.\n"); + return -EINVAL; + } + if (dev->iommu->attach_deferred) { if (new_domain == group->default_domain) return 0; @@ -3282,7 +3314,7 @@ static void __iommu_release_dma_ownership(struct iommu_group *group) /** * iommu_group_release_dma_owner() - Release DMA ownership of a group - * @dev: The device + * @group: The group * * Release the DMA ownership claimed by iommu_group_claim_dma_owner(). */ @@ -3296,7 +3328,7 @@ EXPORT_SYMBOL_GPL(iommu_group_release_dma_owner); /** * iommu_device_release_dma_owner() - Release DMA ownership of a device - * @group: The device. + * @dev: The device. * * Release the DMA ownership claimed by iommu_device_claim_dma_owner(). */ @@ -3479,3 +3511,30 @@ struct iommu_domain *iommu_sva_domain_alloc(struct device *dev, return domain; } + +ioasid_t iommu_alloc_global_pasid(struct device *dev) +{ + int ret; + + /* max_pasids == 0 means that the device does not support PASID */ + if (!dev->iommu->max_pasids) + return IOMMU_PASID_INVALID; + + /* + * max_pasids is set up by vendor driver based on number of PASID bits + * supported but the IDA allocation is inclusive. + */ + ret = ida_alloc_range(&iommu_global_pasid_ida, IOMMU_FIRST_GLOBAL_PASID, + dev->iommu->max_pasids - 1, GFP_KERNEL); + return ret < 0 ? IOMMU_PASID_INVALID : ret; +} +EXPORT_SYMBOL_GPL(iommu_alloc_global_pasid); + +void iommu_free_global_pasid(ioasid_t pasid) +{ + if (WARN_ON(pasid == IOMMU_PASID_INVALID)) + return; + + ida_free(&iommu_global_pasid_ida, pasid); +} +EXPORT_SYMBOL_GPL(iommu_free_global_pasid); diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 9f64c5c9f5b90ace00aff6ec7b7281bfd68d22eb..65ff69477c43e41b270f504b4f0434c2145c7c69 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -14,11 +14,12 @@ #include #include #include +#include #include #include #include -#include #include +#include #include #include #include @@ -253,17 +254,13 @@ static void ipmmu_imuctr_write(struct ipmmu_vmsa_device *mmu, /* Wait for any pending TLB invalidations to complete */ static void ipmmu_tlb_sync(struct ipmmu_vmsa_domain *domain) { - unsigned int count = 0; + u32 val; - while (ipmmu_ctx_read_root(domain, IMCTR) & IMCTR_FLUSH) { - cpu_relax(); - if (++count == TLB_LOOP_TIMEOUT) { - dev_err_ratelimited(domain->mmu->dev, + if (read_poll_timeout_atomic(ipmmu_ctx_read_root, val, + !(val & IMCTR_FLUSH), 1, TLB_LOOP_TIMEOUT, + false, domain, IMCTR)) + dev_err_ratelimited(domain->mmu->dev, "TLB sync timed out -- MMU may be deadlocked\n"); - return; - } - udelay(1); - } } static void ipmmu_tlb_invalidate(struct ipmmu_vmsa_domain *domain) @@ -723,6 +720,10 @@ static bool ipmmu_device_is_allowed(struct device *dev) if (soc_device_match(soc_denylist)) return false; + /* Check whether this device is a PCI device */ + if (dev_is_pci(dev)) + return true; + /* Check whether this device can work with the IPMMU */ for (i = 0; i < ARRAY_SIZE(devices_allowlist); i++) { if (!strcmp(dev_name(dev), devices_allowlist[i])) diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index e93906d6e112e826facaea508e118e36b78fb666..fab6c347ce578ec7c79131a9ceeaab3f41b0aafe 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -3,6 +3,7 @@ * Copyright (c) 2015-2016 MediaTek Inc. * Author: Yong Wu */ +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include @@ -143,6 +145,7 @@ #define PGTABLE_PA_35_EN BIT(17) #define TF_PORT_TO_ADDR_MT8173 BIT(18) #define INT_ID_PORT_WIDTH_6 BIT(19) +#define CFG_IFA_MASTER_IN_ATF BIT(20) #define MTK_IOMMU_HAS_FLAG_MASK(pdata, _x, mask) \ ((((pdata)->flags) & (mask)) == (_x)) @@ -167,6 +170,7 @@ enum mtk_iommu_plat { M4U_MT8173, M4U_MT8183, M4U_MT8186, + M4U_MT8188, M4U_MT8192, M4U_MT8195, M4U_MT8365, @@ -258,6 +262,8 @@ struct mtk_iommu_data { struct device *smicomm_dev; struct mtk_iommu_bank_data *bank; + struct mtk_iommu_domain *share_dom; + struct regmap *pericfg; struct mutex mutex; /* Protect m4u_group/m4u_dom above */ @@ -577,41 +583,55 @@ static int mtk_iommu_config(struct mtk_iommu_data *data, struct device *dev, unsigned int larbid, portid; struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); const struct mtk_iommu_iova_region *region; - u32 peri_mmuen, peri_mmuen_msk; + unsigned long portid_msk = 0; + struct arm_smccc_res res; int i, ret = 0; for (i = 0; i < fwspec->num_ids; ++i) { - larbid = MTK_M4U_TO_LARB(fwspec->ids[i]); portid = MTK_M4U_TO_PORT(fwspec->ids[i]); + portid_msk |= BIT(portid); + } - if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) { - larb_mmu = &data->larb_imu[larbid]; + if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) { + /* All ports should be in the same larb. just use 0 here */ + larbid = MTK_M4U_TO_LARB(fwspec->ids[0]); + larb_mmu = &data->larb_imu[larbid]; + region = data->plat_data->iova_region + regionid; - region = data->plat_data->iova_region + regionid; + for_each_set_bit(portid, &portid_msk, 32) larb_mmu->bank[portid] = upper_32_bits(region->iova_base); - dev_dbg(dev, "%s iommu for larb(%s) port %d region %d rgn-bank %d.\n", - enable ? "enable" : "disable", dev_name(larb_mmu->dev), - portid, regionid, larb_mmu->bank[portid]); + dev_dbg(dev, "%s iommu for larb(%s) port 0x%lx region %d rgn-bank %d.\n", + enable ? "enable" : "disable", dev_name(larb_mmu->dev), + portid_msk, regionid, upper_32_bits(region->iova_base)); - if (enable) - larb_mmu->mmu |= MTK_SMI_MMU_EN(portid); - else - larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid); - } else if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_INFRA)) { - peri_mmuen_msk = BIT(portid); + if (enable) + larb_mmu->mmu |= portid_msk; + else + larb_mmu->mmu &= ~portid_msk; + } else if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_INFRA)) { + if (MTK_IOMMU_HAS_FLAG(data->plat_data, CFG_IFA_MASTER_IN_ATF)) { + arm_smccc_smc(MTK_SIP_KERNEL_IOMMU_CONTROL, + IOMMU_ATF_CMD_CONFIG_INFRA_IOMMU, + portid_msk, enable, 0, 0, 0, 0, &res); + ret = res.a0; + } else { /* PCI dev has only one output id, enable the next writing bit for PCIe */ - if (dev_is_pci(dev)) - peri_mmuen_msk |= BIT(portid + 1); + if (dev_is_pci(dev)) { + if (fwspec->num_ids != 1) { + dev_err(dev, "PCI dev can only have one port.\n"); + return -ENODEV; + } + portid_msk |= BIT(portid + 1); + } - peri_mmuen = enable ? peri_mmuen_msk : 0; ret = regmap_update_bits(data->pericfg, PERICFG_IOMMU_1, - peri_mmuen_msk, peri_mmuen); - if (ret) - dev_err(dev, "%s iommu(%s) inframaster 0x%x fail(%d).\n", - enable ? "enable" : "disable", - dev_name(data->dev), peri_mmuen_msk, ret); + (u32)portid_msk, enable ? (u32)portid_msk : 0); } + if (ret) + dev_err(dev, "%s iommu(%s) inframaster 0x%lx fail(%d).\n", + enable ? "enable" : "disable", + dev_name(data->dev), portid_msk, ret); } return ret; } @@ -620,15 +640,14 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom, struct mtk_iommu_data *data, unsigned int region_id) { + struct mtk_iommu_domain *share_dom = data->share_dom; const struct mtk_iommu_iova_region *region; - struct mtk_iommu_domain *m4u_dom; - - /* Always use bank0 in sharing pgtable case */ - m4u_dom = data->bank[0].m4u_dom; - if (m4u_dom) { - dom->iop = m4u_dom->iop; - dom->cfg = m4u_dom->cfg; - dom->domain.pgsize_bitmap = m4u_dom->cfg.pgsize_bitmap; + + /* Share pgtable when 2 MM IOMMU share the pgtable or one IOMMU use multiple iova ranges */ + if (share_dom) { + dom->iop = share_dom->iop; + dom->cfg = share_dom->cfg; + dom->domain.pgsize_bitmap = share_dom->cfg.pgsize_bitmap; goto update_iova_region; } @@ -658,6 +677,8 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom, /* Update our support page sizes bitmap */ dom->domain.pgsize_bitmap = dom->cfg.pgsize_bitmap; + data->share_dom = dom; + update_iova_region: /* Update the iova region for this domain */ region = data->plat_data->iova_region + region_id; @@ -708,7 +729,9 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain, /* Data is in the frstdata in sharing pgtable case. */ frstdata = mtk_iommu_get_frst_data(hw_list); + mutex_lock(&frstdata->mutex); ret = mtk_iommu_domain_finalise(dom, frstdata, region_id); + mutex_unlock(&frstdata->mutex); if (ret) { mutex_unlock(&dom->mutex); return ret; @@ -1318,7 +1341,8 @@ static int mtk_iommu_probe(struct platform_device *pdev) dev_err_probe(dev, ret, "mm dts parse fail\n"); goto out_runtime_disable; } - } else if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_INFRA)) { + } else if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_INFRA) && + !MTK_IOMMU_HAS_FLAG(data->plat_data, CFG_IFA_MASTER_IN_ATF)) { p = data->plat_data->pericfg_comp_str; data->pericfg = syscon_regmap_lookup_by_compatible(p); if (IS_ERR(data->pericfg)) { @@ -1570,6 +1594,67 @@ static const struct mtk_iommu_plat_data mt8186_data_mm = { .iova_region_larb_msk = mt8186_larb_region_msk, }; +static const struct mtk_iommu_plat_data mt8188_data_infra = { + .m4u_plat = M4U_MT8188, + .flags = WR_THROT_EN | DCM_DISABLE | STD_AXI_MODE | PM_CLK_AO | + MTK_IOMMU_TYPE_INFRA | IFA_IOMMU_PCIE_SUPPORT | + PGTABLE_PA_35_EN | CFG_IFA_MASTER_IN_ATF, + .inv_sel_reg = REG_MMU_INV_SEL_GEN2, + .banks_num = 1, + .banks_enable = {true}, + .iova_region = single_domain, + .iova_region_nr = ARRAY_SIZE(single_domain), +}; + +static const u32 mt8188_larb_region_msk[MT8192_MULTI_REGION_NR_MAX][MTK_LARB_NR_MAX] = { + [0] = {~0, ~0, ~0, ~0}, /* Region0: all ports for larb0/1/2/3 */ + [1] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, ~0, ~0, ~0}, /* Region1: larb19(21)/21(22)/23 */ + [2] = {0, 0, 0, 0, ~0, ~0, ~0, ~0, /* Region2: the other larbs. */ + ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, + ~0, ~0, ~0, ~0, ~0, 0, 0, 0, + 0, ~0}, + [3] = {0}, + [4] = {[24] = BIT(0) | BIT(1)}, /* Only larb27(24) port0/1 */ + [5] = {[24] = BIT(2) | BIT(3)}, /* Only larb27(24) port2/3 */ +}; + +static const struct mtk_iommu_plat_data mt8188_data_vdo = { + .m4u_plat = M4U_MT8188, + .flags = HAS_BCLK | HAS_SUB_COMM_3BITS | OUT_ORDER_WR_EN | + WR_THROT_EN | IOVA_34_EN | SHARE_PGTABLE | + PGTABLE_PA_35_EN | MTK_IOMMU_TYPE_MM, + .hw_list = &m4ulist, + .inv_sel_reg = REG_MMU_INV_SEL_GEN2, + .banks_num = 1, + .banks_enable = {true}, + .iova_region = mt8192_multi_dom, + .iova_region_nr = ARRAY_SIZE(mt8192_multi_dom), + .iova_region_larb_msk = mt8188_larb_region_msk, + .larbid_remap = {{2}, {0}, {21}, {0}, {19}, {9, 10, + 11 /* 11a */, 25 /* 11c */}, + {13, 0, 29 /* 16b */, 30 /* 17b */, 0}, {5}}, +}; + +static const struct mtk_iommu_plat_data mt8188_data_vpp = { + .m4u_plat = M4U_MT8188, + .flags = HAS_BCLK | HAS_SUB_COMM_3BITS | OUT_ORDER_WR_EN | + WR_THROT_EN | IOVA_34_EN | SHARE_PGTABLE | + PGTABLE_PA_35_EN | MTK_IOMMU_TYPE_MM, + .hw_list = &m4ulist, + .inv_sel_reg = REG_MMU_INV_SEL_GEN2, + .banks_num = 1, + .banks_enable = {true}, + .iova_region = mt8192_multi_dom, + .iova_region_nr = ARRAY_SIZE(mt8192_multi_dom), + .iova_region_larb_msk = mt8188_larb_region_msk, + .larbid_remap = {{1}, {3}, {23}, {7}, {MTK_INVALID_LARBID}, + {12, 15, 24 /* 11b */}, {14, MTK_INVALID_LARBID, + 16 /* 16a */, 17 /* 17a */, MTK_INVALID_LARBID, + 27, 28 /* ccu0 */, MTK_INVALID_LARBID}, {4, 6}}, +}; + static const unsigned int mt8192_larb_region_msk[MT8192_MULTI_REGION_NR_MAX][MTK_LARB_NR_MAX] = { [0] = {~0, ~0}, /* Region0: larb0/1 */ [1] = {0, 0, 0, 0, ~0, ~0, 0, ~0}, /* Region1: larb4/5/7 */ @@ -1678,6 +1763,9 @@ static const struct of_device_id mtk_iommu_of_ids[] = { { .compatible = "mediatek,mt8173-m4u", .data = &mt8173_data}, { .compatible = "mediatek,mt8183-m4u", .data = &mt8183_data}, { .compatible = "mediatek,mt8186-iommu-mm", .data = &mt8186_data_mm}, /* mm: m4u */ + { .compatible = "mediatek,mt8188-iommu-infra", .data = &mt8188_data_infra}, + { .compatible = "mediatek,mt8188-iommu-vdo", .data = &mt8188_data_vdo}, + { .compatible = "mediatek,mt8188-iommu-vpp", .data = &mt8188_data_vpp}, { .compatible = "mediatek,mt8192-m4u", .data = &mt8192_data}, { .compatible = "mediatek,mt8195-iommu-infra", .data = &mt8195_data_infra}, { .compatible = "mediatek,mt8195-iommu-vdo", .data = &mt8195_data_vdo}, diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 40f57d293a79d45647650560122cc2b993907abc..157b286e36bf3a1ee1b312138c761d6e8dc97911 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -159,7 +159,7 @@ const struct iommu_ops *of_iommu_configure(struct device *dev, * If we have reason to believe the IOMMU driver missed the initial * probe for dev, replay it to get things in order. */ - if (!err && dev->bus && !device_iommu_mapped(dev)) + if (!err && dev->bus) err = iommu_probe_device(dev); /* Ignore all other errors apart from EPROBE_DEFER */ diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index 4054030c32379533eca2e96003016a8a6a51481a..8ff69fbf9f65db876964a7a90ca9abe1d27f65d2 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -98,9 +98,8 @@ struct rk_iommu_ops { phys_addr_t (*pt_address)(u32 dte); u32 (*mk_dtentries)(dma_addr_t pt_dma); u32 (*mk_ptentries)(phys_addr_t page, int prot); - phys_addr_t (*dte_addr_phys)(u32 addr); - u32 (*dma_addr_dte)(dma_addr_t dt_dma); u64 dma_bit_mask; + gfp_t gfp_flags; }; struct rk_iommu { @@ -278,8 +277,8 @@ static u32 rk_mk_pte(phys_addr_t page, int prot) /* * In v2: * 31:12 - Page address bit 31:0 - * 11:9 - Page address bit 34:32 - * 8:4 - Page address bit 39:35 + * 11: 8 - Page address bit 35:32 + * 7: 4 - Page address bit 39:36 * 3 - Security * 2 - Writable * 1 - Readable @@ -506,7 +505,7 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu) /* * Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY - * and verifying that upper 5 nybbles are read back. + * and verifying that upper 5 (v1) or 7 (v2) nybbles are read back. */ for (i = 0; i < iommu->num_mmu; i++) { dte_addr = rk_ops->pt_address(DTE_ADDR_DUMMY); @@ -531,33 +530,6 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu) return 0; } -static inline phys_addr_t rk_dte_addr_phys(u32 addr) -{ - return (phys_addr_t)addr; -} - -static inline u32 rk_dma_addr_dte(dma_addr_t dt_dma) -{ - return dt_dma; -} - -#define DT_HI_MASK GENMASK_ULL(39, 32) -#define DTE_BASE_HI_MASK GENMASK(11, 4) -#define DT_SHIFT 28 - -static inline phys_addr_t rk_dte_addr_phys_v2(u32 addr) -{ - u64 addr64 = addr; - return (phys_addr_t)(addr64 & RK_DTE_PT_ADDRESS_MASK) | - ((addr64 & DTE_BASE_HI_MASK) << DT_SHIFT); -} - -static inline u32 rk_dma_addr_dte_v2(dma_addr_t dt_dma) -{ - return (dt_dma & RK_DTE_PT_ADDRESS_MASK) | - ((dt_dma & DT_HI_MASK) >> DT_SHIFT); -} - static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova) { void __iomem *base = iommu->bases[index]; @@ -577,7 +549,7 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova) page_offset = rk_iova_page_offset(iova); mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR); - mmu_dte_addr_phys = rk_ops->dte_addr_phys(mmu_dte_addr); + mmu_dte_addr_phys = rk_ops->pt_address(mmu_dte_addr); dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index); dte_addr = phys_to_virt(dte_addr_phys); @@ -756,7 +728,7 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain, if (rk_dte_is_pt_valid(dte)) goto done; - page_table = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); + page_table = (u32 *)get_zeroed_page(GFP_ATOMIC | rk_ops->gfp_flags); if (!page_table) return ERR_PTR(-ENOMEM); @@ -967,7 +939,7 @@ static int rk_iommu_enable(struct rk_iommu *iommu) for (i = 0; i < iommu->num_mmu; i++) { rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, - rk_ops->dma_addr_dte(rk_domain->dt_dma)); + rk_ops->mk_dtentries(rk_domain->dt_dma)); rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE); rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK); } @@ -1105,7 +1077,7 @@ static struct iommu_domain *rk_iommu_domain_alloc(unsigned type) * Each level1 (dt) and level2 (pt) table has 1024 4-byte entries. * Allocate one 4 KiB page for each table. */ - rk_domain->dt = (u32 *)get_zeroed_page(GFP_KERNEL | GFP_DMA32); + rk_domain->dt = (u32 *)get_zeroed_page(GFP_KERNEL | rk_ops->gfp_flags); if (!rk_domain->dt) goto err_free_domain; @@ -1405,18 +1377,16 @@ static struct rk_iommu_ops iommu_data_ops_v1 = { .pt_address = &rk_dte_pt_address, .mk_dtentries = &rk_mk_dte, .mk_ptentries = &rk_mk_pte, - .dte_addr_phys = &rk_dte_addr_phys, - .dma_addr_dte = &rk_dma_addr_dte, .dma_bit_mask = DMA_BIT_MASK(32), + .gfp_flags = GFP_DMA32, }; static struct rk_iommu_ops iommu_data_ops_v2 = { .pt_address = &rk_dte_pt_address_v2, .mk_dtentries = &rk_mk_dte_v2, .mk_ptentries = &rk_mk_pte_v2, - .dte_addr_phys = &rk_dte_addr_phys_v2, - .dma_addr_dte = &rk_dma_addr_dte_v2, .dma_bit_mask = DMA_BIT_MASK(40), + .gfp_flags = 0, }; static const struct of_device_id rk_iommu_dt_ids[] = { diff --git a/drivers/iommu/sprd-iommu.c b/drivers/iommu/sprd-iommu.c index 39e34fdeccda7842fa1ff677b4b0949c6ddbd120..2fa9afebd4f5f023ef494e5e1e6d717de7c443c1 100644 --- a/drivers/iommu/sprd-iommu.c +++ b/drivers/iommu/sprd-iommu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -148,6 +149,7 @@ static struct iommu_domain *sprd_iommu_domain_alloc(unsigned int domain_type) dom->domain.geometry.aperture_start = 0; dom->domain.geometry.aperture_end = SZ_256M - 1; + dom->domain.geometry.force_aperture = true; return &dom->domain; } diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 1cbf063ccf147ad9944fc34965dda5791d51ee50..e445f80d02263b405f9ed9a8218418dd6c9585a5 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c index 3551ed057774e648311f8c3f8ca71fb09f32161b..17dcd826f5c20e16b62daf837c2dc88ad5275d5f 100644 --- a/drivers/iommu/virtio-iommu.c +++ b/drivers/iommu/virtio-iommu.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/irqchip/irq-gic-common.h b/drivers/irqchip/irq-gic-common.h index 3db4592cda1c01ad8a27e73f62b9d0890019667d..f407cce9ecaaae3e1ef4e0265784336845648f8b 100644 --- a/drivers/irqchip/irq-gic-common.h +++ b/drivers/irqchip/irq-gic-common.h @@ -29,4 +29,8 @@ void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks, void gic_enable_of_quirks(const struct device_node *np, const struct gic_quirk *quirks, void *data); +#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) +#define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1) +#define RDIST_FLAGS_FORCE_NON_SHAREABLE (1 << 2) + #endif /* _IRQ_GIC_COMMON_H */ diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e0c2b10d154d64586ac887102c988826f0fa3874..75a2dd550625bc015dfe34a41e489d2006c8887c 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -44,10 +44,6 @@ #define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) #define ITS_FLAGS_FORCE_NON_SHAREABLE (1ULL << 3) -#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) -#define RDIST_FLAGS_RD_TABLES_PREALLOCATED (1 << 1) -#define RDIST_FLAGS_FORCE_NON_SHAREABLE (1 << 2) - #define RD_LOCAL_LPI_ENABLED BIT(0) #define RD_LOCAL_PENDTABLE_PREALLOCATED BIT(1) #define RD_LOCAL_MEMRESERVE_DONE BIT(2) @@ -4754,6 +4750,14 @@ static bool __maybe_unused its_enable_rk3588001(void *data) return true; } +static bool its_set_non_coherent(void *data) +{ + struct its_node *its = data; + + its->flags |= ITS_FLAGS_FORCE_NON_SHAREABLE; + return true; +} + static const struct gic_quirk its_quirks[] = { #ifdef CONFIG_CAVIUM_ERRATUM_22375 { @@ -4808,6 +4812,11 @@ static const struct gic_quirk its_quirks[] = { .init = its_enable_rk3588001, }, #endif + { + .desc = "ITS: non-coherent attribute", + .property = "dma-noncoherent", + .init = its_set_non_coherent, + }, { } }; @@ -4817,6 +4826,10 @@ static void its_enable_quirks(struct its_node *its) u32 iidr = readl_relaxed(its->base + GITS_IIDR); gic_enable_quirks(iidr, its_quirks, its); + + if (is_of_node(its->fwnode_handle)) + gic_enable_of_quirks(to_of_node(its->fwnode_handle), + its_quirks, its); } static int its_save_disable(void) @@ -4952,7 +4965,7 @@ static void __init __iomem *its_map_one(struct resource *res, int *err) return NULL; } -static int its_init_domain(struct fwnode_handle *handle, struct its_node *its) +static int its_init_domain(struct its_node *its) { struct irq_domain *inner_domain; struct msi_domain_info *info; @@ -4966,7 +4979,7 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its) inner_domain = irq_domain_create_hierarchy(its_parent, its->msi_domain_flags, 0, - handle, &its_domain_ops, + its->fwnode_handle, &its_domain_ops, info); if (!inner_domain) { kfree(info); @@ -5017,8 +5030,7 @@ static int its_init_vpe_domain(void) return 0; } -static int __init its_compute_its_list_map(struct resource *res, - void __iomem *its_base) +static int __init its_compute_its_list_map(struct its_node *its) { int its_number; u32 ctlr; @@ -5032,15 +5044,15 @@ static int __init its_compute_its_list_map(struct resource *res, its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX); if (its_number >= GICv4_ITS_LIST_MAX) { pr_err("ITS@%pa: No ITSList entry available!\n", - &res->start); + &its->phys_base); return -EINVAL; } - ctlr = readl_relaxed(its_base + GITS_CTLR); + ctlr = readl_relaxed(its->base + GITS_CTLR); ctlr &= ~GITS_CTLR_ITS_NUMBER; ctlr |= its_number << GITS_CTLR_ITS_NUMBER_SHIFT; - writel_relaxed(ctlr, its_base + GITS_CTLR); - ctlr = readl_relaxed(its_base + GITS_CTLR); + writel_relaxed(ctlr, its->base + GITS_CTLR); + ctlr = readl_relaxed(its->base + GITS_CTLR); if ((ctlr & GITS_CTLR_ITS_NUMBER) != (its_number << GITS_CTLR_ITS_NUMBER_SHIFT)) { its_number = ctlr & GITS_CTLR_ITS_NUMBER; its_number >>= GITS_CTLR_ITS_NUMBER_SHIFT; @@ -5048,75 +5060,50 @@ static int __init its_compute_its_list_map(struct resource *res, if (test_and_set_bit(its_number, &its_list_map)) { pr_err("ITS@%pa: Duplicate ITSList entry %d\n", - &res->start, its_number); + &its->phys_base, its_number); return -EINVAL; } return its_number; } -static int __init its_probe_one(struct resource *res, - struct fwnode_handle *handle, int numa_node) +static int __init its_probe_one(struct its_node *its) { - struct its_node *its; - void __iomem *its_base; - u64 baser, tmp, typer; + u64 baser, tmp; struct page *page; u32 ctlr; int err; - its_base = its_map_one(res, &err); - if (!its_base) - return err; - - pr_info("ITS %pR\n", res); - - its = kzalloc(sizeof(*its), GFP_KERNEL); - if (!its) { - err = -ENOMEM; - goto out_unmap; - } - - raw_spin_lock_init(&its->lock); - mutex_init(&its->dev_alloc_lock); - INIT_LIST_HEAD(&its->entry); - INIT_LIST_HEAD(&its->its_device_list); - typer = gic_read_typer(its_base + GITS_TYPER); - its->typer = typer; - its->base = its_base; - its->phys_base = res->start; if (is_v4(its)) { - if (!(typer & GITS_TYPER_VMOVP)) { - err = its_compute_its_list_map(res, its_base); + if (!(its->typer & GITS_TYPER_VMOVP)) { + err = its_compute_its_list_map(its); if (err < 0) - goto out_free_its; + goto out; its->list_nr = err; pr_info("ITS@%pa: Using ITS number %d\n", - &res->start, err); + &its->phys_base, err); } else { - pr_info("ITS@%pa: Single VMOVP capable\n", &res->start); + pr_info("ITS@%pa: Single VMOVP capable\n", &its->phys_base); } if (is_v4_1(its)) { - u32 svpet = FIELD_GET(GITS_TYPER_SVPET, typer); + u32 svpet = FIELD_GET(GITS_TYPER_SVPET, its->typer); - its->sgir_base = ioremap(res->start + SZ_128K, SZ_64K); + its->sgir_base = ioremap(its->phys_base + SZ_128K, SZ_64K); if (!its->sgir_base) { err = -ENOMEM; - goto out_free_its; + goto out; } - its->mpidr = readl_relaxed(its_base + GITS_MPIDR); + its->mpidr = readl_relaxed(its->base + GITS_MPIDR); pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n", - &res->start, its->mpidr, svpet); + &its->phys_base, its->mpidr, svpet); } } - its->numa_node = numa_node; - page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, get_order(ITS_CMD_QUEUE_SZ)); if (!page) { @@ -5125,12 +5112,9 @@ static int __init its_probe_one(struct resource *res, } its->cmd_base = (void *)page_address(page); its->cmd_write = its->cmd_base; - its->fwnode_handle = handle; its->get_msi_base = its_irq_get_msi_base; its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI; - its_enable_quirks(its); - err = its_alloc_tables(its); if (err) goto out_free_cmd; @@ -5174,7 +5158,7 @@ static int __init its_probe_one(struct resource *res, ctlr |= GITS_CTLR_ImDe; writel_relaxed(ctlr, its->base + GITS_CTLR); - err = its_init_domain(handle, its); + err = its_init_domain(its); if (err) goto out_free_tables; @@ -5191,11 +5175,8 @@ static int __init its_probe_one(struct resource *res, out_unmap_sgir: if (its->sgir_base) iounmap(its->sgir_base); -out_free_its: - kfree(its); -out_unmap: - iounmap(its_base); - pr_err("ITS@%pa: failed probing (%d)\n", &res->start, err); +out: + pr_err("ITS@%pa: failed probing (%d)\n", &its->phys_base, err); return err; } @@ -5356,10 +5337,53 @@ static const struct of_device_id its_device_id[] = { {}, }; +static struct its_node __init *its_node_init(struct resource *res, + struct fwnode_handle *handle, int numa_node) +{ + void __iomem *its_base; + struct its_node *its; + int err; + + its_base = its_map_one(res, &err); + if (!its_base) + return NULL; + + pr_info("ITS %pR\n", res); + + its = kzalloc(sizeof(*its), GFP_KERNEL); + if (!its) + goto out_unmap; + + raw_spin_lock_init(&its->lock); + mutex_init(&its->dev_alloc_lock); + INIT_LIST_HEAD(&its->entry); + INIT_LIST_HEAD(&its->its_device_list); + + its->typer = gic_read_typer(its_base + GITS_TYPER); + its->base = its_base; + its->phys_base = res->start; + + its->numa_node = numa_node; + its->fwnode_handle = handle; + + return its; + +out_unmap: + iounmap(its_base); + return NULL; +} + +static void its_node_destroy(struct its_node *its) +{ + iounmap(its->base); + kfree(its); +} + static int __init its_of_probe(struct device_node *node) { struct device_node *np; struct resource res; + int err; /* * Make sure *all* the ITS are reset before we probe any, as @@ -5369,8 +5393,6 @@ static int __init its_of_probe(struct device_node *node) */ for (np = of_find_matching_node(node, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { - int err; - if (!of_device_is_available(np) || !of_property_read_bool(np, "msi-controller") || of_address_to_resource(np, 0, &res)) @@ -5383,6 +5405,8 @@ static int __init its_of_probe(struct device_node *node) for (np = of_find_matching_node(node, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { + struct its_node *its; + if (!of_device_is_available(np)) continue; if (!of_property_read_bool(np, "msi-controller")) { @@ -5396,7 +5420,17 @@ static int __init its_of_probe(struct device_node *node) continue; } - its_probe_one(&res, &np->fwnode, of_node_to_nid(np)); + + its = its_node_init(&res, &np->fwnode, of_node_to_nid(np)); + if (!its) + return -ENOMEM; + + its_enable_quirks(its); + err = its_probe_one(its); + if (err) { + its_node_destroy(its); + return err; + } } return 0; } @@ -5508,6 +5542,7 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header, { struct acpi_madt_generic_translator *its_entry; struct fwnode_handle *dom_handle; + struct its_node *its; struct resource res; int err; @@ -5532,11 +5567,18 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header, goto dom_err; } - err = its_probe_one(&res, dom_handle, - acpi_get_its_numa_node(its_entry->translation_id)); + its = its_node_init(&res, dom_handle, + acpi_get_its_numa_node(its_entry->translation_id)); + if (!its) { + err = -ENOMEM; + goto node_err; + } + + err = its_probe_one(its); if (!err) return 0; +node_err: iort_deregister_domain_token(its_entry->translation_id); dom_err: irq_domain_free_fwnode(dom_handle); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index eedfa8e9f0772c6f55e44520a4be443b689ca7f1..f59ac9586b7b1d2240ea66aec29a4e0dde676919 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1857,6 +1857,14 @@ static bool gic_enable_quirk_arm64_2941627(void *data) return true; } +static bool rd_set_non_coherent(void *data) +{ + struct gic_chip_data *d = data; + + d->rdists.flags |= RDIST_FLAGS_FORCE_NON_SHAREABLE; + return true; +} + static const struct gic_quirk gic_quirks[] = { { .desc = "GICv3: Qualcomm MSM8996 broken firmware", @@ -1923,6 +1931,11 @@ static const struct gic_quirk gic_quirks[] = { .mask = 0xff0f0fff, .init = gic_enable_quirk_arm64_2941627, }, + { + .desc = "GICv3: non-coherent attribute", + .property = "dma-noncoherent", + .init = rd_set_non_coherent, + }, { } }; diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c index 4bbfa2b0a4df9b83e0fd40942eed805fd984b498..96f4e322ed6b727cb9a4d732b28ed309e21e04f6 100644 --- a/drivers/irqchip/irq-renesas-rzg2l.c +++ b/drivers/irqchip/irq-renesas-rzg2l.c @@ -118,7 +118,7 @@ static void rzg2l_irqc_irq_disable(struct irq_data *d) raw_spin_lock(&priv->lock); reg = readl_relaxed(priv->base + TSSR(tssr_index)); - reg &= ~(TSSEL_MASK << tssr_offset); + reg &= ~(TSSEL_MASK << TSSEL_SHIFT(tssr_offset)); writel_relaxed(reg, priv->base + TSSR(tssr_index)); raw_spin_unlock(&priv->lock); } @@ -130,8 +130,8 @@ static void rzg2l_irqc_irq_enable(struct irq_data *d) unsigned int hw_irq = irqd_to_hwirq(d); if (hw_irq >= IRQC_TINT_START && hw_irq < IRQC_NUM_IRQ) { + unsigned long tint = (uintptr_t)irq_data_get_irq_chip_data(d); struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); - unsigned long tint = (uintptr_t)d->chip_data; u32 offset = hw_irq - IRQC_TINT_START; u32 tssr_offset = TSSR_OFFSET(offset); u8 tssr_index = TSSR_INDEX(offset); diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 4adeee1bc391fb17aed9a3097a0bb70665ecc166..e8d01b14ccdde7848c7fb14489a5a015b86e8e95 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -155,8 +155,16 @@ static int __init riscv_intc_init(struct device_node *node, * for each INTC DT node. We only need to do INTC initialization * for the INTC DT node belonging to boot CPU (or boot HART). */ - if (riscv_hartid_to_cpuid(hartid) != smp_processor_id()) + if (riscv_hartid_to_cpuid(hartid) != smp_processor_id()) { + /* + * The INTC nodes of each CPU are suppliers for downstream + * interrupt controllers (such as PLIC, IMSIC and APLIC + * direct-mode) so we should mark an INTC node as initialized + * if we are not creating IRQ domain for it. + */ + fwnode_dev_initialized(of_fwnode_handle(node), true); return 0; + } return riscv_intc_init_common(of_node_to_fwnode(node)); } diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index d8ba5fba7450a94f0c6dabebb186b8200fe0d866..971240e2e31b48ef284d06f406eb62e41ff7cc53 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c @@ -460,6 +460,7 @@ static const struct irq_domain_ops irq_exti_domain_ops = { .map = irq_map_generic_chip, .alloc = stm32_exti_alloc, .free = stm32_exti_free, + .xlate = irq_domain_xlate_twocell, }; static void stm32_irq_ack(struct irq_data *d) diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c index 8c581c985aa7ddb0e9c96008807fb945eb1361ea..7f314e58f3ce56f10e3e9fc7ad4e266b25180231 100644 --- a/drivers/irqchip/irq-xtensa-mx.c +++ b/drivers/irqchip/irq-xtensa-mx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index a32c0d28d038467ac10c28cb7c00e4305bab720e..74b2f124116e3415d77269959c1ed5e7d7efd671 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -22,9 +22,20 @@ #define PDC_MAX_GPIO_IRQS 256 +/* Valid only on HW version < 3.2 */ #define IRQ_ENABLE_BANK 0x10 #define IRQ_i_CFG 0x110 +/* Valid only on HW version >= 3.2 */ +#define IRQ_i_CFG_IRQ_ENABLE 3 + +#define IRQ_i_CFG_TYPE_MASK GENMASK(2, 0) + +#define PDC_VERSION_REG 0x1000 + +/* Notable PDC versions */ +#define PDC_VERSION_3_2 0x30200 + struct pdc_pin_region { u32 pin_base; u32 parent_base; @@ -37,6 +48,7 @@ static DEFINE_RAW_SPINLOCK(pdc_lock); static void __iomem *pdc_base; static struct pdc_pin_region *pdc_region; static int pdc_region_cnt; +static unsigned int pdc_version; static void pdc_reg_write(int reg, u32 i, u32 val) { @@ -48,20 +60,32 @@ static u32 pdc_reg_read(int reg, u32 i) return readl_relaxed(pdc_base + reg + i * sizeof(u32)); } -static void pdc_enable_intr(struct irq_data *d, bool on) +static void __pdc_enable_intr(int pin_out, bool on) { - int pin_out = d->hwirq; unsigned long enable; - unsigned long flags; - u32 index, mask; - index = pin_out / 32; - mask = pin_out % 32; + if (pdc_version < PDC_VERSION_3_2) { + u32 index, mask; + + index = pin_out / 32; + mask = pin_out % 32; + + enable = pdc_reg_read(IRQ_ENABLE_BANK, index); + __assign_bit(mask, &enable, on); + pdc_reg_write(IRQ_ENABLE_BANK, index, enable); + } else { + enable = pdc_reg_read(IRQ_i_CFG, pin_out); + __assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on); + pdc_reg_write(IRQ_i_CFG, pin_out, enable); + } +} + +static void pdc_enable_intr(struct irq_data *d, bool on) +{ + unsigned long flags; raw_spin_lock_irqsave(&pdc_lock, flags); - enable = pdc_reg_read(IRQ_ENABLE_BANK, index); - __assign_bit(mask, &enable, on); - pdc_reg_write(IRQ_ENABLE_BANK, index, enable); + __pdc_enable_intr(d->hwirq, on); raw_spin_unlock_irqrestore(&pdc_lock, flags); } @@ -142,6 +166,7 @@ static int qcom_pdc_gic_set_type(struct irq_data *d, unsigned int type) } old_pdc_type = pdc_reg_read(IRQ_i_CFG, d->hwirq); + pdc_type |= (old_pdc_type & ~IRQ_i_CFG_TYPE_MASK); pdc_reg_write(IRQ_i_CFG, d->hwirq, pdc_type); ret = irq_chip_set_type_parent(d, type); @@ -246,7 +271,6 @@ static const struct irq_domain_ops qcom_pdc_ops = { static int pdc_setup_pin_mapping(struct device_node *np) { int ret, n, i; - u32 irq_index, reg_index, val; n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32)); if (n <= 0 || n % 3) @@ -276,29 +300,38 @@ static int pdc_setup_pin_mapping(struct device_node *np) if (ret) return ret; - for (i = 0; i < pdc_region[n].cnt; i++) { - reg_index = (i + pdc_region[n].pin_base) >> 5; - irq_index = (i + pdc_region[n].pin_base) & 0x1f; - val = pdc_reg_read(IRQ_ENABLE_BANK, reg_index); - val &= ~BIT(irq_index); - pdc_reg_write(IRQ_ENABLE_BANK, reg_index, val); - } + for (i = 0; i < pdc_region[n].cnt; i++) + __pdc_enable_intr(i + pdc_region[n].pin_base, 0); } return 0; } +#define QCOM_PDC_SIZE 0x30000 + static int qcom_pdc_init(struct device_node *node, struct device_node *parent) { struct irq_domain *parent_domain, *pdc_domain; + resource_size_t res_size; + struct resource res; int ret; - pdc_base = of_iomap(node, 0); + /* compat with old sm8150 DT which had very small region for PDC */ + if (of_address_to_resource(node, 0, &res)) + return -EINVAL; + + res_size = max_t(resource_size_t, resource_size(&res), QCOM_PDC_SIZE); + if (res_size > resource_size(&res)) + pr_warn("%pOF: invalid reg size, please fix DT\n", node); + + pdc_base = ioremap(res.start, res_size); if (!pdc_base) { pr_err("%pOF: unable to map PDC registers\n", node); return -ENXIO; } + pdc_version = pdc_reg_read(PDC_VERSION_REG, 0); + parent_domain = irq_find_host(parent); if (!parent_domain) { pr_err("%pOF: unable to find PDC's parent domain\n", node); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 6046dfeca16fce9087fe6c25ef56669f668fee41..b92208eccdea9aeb48a91fba79298b2d5908db7f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -521,6 +521,15 @@ config LEDS_PCA963X LED driver chip accessed via the I2C bus. Supported devices include PCA9633 and PCA9634 +config LEDS_PCA995X + tristate "LED Support for PCA995x I2C chips" + depends on LEDS_CLASS + depends on I2C + help + This option enables support for LEDs connected to PCA995x + LED driver chips accessed via the I2C bus. Supported + devices include PCA9955BTW, PCA9952TW and PCA9955TW. + config LEDS_WM831X_STATUS tristate "LED support for status LEDs on WM831x PMICs" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index d71f1226540c2ea2e1e95968def2426e086e83df..d7348e8bc019acf21a713507410d050fcb5f9251 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_LEDS_OT200) += leds-ot200.o obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o obj-$(CONFIG_LEDS_PCA963X) += leds-pca963x.o +obj-$(CONFIG_LEDS_PCA995X) += leds-pca995x.o obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o obj-$(CONFIG_LEDS_POWERNV) += leds-powernv.o obj-$(CONFIG_LEDS_PWM) += leds-pwm.o diff --git a/drivers/leds/blink/Kconfig b/drivers/leds/blink/Kconfig index 945c84286a4ed8ebe5b32be43f31e08e55e3753e..bdcb7377cd4e0fc1860207b6f60bfbfd6a4367f0 100644 --- a/drivers/leds/blink/Kconfig +++ b/drivers/leds/blink/Kconfig @@ -1,10 +1,10 @@ config LEDS_BCM63138 tristate "LED Support for Broadcom BCM63138 SoC" depends on LEDS_CLASS - depends on ARCH_BCM4908 || ARCH_BCM_5301X || BCM63XX || COMPILE_TEST + depends on ARCH_BCMBCA || ARCH_BCM_5301X || BCM63XX || COMPILE_TEST depends on HAS_IOMEM depends on OF - default ARCH_BCM4908 + default ARCH_BCMBCA help This option enables support for LED controller that is part of BCM63138 SoC. The same hardware block is known to be also used diff --git a/drivers/leds/flash/Kconfig b/drivers/leds/flash/Kconfig index 4ed2efc654345e7f206ba5a2a3a4dcdcc0229c02..4e08dbc057096f7239b5f628cf3cb6bb7407040c 100644 --- a/drivers/leds/flash/Kconfig +++ b/drivers/leds/flash/Kconfig @@ -89,6 +89,8 @@ config LEDS_QCOM_FLASH the total LED current will be split symmetrically on each channel and they will be enabled/disabled at the same time. + This driver can be built as a module, it will be called "leds-qcom-flash". + config LEDS_RT4505 tristate "LED support for RT4505 flashlight controller" depends on I2C && OF diff --git a/drivers/leds/flash/leds-qcom-flash.c b/drivers/leds/flash/leds-qcom-flash.c index b089ca1a19012eca234e99a9daee90ed32fdbf9a..a73d3ea5c97a3315f94946a5ddcd61c759c7cc21 100644 --- a/drivers/leds/flash/leds-qcom-flash.c +++ b/drivers/leds/flash/leds-qcom-flash.c @@ -309,6 +309,10 @@ static int qcom_flash_strobe_set(struct led_classdev_flash *fled_cdev, bool stat struct qcom_flash_led *led = flcdev_to_qcom_fled(fled_cdev); int rc; + rc = set_flash_strobe(led, SW_STROBE, false); + if (rc) + return rc; + rc = set_flash_current(led, led->flash_current_ma, FLASH_MODE); if (rc) return rc; @@ -745,6 +749,7 @@ static int qcom_flash_led_probe(struct platform_device *pdev) return 0; release: + fwnode_handle_put(child); while (flash_data->v4l2_flash[flash_data->leds_count] && flash_data->leds_count) v4l2_flash_release(flash_data->v4l2_flash[flash_data->leds_count--]); return rc; diff --git a/drivers/leds/led-class-multicolor.c b/drivers/leds/led-class-multicolor.c index e317408583df9f829f5d6f9544943971c31fdce4..ec62a48116135c5cf8069bfdbd4aa23aa9dcca39 100644 --- a/drivers/leds/led-class-multicolor.c +++ b/drivers/leds/led-class-multicolor.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -19,9 +20,10 @@ int led_mc_calc_color_components(struct led_classdev_mc *mcled_cdev, int i; for (i = 0; i < mcled_cdev->num_colors; i++) - mcled_cdev->subled_info[i].brightness = brightness * - mcled_cdev->subled_info[i].intensity / - led_cdev->max_brightness; + mcled_cdev->subled_info[i].brightness = + DIV_ROUND_CLOSEST(brightness * + mcled_cdev->subled_info[i].intensity, + led_cdev->max_brightness); return 0; } diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 6dae56b914fe3d0007d11cca51fc00e898b83455..974b84f6bd6af7ab0003d507f7dbcae62d641767 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -22,7 +22,6 @@ #include #include "leds.h" -static struct class *leds_class; static DEFINE_MUTEX(leds_lookup_lock); static LIST_HEAD(leds_lookup_list); @@ -76,6 +75,19 @@ static ssize_t max_brightness_show(struct device *dev, } static DEVICE_ATTR_RO(max_brightness); +static ssize_t color_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const char *color_text = "invalid"; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + + if (led_cdev->color < LED_COLOR_ID_MAX) + color_text = led_colors[led_cdev->color]; + + return sysfs_emit(buf, "%s\n", color_text); +} +static DEVICE_ATTR_RO(color); + #ifdef CONFIG_LEDS_TRIGGERS static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0); static struct bin_attribute *led_trigger_bin_attrs[] = { @@ -90,6 +102,7 @@ static const struct attribute_group led_trigger_group = { static struct attribute *led_class_attrs[] = { &dev_attr_brightness.attr, &dev_attr_max_brightness.attr, + &dev_attr_color.attr, NULL, }; @@ -234,6 +247,12 @@ static struct led_classdev *led_module_get(struct device *led_dev) return led_cdev; } +static const struct class leds_class = { + .name = "leds", + .dev_groups = led_groups, + .pm = &leds_class_dev_pm_ops, +}; + /** * of_led_get() - request a LED device via the LED framework * @np: device node to get the LED device from @@ -251,7 +270,7 @@ struct led_classdev *of_led_get(struct device_node *np, int index) if (!led_node) return ERR_PTR(-ENOENT); - led_dev = class_find_device_by_of_node(leds_class, led_node); + led_dev = class_find_device_by_of_node(&leds_class, led_node); of_node_put(led_node); put_device(led_dev); @@ -346,7 +365,7 @@ struct led_classdev *led_get(struct device *dev, char *con_id) if (!provider) return ERR_PTR(-ENOENT); - led_dev = class_find_device_by_name(leds_class, provider); + led_dev = class_find_device_by_name(&leds_class, provider); kfree_const(provider); return led_module_get(led_dev); @@ -402,6 +421,31 @@ void led_remove_lookup(struct led_lookup_data *led_lookup) } EXPORT_SYMBOL_GPL(led_remove_lookup); +/** + * devm_of_led_get_optional - Resource-managed request of an optional LED device + * @dev: LED consumer + * @index: index of the LED to obtain in the consumer + * + * The device node of the device is parsed to find the requested LED device. + * The LED device returned from this function is automatically released + * on driver detach. + * + * @return a pointer to a LED device, ERR_PTR(errno) on failure and NULL if the + * led was not found. + */ +struct led_classdev *__must_check devm_of_led_get_optional(struct device *dev, + int index) +{ + struct led_classdev *led; + + led = devm_of_led_get(dev, index); + if (IS_ERR(led) && PTR_ERR(led) == -ENOENT) + return NULL; + + return led; +} +EXPORT_SYMBOL_GPL(devm_of_led_get_optional); + static int led_classdev_next_name(const char *init_name, char *name, size_t len) { @@ -412,7 +456,7 @@ static int led_classdev_next_name(const char *init_name, char *name, strscpy(name, init_name, len); while ((ret < len) && - (dev = class_find_device_by_name(leds_class, name))) { + (dev = class_find_device_by_name(&leds_class, name))) { put_device(dev); ret = snprintf(name, len, "%s_%u", init_name, ++i); } @@ -457,6 +501,14 @@ int led_classdev_register_ext(struct device *parent, if (fwnode_property_present(init_data->fwnode, "retain-state-shutdown")) led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + + fwnode_property_read_u32(init_data->fwnode, + "max-brightness", + &led_cdev->max_brightness); + + if (fwnode_property_present(init_data->fwnode, "color")) + fwnode_property_read_u32(init_data->fwnode, "color", + &led_cdev->color); } } else { proposed_name = led_cdev->name; @@ -466,10 +518,13 @@ int led_classdev_register_ext(struct device *parent, if (ret < 0) return ret; + if (led_cdev->color >= LED_COLOR_ID_MAX) + dev_warn(parent, "LED %s color identifier out of range\n", final_name); + mutex_init(&led_cdev->led_access); mutex_lock(&led_cdev->led_access); - led_cdev->dev = device_create_with_groups(leds_class, parent, 0, - led_cdev, led_cdev->groups, "%s", final_name); + led_cdev->dev = device_create_with_groups(&leds_class, parent, 0, + led_cdev, led_cdev->groups, "%s", final_name); if (IS_ERR(led_cdev->dev)) { mutex_unlock(&led_cdev->led_access); return PTR_ERR(led_cdev->dev); @@ -626,17 +681,12 @@ EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); static int __init leds_init(void) { - leds_class = class_create("leds"); - if (IS_ERR(leds_class)) - return PTR_ERR(leds_class); - leds_class->pm = &leds_class_dev_pm_ops; - leds_class->dev_groups = led_groups; - return 0; + return class_register(&leds_class); } static void __exit leds_exit(void) { - class_destroy(leds_class); + class_unregister(&leds_class); } subsys_initcall(leds_init); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index b9b1295833c90a314e22447f6b6d0a4072bd2577..214ed81eb0e926cf8be1502395186c7d2bf6f0c1 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -474,10 +474,6 @@ int led_compose_name(struct device *dev, struct led_init_data *init_data, struct fwnode_handle *fwnode = init_data->fwnode; const char *devicename = init_data->devicename; - /* We want to label LEDs that can produce full range of colors - * as RGB, not multicolor */ - BUG_ON(props.color == LED_COLOR_ID_MULTI); - if (!led_classdev_name) return -EINVAL; diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index 24b1041213c27e2563e3ac1c14e41e3b60ce7655..0216afed3b6e72cafe8282ba44a114cae098512d 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -344,7 +344,7 @@ MODULE_DEVICE_TABLE(i2c, an30259a_id); static struct i2c_driver an30259a_driver = { .driver = { .name = "leds-an30259a", - .of_match_table = of_match_ptr(an30259a_match_table), + .of_match_table = an30259a_match_table, }, .probe = an30259a_probe, .remove = an30259a_remove, diff --git a/drivers/leds/leds-ariel.c b/drivers/leds/leds-ariel.c index 49e1bddaa15e0f6a845c76468b827e91c778c4e7..dd319c7e385fc1ad3e78cc2069a6b82caf43723c 100644 --- a/drivers/leds/leds-ariel.c +++ b/drivers/leds/leds-ariel.c @@ -7,8 +7,8 @@ #include #include +#include #include -#include enum ec_index { EC_BLUE_LED = 0x01, diff --git a/drivers/leds/leds-aw200xx.c b/drivers/leds/leds-aw200xx.c index 96979b8e09b7d70292f804870cd1435cb74ff752..691a743cc9b0fdb6c0b4df4ecc402cbc30dd37b3 100644 --- a/drivers/leds/leds-aw200xx.c +++ b/drivers/leds/leds-aw200xx.c @@ -368,7 +368,7 @@ static int aw200xx_probe_fw(struct device *dev, struct aw200xx *chip) if (!chip->display_rows || chip->display_rows > chip->cdef->display_size_rows_max) { - return dev_err_probe(dev, ret, + return dev_err_probe(dev, -EINVAL, "Invalid leds display size %u\n", chip->display_rows); } @@ -583,7 +583,7 @@ static struct i2c_driver aw200xx_driver = { .name = "aw200xx", .of_match_table = aw200xx_match_table, }, - .probe_new = aw200xx_probe, + .probe = aw200xx_probe, .remove = aw200xx_remove, .id_table = aw200xx_id, }; diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c index 59765640b70fe9c68aafd94feaafd5edd19aacb4..91f44b23cb113d6b53e877c8fc5ad17ab811d39e 100644 --- a/drivers/leds/leds-aw2013.c +++ b/drivers/leds/leds-aw2013.c @@ -62,7 +62,7 @@ struct aw2013_led { struct aw2013 { struct mutex mutex; /* held when writing to registers */ - struct regulator *vcc_regulator; + struct regulator_bulk_data regulators[2]; struct i2c_client *client; struct aw2013_led leds[AW2013_MAX_LEDS]; struct regmap *regmap; @@ -106,10 +106,11 @@ static void aw2013_chip_disable(struct aw2013 *chip) regmap_write(chip->regmap, AW2013_GCR, 0); - ret = regulator_disable(chip->vcc_regulator); + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), + chip->regulators); if (ret) { dev_err(&chip->client->dev, - "Failed to disable regulator: %d\n", ret); + "Failed to disable regulators: %d\n", ret); return; } @@ -123,10 +124,11 @@ static int aw2013_chip_enable(struct aw2013 *chip) if (chip->enabled) return 0; - ret = regulator_enable(chip->vcc_regulator); + ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), + chip->regulators); if (ret) { dev_err(&chip->client->dev, - "Failed to enable regulator: %d\n", ret); + "Failed to enable regulators: %d\n", ret); return ret; } chip->enabled = true; @@ -348,19 +350,23 @@ static int aw2013_probe(struct i2c_client *client) goto error; } - chip->vcc_regulator = devm_regulator_get(&client->dev, "vcc"); - ret = PTR_ERR_OR_ZERO(chip->vcc_regulator); - if (ret) { + chip->regulators[0].supply = "vcc"; + chip->regulators[1].supply = "vio"; + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(chip->regulators), + chip->regulators); + if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(&client->dev, - "Failed to request regulator: %d\n", ret); + "Failed to request regulators: %d\n", ret); goto error; } - ret = regulator_enable(chip->vcc_regulator); + ret = regulator_bulk_enable(ARRAY_SIZE(chip->regulators), + chip->regulators); if (ret) { dev_err(&client->dev, - "Failed to enable regulator: %d\n", ret); + "Failed to enable regulators: %d\n", ret); goto error; } @@ -382,10 +388,11 @@ static int aw2013_probe(struct i2c_client *client) if (ret < 0) goto error_reg; - ret = regulator_disable(chip->vcc_regulator); + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators), + chip->regulators); if (ret) { dev_err(&client->dev, - "Failed to disable regulator: %d\n", ret); + "Failed to disable regulators: %d\n", ret); goto error; } @@ -394,7 +401,8 @@ static int aw2013_probe(struct i2c_client *client) return 0; error_reg: - regulator_disable(chip->vcc_regulator); + regulator_bulk_disable(ARRAY_SIZE(chip->regulators), + chip->regulators); error: mutex_destroy(&chip->mutex); @@ -420,7 +428,7 @@ MODULE_DEVICE_TABLE(of, aw2013_match_table); static struct i2c_driver aw2013_driver = { .driver = { .name = "leds-aw2013", - .of_match_table = of_match_ptr(aw2013_match_table), + .of_match_table = aw2013_match_table, }, .probe = aw2013_probe, .remove = aw2013_remove, diff --git a/drivers/leds/leds-cpcap.c b/drivers/leds/leds-cpcap.c index 7d41ce8c9bb1e0c8f3bcff530e5a5d34eaff0d21..87354f17644bac3fac50467cef3acc15129652a9 100644 --- a/drivers/leds/leds-cpcap.c +++ b/drivers/leds/leds-cpcap.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/leds/leds-cr0014114.c b/drivers/leds/leds-cr0014114.c index c87686bd7c189fccc0629bcefdea8306d9c67d70..b33bca397ea60df451f31cd3dcafa40f0ac7475e 100644 --- a/drivers/leds/leds-cr0014114.c +++ b/drivers/leds/leds-cr0014114.c @@ -4,8 +4,8 @@ #include #include +#include #include -#include #include #include diff --git a/drivers/leds/leds-ip30.c b/drivers/leds/leds-ip30.c index 1f952bad0fe865268c9e4d79a12413d6e5977f4e..2df24c30336657288e60b13b7ab50c2f2051a98b 100644 --- a/drivers/leds/leds-ip30.c +++ b/drivers/leds/leds-ip30.c @@ -27,22 +27,16 @@ static void ip30led_set(struct led_classdev *led_cdev, static int ip30led_create(struct platform_device *pdev, int num) { - struct resource *res; struct ip30_led *data; - res = platform_get_resource(pdev, IORESOURCE_MEM, num); - if (!res) - return -EBUSY; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->reg = devm_ioremap_resource(&pdev->dev, res); + data->reg = devm_platform_ioremap_resource(pdev, num); if (IS_ERR(data->reg)) return PTR_ERR(data->reg); - switch (num) { case IP30_LED_SYSTEM: data->cdev.name = "white:power"; diff --git a/drivers/leds/leds-is31fl32xx.c b/drivers/leds/leds-is31fl32xx.c index 72cb56d305c433d0540a6ae8212760ed572ddc98..b0a0be77bb33b94039d44286e5941c72b6077eaf 100644 --- a/drivers/leds/leds-is31fl32xx.c +++ b/drivers/leds/leds-is31fl32xx.c @@ -15,7 +15,6 @@ #include #include #include -#include /* Used to indicate a device has no such register */ #define IS31FL32XX_REG_NONE 0xFF diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 030c040fdf6d69e899ef7bca995b058275c57fb5..2ef19ad23b1d5f5f7d4bb4022cac625c98bcd270 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -594,18 +594,17 @@ static const struct i2c_device_id lp5521_id[] = { }; MODULE_DEVICE_TABLE(i2c, lp5521_id); -#ifdef CONFIG_OF static const struct of_device_id of_lp5521_leds_match[] = { { .compatible = "national,lp5521", }, {}, }; MODULE_DEVICE_TABLE(of, of_lp5521_leds_match); -#endif + static struct i2c_driver lp5521_driver = { .driver = { .name = "lp5521", - .of_match_table = of_match_ptr(of_lp5521_leds_match), + .of_match_table = of_lp5521_leds_match, }, .probe = lp5521_probe, .remove = lp5521_remove, diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index daa6a165fba6be5231258c38c2f2ab85c55a7eff..38de853f9939a556a8a7efbe0dc760cb0ae2b9f4 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -972,7 +972,6 @@ static const struct i2c_device_id lp5523_id[] = { MODULE_DEVICE_TABLE(i2c, lp5523_id); -#ifdef CONFIG_OF static const struct of_device_id of_lp5523_leds_match[] = { { .compatible = "national,lp5523", }, { .compatible = "ti,lp55231", }, @@ -980,12 +979,11 @@ static const struct of_device_id of_lp5523_leds_match[] = { }; MODULE_DEVICE_TABLE(of, of_lp5523_leds_match); -#endif static struct i2c_driver lp5523_driver = { .driver = { .name = "lp5523x", - .of_match_table = of_match_ptr(of_lp5523_leds_match), + .of_match_table = of_lp5523_leds_match, }, .probe = lp5523_probe, .remove = lp5523_remove, diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index 4565cc12cea80031da2bf5a9a3da25384f5c76bc..39db9aeb67c5c2f40565cf48d20f8a9e9132a65a 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -589,19 +589,17 @@ static const struct i2c_device_id lp5562_id[] = { }; MODULE_DEVICE_TABLE(i2c, lp5562_id); -#ifdef CONFIG_OF static const struct of_device_id of_lp5562_leds_match[] = { { .compatible = "ti,lp5562", }, {}, }; MODULE_DEVICE_TABLE(of, of_lp5562_leds_match); -#endif static struct i2c_driver lp5562_driver = { .driver = { .name = "lp5562", - .of_match_table = of_match_ptr(of_lp5562_leds_match), + .of_match_table = of_lp5562_leds_match, }, .probe = lp5562_probe, .remove = lp5562_remove, diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index f11886aa8965dfa3c8b0ee250ae273604fff4f6a..ac50aa88939a3e71351bfe1fd2bb60ecf4df0125 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -380,19 +380,17 @@ static const struct i2c_device_id lp8501_id[] = { }; MODULE_DEVICE_TABLE(i2c, lp8501_id); -#ifdef CONFIG_OF static const struct of_device_id of_lp8501_leds_match[] = { { .compatible = "ti,lp8501", }, {}, }; MODULE_DEVICE_TABLE(of, of_lp8501_leds_match); -#endif static struct i2c_driver lp8501_driver = { .driver = { .name = "lp8501", - .of_match_table = of_match_ptr(of_lp8501_leds_match), + .of_match_table = of_lp8501_leds_match, }, .probe = lp8501_probe, .remove = lp8501_remove, diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c index b7855c93bd72507d78a2777411011bc0334b9d72..39210653acf7fe8c73ff0a006842483102c7bca6 100644 --- a/drivers/leds/leds-mlxreg.c +++ b/drivers/leds/leds-mlxreg.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 1677d66d8b0e349b79e5d8c4fbaaee855f62a3a1..f3010c472bbd4f95e51e02ad79da61dfa38318fb 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -247,7 +247,7 @@ static int ns2_led_probe(struct platform_device *pdev) if (!count) return -ENODEV; - leds = devm_kzalloc(dev, array_size(sizeof(*leds), count), GFP_KERNEL); + leds = devm_kcalloc(dev, count, sizeof(*leds), GFP_KERNEL); if (!leds) return -ENOMEM; diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 8b5c62083e50827ced35ab5e54284ded8e09b29e..bf8bb8fc007c63bb31e97d298ca826fb4974c39e 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -18,7 +18,6 @@ #include #include #include -#include /* m = num_leds*/ #define PCA9532_REG_INPUT(i) ((i) >> 3) diff --git a/drivers/leds/leds-pca995x.c b/drivers/leds/leds-pca995x.c new file mode 100644 index 0000000000000000000000000000000000000000..78215dff14997c931e6879dda136550241cf120d --- /dev/null +++ b/drivers/leds/leds-pca995x.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LED driver for PCA995x I2C LED drivers + * + * Copyright 2011 bct electronic GmbH + * Copyright 2013 Qtechnology/AS + * Copyright 2022 NXP + * Copyright 2023 Marek Vasut + */ + +#include +#include +#include +#include +#include +#include + +/* Register definition */ +#define PCA995X_MODE1 0x00 +#define PCA995X_MODE2 0x01 +#define PCA995X_LEDOUT0 0x02 +#define PCA9955B_PWM0 0x08 +#define PCA9952_PWM0 0x0A +#define PCA9952_IREFALL 0x43 +#define PCA9955B_IREFALL 0x45 + +/* Auto-increment disabled. Normal mode */ +#define PCA995X_MODE1_CFG 0x00 + +/* LED select registers determine the source that drives LED outputs */ +#define PCA995X_LED_OFF 0x0 +#define PCA995X_LED_ON 0x1 +#define PCA995X_LED_PWM_MODE 0x2 +#define PCA995X_LDRX_MASK 0x3 +#define PCA995X_LDRX_BITS 2 + +#define PCA995X_MAX_OUTPUTS 16 +#define PCA995X_OUTPUTS_PER_REG 4 + +#define PCA995X_IREFALL_FULL_CFG 0xFF +#define PCA995X_IREFALL_HALF_CFG (PCA995X_IREFALL_FULL_CFG / 2) + +#define PCA995X_TYPE_NON_B 0 +#define PCA995X_TYPE_B 1 + +#define ldev_to_led(c) container_of(c, struct pca995x_led, ldev) + +struct pca995x_led { + unsigned int led_no; + struct led_classdev ldev; + struct pca995x_chip *chip; +}; + +struct pca995x_chip { + struct regmap *regmap; + struct pca995x_led leds[PCA995X_MAX_OUTPUTS]; + int btype; +}; + +static int pca995x_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct pca995x_led *led = ldev_to_led(led_cdev); + struct pca995x_chip *chip = led->chip; + u8 ledout_addr, pwmout_addr; + int shift, ret; + + pwmout_addr = (chip->btype ? PCA9955B_PWM0 : PCA9952_PWM0) + led->led_no; + ledout_addr = PCA995X_LEDOUT0 + (led->led_no / PCA995X_OUTPUTS_PER_REG); + shift = PCA995X_LDRX_BITS * (led->led_no % PCA995X_OUTPUTS_PER_REG); + + switch (brightness) { + case LED_FULL: + return regmap_update_bits(chip->regmap, ledout_addr, + PCA995X_LDRX_MASK << shift, + PCA995X_LED_ON << shift); + case LED_OFF: + return regmap_update_bits(chip->regmap, ledout_addr, + PCA995X_LDRX_MASK << shift, 0); + default: + /* Adjust brightness as per user input by changing individual PWM */ + ret = regmap_write(chip->regmap, pwmout_addr, brightness); + if (ret) + return ret; + + /* + * Change LDRx configuration to individual brightness via PWM. + * LED will stop blinking if it's doing so. + */ + return regmap_update_bits(chip->regmap, ledout_addr, + PCA995X_LDRX_MASK << shift, + PCA995X_LED_PWM_MODE << shift); + } +} + +static const struct regmap_config pca995x_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x49, +}; + +static int pca995x_probe(struct i2c_client *client) +{ + struct fwnode_handle *led_fwnodes[PCA995X_MAX_OUTPUTS] = { 0 }; + struct fwnode_handle *np, *child; + struct device *dev = &client->dev; + struct pca995x_chip *chip; + struct pca995x_led *led; + int i, btype, reg, ret; + + btype = (unsigned long)device_get_match_data(&client->dev); + + np = dev_fwnode(dev); + if (!np) + return -ENODEV; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->btype = btype; + chip->regmap = devm_regmap_init_i2c(client, &pca995x_regmap); + if (IS_ERR(chip->regmap)) + return PTR_ERR(chip->regmap); + + i2c_set_clientdata(client, chip); + + fwnode_for_each_available_child_node(np, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) { + fwnode_handle_put(child); + return ret; + } + + if (reg < 0 || reg >= PCA995X_MAX_OUTPUTS || led_fwnodes[reg]) { + fwnode_handle_put(child); + return -EINVAL; + } + + led = &chip->leds[reg]; + led_fwnodes[reg] = child; + led->chip = chip; + led->led_no = reg; + led->ldev.brightness_set_blocking = pca995x_brightness_set; + led->ldev.max_brightness = 255; + } + + for (i = 0; i < PCA995X_MAX_OUTPUTS; i++) { + struct led_init_data init_data = {}; + + if (!led_fwnodes[i]) + continue; + + init_data.fwnode = led_fwnodes[i]; + + ret = devm_led_classdev_register_ext(dev, + &chip->leds[i].ldev, + &init_data); + if (ret < 0) { + fwnode_handle_put(child); + return dev_err_probe(dev, ret, + "Could not register LED %s\n", + chip->leds[i].ldev.name); + } + } + + /* Disable LED all-call address and set normal mode */ + ret = regmap_write(chip->regmap, PCA995X_MODE1, PCA995X_MODE1_CFG); + if (ret) + return ret; + + /* IREF Output current value for all LEDn outputs */ + return regmap_write(chip->regmap, + btype ? PCA9955B_IREFALL : PCA9952_IREFALL, + PCA995X_IREFALL_HALF_CFG); +} + +static const struct i2c_device_id pca995x_id[] = { + { "pca9952", .driver_data = (kernel_ulong_t)PCA995X_TYPE_NON_B }, + { "pca9955b", .driver_data = (kernel_ulong_t)PCA995X_TYPE_B }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pca995x_id); + +static const struct of_device_id pca995x_of_match[] = { + { .compatible = "nxp,pca9952", .data = (void *)PCA995X_TYPE_NON_B }, + { .compatible = "nxp,pca9955b", .data = (void *)PCA995X_TYPE_B }, + {}, +}; +MODULE_DEVICE_TABLE(of, pca995x_of_match); + +static struct i2c_driver pca995x_driver = { + .driver = { + .name = "leds-pca995x", + .of_match_table = pca995x_of_match, + }, + .probe = pca995x_probe, + .id_table = pca995x_id, +}; +module_i2c_driver(pca995x_driver); + +MODULE_AUTHOR("Isai Gaspar "); +MODULE_DESCRIPTION("PCA995x LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-pm8058.c b/drivers/leds/leds-pm8058.c index b9233f14b6467e26d389c0c87ea3901d5b3ab680..3f49a518189252447450bf9ac2d23889ac63ece6 100644 --- a/drivers/leds/leds-pm8058.c +++ b/drivers/leds/leds-pm8058.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 29194cc382afbc58315e9be435dc906d35fcf822..419b710984ab6200ce7cffe9a728cd27ad0354f6 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -146,7 +146,7 @@ static int led_pwm_create_fwnode(struct device *dev, struct led_pwm_priv *priv) led.name = to_of_node(fwnode)->name; if (!led.name) { - ret = EINVAL; + ret = -EINVAL; goto err_child_out; } diff --git a/drivers/leds/leds-spi-byte.c b/drivers/leds/leds-spi-byte.c index 2c7ffc3c78e666ede757bd077ea0c798060d80a7..9d91f21842f2b5568976bfa968a50a0f08c72c98 100644 --- a/drivers/leds/leds-spi-byte.c +++ b/drivers/leds/leds-spi-byte.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c index e38abb5e60c1bbbf9d0f2804179f290435628dc0..360a376fa738c4d305f48f99e1202b273934c264 100644 --- a/drivers/leds/leds-syscon.c +++ b/drivers/leds/leds-syscon.c @@ -7,8 +7,7 @@ */ #include #include -#include -#include +#include #include #include #include diff --git a/drivers/leds/leds-ti-lmu-common.c b/drivers/leds/leds-ti-lmu-common.c index d7f10ad721badc62bd79c72c060deddf87d4a77b..b2491666b5dc97dc757610ae453a2269532b0f02 100644 --- a/drivers/leds/leds-ti-lmu-common.c +++ b/drivers/leds/leds-ti-lmu-common.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index dfc6fb2b3e5224cc8d39154c9561f0133cf731b1..945e831ef4ac4db19725f10aecf920fa1172817e 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/drivers/leds/leds-turris-omnia.c b/drivers/leds/leds-turris-omnia.c index 64b2d7b6d3f315cfdd75dc5be5ee3855ff2b306a..b8a95a917cfa49983e1d5e797f0e7a60c5bf5360 100644 --- a/drivers/leds/leds-turris-omnia.c +++ b/drivers/leds/leds-turris-omnia.c @@ -156,24 +156,20 @@ static ssize_t brightness_show(struct device *dev, struct device_attribute *a, char *buf) { struct i2c_client *client = to_i2c_client(dev); - struct omnia_leds *leds = i2c_get_clientdata(client); int ret; - mutex_lock(&leds->lock); ret = i2c_smbus_read_byte_data(client, CMD_LED_GET_BRIGHTNESS); - mutex_unlock(&leds->lock); if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } static ssize_t brightness_store(struct device *dev, struct device_attribute *a, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - struct omnia_leds *leds = i2c_get_clientdata(client); unsigned long brightness; int ret; @@ -183,15 +179,10 @@ static ssize_t brightness_store(struct device *dev, struct device_attribute *a, if (brightness > 100) return -EINVAL; - mutex_lock(&leds->lock); ret = i2c_smbus_write_byte_data(client, CMD_LED_SET_BRIGHTNESS, (u8)brightness); - mutex_unlock(&leds->lock); - - if (ret < 0) - return ret; - return count; + return ret < 0 ? ret : count; } static DEVICE_ATTR_RW(brightness); diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig index 360c8679c6e236693968499e092853c000cbfdc0..183bccc06cf3ec5cc82dc575e0d49025aca7407a 100644 --- a/drivers/leds/rgb/Kconfig +++ b/drivers/leds/rgb/Kconfig @@ -2,6 +2,18 @@ if LEDS_CLASS_MULTICOLOR +config LEDS_GROUP_MULTICOLOR + tristate "LEDs group multi-color support" + depends on OF || COMPILE_TEST + help + This option enables support for monochrome LEDs that are grouped + into multicolor LEDs which is useful in the case where LEDs of + different colors are physically grouped in a single multi-color LED + and driven by a controller that doesn't have multi-color support. + + To compile this driver as a module, choose M here: the module + will be called leds-group-multicolor. + config LEDS_PWM_MULTICOLOR tristate "PWM driven multi-color LED Support" depends on PWM diff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile index 8c01daf63f61c4a770162d79a234fddfa957ad1f..c11cc56384e70ca39e1eaa886a81e8d61e14bff0 100644 --- a/drivers/leds/rgb/Makefile +++ b/drivers/leds/rgb/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_LEDS_GROUP_MULTICOLOR) += leds-group-multicolor.o obj-$(CONFIG_LEDS_PWM_MULTICOLOR) += leds-pwm-multicolor.o obj-$(CONFIG_LEDS_QCOM_LPG) += leds-qcom-lpg.o obj-$(CONFIG_LEDS_MT6370_RGB) += leds-mt6370-rgb.o diff --git a/drivers/leds/rgb/leds-group-multicolor.c b/drivers/leds/rgb/leds-group-multicolor.c new file mode 100644 index 0000000000000000000000000000000000000000..39f58be32af51a20b41c18b32bf5178d13e79e67 --- /dev/null +++ b/drivers/leds/rgb/leds-group-multicolor.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Multi-color LED built with monochromatic LED devices + * + * This driver groups several monochromatic LED devices in a single multicolor LED device. + * + * Compared to handling this grouping in user-space, the benefits are: + * - The state of the monochromatic LED relative to each other is always consistent. + * - The sysfs interface of the LEDs can be used for the group as a whole. + * + * Copyright 2023 Jean-Jacques Hiblot + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct leds_multicolor { + struct led_classdev_mc mc_cdev; + struct led_classdev **monochromatics; +}; + +static int leds_gmc_set(struct led_classdev *cdev, enum led_brightness brightness) +{ + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(cdev); + struct leds_multicolor *priv = container_of(mc_cdev, struct leds_multicolor, mc_cdev); + const unsigned int group_max_brightness = mc_cdev->led_cdev.max_brightness; + int i; + + for (i = 0; i < mc_cdev->num_colors; i++) { + struct led_classdev *mono = priv->monochromatics[i]; + const unsigned int mono_max_brightness = mono->max_brightness; + unsigned int intensity = mc_cdev->subled_info[i].intensity; + int mono_brightness; + + /* + * Scale the brightness according to relative intensity of the + * color AND the max brightness of the monochromatic LED. + */ + mono_brightness = DIV_ROUND_CLOSEST(brightness * intensity * mono_max_brightness, + group_max_brightness * group_max_brightness); + + led_set_brightness(mono, mono_brightness); + } + + return 0; +} + +static void restore_sysfs_write_access(void *data) +{ + struct led_classdev *led_cdev = data; + + /* Restore the write acccess to the LED */ + mutex_lock(&led_cdev->led_access); + led_sysfs_enable(led_cdev); + mutex_unlock(&led_cdev->led_access); +} + +static int leds_gmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct led_init_data init_data = {}; + struct led_classdev *cdev; + struct mc_subled *subled; + struct leds_multicolor *priv; + unsigned int max_brightness = 0; + int i, ret, count = 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + for (;;) { + struct led_classdev *led_cdev; + + led_cdev = devm_of_led_get_optional(dev, count); + if (IS_ERR(led_cdev)) + return dev_err_probe(dev, PTR_ERR(led_cdev), "Unable to get LED #%d", + count); + if (!led_cdev) + break; + + priv->monochromatics = devm_krealloc_array(dev, priv->monochromatics, + count + 1, sizeof(*priv->monochromatics), + GFP_KERNEL); + if (!priv->monochromatics) + return -ENOMEM; + + priv->monochromatics[count] = led_cdev; + + max_brightness = max(max_brightness, led_cdev->max_brightness); + + count++; + } + + subled = devm_kcalloc(dev, count, sizeof(*subled), GFP_KERNEL); + if (!subled) + return -ENOMEM; + priv->mc_cdev.subled_info = subled; + + for (i = 0; i < count; i++) { + struct led_classdev *led_cdev = priv->monochromatics[i]; + + subled[i].color_index = led_cdev->color; + + /* Configure the LED intensity to its maximum */ + subled[i].intensity = max_brightness; + } + + /* Initialise the multicolor's LED class device */ + cdev = &priv->mc_cdev.led_cdev; + cdev->flags = LED_CORE_SUSPENDRESUME; + cdev->brightness_set_blocking = leds_gmc_set; + cdev->max_brightness = max_brightness; + cdev->color = LED_COLOR_ID_MULTI; + priv->mc_cdev.num_colors = count; + + init_data.fwnode = dev_fwnode(dev); + ret = devm_led_classdev_multicolor_register_ext(dev, &priv->mc_cdev, &init_data); + if (ret) + return dev_err_probe(dev, ret, "failed to register multicolor LED for %s.\n", + cdev->name); + + ret = leds_gmc_set(cdev, cdev->brightness); + if (ret) + return dev_err_probe(dev, ret, "failed to set LED value for %s.", cdev->name); + + for (i = 0; i < count; i++) { + struct led_classdev *led_cdev = priv->monochromatics[i]; + + /* + * Make the individual LED sysfs interface read-only to prevent the user + * to change the brightness of the individual LEDs of the group. + */ + mutex_lock(&led_cdev->led_access); + led_sysfs_disable(led_cdev); + mutex_unlock(&led_cdev->led_access); + + /* Restore the write access to the LED sysfs when the group is destroyed */ + devm_add_action_or_reset(dev, restore_sysfs_write_access, led_cdev); + } + + return 0; +} + +static const struct of_device_id of_leds_group_multicolor_match[] = { + { .compatible = "leds-group-multicolor" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_leds_group_multicolor_match); + +static struct platform_driver leds_group_multicolor_driver = { + .probe = leds_gmc_probe, + .driver = { + .name = "leds_group_multicolor", + .of_match_table = of_leds_group_multicolor_match, + } +}; +module_platform_driver(leds_group_multicolor_driver); + +MODULE_AUTHOR("Jean-Jacques Hiblot "); +MODULE_DESCRIPTION("LEDs group multicolor driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:leds-group-multicolor"); diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c index 59581b3e25ca98ff87dc6d749034554153df47a0..df469aaa7e6e7ecf00d4be99f736c8da5fd262f4 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -1093,7 +1092,6 @@ static int lpg_add_pwm(struct lpg *lpg) { int ret; - lpg->pwm.base = -1; lpg->pwm.dev = lpg->dev; lpg->pwm.npwm = lpg->num_channels; lpg->pwm.ops = &lpg_pwm_ops; diff --git a/drivers/leds/simple/Kconfig b/drivers/leds/simple/Kconfig index 609e438af9f65743c6249a3d6f28575c06c6399a..e616cc6d6051f520e2036e1f19c9cb8e2fa16d3e 100644 --- a/drivers/leds/simple/Kconfig +++ b/drivers/leds/simple/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config LEDS_SIEMENS_SIMATIC_IPC tristate "LED driver for Siemens Simatic IPCs" + depends on LEDS_CLASS depends on SIEMENS_SIMATIC_IPC default y help @@ -35,3 +36,16 @@ config LEDS_SIEMENS_SIMATIC_IPC_F7188X To compile this driver as a module, choose M here: the module will be called simatic-ipc-leds-gpio-f7188x. + +config LEDS_SIEMENS_SIMATIC_IPC_ELKHARTLAKE + tristate "LED driver for Siemens Simatic IPCs based on Intel Elkhart Lake GPIO" + depends on LEDS_GPIO + depends on PINCTRL_ELKHARTLAKE + depends on SIEMENS_SIMATIC_IPC + default LEDS_SIEMENS_SIMATIC_IPC + help + This option enables support for the LEDs of several Industrial PCs + from Siemens based on Elkhart Lake GPIO i.e. BX-21A. + + To compile this driver as a module, choose M here: the module + will be called simatic-ipc-leds-gpio-elkhartlake. diff --git a/drivers/leds/simple/Makefile b/drivers/leds/simple/Makefile index e3e840cea275ca8870aaddee09d46670f1025912..783578f11bb0a1462263cdb9ad4266d7bfd1a3ed 100644 --- a/drivers/leds/simple/Makefile +++ b/drivers/leds/simple/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_APOLLOLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-apollolake.o obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_F7188X) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-f7188x.o +obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC_ELKHARTLAKE) += simatic-ipc-leds-gpio-core.o simatic-ipc-leds-gpio-elkhartlake.o diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c index 2a21b663df8746f4c285ea411f837a6a2a84cb1a..c552ea73ed9df62d345174cb0b7033a1d03ec6f0 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio-core.c +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-core.c @@ -57,6 +57,7 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev, switch (plat->devmode) { case SIMATIC_IPC_DEVICE_127E: case SIMATIC_IPC_DEVICE_227G: + case SIMATIC_IPC_DEVICE_BX_21A: break; default: return -ENODEV; @@ -72,6 +73,9 @@ int simatic_ipc_leds_gpio_probe(struct platform_device *pdev, goto out; } + if (!table_extra) + return 0; + table_extra->dev_id = dev_name(dev); gpiod_add_lookup_table(table_extra); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c new file mode 100644 index 0000000000000000000000000000000000000000..6ba21dbb3ba0a6027c318426ea1d6babbeac7901 --- /dev/null +++ b/drivers/leds/simple/simatic-ipc-leds-gpio-elkhartlake.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2023 + * + * Author: + * Henning Schild + */ + +#include +#include +#include +#include +#include +#include + +#include "simatic-ipc-leds-gpio.h" + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("INTC1020:04", 72, NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 77, NULL, 1, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 78, NULL, 2, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 58, NULL, 3, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 60, NULL, 4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("INTC1020:04", 62, NULL, 5, GPIO_ACTIVE_HIGH), + {} /* Terminating entry */ + }, +}; + +static int simatic_ipc_leds_gpio_elkhartlake_probe(struct platform_device *pdev) +{ + return simatic_ipc_leds_gpio_probe(pdev, &simatic_ipc_led_gpio_table, + NULL); +} + +static int simatic_ipc_leds_gpio_elkhartlake_remove(struct platform_device *pdev) +{ + return simatic_ipc_leds_gpio_remove(pdev, &simatic_ipc_led_gpio_table, + NULL); +} + +static struct platform_driver simatic_ipc_led_gpio_elkhartlake_driver = { + .probe = simatic_ipc_leds_gpio_elkhartlake_probe, + .remove = simatic_ipc_leds_gpio_elkhartlake_remove, + .driver = { + .name = KBUILD_MODNAME, + }, +}; +module_platform_driver(simatic_ipc_led_gpio_elkhartlake_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_SOFTDEP("pre: simatic-ipc-leds-gpio-core platform:elkhartlake-pinctrl"); +MODULE_AUTHOR("Henning Schild "); diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.h b/drivers/leds/simple/simatic-ipc-leds-gpio.h index bf258c32f83def04081592b15d374a8abb68cb5e..3d4877aa4e0cb4a27aa2e7808bcbfc85cd8bc89d 100644 --- a/drivers/leds/simple/simatic-ipc-leds-gpio.h +++ b/drivers/leds/simple/simatic-ipc-leds-gpio.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * Siemens SIMATIC IPC driver for GPIO based LEDs * diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index cc3261543a5e5dfb24c7955eeafb75a6cee19388..58f3352539e8e5404a3858768934ac5319a8c14d 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -609,18 +609,7 @@ static struct led_trigger netdev_led_trigger = { .groups = netdev_trig_groups, }; -static int __init netdev_trig_init(void) -{ - return led_trigger_register(&netdev_led_trigger); -} - -static void __exit netdev_trig_exit(void) -{ - led_trigger_unregister(&netdev_led_trigger); -} - -module_init(netdev_trig_init); -module_exit(netdev_trig_exit); +module_led_trigger(netdev_led_trigger); MODULE_AUTHOR("Ben Whitten "); MODULE_AUTHOR("Oliver Jowett "); diff --git a/drivers/leds/trigger/ledtrig-tty.c b/drivers/leds/trigger/ledtrig-tty.c index f62db7e520b52353a0037cdfddecf313663b095a..8ae0d2d284aff758f7ac786d540e5ae322248161 100644 --- a/drivers/leds/trigger/ledtrig-tty.c +++ b/drivers/leds/trigger/ledtrig-tty.c @@ -7,6 +7,8 @@ #include #include +#define LEDTRIG_TTY_INTERVAL 50 + struct ledtrig_tty_data { struct led_classdev *led_cdev; struct delayed_work dwork; @@ -122,17 +124,19 @@ static void ledtrig_tty_work(struct work_struct *work) if (icount.rx != trigger_data->rx || icount.tx != trigger_data->tx) { - led_set_brightness_sync(trigger_data->led_cdev, LED_ON); + unsigned long interval = LEDTRIG_TTY_INTERVAL; + + led_blink_set_oneshot(trigger_data->led_cdev, &interval, + &interval, 0); trigger_data->rx = icount.rx; trigger_data->tx = icount.tx; - } else { - led_set_brightness_sync(trigger_data->led_cdev, LED_OFF); } out: mutex_unlock(&trigger_data->mutex); - schedule_delayed_work(&trigger_data->dwork, msecs_to_jiffies(100)); + schedule_delayed_work(&trigger_data->dwork, + msecs_to_jiffies(LEDTRIG_TTY_INTERVAL * 2)); } static struct attribute *ledtrig_tty_attrs[] = { diff --git a/drivers/leds/uleds.c b/drivers/leds/uleds.c index 7320337b22d2cf6bd6324c713c6ef032e77c6cb3..3d361c9200302e87330615316eb2dbc10b83d244 100644 --- a/drivers/leds/uleds.c +++ b/drivers/leds/uleds.c @@ -209,17 +209,7 @@ static struct miscdevice uleds_misc = { .name = ULEDS_NAME, }; -static int __init uleds_init(void) -{ - return misc_register(&uleds_misc); -} -module_init(uleds_init); - -static void __exit uleds_exit(void) -{ - misc_deregister(&uleds_misc); -} -module_exit(uleds_exit); +module_misc_device(uleds_misc); MODULE_AUTHOR("David Lechner "); MODULE_DESCRIPTION("Userspace driver for the LED subsystem"); diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index 22243cabe056542ffadb906e6929c0fa7316d74b..537f7bfb7b0645538363d0b0a1ec0ec59fa10eb4 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -12,6 +12,7 @@ #include #include #include +#include #define INTR_STAT_OFS 0x0 #define INTR_SET_OFS 0x8 diff --git a/drivers/mailbox/arm_mhu_db.c b/drivers/mailbox/arm_mhu_db.c index aa0a4d83880fc1759e86de2ee9cd17d589129f3c..27a510d46908236f371c33d40f2265aa5bc55b1f 100644 --- a/drivers/mailbox/arm_mhu_db.c +++ b/drivers/mailbox/arm_mhu_db.c @@ -15,7 +15,6 @@ #include #include #include -#include #define INTR_STAT_OFS 0x0 #define INTR_SET_OFS 0x8 diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c index bf6e86b0ed09cd1cc9c5a543d94ca5b965f500c8..a2b8839d4e7c5e5009581e528f648e9450b3c27b 100644 --- a/drivers/mailbox/bcm-flexrm-mailbox.c +++ b/drivers/mailbox/bcm-flexrm-mailbox.c @@ -1501,16 +1501,12 @@ static int flexrm_mbox_probe(struct platform_device *pdev) mbox->dev = dev; platform_set_drvdata(pdev, mbox); - /* Get resource for registers */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* Get resource for registers and map registers of all rings */ + mbox->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &iomem); if (!iomem || (resource_size(iomem) < RING_REGS_SIZE)) { ret = -ENODEV; goto fail; - } - - /* Map registers of all rings */ - mbox->regs = devm_ioremap_resource(&pdev->dev, iomem); - if (IS_ERR(mbox->regs)) { + } else if (IS_ERR(mbox->regs)) { ret = PTR_ERR(mbox->regs); goto fail; } diff --git a/drivers/mailbox/bcm-pdc-mailbox.c b/drivers/mailbox/bcm-pdc-mailbox.c index 8c95e3ce295f31495258773ecbba1ff075de6283..d67db63b482d8073a89180f9ac4ce0a4f46415ed 100644 --- a/drivers/mailbox/bcm-pdc-mailbox.c +++ b/drivers/mailbox/bcm-pdc-mailbox.c @@ -694,7 +694,7 @@ pdc_receive(struct pdc_state *pdcs) * pdc_tx_list_sg_add() - Add the buffers in a scatterlist to the transmit * descriptors for a given SPU. The scatterlist buffers contain the data for a * SPU request message. - * @spu_idx: The index of the SPU to submit the request to, [0, max_spu) + * @pdcs: PDC state for the SPU that will process this request * @sg: Scatterlist whose buffers contain part of the SPU request * * If a scatterlist buffer is larger than PDC_DMA_BUF_MAX, multiple descriptors @@ -861,7 +861,7 @@ static int pdc_rx_list_init(struct pdc_state *pdcs, struct scatterlist *dst_sg, * pdc_rx_list_sg_add() - Add the buffers in a scatterlist to the receive * descriptors for a given SPU. The caller must have already DMA mapped the * scatterlist. - * @spu_idx: Indicates which SPU the buffers are for + * @pdcs: PDC state for the SPU that will process this request * @sg: Scatterlist whose buffers are added to the receive ring * * If a receive buffer in the scatterlist is larger than PDC_DMA_BUF_MAX, @@ -960,7 +960,7 @@ static irqreturn_t pdc_irq_handler(int irq, void *data) /** * pdc_tasklet_cb() - Tasklet callback that runs the deferred processing after * a DMA receive interrupt. Reenables the receive interrupt. - * @data: PDC state structure + * @t: Pointer to the Altera sSGDMA channel structure */ static void pdc_tasklet_cb(struct tasklet_struct *t) { @@ -1566,19 +1566,13 @@ static int pdc_probe(struct platform_device *pdev) if (err) goto cleanup_ring_pool; - pdc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!pdc_regs) { - err = -ENODEV; - goto cleanup_ring_pool; - } - dev_dbg(dev, "PDC register region res.start = %pa, res.end = %pa", - &pdc_regs->start, &pdc_regs->end); - - pdcs->pdc_reg_vbase = devm_ioremap_resource(&pdev->dev, pdc_regs); + pdcs->pdc_reg_vbase = devm_platform_get_and_ioremap_resource(pdev, 0, &pdc_regs); if (IS_ERR(pdcs->pdc_reg_vbase)) { err = PTR_ERR(pdcs->pdc_reg_vbase); goto cleanup_ring_pool; } + dev_dbg(dev, "PDC register region res.start = %pa, res.end = %pa", + &pdc_regs->start, &pdc_regs->end); /* create rx buffer pool after dt read to know how big buffers are */ err = pdc_rx_buf_pool_create(pdcs); diff --git a/drivers/mailbox/hi3660-mailbox.c b/drivers/mailbox/hi3660-mailbox.c index ab24e731a782350905600c4ba0c95d4009935f6c..17c29e960fbf8961e8786eedfa032d4d0aadd86c 100644 --- a/drivers/mailbox/hi3660-mailbox.c +++ b/drivers/mailbox/hi3660-mailbox.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c index 1c73c63598f553a6becfd7bae0f6b2a4be303f30..f77741ce42e72836c696e643a66f27eb0ea9033c 100644 --- a/drivers/mailbox/hi6220-mailbox.c +++ b/drivers/mailbox/hi6220-mailbox.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 20f2ec880ad690e304df234d5b792369b96bb272..3ef4dd8adf5db1bd3f28a5c2560377007dde0e00 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -14,7 +14,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/drivers/mailbox/mailbox-mpfs.c b/drivers/mailbox/mailbox-mpfs.c index 162df49654fba2e89ad7051cf13482af1e425d0a..20ee283a04cc6afc890ad25ead3d2fdb16cec3e3 100644 --- a/drivers/mailbox/mailbox-mpfs.c +++ b/drivers/mailbox/mailbox-mpfs.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c index fc6a12a51b403863d7dcb36a63a54b5a76c2f135..22d6018ceec3cb08b4b727df9755ea4449b24007 100644 --- a/drivers/mailbox/mailbox-test.c +++ b/drivers/mailbox/mailbox-test.c @@ -367,8 +367,7 @@ static int mbox_test_probe(struct platform_device *pdev) return -ENOMEM; /* It's okay for MMIO to be NULL */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res); + tdev->tx_mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (PTR_ERR(tdev->tx_mmio) == -EBUSY) { /* if reserved area in SRAM, try just ioremap */ size = resource_size(res); @@ -378,8 +377,7 @@ static int mbox_test_probe(struct platform_device *pdev) } /* If specified, second reg entry is Rx MMIO */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res); + tdev->rx_mmio = devm_platform_get_and_ioremap_resource(pdev, 1, &res); if (PTR_ERR(tdev->rx_mmio) == -EBUSY) { size = resource_size(res); tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size); @@ -390,7 +388,7 @@ static int mbox_test_probe(struct platform_device *pdev) tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); - if (!tdev->tx_channel && !tdev->rx_channel) + if (IS_ERR_OR_NULL(tdev->tx_channel) && IS_ERR_OR_NULL(tdev->rx_channel)) return -EPROBE_DEFER; /* If Rx is not specified but has Rx MMIO, then Rx = Tx */ diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index adf36c05fa43fb7061f021c5c6cb663596acc0aa..ebff3baf304515df2e8b0ed2435a8c37180bc24b 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "mailbox.h" diff --git a/drivers/mailbox/mtk-adsp-mailbox.c b/drivers/mailbox/mtk-adsp-mailbox.c index 14bc0057de814caf4a0fa1f1265633dddf467a41..91487aa4d7da08f4f70a91c8635ec630b29b4c7c 100644 --- a/drivers/mailbox/mtk-adsp-mailbox.c +++ b/drivers/mailbox/mtk-adsp-mailbox.c @@ -10,7 +10,8 @@ #include #include #include -#include +#include +#include #include struct mtk_adsp_mbox_priv { diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index b18d47ea13a0e293549405bcec3c36502b56c81c..4d62b07c14118ab4e413a653792caac8ec18121e 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT) #define CMDQ_NUM_CMD(t) (t->cmd_buf_size / CMDQ_INST_SIZE) diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index fa2ce3246b705c59c6d4b30c4376e45735a6c1f7..792bcaebbc9b8c29b8c957adbfa5a4256b1c5b54 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mailbox/platform_mhu.c b/drivers/mailbox/platform_mhu.c index a5922ac0b0bfd5c28289795c9fed19bb8744f446..834aecd720ac1c93e5391cb764b11c7823a04067 100644 --- a/drivers/mailbox/platform_mhu.c +++ b/drivers/mailbox/platform_mhu.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -135,10 +136,8 @@ static int platform_mhu_probe(struct platform_device *pdev) for (i = 0; i < MHU_CHANS; i++) { mhu->chan[i].con_priv = &mhu->mlink[i]; mhu->mlink[i].irq = platform_get_irq(pdev, i); - if (mhu->mlink[i].irq < 0) { - dev_err(dev, "failed to get irq%d\n", i); + if (mhu->mlink[i].irq < 0) return mhu->mlink[i].irq; - } mhu->mlink[i].rx_reg = mhu->base + platform_mhu_reg[i]; mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET; } diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c index 7e27acf6c0ccaa8e6323262ee20cd4e5b00deb67..f597a1bd5684782f4017bdd441ca738daced8aa8 100644 --- a/drivers/mailbox/qcom-ipcc.c +++ b/drivers/mailbox/qcom-ipcc.c @@ -227,10 +227,8 @@ static int qcom_ipcc_setup_mbox(struct qcom_ipcc *ipcc, ret = of_parse_phandle_with_args(client_dn, "mboxes", "#mbox-cells", j, &curr_ph); of_node_put(curr_ph.np); - if (!ret && curr_ph.np == controller_dn) { + if (!ret && curr_ph.np == controller_dn) ipcc->num_chans++; - break; - } } } diff --git a/drivers/mailbox/rockchip-mailbox.c b/drivers/mailbox/rockchip-mailbox.c index 116286ecc5a0c6181a0270350cfab37b2e1b3b1f..8ffad059e8984ecc0585974ae364c82e5e080ca6 100644 --- a/drivers/mailbox/rockchip-mailbox.c +++ b/drivers/mailbox/rockchip-mailbox.c @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include #include #define MAILBOX_A2B_INTEN 0x00 @@ -194,11 +194,7 @@ static int rockchip_mbox_probe(struct platform_device *pdev) mb->mbox.ops = &rockchip_mbox_chan_ops; mb->mbox.txdone_irq = true; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - mb->mbox_base = devm_ioremap_resource(&pdev->dev, res); + mb->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(mb->mbox_base)) return PTR_ERR(mb->mbox_base); diff --git a/drivers/mailbox/sprd-mailbox.c b/drivers/mailbox/sprd-mailbox.c index e3c899abeed8b0842af090f26aafe9339448d34d..9ae57de77d4d76a9840f24ce40115b8d208e858a 100644 --- a/drivers/mailbox/sprd-mailbox.c +++ b/drivers/mailbox/sprd-mailbox.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index 15d538fe2113b04cd1b1ea321abef85c4b4012d2..4ad3653f38666bb3db7ef16a86bf63c955e01c04 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index 7f98e7436d9448dccb235feff8aac74131516706..fe29fc2ca5260d104b36543eb7f39ae00faf80ab 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -728,7 +727,6 @@ static int tegra_hsp_request_shared_irq(struct tegra_hsp *hsp) static int tegra_hsp_probe(struct platform_device *pdev) { struct tegra_hsp *hsp; - struct resource *res; unsigned int i; u32 value; int err; @@ -742,8 +740,7 @@ static int tegra_hsp_probe(struct platform_device *pdev) INIT_LIST_HEAD(&hsp->doorbells); spin_lock_init(&hsp->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hsp->regs = devm_ioremap_resource(&pdev->dev, res); + hsp->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hsp->regs)) return PTR_ERR(hsp->regs); diff --git a/drivers/mailbox/ti-msgmgr.c b/drivers/mailbox/ti-msgmgr.c index 03048cbda525e47d9b82a14f18649003d63e017b..a94577f16a4730ab3f20e9100251806d1632a692 100644 --- a/drivers/mailbox/ti-msgmgr.c +++ b/drivers/mailbox/ti-msgmgr.c @@ -812,7 +812,6 @@ static int ti_msgmgr_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct of_device_id *of_id; struct device_node *np; - struct resource *res; const struct ti_msgmgr_desc *desc; struct ti_msgmgr_inst *inst; struct ti_queue_inst *qinst; @@ -843,22 +842,19 @@ static int ti_msgmgr_probe(struct platform_device *pdev) inst->dev = dev; inst->desc = desc; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - desc->data_region_name); - inst->queue_proxy_region = devm_ioremap_resource(dev, res); + inst->queue_proxy_region = + devm_platform_ioremap_resource_byname(pdev, desc->data_region_name); if (IS_ERR(inst->queue_proxy_region)) return PTR_ERR(inst->queue_proxy_region); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - desc->status_region_name); - inst->queue_state_debug_region = devm_ioremap_resource(dev, res); + inst->queue_state_debug_region = + devm_platform_ioremap_resource_byname(pdev, desc->status_region_name); if (IS_ERR(inst->queue_state_debug_region)) return PTR_ERR(inst->queue_state_debug_region); if (desc->is_sproxy) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - desc->ctrl_region_name); - inst->queue_ctrl_region = devm_ioremap_resource(dev, res); + inst->queue_ctrl_region = + devm_platform_ioremap_resource_byname(pdev, desc->ctrl_region_name); if (IS_ERR(inst->queue_ctrl_region)) return PTR_ERR(inst->queue_ctrl_region); } diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index d097f45b0e5f5cedeb079031c9fbf45da07bd940..e4fcac97dbfaa0628367c626280b338c14fa5178 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include /* IPI agent ID any */ diff --git a/drivers/md/dm-core.h b/drivers/md/dm-core.h index 0d93661f88d306e0d0aa3a1ac085880a8b63d9d6..095b9b49aa8250a1f56c531883cce5f6e8a24727 100644 --- a/drivers/md/dm-core.h +++ b/drivers/md/dm-core.h @@ -214,6 +214,7 @@ struct dm_table { /* a list of devices used by this table */ struct list_head devices; + struct rw_semaphore devices_lock; /* events get handed up using this callback */ void (*event_fn)(void *data); diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index f2662c21a6dfe7ac1c6db10fa21ef94b45dee2ac..5315fd261c23b7f084b4b049b374e9b8941bc7eb 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -753,7 +753,8 @@ static int crypt_iv_eboiv_gen(struct crypt_config *cc, u8 *iv, int err; u8 *buf; - reqsize = ALIGN(crypto_skcipher_reqsize(tfm), __alignof__(__le64)); + reqsize = sizeof(*req) + crypto_skcipher_reqsize(tfm); + reqsize = ALIGN(reqsize, __alignof__(__le64)); req = kmalloc(reqsize + cc->iv_size, GFP_NOIO); if (!req) diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index f5ed729a8e0cd300cd8acfb6864ae8c289da00ad..21ebb6c39394b6298ac748ffb32d11d00ad218af 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1630,6 +1630,8 @@ static void retrieve_deps(struct dm_table *table, struct dm_dev_internal *dd; struct dm_target_deps *deps; + down_read(&table->devices_lock); + deps = get_result_buffer(param, param_size, &len); /* @@ -1644,7 +1646,7 @@ static void retrieve_deps(struct dm_table *table, needed = struct_size(deps, dev, count); if (len < needed) { param->flags |= DM_BUFFER_FULL_FLAG; - return; + goto out; } /* @@ -1656,6 +1658,9 @@ static void retrieve_deps(struct dm_table *table, deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev); param->data_size = param->data_start + needed; + +out: + up_read(&table->devices_lock); } static int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 7d208b2b1a192d88eaaec34d5247d8f7ef6273e8..37b48f63ae6a5e40b02234f214eaff487ca35e12 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -135,6 +135,7 @@ int dm_table_create(struct dm_table **result, blk_mode_t mode, return -ENOMEM; INIT_LIST_HEAD(&t->devices); + init_rwsem(&t->devices_lock); if (!num_targets) num_targets = KEYS_PER_NODE; @@ -359,16 +360,20 @@ int __ref dm_get_device(struct dm_target *ti, const char *path, blk_mode_t mode, if (dev == disk_devt(t->md->disk)) return -EINVAL; + down_write(&t->devices_lock); + dd = find_device(&t->devices, dev); if (!dd) { dd = kmalloc(sizeof(*dd), GFP_KERNEL); - if (!dd) - return -ENOMEM; + if (!dd) { + r = -ENOMEM; + goto unlock_ret_r; + } r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev); if (r) { kfree(dd); - return r; + goto unlock_ret_r; } refcount_set(&dd->count, 1); @@ -378,12 +383,17 @@ int __ref dm_get_device(struct dm_target *ti, const char *path, blk_mode_t mode, } else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) { r = upgrade_mode(dd, mode, t->md); if (r) - return r; + goto unlock_ret_r; } refcount_inc(&dd->count); out: + up_write(&t->devices_lock); *result = dd->dm_dev; return 0; + +unlock_ret_r: + up_write(&t->devices_lock); + return r; } EXPORT_SYMBOL(dm_get_device); @@ -419,9 +429,12 @@ static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, void dm_put_device(struct dm_target *ti, struct dm_dev *d) { int found = 0; - struct list_head *devices = &ti->table->devices; + struct dm_table *t = ti->table; + struct list_head *devices = &t->devices; struct dm_dev_internal *dd; + down_write(&t->devices_lock); + list_for_each_entry(dd, devices, list) { if (dd->dm_dev == d) { found = 1; @@ -430,14 +443,17 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d) } if (!found) { DMERR("%s: device %s not in table devices list", - dm_device_name(ti->table->md), d->name); - return; + dm_device_name(t->md), d->name); + goto unlock_ret; } if (refcount_dec_and_test(&dd->count)) { - dm_put_table_device(ti->table->md, d); + dm_put_table_device(t->md, d); list_del(&dd->list); kfree(dd); } + +unlock_ret: + up_write(&t->devices_lock); } EXPORT_SYMBOL(dm_put_device); diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c index ad8e670a2f9be8c12697ad92bb25f60a2e046c46..b487f7acc860f77eda051fb7a7f6495ed8e7040e 100644 --- a/drivers/md/dm-zoned-target.c +++ b/drivers/md/dm-zoned-target.c @@ -748,17 +748,16 @@ static int dmz_get_zoned_device(struct dm_target *ti, char *path, /* * Cleanup zoned device information. */ -static void dmz_put_zoned_device(struct dm_target *ti) +static void dmz_put_zoned_devices(struct dm_target *ti) { struct dmz_target *dmz = ti->private; int i; - for (i = 0; i < dmz->nr_ddevs; i++) { - if (dmz->ddev[i]) { + for (i = 0; i < dmz->nr_ddevs; i++) + if (dmz->ddev[i]) dm_put_device(ti, dmz->ddev[i]); - dmz->ddev[i] = NULL; - } - } + + kfree(dmz->ddev); } static int dmz_fixup_devices(struct dm_target *ti) @@ -948,7 +947,7 @@ static int dmz_ctr(struct dm_target *ti, unsigned int argc, char **argv) err_meta: dmz_dtr_metadata(dmz->metadata); err_dev: - dmz_put_zoned_device(ti); + dmz_put_zoned_devices(ti); err: kfree(dmz->dev); kfree(dmz); @@ -978,7 +977,7 @@ static void dmz_dtr(struct dm_target *ti) bioset_exit(&dmz->bio_set); - dmz_put_zoned_device(ti); + dmz_put_zoned_devices(ti); mutex_destroy(&dmz->chunk_lock); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index f0f118ab20fa26371c9bd5395d3763fa34c7ff34..64a1f306c96c11eea2ac754334646dcb7c4e8586 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -715,24 +715,6 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU) rcu_read_unlock(); } -static inline struct dm_table *dm_get_live_table_bio(struct mapped_device *md, - int *srcu_idx, blk_opf_t bio_opf) -{ - if (bio_opf & REQ_NOWAIT) - return dm_get_live_table_fast(md); - else - return dm_get_live_table(md, srcu_idx); -} - -static inline void dm_put_live_table_bio(struct mapped_device *md, int srcu_idx, - blk_opf_t bio_opf) -{ - if (bio_opf & REQ_NOWAIT) - dm_put_live_table_fast(md); - else - dm_put_live_table(md, srcu_idx); -} - static char *_dm_claim_ptr = "I belong to device-mapper"; /* @@ -1833,9 +1815,8 @@ static void dm_submit_bio(struct bio *bio) struct mapped_device *md = bio->bi_bdev->bd_disk->private_data; int srcu_idx; struct dm_table *map; - blk_opf_t bio_opf = bio->bi_opf; - map = dm_get_live_table_bio(md, &srcu_idx, bio_opf); + map = dm_get_live_table(md, &srcu_idx); /* If suspended, or map not yet available, queue this IO for later */ if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) || @@ -1851,7 +1832,7 @@ static void dm_submit_bio(struct bio *bio) dm_split_and_process_bio(md, map, bio); out: - dm_put_live_table_bio(md, srcu_idx, bio_opf); + dm_put_live_table(md, srcu_idx); } static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob, diff --git a/drivers/md/md.c b/drivers/md/md.c index 0fe7ab6e8ab9fef50e67d63a563ef7f692c9fe63..a104a025084dc726056c42b0b1a0dd37ea774803 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -798,14 +798,14 @@ void mddev_unlock(struct mddev *mddev) } else mutex_unlock(&mddev->reconfig_mutex); + md_wakeup_thread(mddev->thread); + wake_up(&mddev->sb_wait); + list_for_each_entry_safe(rdev, tmp, &delete, same_set) { list_del_init(&rdev->same_set); kobject_del(&rdev->kobj); export_rdev(rdev, mddev); } - - md_wakeup_thread(mddev->thread); - wake_up(&mddev->sb_wait); } EXPORT_SYMBOL_GPL(mddev_unlock); @@ -2452,7 +2452,8 @@ static void export_rdev(struct md_rdev *rdev, struct mddev *mddev) if (test_bit(AutoDetected, &rdev->flags)) md_autodetect_dev(rdev->bdev->bd_dev); #endif - blkdev_put(rdev->bdev, mddev->external ? &claim_rdev : rdev); + blkdev_put(rdev->bdev, + test_bit(Holder, &rdev->flags) ? rdev : &claim_rdev); rdev->bdev = NULL; kobject_put(&rdev->kobj); } @@ -3632,6 +3633,7 @@ EXPORT_SYMBOL_GPL(md_rdev_init); static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor) { struct md_rdev *rdev; + struct md_rdev *holder; sector_t size; int err; @@ -3646,8 +3648,15 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe if (err) goto out_clear_rdev; + if (super_format == -2) { + holder = &claim_rdev; + } else { + holder = rdev; + set_bit(Holder, &rdev->flags); + } + rdev->bdev = blkdev_get_by_dev(newdev, BLK_OPEN_READ | BLK_OPEN_WRITE, - super_format == -2 ? &claim_rdev : rdev, NULL); + holder, NULL); if (IS_ERR(rdev->bdev)) { pr_warn("md: could not open device unknown-block(%u,%u).\n", MAJOR(newdev), MINOR(newdev)); @@ -3684,7 +3693,7 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe return rdev; out_blkdev_put: - blkdev_put(rdev->bdev, super_format == -2 ? &claim_rdev : rdev); + blkdev_put(rdev->bdev, holder); out_clear_rdev: md_rdev_clear(rdev); out_free_rdev: @@ -8256,7 +8265,7 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) spin_unlock(&all_mddevs_lock); if (to_put) - mddev_put(mddev); + mddev_put(to_put); return next_mddev; } diff --git a/drivers/md/md.h b/drivers/md/md.h index 9bcb77bca963922193b6d04e4a9b134ee7cd5ce7..7c9c13abd7cac00e36b5b2b44784735a645afeca 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -211,6 +211,9 @@ enum flag_bits { * check if there is collision between raid1 * serial bios. */ + Holder, /* rdev is used as holder while opening + * underlying disk exclusively. + */ }; static inline int is_badblock(struct md_rdev *rdev, sector_t s, int sectors, diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 4b30a174216232564cd88781d603d6ac5d7e5c95..2aabac773fe72aa0db6bb6344e128b3d3dd08460 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1837,12 +1837,11 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev) struct r1conf *conf = mddev->private; int err = 0; int number = rdev->raid_disk; + struct raid1_info *p = conf->mirrors + number; if (unlikely(number >= conf->raid_disks)) goto abort; - struct raid1_info *p = conf->mirrors + number; - if (rdev != p->rdev) p = conf->mirrors + conf->raid_disks + number; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 4cb9c608ee1919ac543e93da6e86f4a1dca940d3..284cd71bcc685b7dd4a44d4cd1d009684b57b5cd 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -854,6 +854,13 @@ struct stripe_head *raid5_get_active_stripe(struct r5conf *conf, set_bit(R5_INACTIVE_BLOCKED, &conf->cache_state); r5l_wake_reclaim(conf->log, 0); + + /* release batch_last before wait to avoid risk of deadlock */ + if (ctx && ctx->batch_last) { + raid5_release_stripe(ctx->batch_last); + ctx->batch_last = NULL; + } + wait_event_lock_irq(conf->wait_for_stripe, is_inactive_blocked(conf, hash), *(conf->hash_locks + hash)); diff --git a/drivers/media/common/videobuf2/frame_vector.c b/drivers/media/common/videobuf2/frame_vector.c index 0f430ddc1f6704e96dd42357eaf77f03be19793f..fd87747be9b177c07c25048a62fbbded363fab5e 100644 --- a/drivers/media/common/videobuf2/frame_vector.c +++ b/drivers/media/common/videobuf2/frame_vector.c @@ -31,6 +31,10 @@ * different type underlying the specified range of virtual addresses. * When the function isn't able to map a single page, it returns error. * + * Note that get_vaddr_frames() cannot follow VM_IO mappings. It used + * to be able to do that, but that could (racily) return non-refcounted + * pfns. + * * This function takes care of grabbing mmap_lock as necessary. */ int get_vaddr_frames(unsigned long start, unsigned int nr_frames, bool write, @@ -59,8 +63,6 @@ int get_vaddr_frames(unsigned long start, unsigned int nr_frames, bool write, if (likely(ret > 0)) return ret; - /* This used to (racily) return non-refcounted pfns. Let people know */ - WARN_ONCE(1, "get_vaddr_frames() cannot follow VM_IO mapping"); vec->nr_frames = 0; return ret ? ret : -EFAULT; } diff --git a/drivers/media/dvb-frontends/ascot2e.c b/drivers/media/dvb-frontends/ascot2e.c index 9b00b56230b61ed418b4b795b85efe02ef46815b..cf8e5f1bd10181a65376660bfa4648c0e536fd25 100644 --- a/drivers/media/dvb-frontends/ascot2e.c +++ b/drivers/media/dvb-frontends/ascot2e.c @@ -533,7 +533,7 @@ struct dvb_frontend *ascot2e_attach(struct dvb_frontend *fe, priv->i2c_address, priv->i2c); return fe; } -EXPORT_SYMBOL(ascot2e_attach); +EXPORT_SYMBOL_GPL(ascot2e_attach); MODULE_DESCRIPTION("Sony ASCOT2E terr/cab tuner driver"); MODULE_AUTHOR("info@netup.ru"); diff --git a/drivers/media/dvb-frontends/atbm8830.c b/drivers/media/dvb-frontends/atbm8830.c index bdd16b9c582449f248f488511390617baee94769..778c865085bf90b53b72d12c1660119051d105ca 100644 --- a/drivers/media/dvb-frontends/atbm8830.c +++ b/drivers/media/dvb-frontends/atbm8830.c @@ -489,7 +489,7 @@ struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, return NULL; } -EXPORT_SYMBOL(atbm8830_attach); +EXPORT_SYMBOL_GPL(atbm8830_attach); MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); MODULE_AUTHOR("David T. L. Wong "); diff --git a/drivers/media/dvb-frontends/au8522_dig.c b/drivers/media/dvb-frontends/au8522_dig.c index 78cafdf2796188ec65631f69ded40035e4e9d43c..230436bf6cbd9af58c4b50bf1ed9ab62c702f8d9 100644 --- a/drivers/media/dvb-frontends/au8522_dig.c +++ b/drivers/media/dvb-frontends/au8522_dig.c @@ -879,7 +879,7 @@ struct dvb_frontend *au8522_attach(const struct au8522_config *config, au8522_release_state(state); return NULL; } -EXPORT_SYMBOL(au8522_attach); +EXPORT_SYMBOL_GPL(au8522_attach); static const struct dvb_frontend_ops au8522_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/bcm3510.c b/drivers/media/dvb-frontends/bcm3510.c index 68b92b4419cff470237c6fe670891a0f5716e490..b3f5c49accafd2b335bbee8c59760e182f9c241f 100644 --- a/drivers/media/dvb-frontends/bcm3510.c +++ b/drivers/media/dvb-frontends/bcm3510.c @@ -835,7 +835,7 @@ struct dvb_frontend* bcm3510_attach(const struct bcm3510_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(bcm3510_attach); +EXPORT_SYMBOL_GPL(bcm3510_attach); static const struct dvb_frontend_ops bcm3510_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/cx22700.c b/drivers/media/dvb-frontends/cx22700.c index b39ff516271b24d0d1bb34aa0f5d32f84f18361d..1d04c0a652b26791cc69c72efb21e930aa7331fa 100644 --- a/drivers/media/dvb-frontends/cx22700.c +++ b/drivers/media/dvb-frontends/cx22700.c @@ -432,4 +432,4 @@ MODULE_DESCRIPTION("Conexant CX22700 DVB-T Demodulator driver"); MODULE_AUTHOR("Holger Waechtler"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(cx22700_attach); +EXPORT_SYMBOL_GPL(cx22700_attach); diff --git a/drivers/media/dvb-frontends/cx22702.c b/drivers/media/dvb-frontends/cx22702.c index cc6acbf6393d44e02d11c885cc256ba179894a8d..61ad34b7004b5a42ca7b2cf2f9b976d862f71866 100644 --- a/drivers/media/dvb-frontends/cx22702.c +++ b/drivers/media/dvb-frontends/cx22702.c @@ -604,7 +604,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(cx22702_attach); +EXPORT_SYMBOL_GPL(cx22702_attach); static const struct dvb_frontend_ops cx22702_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/cx24110.c b/drivers/media/dvb-frontends/cx24110.c index 6f99d6a27be2d3382abc7849441af87205120197..9aeea089756fe1c9dbb0073a0fec8540d8a9383e 100644 --- a/drivers/media/dvb-frontends/cx24110.c +++ b/drivers/media/dvb-frontends/cx24110.c @@ -653,4 +653,4 @@ MODULE_DESCRIPTION("Conexant CX24110 DVB-S Demodulator driver"); MODULE_AUTHOR("Peter Hettkamp"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(cx24110_attach); +EXPORT_SYMBOL_GPL(cx24110_attach); diff --git a/drivers/media/dvb-frontends/cx24113.c b/drivers/media/dvb-frontends/cx24113.c index dd55d314bf9af1648c350ed9374b81386f7dda8b..203cb6b3f941b43d92ba8570b648dd7bacb26b5a 100644 --- a/drivers/media/dvb-frontends/cx24113.c +++ b/drivers/media/dvb-frontends/cx24113.c @@ -590,7 +590,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe, return NULL; } -EXPORT_SYMBOL(cx24113_attach); +EXPORT_SYMBOL_GPL(cx24113_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); diff --git a/drivers/media/dvb-frontends/cx24116.c b/drivers/media/dvb-frontends/cx24116.c index ea8264ccbb4e841c448f783ccec5327075124162..8b978a9f74a4e593ca6786c4d3c35a05c08291bf 100644 --- a/drivers/media/dvb-frontends/cx24116.c +++ b/drivers/media/dvb-frontends/cx24116.c @@ -1133,7 +1133,7 @@ struct dvb_frontend *cx24116_attach(const struct cx24116_config *config, state->frontend.demodulator_priv = state; return &state->frontend; } -EXPORT_SYMBOL(cx24116_attach); +EXPORT_SYMBOL_GPL(cx24116_attach); /* * Initialise or wake up device diff --git a/drivers/media/dvb-frontends/cx24120.c b/drivers/media/dvb-frontends/cx24120.c index 0f778660c72b85beb9cf93f0312ef7c92b0b55d8..44515fdbe91d4f5eb23fb13dfc571507255655cb 100644 --- a/drivers/media/dvb-frontends/cx24120.c +++ b/drivers/media/dvb-frontends/cx24120.c @@ -305,7 +305,7 @@ struct dvb_frontend *cx24120_attach(const struct cx24120_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(cx24120_attach); +EXPORT_SYMBOL_GPL(cx24120_attach); static int cx24120_test_rom(struct cx24120_state *state) { diff --git a/drivers/media/dvb-frontends/cx24123.c b/drivers/media/dvb-frontends/cx24123.c index 3d84ee17e54c69472bcbcae64738c27e66169099..539889e638ccc165220a09b8f681bd61817cb42a 100644 --- a/drivers/media/dvb-frontends/cx24123.c +++ b/drivers/media/dvb-frontends/cx24123.c @@ -1096,7 +1096,7 @@ struct dvb_frontend *cx24123_attach(const struct cx24123_config *config, return NULL; } -EXPORT_SYMBOL(cx24123_attach); +EXPORT_SYMBOL_GPL(cx24123_attach); static const struct dvb_frontend_ops cx24123_ops = { .delsys = { SYS_DVBS }, diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c index d7ee294c6833494c2b7c17b8c611a10cf0b842d6..7feb08dccfa1c8181e8f71335d47200cc9bbe4b7 100644 --- a/drivers/media/dvb-frontends/cxd2820r_core.c +++ b/drivers/media/dvb-frontends/cxd2820r_core.c @@ -536,7 +536,7 @@ struct dvb_frontend *cxd2820r_attach(const struct cxd2820r_config *config, return pdata.get_dvb_frontend(client); } -EXPORT_SYMBOL(cxd2820r_attach); +EXPORT_SYMBOL_GPL(cxd2820r_attach); static struct dvb_frontend *cxd2820r_get_dvb_frontend(struct i2c_client *client) { diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index ef403a9fb7531a257b848914ed8d77aff2fc8f3b..d925ca24183b50175b36cb57a84b9a29e817f14d 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -3930,14 +3930,14 @@ struct dvb_frontend *cxd2841er_attach_s(struct cxd2841er_config *cfg, { return cxd2841er_attach(cfg, i2c, SYS_DVBS); } -EXPORT_SYMBOL(cxd2841er_attach_s); +EXPORT_SYMBOL_GPL(cxd2841er_attach_s); struct dvb_frontend *cxd2841er_attach_t_c(struct cxd2841er_config *cfg, struct i2c_adapter *i2c) { return cxd2841er_attach(cfg, i2c, 0); } -EXPORT_SYMBOL(cxd2841er_attach_t_c); +EXPORT_SYMBOL_GPL(cxd2841er_attach_t_c); static const struct dvb_frontend_ops cxd2841er_dvbs_s2_ops = { .delsys = { SYS_DVBS, SYS_DVBS2 }, diff --git a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c index f67b6d24b8d46ce26b4635f613933cdd3e304149..a06d8368ca795bb270505bac611028ea9892dcf0 100644 --- a/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c +++ b/drivers/media/dvb-frontends/cxd2880/cxd2880_top.c @@ -1950,7 +1950,7 @@ struct dvb_frontend *cxd2880_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(cxd2880_attach); +EXPORT_SYMBOL_GPL(cxd2880_attach); MODULE_DESCRIPTION("Sony CXD2880 DVB-T2/T tuner + demod driver"); MODULE_AUTHOR("Sony Semiconductor Solutions Corporation"); diff --git a/drivers/media/dvb-frontends/dib0070.c b/drivers/media/dvb-frontends/dib0070.c index cafb41dba861c02a13b3e1558e8c51f971e2234c..9a8e7cdd2a24772418b93a4874d8d19142f9da67 100644 --- a/drivers/media/dvb-frontends/dib0070.c +++ b/drivers/media/dvb-frontends/dib0070.c @@ -762,7 +762,7 @@ struct dvb_frontend *dib0070_attach(struct dvb_frontend *fe, struct i2c_adapter fe->tuner_priv = NULL; return NULL; } -EXPORT_SYMBOL(dib0070_attach); +EXPORT_SYMBOL_GPL(dib0070_attach); MODULE_AUTHOR("Patrick Boettcher "); MODULE_DESCRIPTION("Driver for the DiBcom 0070 base-band RF Tuner"); diff --git a/drivers/media/dvb-frontends/dib0090.c b/drivers/media/dvb-frontends/dib0090.c index 903da33642dff1e097b9e00e3e8c4fe290aa0eb5..c958bcff026ec56e22c67beefcea534b8d8c56ce 100644 --- a/drivers/media/dvb-frontends/dib0090.c +++ b/drivers/media/dvb-frontends/dib0090.c @@ -2634,7 +2634,7 @@ struct dvb_frontend *dib0090_register(struct dvb_frontend *fe, struct i2c_adapte return NULL; } -EXPORT_SYMBOL(dib0090_register); +EXPORT_SYMBOL_GPL(dib0090_register); struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_adapter *i2c, const struct dib0090_config *config) { @@ -2660,7 +2660,7 @@ struct dvb_frontend *dib0090_fw_register(struct dvb_frontend *fe, struct i2c_ada fe->tuner_priv = NULL; return NULL; } -EXPORT_SYMBOL(dib0090_fw_register); +EXPORT_SYMBOL_GPL(dib0090_fw_register); MODULE_AUTHOR("Patrick Boettcher "); MODULE_AUTHOR("Olivier Grenie "); diff --git a/drivers/media/dvb-frontends/dib3000mb.c b/drivers/media/dvb-frontends/dib3000mb.c index a6c2fc4586eb36bda9beea1736960c3297f03473..c598b2a633256518944cb418f6032c50dfaef630 100644 --- a/drivers/media/dvb-frontends/dib3000mb.c +++ b/drivers/media/dvb-frontends/dib3000mb.c @@ -815,4 +815,4 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(dib3000mb_attach); +EXPORT_SYMBOL_GPL(dib3000mb_attach); diff --git a/drivers/media/dvb-frontends/dib3000mc.c b/drivers/media/dvb-frontends/dib3000mc.c index 2e11a246aae0d320b6d1cd47c3e2686dbc5cb821..c2fca8289abaeb159d18be78e35447070720f2da 100644 --- a/drivers/media/dvb-frontends/dib3000mc.c +++ b/drivers/media/dvb-frontends/dib3000mc.c @@ -935,7 +935,7 @@ struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr kfree(st); return NULL; } -EXPORT_SYMBOL(dib3000mc_attach); +EXPORT_SYMBOL_GPL(dib3000mc_attach); static const struct dvb_frontend_ops dib3000mc_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/dib7000m.c b/drivers/media/dvb-frontends/dib7000m.c index 97ce97789c9e34bdfeeff162088dbf61c93ab6f5..fdb22f32e3a11ae82e68f54c893de906e33062df 100644 --- a/drivers/media/dvb-frontends/dib7000m.c +++ b/drivers/media/dvb-frontends/dib7000m.c @@ -1434,7 +1434,7 @@ struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, kfree(st); return NULL; } -EXPORT_SYMBOL(dib7000m_attach); +EXPORT_SYMBOL_GPL(dib7000m_attach); static const struct dvb_frontend_ops dib7000m_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c index 9273758bf14006f1202f762d521d8fda608cd517..444fe1c4bf2d71030f1629ed741133b21a78300e 100644 --- a/drivers/media/dvb-frontends/dib7000p.c +++ b/drivers/media/dvb-frontends/dib7000p.c @@ -2822,7 +2822,7 @@ void *dib7000p_attach(struct dib7000p_ops *ops) return ops; } -EXPORT_SYMBOL(dib7000p_attach); +EXPORT_SYMBOL_GPL(dib7000p_attach); static const struct dvb_frontend_ops dib7000p_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 2abda7d1cb6e35fc3da757dc3e66945a94cd8213..2f5165918163dfff5733831045edd81a19bbc75c 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -4527,7 +4527,7 @@ void *dib8000_attach(struct dib8000_ops *ops) return ops; } -EXPORT_SYMBOL(dib8000_attach); +EXPORT_SYMBOL_GPL(dib8000_attach); MODULE_AUTHOR("Olivier Grenie "); MODULE_DESCRIPTION("Driver for the DiBcom 8000 ISDB-T demodulator"); diff --git a/drivers/media/dvb-frontends/dib9000.c b/drivers/media/dvb-frontends/dib9000.c index 1c57587a917a288b3445ffd2a77c7dbd7085590a..83cf6eadd49c6ee4480d38f00afc28fe80bef774 100644 --- a/drivers/media/dvb-frontends/dib9000.c +++ b/drivers/media/dvb-frontends/dib9000.c @@ -2546,7 +2546,7 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c kfree(st); return NULL; } -EXPORT_SYMBOL(dib9000_attach); +EXPORT_SYMBOL_GPL(dib9000_attach); static const struct dvb_frontend_ops dib9000_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c index 68f4e8b5a0abbda6667e58c60e874db9af039f23..a738573c8cd7a020a6b0e3d680594e7f275b5bd9 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drxj.c +++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c @@ -12372,7 +12372,7 @@ struct dvb_frontend *drx39xxj_attach(struct i2c_adapter *i2c) return NULL; } -EXPORT_SYMBOL(drx39xxj_attach); +EXPORT_SYMBOL_GPL(drx39xxj_attach); static const struct dvb_frontend_ops drx39xxj_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/drxd_hard.c b/drivers/media/dvb-frontends/drxd_hard.c index 9860cae65f1cf4a290ef80c28a1da05834e28acc..6a531937f4bbb7336ebf5b12acc72c77fdad4c85 100644 --- a/drivers/media/dvb-frontends/drxd_hard.c +++ b/drivers/media/dvb-frontends/drxd_hard.c @@ -2939,7 +2939,7 @@ struct dvb_frontend *drxd_attach(const struct drxd_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(drxd_attach); +EXPORT_SYMBOL_GPL(drxd_attach); MODULE_DESCRIPTION("DRXD driver"); MODULE_AUTHOR("Micronas"); diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c index 2770baebbbbc9eb76fc30a8842c781e233738e28..87f3d4f0eb8c484ddee4104162679ddeb88f6d73 100644 --- a/drivers/media/dvb-frontends/drxk_hard.c +++ b/drivers/media/dvb-frontends/drxk_hard.c @@ -6814,7 +6814,7 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(drxk_attach); +EXPORT_SYMBOL_GPL(drxk_attach); MODULE_DESCRIPTION("DRX-K driver"); MODULE_AUTHOR("Ralph Metzler"); diff --git a/drivers/media/dvb-frontends/ds3000.c b/drivers/media/dvb-frontends/ds3000.c index 20fcf31af1658b77aef94b1f193345d98602233c..515aa7c7baf2a64807fc479b9a08afdbf903dbd2 100644 --- a/drivers/media/dvb-frontends/ds3000.c +++ b/drivers/media/dvb-frontends/ds3000.c @@ -859,7 +859,7 @@ struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, ds3000_set_voltage(&state->frontend, SEC_VOLTAGE_OFF); return &state->frontend; } -EXPORT_SYMBOL(ds3000_attach); +EXPORT_SYMBOL_GPL(ds3000_attach); static int ds3000_set_carrier_offset(struct dvb_frontend *fe, s32 carrier_offset_khz) diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c index 90cb41eacf98cc6b6c2bc9cd17dcf0a3f16ca0fa..ef697ab6bc2e5287d02c5af55bc8f0359cf98523 100644 --- a/drivers/media/dvb-frontends/dvb-pll.c +++ b/drivers/media/dvb-frontends/dvb-pll.c @@ -866,7 +866,7 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, return NULL; } -EXPORT_SYMBOL(dvb_pll_attach); +EXPORT_SYMBOL_GPL(dvb_pll_attach); static int diff --git a/drivers/media/dvb-frontends/ec100.c b/drivers/media/dvb-frontends/ec100.c index 03bd80666cf83ba8c342d01ff30ae401d1810c8b..2ad0a3c2f756705ab0ec917d9788ddba354872f5 100644 --- a/drivers/media/dvb-frontends/ec100.c +++ b/drivers/media/dvb-frontends/ec100.c @@ -299,7 +299,7 @@ struct dvb_frontend *ec100_attach(const struct ec100_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(ec100_attach); +EXPORT_SYMBOL_GPL(ec100_attach); static const struct dvb_frontend_ops ec100_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 68c1a3e0e2ba5b530adad00173757bbd2b2321ff..f127adee3ebb7d041f0b85607a49aa1ec45b53e2 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -1025,7 +1025,7 @@ struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, priv->i2c_address, priv->i2c); return fe; } -EXPORT_SYMBOL(helene_attach_s); +EXPORT_SYMBOL_GPL(helene_attach_s); struct dvb_frontend *helene_attach(struct dvb_frontend *fe, const struct helene_config *config, @@ -1061,7 +1061,7 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, priv->i2c_address, priv->i2c); return fe; } -EXPORT_SYMBOL(helene_attach); +EXPORT_SYMBOL_GPL(helene_attach); static int helene_probe(struct i2c_client *client) { diff --git a/drivers/media/dvb-frontends/horus3a.c b/drivers/media/dvb-frontends/horus3a.c index 24bf5cbcc1846e0a0c12d63e7e40cce7adac2884..0330b78a5b3f29a88e65dcc228a6f09a2125434a 100644 --- a/drivers/media/dvb-frontends/horus3a.c +++ b/drivers/media/dvb-frontends/horus3a.c @@ -395,7 +395,7 @@ struct dvb_frontend *horus3a_attach(struct dvb_frontend *fe, priv->i2c_address, priv->i2c); return fe; } -EXPORT_SYMBOL(horus3a_attach); +EXPORT_SYMBOL_GPL(horus3a_attach); MODULE_DESCRIPTION("Sony HORUS3A satellite tuner driver"); MODULE_AUTHOR("Sergey Kozlov "); diff --git a/drivers/media/dvb-frontends/isl6405.c b/drivers/media/dvb-frontends/isl6405.c index 2cd69b4ff82cb05529694cc5a38f046240837802..7d28a743f97eba6e9624556abad9410614212783 100644 --- a/drivers/media/dvb-frontends/isl6405.c +++ b/drivers/media/dvb-frontends/isl6405.c @@ -141,7 +141,7 @@ struct dvb_frontend *isl6405_attach(struct dvb_frontend *fe, struct i2c_adapter return fe; } -EXPORT_SYMBOL(isl6405_attach); +EXPORT_SYMBOL_GPL(isl6405_attach); MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6405"); MODULE_AUTHOR("Hartmut Hackmann & Oliver Endriss"); diff --git a/drivers/media/dvb-frontends/isl6421.c b/drivers/media/dvb-frontends/isl6421.c index 43b0dfc6f453e3cf51c375abd4fb686428ba00a3..2e9f6f12f849e8597aa2054d44e4222a00ddf494 100644 --- a/drivers/media/dvb-frontends/isl6421.c +++ b/drivers/media/dvb-frontends/isl6421.c @@ -213,7 +213,7 @@ struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter return fe; } -EXPORT_SYMBOL(isl6421_attach); +EXPORT_SYMBOL_GPL(isl6421_attach); MODULE_DESCRIPTION("Driver for lnb supply and control ic isl6421"); MODULE_AUTHOR("Andrew de Quincey & Oliver Endriss"); diff --git a/drivers/media/dvb-frontends/isl6423.c b/drivers/media/dvb-frontends/isl6423.c index 8cd1bb88ce6e7555acb89ec3ef3aa0834098cc8b..a0d0a38340574ddb3251e3b040e9bc9e01ad1573 100644 --- a/drivers/media/dvb-frontends/isl6423.c +++ b/drivers/media/dvb-frontends/isl6423.c @@ -289,7 +289,7 @@ struct dvb_frontend *isl6423_attach(struct dvb_frontend *fe, fe->sec_priv = NULL; return NULL; } -EXPORT_SYMBOL(isl6423_attach); +EXPORT_SYMBOL_GPL(isl6423_attach); MODULE_DESCRIPTION("ISL6423 SEC"); MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/dvb-frontends/itd1000.c b/drivers/media/dvb-frontends/itd1000.c index 1b33478653d16ff433a24fce3c28ebc4014a6e60..f8f362f50e78da413245c49e3db86112b33243e4 100644 --- a/drivers/media/dvb-frontends/itd1000.c +++ b/drivers/media/dvb-frontends/itd1000.c @@ -389,7 +389,7 @@ struct dvb_frontend *itd1000_attach(struct dvb_frontend *fe, struct i2c_adapter return fe; } -EXPORT_SYMBOL(itd1000_attach); +EXPORT_SYMBOL_GPL(itd1000_attach); MODULE_AUTHOR("Patrick Boettcher "); MODULE_DESCRIPTION("Integrant ITD1000 driver"); diff --git a/drivers/media/dvb-frontends/ix2505v.c b/drivers/media/dvb-frontends/ix2505v.c index 73f27105c139d74ae4afdca16b83c6b8fef6aaac..3212e333d472b0e7de21ceef86d6564413ed73f9 100644 --- a/drivers/media/dvb-frontends/ix2505v.c +++ b/drivers/media/dvb-frontends/ix2505v.c @@ -302,7 +302,7 @@ struct dvb_frontend *ix2505v_attach(struct dvb_frontend *fe, kfree(state); return NULL; } -EXPORT_SYMBOL(ix2505v_attach); +EXPORT_SYMBOL_GPL(ix2505v_attach); module_param_named(debug, ix2505v_debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/l64781.c b/drivers/media/dvb-frontends/l64781.c index c5106a1ea1cd007feac7e54af5b3818bfd69c347..fe5af2453d559c644194a6e8834da4f4e563048f 100644 --- a/drivers/media/dvb-frontends/l64781.c +++ b/drivers/media/dvb-frontends/l64781.c @@ -593,4 +593,4 @@ MODULE_DESCRIPTION("LSI L64781 DVB-T Demodulator driver"); MODULE_AUTHOR("Holger Waechtler, Marko Kohtala"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(l64781_attach); +EXPORT_SYMBOL_GPL(l64781_attach); diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c index f343066c297e2b4e62a43696ae11652705891ac5..fe700aa56bff39defcdb1ca0791dbd11fe7c3793 100644 --- a/drivers/media/dvb-frontends/lg2160.c +++ b/drivers/media/dvb-frontends/lg2160.c @@ -1426,7 +1426,7 @@ struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, return &state->frontend; } -EXPORT_SYMBOL(lg2160_attach); +EXPORT_SYMBOL_GPL(lg2160_attach); MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); MODULE_AUTHOR("Michael Krufky "); diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c index c15d3735d34c0d72c43e5ae440d9621b9ab2fac0..bdc8311e1c0b5b3b52aa471aaea6cbf3685e8271 100644 --- a/drivers/media/dvb-frontends/lgdt3305.c +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -1148,7 +1148,7 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(lgdt3305_attach); +EXPORT_SYMBOL_GPL(lgdt3305_attach); static const struct dvb_frontend_ops lgdt3304_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 3c6650f6e9a37ce77a7124ee88249d56770749ae..2638875924153a79a8435034e363e968940645c6 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -1859,7 +1859,7 @@ struct dvb_frontend *lgdt3306a_attach(const struct lgdt3306a_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(lgdt3306a_attach); +EXPORT_SYMBOL_GPL(lgdt3306a_attach); #ifdef DBG_DUMP diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index 97a10996c7facaa50e8f69f82dc8f33d75d31fc3..081d6ad3ce729b99099e22ac2b48ef0a7b042e52 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -927,7 +927,7 @@ struct dvb_frontend *lgdt330x_attach(const struct lgdt330x_config *_config, return lgdt330x_get_dvb_frontend(client); } -EXPORT_SYMBOL(lgdt330x_attach); +EXPORT_SYMBOL_GPL(lgdt330x_attach); static const struct dvb_frontend_ops lgdt3302_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/lgs8gxx.c b/drivers/media/dvb-frontends/lgs8gxx.c index 30014979b985b8961cbeb9f81733e3753d9b69af..ffaf60e16ecd4958e933a42cb4535bc13ef34f2d 100644 --- a/drivers/media/dvb-frontends/lgs8gxx.c +++ b/drivers/media/dvb-frontends/lgs8gxx.c @@ -1043,7 +1043,7 @@ struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, return NULL; } -EXPORT_SYMBOL(lgs8gxx_attach); +EXPORT_SYMBOL_GPL(lgs8gxx_attach); MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); MODULE_AUTHOR("David T. L. Wong "); diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c index 9ffe06cd787dd06dafbb450883866406c2a6b056..41bec050642b5885d571939bbc46e4eb5d0abec4 100644 --- a/drivers/media/dvb-frontends/lnbh25.c +++ b/drivers/media/dvb-frontends/lnbh25.c @@ -173,7 +173,7 @@ struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, __func__, priv->i2c_address); return fe; } -EXPORT_SYMBOL(lnbh25_attach); +EXPORT_SYMBOL_GPL(lnbh25_attach); MODULE_DESCRIPTION("ST LNBH25 driver"); MODULE_AUTHOR("info@netup.ru"); diff --git a/drivers/media/dvb-frontends/lnbp21.c b/drivers/media/dvb-frontends/lnbp21.c index e564974162d65caab5dc224c0540068d0df12aed..32593b1f75a380a476ee891f1457ded1675c0d95 100644 --- a/drivers/media/dvb-frontends/lnbp21.c +++ b/drivers/media/dvb-frontends/lnbp21.c @@ -155,7 +155,7 @@ struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe, return lnbx2x_attach(fe, i2c, override_set, override_clear, i2c_addr, LNBH24_TTX); } -EXPORT_SYMBOL(lnbh24_attach); +EXPORT_SYMBOL_GPL(lnbh24_attach); struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, @@ -164,7 +164,7 @@ struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, return lnbx2x_attach(fe, i2c, override_set, override_clear, 0x08, LNBP21_ISEL); } -EXPORT_SYMBOL(lnbp21_attach); +EXPORT_SYMBOL_GPL(lnbp21_attach); MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24"); MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin"); diff --git a/drivers/media/dvb-frontends/lnbp22.c b/drivers/media/dvb-frontends/lnbp22.c index b8c7145d4cefebc6b905018d511c9d254b8bc085..cb4ea5d3fad4a4e5224855451efe82219005e270 100644 --- a/drivers/media/dvb-frontends/lnbp22.c +++ b/drivers/media/dvb-frontends/lnbp22.c @@ -125,7 +125,7 @@ struct dvb_frontend *lnbp22_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(lnbp22_attach); +EXPORT_SYMBOL_GPL(lnbp22_attach); MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp22"); MODULE_AUTHOR("Dominik Kuhlen"); diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c index cf49ac56a37eda5475bfc776deb8dbdcfd7c8b02..cf037b61b226bc1bcf8d0bdfe4a8bb757a154d5a 100644 --- a/drivers/media/dvb-frontends/m88ds3103.c +++ b/drivers/media/dvb-frontends/m88ds3103.c @@ -1695,7 +1695,7 @@ struct dvb_frontend *m88ds3103_attach(const struct m88ds3103_config *cfg, *tuner_i2c_adapter = pdata.get_i2c_adapter(client); return pdata.get_dvb_frontend(client); } -EXPORT_SYMBOL(m88ds3103_attach); +EXPORT_SYMBOL_GPL(m88ds3103_attach); static const struct dvb_frontend_ops m88ds3103_ops = { .delsys = {SYS_DVBS, SYS_DVBS2}, diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index b294ba87e934f695e914fe26e5181401f4697104..2aa98203cd65955714fc6d121a0ff2583dd44efd 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -808,7 +808,7 @@ struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config, return NULL; } -EXPORT_SYMBOL(m88rs2000_attach); +EXPORT_SYMBOL_GPL(m88rs2000_attach); MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver"); MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com"); diff --git a/drivers/media/dvb-frontends/mb86a16.c b/drivers/media/dvb-frontends/mb86a16.c index 3ec2cb4fa50458a81ad88f1e068f58d3fabac550..0fc45896e7b8808a0849ebed2da765cef9aa08d5 100644 --- a/drivers/media/dvb-frontends/mb86a16.c +++ b/drivers/media/dvb-frontends/mb86a16.c @@ -1853,6 +1853,6 @@ struct dvb_frontend *mb86a16_attach(const struct mb86a16_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(mb86a16_attach); +EXPORT_SYMBOL_GPL(mb86a16_attach); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index 125fed4891ba9710726ac750edaa1679817da87d..f8e4bbee5bd502b461bd13b9cf66978c2c3a6e4b 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -2078,7 +2078,7 @@ struct dvb_frontend *mb86a20s_attach(const struct mb86a20s_config *config, dev_info(&i2c->dev, "Detected a Fujitsu mb86a20s frontend\n"); return &state->frontend; } -EXPORT_SYMBOL(mb86a20s_attach); +EXPORT_SYMBOL_GPL(mb86a20s_attach); static const struct dvb_frontend_ops mb86a20s_ops = { .delsys = { SYS_ISDBT }, diff --git a/drivers/media/dvb-frontends/mt312.c b/drivers/media/dvb-frontends/mt312.c index d43a67045dbe77421c6b73f290a1fafc5de3789c..fb867dd8a26beff48a371e38960b93a7379e93c1 100644 --- a/drivers/media/dvb-frontends/mt312.c +++ b/drivers/media/dvb-frontends/mt312.c @@ -827,7 +827,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(mt312_attach); +EXPORT_SYMBOL_GPL(mt312_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/mt352.c b/drivers/media/dvb-frontends/mt352.c index 399d5c519027e845b0f757d18dbf00e24b0d1f45..1b2889f5cf67de822f61ad3b7a22b1094e0f9188 100644 --- a/drivers/media/dvb-frontends/mt352.c +++ b/drivers/media/dvb-frontends/mt352.c @@ -593,4 +593,4 @@ MODULE_DESCRIPTION("Zarlink MT352 DVB-T Demodulator driver"); MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(mt352_attach); +EXPORT_SYMBOL_GPL(mt352_attach); diff --git a/drivers/media/dvb-frontends/nxt200x.c b/drivers/media/dvb-frontends/nxt200x.c index 200b6dbc75f814794b6953788000e910b6ec362d..1c549ada6ebf968b39003766c5163044581569d3 100644 --- a/drivers/media/dvb-frontends/nxt200x.c +++ b/drivers/media/dvb-frontends/nxt200x.c @@ -1216,5 +1216,5 @@ MODULE_DESCRIPTION("NXT200X (ATSC 8VSB & ITU-T J.83 AnnexB 64/256 QAM) Demodulat MODULE_AUTHOR("Kirk Lapray, Michael Krufky, Jean-Francois Thibert, and Taylor Jacob"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(nxt200x_attach); +EXPORT_SYMBOL_GPL(nxt200x_attach); diff --git a/drivers/media/dvb-frontends/nxt6000.c b/drivers/media/dvb-frontends/nxt6000.c index 136918f82dda0f1996635ca2dafca66ecbf1c5e3..e8d4940370ddf32f8efd73627805b918281ef801 100644 --- a/drivers/media/dvb-frontends/nxt6000.c +++ b/drivers/media/dvb-frontends/nxt6000.c @@ -621,4 +621,4 @@ MODULE_DESCRIPTION("NxtWave NXT6000 DVB-T demodulator driver"); MODULE_AUTHOR("Florian Schirmer"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(nxt6000_attach); +EXPORT_SYMBOL_GPL(nxt6000_attach); diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index 355f3598627bff200d1d5f3fcb4b4e9d0fb1f27f..74e04c7cca1efee3d5f6a43d7897647a192db31a 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -605,4 +605,4 @@ MODULE_AUTHOR("Kirk Lapray"); MODULE_AUTHOR("Trent Piepho"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(or51132_attach); +EXPORT_SYMBOL_GPL(or51132_attach); diff --git a/drivers/media/dvb-frontends/or51211.c b/drivers/media/dvb-frontends/or51211.c index ae732dc5116e2fd61d39526f1b16200598d342fc..2e8e7071a67aa8ea7725e95ac7d16ced6038ab7c 100644 --- a/drivers/media/dvb-frontends/or51211.c +++ b/drivers/media/dvb-frontends/or51211.c @@ -551,5 +551,5 @@ MODULE_DESCRIPTION("Oren OR51211 VSB [pcHDTV HD-2000] Demodulator Driver"); MODULE_AUTHOR("Kirk Lapray"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(or51211_attach); +EXPORT_SYMBOL_GPL(or51211_attach); diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c index 3089cc174a6f5bd75a1b468da60735ed602036ee..28b1dca077ead586eda41da97af30aaf933e4b7b 100644 --- a/drivers/media/dvb-frontends/s5h1409.c +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -981,7 +981,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(s5h1409_attach); +EXPORT_SYMBOL_GPL(s5h1409_attach); static const struct dvb_frontend_ops s5h1409_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index 2563a72e98b70324aec546d754e4ff33da78ad93..fc48e659c2d8a86f671e809bbe1bdcea6fd1e781 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -900,7 +900,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(s5h1411_attach); +EXPORT_SYMBOL_GPL(s5h1411_attach); static const struct dvb_frontend_ops s5h1411_ops = { .delsys = { SYS_ATSC, SYS_DVBC_ANNEX_B }, diff --git a/drivers/media/dvb-frontends/s5h1420.c b/drivers/media/dvb-frontends/s5h1420.c index 6bdec2898bc81f859597cd99025c7845528ce60f..d700de1ea6c2436bea102a9e764450973758fda9 100644 --- a/drivers/media/dvb-frontends/s5h1420.c +++ b/drivers/media/dvb-frontends/s5h1420.c @@ -918,7 +918,7 @@ struct dvb_frontend *s5h1420_attach(const struct s5h1420_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(s5h1420_attach); +EXPORT_SYMBOL_GPL(s5h1420_attach); static const struct dvb_frontend_ops s5h1420_ops = { .delsys = { SYS_DVBS }, diff --git a/drivers/media/dvb-frontends/s5h1432.c b/drivers/media/dvb-frontends/s5h1432.c index 956e8ee4b388e87c7fe027ce5950f521e44fb7c7..ff5d3bdf3bc67b5786b207c2900bbeabfae0653a 100644 --- a/drivers/media/dvb-frontends/s5h1432.c +++ b/drivers/media/dvb-frontends/s5h1432.c @@ -355,7 +355,7 @@ struct dvb_frontend *s5h1432_attach(const struct s5h1432_config *config, return &state->frontend; } -EXPORT_SYMBOL(s5h1432_attach); +EXPORT_SYMBOL_GPL(s5h1432_attach); static const struct dvb_frontend_ops s5h1432_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/s921.c b/drivers/media/dvb-frontends/s921.c index f118d8e641030402b10c1d4768ff9f4fe03f45ec..7e461ac159fc14a68493add116818354f5bb812f 100644 --- a/drivers/media/dvb-frontends/s921.c +++ b/drivers/media/dvb-frontends/s921.c @@ -495,7 +495,7 @@ struct dvb_frontend *s921_attach(const struct s921_config *config, return &state->frontend; } -EXPORT_SYMBOL(s921_attach); +EXPORT_SYMBOL_GPL(s921_attach); static const struct dvb_frontend_ops s921_ops = { .delsys = { SYS_ISDBT }, diff --git a/drivers/media/dvb-frontends/si21xx.c b/drivers/media/dvb-frontends/si21xx.c index 2d29d2c4d434c8e801cb8b8f3603b4f1edb32e86..210ccd356e2bf6ce09f4b7a1e1804b3c0b5af8e3 100644 --- a/drivers/media/dvb-frontends/si21xx.c +++ b/drivers/media/dvb-frontends/si21xx.c @@ -937,7 +937,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(si21xx_attach); +EXPORT_SYMBOL_GPL(si21xx_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/sp887x.c b/drivers/media/dvb-frontends/sp887x.c index 146e7f2dd3c5ee6aad54650c2549d27a7f478ad5..f59c0f96416b56e94106198606971180201930ce 100644 --- a/drivers/media/dvb-frontends/sp887x.c +++ b/drivers/media/dvb-frontends/sp887x.c @@ -624,4 +624,4 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("Spase sp887x DVB-T demodulator driver"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(sp887x_attach); +EXPORT_SYMBOL_GPL(sp887x_attach); diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c index 4ee6c1e1e9f7d11d2b7fe74d3a85e802391c544a..2f4d8fb400cd661cea94c3cecf61f9c2f2d3f123 100644 --- a/drivers/media/dvb-frontends/stb0899_drv.c +++ b/drivers/media/dvb-frontends/stb0899_drv.c @@ -1638,7 +1638,7 @@ struct dvb_frontend *stb0899_attach(struct stb0899_config *config, struct i2c_ad kfree(state); return NULL; } -EXPORT_SYMBOL(stb0899_attach); +EXPORT_SYMBOL_GPL(stb0899_attach); MODULE_PARM_DESC(verbose, "Set Verbosity level"); MODULE_AUTHOR("Manu Abraham"); MODULE_DESCRIPTION("STB0899 Multi-Std frontend"); diff --git a/drivers/media/dvb-frontends/stb6000.c b/drivers/media/dvb-frontends/stb6000.c index 8c9800d577e03548d3e1b5223c4d4f435ac28798..d74e34677b92533f138b1f30b87ef087875cbc58 100644 --- a/drivers/media/dvb-frontends/stb6000.c +++ b/drivers/media/dvb-frontends/stb6000.c @@ -232,7 +232,7 @@ struct dvb_frontend *stb6000_attach(struct dvb_frontend *fe, int addr, return fe; } -EXPORT_SYMBOL(stb6000_attach); +EXPORT_SYMBOL_GPL(stb6000_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/stb6100.c b/drivers/media/dvb-frontends/stb6100.c index 698866c4f15a7c80a25ba555b35e8d2fb66a7386..c5818a15a0d700e0d51ab8e7a7772cacb50017f0 100644 --- a/drivers/media/dvb-frontends/stb6100.c +++ b/drivers/media/dvb-frontends/stb6100.c @@ -557,7 +557,7 @@ static void stb6100_release(struct dvb_frontend *fe) kfree(state); } -EXPORT_SYMBOL(stb6100_attach); +EXPORT_SYMBOL_GPL(stb6100_attach); MODULE_PARM_DESC(verbose, "Set Verbosity level"); MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/dvb-frontends/stv0288.c b/drivers/media/dvb-frontends/stv0288.c index 3ae1f3a2f1420e7f38a22d3de83620b2b371dc86..a5581bd60f9e89267972c5335a93495f13572aa4 100644 --- a/drivers/media/dvb-frontends/stv0288.c +++ b/drivers/media/dvb-frontends/stv0288.c @@ -590,7 +590,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, return NULL; } -EXPORT_SYMBOL(stv0288_attach); +EXPORT_SYMBOL_GPL(stv0288_attach); module_param(debug_legacy_dish_switch, int, 0444); MODULE_PARM_DESC(debug_legacy_dish_switch, diff --git a/drivers/media/dvb-frontends/stv0297.c b/drivers/media/dvb-frontends/stv0297.c index 6d5962d5697ac95e94ba1507075614bd27e8cea3..9d4dbd99a5a797140de6ad2c72961e4fd4a21d78 100644 --- a/drivers/media/dvb-frontends/stv0297.c +++ b/drivers/media/dvb-frontends/stv0297.c @@ -710,4 +710,4 @@ MODULE_DESCRIPTION("ST STV0297 DVB-C Demodulator driver"); MODULE_AUTHOR("Dennis Noermann and Andrew de Quincey"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(stv0297_attach); +EXPORT_SYMBOL_GPL(stv0297_attach); diff --git a/drivers/media/dvb-frontends/stv0299.c b/drivers/media/dvb-frontends/stv0299.c index b5263a0ee5aa595a6a4dc1558cdfaa4a8ea992ef..da7ff2c2e8e55a9ab5d0ee08646aaca4e8417fa0 100644 --- a/drivers/media/dvb-frontends/stv0299.c +++ b/drivers/media/dvb-frontends/stv0299.c @@ -752,4 +752,4 @@ MODULE_DESCRIPTION("ST STV0299 DVB Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, Andreas Oberritter, Andrew de Quincey, Kenneth Aafly"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(stv0299_attach); +EXPORT_SYMBOL_GPL(stv0299_attach); diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index a93f40617469627c57dada1de2aec7a03d0c3206..48326434488c4fd32457d0b99638da9d6dea67b4 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -1750,7 +1750,7 @@ struct dvb_frontend *stv0367ter_attach(const struct stv0367_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(stv0367ter_attach); +EXPORT_SYMBOL_GPL(stv0367ter_attach); static int stv0367cab_gate_ctrl(struct dvb_frontend *fe, int enable) { @@ -2919,7 +2919,7 @@ struct dvb_frontend *stv0367cab_attach(const struct stv0367_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(stv0367cab_attach); +EXPORT_SYMBOL_GPL(stv0367cab_attach); /* * Functions for operation on Digital Devices hardware @@ -3340,7 +3340,7 @@ struct dvb_frontend *stv0367ddb_attach(const struct stv0367_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(stv0367ddb_attach); +EXPORT_SYMBOL_GPL(stv0367ddb_attach); MODULE_PARM_DESC(debug, "Set debug"); MODULE_PARM_DESC(i2c_debug, "Set i2c debug"); diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 212312d20ff620c854d68d71870511750fd199e1..e7b9b9b11d7df9672483558bc49ee8f9474362a0 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -1957,7 +1957,7 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(stv0900_attach); +EXPORT_SYMBOL_GPL(stv0900_attach); MODULE_PARM_DESC(debug, "Set debug"); diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c index a07dc5fdeb3d87312c3b6e1c9fa6f391b3658465..cc45139057ba83ea8f6ae3cea72204e4912e5ea4 100644 --- a/drivers/media/dvb-frontends/stv090x.c +++ b/drivers/media/dvb-frontends/stv090x.c @@ -5071,7 +5071,7 @@ struct dvb_frontend *stv090x_attach(struct stv090x_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(stv090x_attach); +EXPORT_SYMBOL_GPL(stv090x_attach); static const struct i2c_device_id stv090x_id_table[] = { {"stv090x", 0}, diff --git a/drivers/media/dvb-frontends/stv6110.c b/drivers/media/dvb-frontends/stv6110.c index 963f6a896102ac8fd506ea89d13fc57578a9d3be..1cf9c095dbff02ca62382193c1bf4549336f3df1 100644 --- a/drivers/media/dvb-frontends/stv6110.c +++ b/drivers/media/dvb-frontends/stv6110.c @@ -427,7 +427,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(stv6110_attach); +EXPORT_SYMBOL_GPL(stv6110_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c index 11653f846c12381bda376db3d4e978ee40954986..c678f47d2449c7e6192d618473c411bf668f168c 100644 --- a/drivers/media/dvb-frontends/stv6110x.c +++ b/drivers/media/dvb-frontends/stv6110x.c @@ -467,7 +467,7 @@ const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, dev_info(&stv6110x->i2c->dev, "Attaching STV6110x\n"); return stv6110x->devctl; } -EXPORT_SYMBOL(stv6110x_attach); +EXPORT_SYMBOL_GPL(stv6110x_attach); static const struct i2c_device_id stv6110x_id_table[] = { {"stv6110x", 0}, diff --git a/drivers/media/dvb-frontends/tda10021.c b/drivers/media/dvb-frontends/tda10021.c index faa6e54b33729266f5e28e7d631eb93584cf5720..462e12ab6bd14007ee9d790aabc98fabd3e58152 100644 --- a/drivers/media/dvb-frontends/tda10021.c +++ b/drivers/media/dvb-frontends/tda10021.c @@ -523,4 +523,4 @@ MODULE_DESCRIPTION("Philips TDA10021 DVB-C demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Markus Schulz"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(tda10021_attach); +EXPORT_SYMBOL_GPL(tda10021_attach); diff --git a/drivers/media/dvb-frontends/tda10023.c b/drivers/media/dvb-frontends/tda10023.c index 8f32edf6b700e37c6ce50ca66cd9f11dfb641f8b..4c2541ecd743320c37a127fdb685252cd426fbb7 100644 --- a/drivers/media/dvb-frontends/tda10023.c +++ b/drivers/media/dvb-frontends/tda10023.c @@ -594,4 +594,4 @@ MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver"); MODULE_AUTHOR("Georg Acher, Hartmut Birr"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(tda10023_attach); +EXPORT_SYMBOL_GPL(tda10023_attach); diff --git a/drivers/media/dvb-frontends/tda10048.c b/drivers/media/dvb-frontends/tda10048.c index 3cb4e5270e4fab9817f1fbe3131ee39d2ec39ece..5d5e4e9e4422e66692b572d449c09796da71477c 100644 --- a/drivers/media/dvb-frontends/tda10048.c +++ b/drivers/media/dvb-frontends/tda10048.c @@ -1138,7 +1138,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, kfree(state); return NULL; } -EXPORT_SYMBOL(tda10048_attach); +EXPORT_SYMBOL_GPL(tda10048_attach); static const struct dvb_frontend_ops tda10048_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/dvb-frontends/tda1004x.c b/drivers/media/dvb-frontends/tda1004x.c index 83a798ca9b00290d03a39d3514b5280ad1750859..6f306db6c615f44cd64f4dcf6880cc55e99f32d0 100644 --- a/drivers/media/dvb-frontends/tda1004x.c +++ b/drivers/media/dvb-frontends/tda1004x.c @@ -1378,5 +1378,5 @@ MODULE_DESCRIPTION("Philips TDA10045H & TDA10046H DVB-T Demodulator"); MODULE_AUTHOR("Andrew de Quincey & Robert Schlabbach"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(tda10045_attach); -EXPORT_SYMBOL(tda10046_attach); +EXPORT_SYMBOL_GPL(tda10045_attach); +EXPORT_SYMBOL_GPL(tda10046_attach); diff --git a/drivers/media/dvb-frontends/tda10086.c b/drivers/media/dvb-frontends/tda10086.c index cdcf97664bba8595d7c54e6b3d5ebc39395f126c..b449514ae5854bd172d7807aa730d0b43b592c51 100644 --- a/drivers/media/dvb-frontends/tda10086.c +++ b/drivers/media/dvb-frontends/tda10086.c @@ -764,4 +764,4 @@ MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator"); MODULE_AUTHOR("Andrew de Quincey"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(tda10086_attach); +EXPORT_SYMBOL_GPL(tda10086_attach); diff --git a/drivers/media/dvb-frontends/tda665x.c b/drivers/media/dvb-frontends/tda665x.c index 13e8969da7f8928222363a681d389d9f3469cd0b..346be5011fb738043b52496dbc4e198110883aaa 100644 --- a/drivers/media/dvb-frontends/tda665x.c +++ b/drivers/media/dvb-frontends/tda665x.c @@ -227,7 +227,7 @@ struct dvb_frontend *tda665x_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(tda665x_attach); +EXPORT_SYMBOL_GPL(tda665x_attach); MODULE_DESCRIPTION("TDA665x driver"); MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/dvb-frontends/tda8083.c b/drivers/media/dvb-frontends/tda8083.c index e3e1c3db2c856f2c365ba8fe4a040ded9664c622..44f53624557bc7ee89cfb6759678330e99f57e04 100644 --- a/drivers/media/dvb-frontends/tda8083.c +++ b/drivers/media/dvb-frontends/tda8083.c @@ -481,4 +481,4 @@ MODULE_DESCRIPTION("Philips TDA8083 DVB-S Demodulator"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(tda8083_attach); +EXPORT_SYMBOL_GPL(tda8083_attach); diff --git a/drivers/media/dvb-frontends/tda8261.c b/drivers/media/dvb-frontends/tda8261.c index 0d576d41c67d8489b04f5ce568e421167082e49c..8b06f92745dcae2cbf7708494684fc08f0701ea5 100644 --- a/drivers/media/dvb-frontends/tda8261.c +++ b/drivers/media/dvb-frontends/tda8261.c @@ -188,7 +188,7 @@ struct dvb_frontend *tda8261_attach(struct dvb_frontend *fe, return NULL; } -EXPORT_SYMBOL(tda8261_attach); +EXPORT_SYMBOL_GPL(tda8261_attach); MODULE_AUTHOR("Manu Abraham"); MODULE_DESCRIPTION("TDA8261 8PSK/QPSK Tuner"); diff --git a/drivers/media/dvb-frontends/tda826x.c b/drivers/media/dvb-frontends/tda826x.c index f9703a1dd758cbdec39fbab279d2d4ce8322e8e0..eafcf5f7da3dcef953958dc54e759055530ebc48 100644 --- a/drivers/media/dvb-frontends/tda826x.c +++ b/drivers/media/dvb-frontends/tda826x.c @@ -164,7 +164,7 @@ struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2 return fe; } -EXPORT_SYMBOL(tda826x_attach); +EXPORT_SYMBOL_GPL(tda826x_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c index f5b60f82769748c889bfebc9aadc86d1016820df..a5ebce57f35e6e1d79511c71ec66bde0154e8806 100644 --- a/drivers/media/dvb-frontends/ts2020.c +++ b/drivers/media/dvb-frontends/ts2020.c @@ -525,7 +525,7 @@ struct dvb_frontend *ts2020_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(ts2020_attach); +EXPORT_SYMBOL_GPL(ts2020_attach); /* * We implement own regmap locking due to legacy DVB attach which uses frontend diff --git a/drivers/media/dvb-frontends/tua6100.c b/drivers/media/dvb-frontends/tua6100.c index 2483f614d0e7df2cdf95ac3fd9d8a1e69db3d1da..41dd9b6d319084f685155801eb6514e56b6de67b 100644 --- a/drivers/media/dvb-frontends/tua6100.c +++ b/drivers/media/dvb-frontends/tua6100.c @@ -186,7 +186,7 @@ struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2 fe->tuner_priv = priv; return fe; } -EXPORT_SYMBOL(tua6100_attach); +EXPORT_SYMBOL_GPL(tua6100_attach); MODULE_DESCRIPTION("DVB tua6100 driver"); MODULE_AUTHOR("Andrew de Quincey"); diff --git a/drivers/media/dvb-frontends/ves1820.c b/drivers/media/dvb-frontends/ves1820.c index 9df14d0be1c1a4e10f5d471dbb4911a9b8630630..ee5620e731e9be3dee6592b9617e8dd352136b42 100644 --- a/drivers/media/dvb-frontends/ves1820.c +++ b/drivers/media/dvb-frontends/ves1820.c @@ -434,4 +434,4 @@ MODULE_DESCRIPTION("VLSI VES1820 DVB-C Demodulator driver"); MODULE_AUTHOR("Ralph Metzler, Holger Waechtler"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(ves1820_attach); +EXPORT_SYMBOL_GPL(ves1820_attach); diff --git a/drivers/media/dvb-frontends/ves1x93.c b/drivers/media/dvb-frontends/ves1x93.c index b747272863025505448c36281b595614e9fa953d..c60e21d26b8817fa737f77d9a4d5e0edd4acc275 100644 --- a/drivers/media/dvb-frontends/ves1x93.c +++ b/drivers/media/dvb-frontends/ves1x93.c @@ -540,4 +540,4 @@ MODULE_DESCRIPTION("VLSI VES1x93 DVB-S Demodulator driver"); MODULE_AUTHOR("Ralph Metzler"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(ves1x93_attach); +EXPORT_SYMBOL_GPL(ves1x93_attach); diff --git a/drivers/media/dvb-frontends/zl10036.c b/drivers/media/dvb-frontends/zl10036.c index d392c7cce2ce0e565abb48ef600300123b8e7198..7ba575e9c55f4356af0d7ff920849169f5bb554a 100644 --- a/drivers/media/dvb-frontends/zl10036.c +++ b/drivers/media/dvb-frontends/zl10036.c @@ -496,7 +496,7 @@ struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe, kfree(state); return NULL; } -EXPORT_SYMBOL(zl10036_attach); +EXPORT_SYMBOL_GPL(zl10036_attach); module_param_named(debug, zl10036_debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/zl10039.c b/drivers/media/dvb-frontends/zl10039.c index 1335bf78d5b7f09a396a77a1217af949bce3ae98..a3e4d219400ce6bebc5bbfbeef0a7a39336c44ef 100644 --- a/drivers/media/dvb-frontends/zl10039.c +++ b/drivers/media/dvb-frontends/zl10039.c @@ -295,7 +295,7 @@ struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, kfree(state); return NULL; } -EXPORT_SYMBOL(zl10039_attach); +EXPORT_SYMBOL_GPL(zl10039_attach); module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); diff --git a/drivers/media/dvb-frontends/zl10353.c b/drivers/media/dvb-frontends/zl10353.c index 2a2cf20a73d615093b96453f411df106df5f5616..8849d05475c27b73d422c78fda497e22400bad5e 100644 --- a/drivers/media/dvb-frontends/zl10353.c +++ b/drivers/media/dvb-frontends/zl10353.c @@ -665,4 +665,4 @@ MODULE_DESCRIPTION("Zarlink ZL10353 DVB-T demodulator driver"); MODULE_AUTHOR("Chris Pascoe"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(zl10353_attach); +EXPORT_SYMBOL_GPL(zl10353_attach); diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index a1136fdfbed2d4b16f858dae07d5fa1db620b2b1..ec53abe2e84e53ef2e24a9760f10f94836340f67 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -691,12 +691,12 @@ static int imx219_init_cfg(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - /* Initialize try_fmt */ + /* Initialize the format. */ format = v4l2_subdev_get_pad_format(sd, state, 0); imx219_update_pad_format(imx219, &supported_modes[0], format, MEDIA_BUS_FMT_SRGGB10_1X10); - /* Initialize crop rectangle. */ + /* Initialize the crop rectangle. */ crop = v4l2_subdev_get_pad_crop(sd, state, 0); crop->top = IMX219_PIXEL_ARRAY_TOP; crop->left = IMX219_PIXEL_ARRAY_LEFT; @@ -750,6 +750,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, const struct imx219_mode *mode; int exposure_max, exposure_def, hblank; struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes), @@ -757,10 +758,12 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, fmt->format.width, fmt->format.height); imx219_update_pad_format(imx219, mode, &fmt->format, fmt->format.code); + format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0); - if (imx219->mode == mode && format->code == fmt->format.code) - return 0; + *format = fmt->format; + *crop = mode->crop; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx219->mode = mode; @@ -788,8 +791,6 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd, hblank); } - *format = fmt->format; - return 0; } diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 20e7c7cf5eeb953d60babada315c2e020acf7d37..be84ff1e2b170562226404ded0c33f3064a1cad5 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -1110,7 +1110,6 @@ static int max9286_v4l2_register(struct max9286_priv *priv) static void max9286_v4l2_unregister(struct max9286_priv *priv) { - fwnode_handle_put(priv->sd.fwnode); v4l2_ctrl_handler_free(&priv->ctrls); v4l2_async_unregister_subdev(&priv->sd); max9286_v4l2_notifier_unregister(priv); diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c index a36a709243fdbd4390ee8d53cc20fa70d1d4fbfc..3e22df36354fdbd03e828d61d652903f854ff098 100644 --- a/drivers/media/i2c/rdacm21.c +++ b/drivers/media/i2c/rdacm21.c @@ -608,7 +608,6 @@ static void rdacm21_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&dev->sd); v4l2_ctrl_handler_free(&dev->ctrls); i2c_unregister_device(dev->isp); - fwnode_handle_put(dev->sd.fwnode); } static const struct of_device_id rdacm21_of_ids[] = { diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c index 436baf6c8b089d43efac8b4eda44dd9c22620717..241a696e374a922b6f8aa058150e75c3e42a11b7 100644 --- a/drivers/media/pci/bt8xx/bttv-risc.c +++ b/drivers/media/pci/bt8xx/bttv-risc.c @@ -68,9 +68,7 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, sg = sglist; for (line = 0; line < store_lines; line++) { if ((line >= (store_lines - VCR_HACK_LINES)) && - (btv->opt_vcr_hack || - (V4L2_FIELD_HAS_BOTH(btv->field) || - btv->field == V4L2_FIELD_ALTERNATE))) + btv->opt_vcr_hack) continue; while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); diff --git a/drivers/media/pci/bt8xx/dst.c b/drivers/media/pci/bt8xx/dst.c index 3e52a51982d76ecede122fa459c32888758be832..110651e478314633be2fc61a75756f3160c78741 100644 --- a/drivers/media/pci/bt8xx/dst.c +++ b/drivers/media/pci/bt8xx/dst.c @@ -1722,7 +1722,7 @@ struct dst_state *dst_attach(struct dst_state *state, struct dvb_adapter *dvb_ad return state; /* Manu (DST is a card not a frontend) */ } -EXPORT_SYMBOL(dst_attach); +EXPORT_SYMBOL_GPL(dst_attach); static const struct dvb_frontend_ops dst_dvbt_ops = { .delsys = { SYS_DVBT }, diff --git a/drivers/media/pci/bt8xx/dst_ca.c b/drivers/media/pci/bt8xx/dst_ca.c index d234a0f404d68a87fdeee5e7530fa7eb81fad904..a9cc6e7a57f99a74e3d077125922239d834fff2d 100644 --- a/drivers/media/pci/bt8xx/dst_ca.c +++ b/drivers/media/pci/bt8xx/dst_ca.c @@ -668,7 +668,7 @@ struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_ return NULL; } -EXPORT_SYMBOL(dst_ca_attach); +EXPORT_SYMBOL_GPL(dst_ca_attach); MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver"); MODULE_AUTHOR("Manu Abraham"); diff --git a/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c b/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c index 6868a0c4fc82ab75868625ebee442d08cdfc703f..520ebd16b0c44cf0c5122ad51f67711303f71826 100644 --- a/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c +++ b/drivers/media/pci/ddbridge/ddbridge-dummy-fe.c @@ -112,7 +112,7 @@ struct dvb_frontend *ddbridge_dummy_fe_qam_attach(void) state->frontend.demodulator_priv = state; return &state->frontend; } -EXPORT_SYMBOL(ddbridge_dummy_fe_qam_attach); +EXPORT_SYMBOL_GPL(ddbridge_dummy_fe_qam_attach); static const struct dvb_frontend_ops ddbridge_dummy_fe_qam_ops = { .delsys = { SYS_DVBC_ANNEX_A }, diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig index e113902fa8064221ce6589c4965fa727120e2e56..ee4684159d3de86bf7051a652ee63ecdc0dd3574 100644 --- a/drivers/media/pci/intel/Kconfig +++ b/drivers/media/pci/intel/Kconfig @@ -1,11 +1,19 @@ # SPDX-License-Identifier: GPL-2.0-only + +source "drivers/media/pci/intel/ipu3/Kconfig" +source "drivers/media/pci/intel/ivsc/Kconfig" + config IPU_BRIDGE - tristate + tristate "Intel IPU Bridge" depends on I2C && ACPI help - This is a helper module for the IPU bridge, which can be - used by ipu3 and other drivers. In order to handle module - dependencies, this is selected by each driver that needs it. + The IPU bridge is a helper library for Intel IPU drivers to + function on systems shipped with Windows. -source "drivers/media/pci/intel/ipu3/Kconfig" -source "drivers/media/pci/intel/ivsc/Kconfig" + Currently used by the ipu3-cio2 and atomisp drivers. + + Supported systems include: + + - Microsoft Surface models (except Surface Pro 3) + - The Lenovo Miix line (for example the 510, 520, 710 and 720) + - Dell 7285 diff --git a/drivers/media/pci/intel/ipu3/Kconfig b/drivers/media/pci/intel/ipu3/Kconfig index 0951545eab21a06b107331cd6d4b8768e557bb6f..c0a250daa9270ec59473f4d44a65ecc826f4f90b 100644 --- a/drivers/media/pci/intel/ipu3/Kconfig +++ b/drivers/media/pci/intel/ipu3/Kconfig @@ -2,13 +2,13 @@ config VIDEO_IPU3_CIO2 tristate "Intel ipu3-cio2 driver" depends on VIDEO_DEV && PCI + depends on IPU_BRIDGE || !IPU_BRIDGE depends on ACPI || COMPILE_TEST depends on X86 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE select VIDEOBUF2_DMA_SG - select IPU_BRIDGE if CIO2_BRIDGE help This is the Intel IPU3 CIO2 CSI-2 receiver unit, found in Intel @@ -18,22 +18,3 @@ config VIDEO_IPU3_CIO2 Say Y or M here if you have a Skylake/Kaby Lake SoC with MIPI CSI-2 connected camera. The module will be called ipu3-cio2. - -config CIO2_BRIDGE - bool "IPU3 CIO2 Sensors Bridge" - depends on VIDEO_IPU3_CIO2 && ACPI - depends on I2C - help - This extension provides an API for the ipu3-cio2 driver to create - connections to cameras that are hidden in the SSDB buffer in ACPI. - It can be used to enable support for cameras in detachable / hybrid - devices that ship with Windows. - - Say Y here if your device is a detachable / hybrid laptop that comes - with Windows installed by the OEM, for example: - - - Microsoft Surface models (except Surface Pro 3) - - The Lenovo Miix line (for example the 510, 520, 710 and 720) - - Dell 7285 - - If in doubt, say N here. diff --git a/drivers/media/pci/intel/ivsc/Kconfig b/drivers/media/pci/intel/ivsc/Kconfig index 1ef1c4e3750d7033ca753ef71f4c01be8817af11..a8cb981544f71f5cdc193b29e26f84ef3552bae0 100644 --- a/drivers/media/pci/intel/ivsc/Kconfig +++ b/drivers/media/pci/intel/ivsc/Kconfig @@ -3,7 +3,10 @@ config INTEL_VSC tristate "Intel Visual Sensing Controller" - depends on INTEL_MEI && ACPI + depends on INTEL_MEI && ACPI && VIDEO_DEV + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select V4L2_FWNODE help This adds support for Intel Visual Sensing Controller (IVSC). diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c index 6e6caf50e11ef8026b5788973ec734ed10ac19e4..59b89e421dc2892c0e02f88b8119af8898be86ed 100644 --- a/drivers/media/platform/intel/pxa_camera.c +++ b/drivers/media/platform/intel/pxa_camera.c @@ -2398,7 +2398,7 @@ static int pxa_camera_probe(struct platform_device *pdev) PXA_CAM_DRV_NAME, pcdev); if (err) { dev_err(&pdev->dev, "Camera interrupt register failed\n"); - goto exit_v4l2_device_unregister; + goto exit_deactivate; } pcdev->notifier.ops = &pxa_camera_sensor_ops; diff --git a/drivers/media/platform/marvell/Kconfig b/drivers/media/platform/marvell/Kconfig index ec1a16734a280dce566149664354b41bd34cad81..d6499ffe30e8b6a3f46ec3266de4c8d598cfdf82 100644 --- a/drivers/media/platform/marvell/Kconfig +++ b/drivers/media/platform/marvell/Kconfig @@ -7,7 +7,7 @@ config VIDEO_CAFE_CCIC depends on V4L_PLATFORM_DRIVERS depends on PCI && I2C && VIDEO_DEV depends on COMMON_CLK - select VIDEO_OV7670 + select VIDEO_OV7670 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG select VIDEOBUF2_DMA_SG @@ -22,7 +22,7 @@ config VIDEO_MMP_CAMERA depends on I2C && VIDEO_DEV depends on ARCH_MMP || COMPILE_TEST depends on COMMON_CLK - select VIDEO_OV7670 + select VIDEO_OV7670 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR select I2C_GPIO select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c index d299cc2962a5ae0eb84e8115c3cf364789d1e4c6..ae6290d28f8e98eabfee667d60a73ea29c5ea60a 100644 --- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c +++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c @@ -138,7 +138,8 @@ int vpu_enc_init(struct venc_vpu_inst *vpu) vpu->ctx->vpu_inst = vpu; status = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, - vpu_enc_ipi_handler, "venc", NULL); + vpu_enc_ipi_handler, "venc", + vpu->ctx->dev); if (status) { mtk_venc_err(vpu->ctx, "vpu_ipi_register fail %d", status); diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c index 16f19a6401301e01e879af3baca3fb59672ac536..5f93712bf485408cb3a7d174b4ddb44b65250ff4 100644 --- a/drivers/media/platform/nxp/imx-mipi-csis.c +++ b/drivers/media/platform/nxp/imx-mipi-csis.c @@ -1490,7 +1490,6 @@ static int mipi_csis_probe(struct platform_device *pdev) v4l2_async_unregister_subdev(&csis->sd); err_disable_clock: mipi_csis_clk_disable(csis); - fwnode_handle_put(csis->sd.fwnode); return ret; } @@ -1510,7 +1509,6 @@ static void mipi_csis_remove(struct platform_device *pdev) mipi_csis_clk_disable(csis); v4l2_subdev_cleanup(&csis->sd); media_entity_cleanup(&csis->sd.entity); - fwnode_handle_put(csis->sd.fwnode); pm_runtime_set_suspended(&pdev->dev); } diff --git a/drivers/media/platform/via/Kconfig b/drivers/media/platform/via/Kconfig index 8926eb0803b27a0a333da2b9a6b58246092b4d5c..6e603c0382487862cf5a76e59b0dce8ed2c29896 100644 --- a/drivers/media/platform/via/Kconfig +++ b/drivers/media/platform/via/Kconfig @@ -7,7 +7,7 @@ config VIDEO_VIA_CAMERA depends on V4L_PLATFORM_DRIVERS depends on FB_VIA && VIDEO_DEV select VIDEOBUF2_DMA_SG - select VIDEO_OV7670 + select VIDEO_OV7670 if VIDEO_CAMERA_SENSOR help Driver support for the integrated camera controller in VIA Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index eaa3bbc903d7eb3dac799302727db6586dd39dc4..3d3b54be29557674de423b798f88fc7eee32a5e4 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -499,7 +499,7 @@ struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(fc0011_attach); +EXPORT_SYMBOL_GPL(fc0011_attach); MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); MODULE_AUTHOR("Michael Buesch "); diff --git a/drivers/media/tuners/fc0012.c b/drivers/media/tuners/fc0012.c index 4429d5e8c5796e096250a333be672c4174d8db26..81e65acbdb170c5ad6151cb7095783b812ee4d1e 100644 --- a/drivers/media/tuners/fc0012.c +++ b/drivers/media/tuners/fc0012.c @@ -495,7 +495,7 @@ struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(fc0012_attach); +EXPORT_SYMBOL_GPL(fc0012_attach); MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); MODULE_AUTHOR("Hans-Frieder Vogt "); diff --git a/drivers/media/tuners/fc0013.c b/drivers/media/tuners/fc0013.c index 29dd9b55ff333e540a0fef463071433fd5daf2b5..1006a2798eefc0220ef6b07d0344bf4cea560ff7 100644 --- a/drivers/media/tuners/fc0013.c +++ b/drivers/media/tuners/fc0013.c @@ -608,7 +608,7 @@ struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(fc0013_attach); +EXPORT_SYMBOL_GPL(fc0013_attach); MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); MODULE_AUTHOR("Hans-Frieder Vogt "); diff --git a/drivers/media/tuners/max2165.c b/drivers/media/tuners/max2165.c index 1c746bed51feebb922acdac2142a1fd5a09c9a03..1575ab94e1c8b0d44379d583efd3e5e4f5b7d9d9 100644 --- a/drivers/media/tuners/max2165.c +++ b/drivers/media/tuners/max2165.c @@ -410,7 +410,7 @@ struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(max2165_attach); +EXPORT_SYMBOL_GPL(max2165_attach); MODULE_AUTHOR("David T. L. Wong "); MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); diff --git a/drivers/media/tuners/mc44s803.c b/drivers/media/tuners/mc44s803.c index 0c9161516abdfc57fdd056d85752ad6c93a4c6ec..ed8bdf7ebd99dc402ccf087a79a92fa2667bae97 100644 --- a/drivers/media/tuners/mc44s803.c +++ b/drivers/media/tuners/mc44s803.c @@ -356,7 +356,7 @@ struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe, kfree(priv); return NULL; } -EXPORT_SYMBOL(mc44s803_attach); +EXPORT_SYMBOL_GPL(mc44s803_attach); MODULE_AUTHOR("Jochen Friedrich"); MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver"); diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c index 0278a9f0aeefacebe8f6029ecdca836e49703b45..4205ed4cf46752c29c49546e3f09049bdfece3f3 100644 --- a/drivers/media/tuners/mt2060.c +++ b/drivers/media/tuners/mt2060.c @@ -440,7 +440,7 @@ struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter return fe; } -EXPORT_SYMBOL(mt2060_attach); +EXPORT_SYMBOL_GPL(mt2060_attach); static int mt2060_probe(struct i2c_client *client) { diff --git a/drivers/media/tuners/mt2131.c b/drivers/media/tuners/mt2131.c index 37f50ff6c0bd2858fc0944bdc9ce3503be55bbea..eebc060883414baf88866ccf4875c02d965834b9 100644 --- a/drivers/media/tuners/mt2131.c +++ b/drivers/media/tuners/mt2131.c @@ -274,7 +274,7 @@ struct dvb_frontend * mt2131_attach(struct dvb_frontend *fe, fe->tuner_priv = priv; return fe; } -EXPORT_SYMBOL(mt2131_attach); +EXPORT_SYMBOL_GPL(mt2131_attach); MODULE_AUTHOR("Steven Toth"); MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); diff --git a/drivers/media/tuners/mt2266.c b/drivers/media/tuners/mt2266.c index 6136f20fa9b7f61d7cefb083e71a5f48bc87fc04..2e92885a6bcb9b968eb1fdc7d1ffb1fdd12ca09d 100644 --- a/drivers/media/tuners/mt2266.c +++ b/drivers/media/tuners/mt2266.c @@ -336,7 +336,7 @@ struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter mt2266_calibrate(priv); return fe; } -EXPORT_SYMBOL(mt2266_attach); +EXPORT_SYMBOL_GPL(mt2266_attach); MODULE_AUTHOR("Olivier DANET"); MODULE_DESCRIPTION("Microtune MT2266 silicon tuner driver"); diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index 06dfab9fb8cbcdde93ca4d1a9036b7f81d10f0f2..d9bfa257a00542ff92e581d244d3668ca34cd0f0 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -4120,7 +4120,7 @@ struct dvb_frontend *mxl5005s_attach(struct dvb_frontend *fe, fe->tuner_priv = state; return fe; } -EXPORT_SYMBOL(mxl5005s_attach); +EXPORT_SYMBOL_GPL(mxl5005s_attach); MODULE_DESCRIPTION("MaxLinear MXL5005S silicon tuner driver"); MODULE_AUTHOR("Steven Toth"); diff --git a/drivers/media/tuners/qt1010.c b/drivers/media/tuners/qt1010.c index a7b19863f489e28174586a8e365bb2182ae65f60..48fc79cd402733aec6d3fdef8a99eb2327cd9eee 100644 --- a/drivers/media/tuners/qt1010.c +++ b/drivers/media/tuners/qt1010.c @@ -441,7 +441,7 @@ struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, fe->tuner_priv = priv; return fe; } -EXPORT_SYMBOL(qt1010_attach); +EXPORT_SYMBOL_GPL(qt1010_attach); MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); diff --git a/drivers/media/tuners/tda18218.c b/drivers/media/tuners/tda18218.c index 4ed94646116fa8119f262be39f0e6b891017e874..7d8d84dcb245971ee8c1642e0f9210fed0f12da9 100644 --- a/drivers/media/tuners/tda18218.c +++ b/drivers/media/tuners/tda18218.c @@ -336,7 +336,7 @@ struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe, return fe; } -EXPORT_SYMBOL(tda18218_attach); +EXPORT_SYMBOL_GPL(tda18218_attach); MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); diff --git a/drivers/media/tuners/xc2028.c b/drivers/media/tuners/xc2028.c index 69c2e1b99bf1723ec4dbc404b46c9a9ba64d1a59..5a967edceca93db8b4c00a2fda397cc1037cfe88 100644 --- a/drivers/media/tuners/xc2028.c +++ b/drivers/media/tuners/xc2028.c @@ -1512,7 +1512,7 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, return NULL; } -EXPORT_SYMBOL(xc2028_attach); +EXPORT_SYMBOL_GPL(xc2028_attach); MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver"); MODULE_AUTHOR("Michel Ludwig "); diff --git a/drivers/media/tuners/xc4000.c b/drivers/media/tuners/xc4000.c index d59b4ab774302aed4acc7bd09fff8debfe696859..57ded9ff3f04350df4c7705487c0110d0437d195 100644 --- a/drivers/media/tuners/xc4000.c +++ b/drivers/media/tuners/xc4000.c @@ -1742,7 +1742,7 @@ struct dvb_frontend *xc4000_attach(struct dvb_frontend *fe, xc4000_release(fe); return NULL; } -EXPORT_SYMBOL(xc4000_attach); +EXPORT_SYMBOL_GPL(xc4000_attach); MODULE_AUTHOR("Steven Toth, Davide Ferri"); MODULE_DESCRIPTION("Xceive xc4000 silicon tuner driver"); diff --git a/drivers/media/tuners/xc5000.c b/drivers/media/tuners/xc5000.c index 7b7d9fe4f945338792dba2d20f944ecbd266cd50..2182e5b7b6064c2f129e06d73ad7aca6ab603e6a 100644 --- a/drivers/media/tuners/xc5000.c +++ b/drivers/media/tuners/xc5000.c @@ -1460,7 +1460,7 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, xc5000_release(fe); return NULL; } -EXPORT_SYMBOL(xc5000_attach); +EXPORT_SYMBOL_GPL(xc5000_attach); MODULE_AUTHOR("Steven Toth"); MODULE_DESCRIPTION("Xceive xc5000 silicon tuner driver"); diff --git a/drivers/media/usb/em28xx/Kconfig b/drivers/media/usb/em28xx/Kconfig index b3c472b8c5a960aa81cfced5d245436d4f99364e..cb61fd6cc6c617dc36ed8f305b02a851ee8fa76f 100644 --- a/drivers/media/usb/em28xx/Kconfig +++ b/drivers/media/usb/em28xx/Kconfig @@ -12,8 +12,8 @@ config VIDEO_EM28XX_V4L2 select VIDEO_SAA711X if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVP5150 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_MT9V011 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_CAMERA_SUPPORT - select VIDEO_OV2640 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_CAMERA_SUPPORT + select VIDEO_MT9V011 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR + select VIDEO_OV2640 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR help This is a video4linux driver for Empia 28xx based TV cards. diff --git a/drivers/media/usb/go7007/Kconfig b/drivers/media/usb/go7007/Kconfig index 4ff79940ad8d46664b252dc75b2b3ab792eb65e0..b2a15d9fb1f33cc7d302f3f99b30fc6921007098 100644 --- a/drivers/media/usb/go7007/Kconfig +++ b/drivers/media/usb/go7007/Kconfig @@ -12,8 +12,8 @@ config VIDEO_GO7007 select VIDEO_TW2804 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TW9903 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TW9906 if MEDIA_SUBDRV_AUTOSELECT - select VIDEO_OV7640 if MEDIA_SUBDRV_AUTOSELECT && MEDIA_CAMERA_SUPPORT select VIDEO_UDA1342 if MEDIA_SUBDRV_AUTOSELECT + select VIDEO_OV7640 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR help This is a video4linux driver for the WIS GO7007 MPEG encoder chip. diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 5e9d3da862dd86ac84634ef8664c69866557d990..e59a463c27618ebbce7618b754c56bb41be2db3b 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -1402,6 +1402,9 @@ int uvc_query_v4l2_menu(struct uvc_video_chain *chain, query_menu->id = id; query_menu->index = index; + if (index >= BITS_PER_TYPE(mapping->menu_mask)) + return -EINVAL; + ret = mutex_lock_interruptible(&chain->ctrl_mutex); if (ret < 0) return -ERESTARTSYS; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 85be64579fc9d3177c96c0421c4b8ecd74f70341..90ce58fd629e5f32dd6cb207e60544b2718ee5c7 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -241,6 +241,7 @@ config MFD_CS42L43 tristate select MFD_CORE select REGMAP + select REGMAP_IRQ config MFD_CS42L43_I2C tristate "Cirrus Logic CS42L43 (I2C)" @@ -1220,7 +1221,7 @@ config MFD_RC5T583 different functionality of the device. config MFD_RK8XX - bool + tristate select MFD_CORE config MFD_RK8XX_I2C @@ -1371,8 +1372,9 @@ config MFD_SC27XX_PMIC and it also adds the irq_chip parts for handling the PMIC chip events. config RZ_MTU3 - bool "Renesas RZ/G2L MTU3a core driver" + tristate "Renesas RZ/G2L MTU3a core driver" depends on (ARCH_RZG2L && OF) || COMPILE_TEST + select MFD_CORE help Select this option to enable Renesas RZ/G2L MTU3a core driver for the Multi-Function Timer Pulse Unit 3 (MTU3a) hardware available diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 9d9e9787d5e8ac70ff624e8ff37b5eae169489be..15c95828b09a73c47fb687283a8e52f061a75ca0 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -21,7 +21,6 @@ #include #include #include -#include /* * Interrupt register offsets diff --git a/drivers/mfd/acer-ec-a500.c b/drivers/mfd/acer-ec-a500.c index feb757e90dc3f3be1ca74bf12b21c5d20801f7c3..79405835ff8e107c5bb6fba8f909641b245cdafe 100644 --- a/drivers/mfd/acer-ec-a500.c +++ b/drivers/mfd/acer-ec-a500.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mfd/act8945a.c b/drivers/mfd/act8945a.c index 2406fcdff5f9cc3546580e695974ade16087f727..4e32ac3d573e860639f6d616f8569b5c8c33b3cb 100644 --- a/drivers/mfd/act8945a.c +++ b/drivers/mfd/act8945a.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include static const struct mfd_cell act8945a_devs[] = { @@ -68,7 +68,7 @@ MODULE_DEVICE_TABLE(of, act8945a_of_match); static struct i2c_driver act8945a_i2c_driver = { .driver = { .name = "act8945a", - .of_match_table = of_match_ptr(act8945a_of_match), + .of_match_table = act8945a_of_match, }, .probe = act8945a_i2c_probe, .id_table = act8945a_i2c_id, diff --git a/drivers/mfd/altera-a10sr.c b/drivers/mfd/altera-a10sr.c index 34ef526f4aee459b645fad66ab2c4760e29535e3..d53e433ab5c1d83289de99d00270ae05214ab7b6 100644 --- a/drivers/mfd/altera-a10sr.c +++ b/drivers/mfd/altera-a10sr.c @@ -163,7 +163,7 @@ static struct spi_driver altr_a10sr_spi_driver = { .probe = altr_a10sr_spi_probe, .driver = { .name = "altr_a10sr", - .of_match_table = of_match_ptr(altr_a10sr_spi_of_match), + .of_match_table = altr_a10sr_spi_of_match, }, .id_table = altr_a10sr_spi_ids, }; diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c index af205813b281d0e17f08020dfd0c9876bab2f266..0e52bd2ebd74bdf44bc59ed941adff628b0e18c0 100644 --- a/drivers/mfd/altera-sysmgr.c +++ b/drivers/mfd/altera-sysmgr.c @@ -14,8 +14,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index c166fcd331f113c157749f6f2ea3fa020767db77..19a0adf8ce3dbc4bca1a82ea5ac107239aefb5ca 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/atc260x-core.c b/drivers/mfd/atc260x-core.c index 7c5de3ae776e5c8e79a4104b8110340d2cf2f0ec..67473b58b03d6c378c7bfc940d0a993206fddf6b 100644 --- a/drivers/mfd/atc260x-core.c +++ b/drivers/mfd/atc260x-core.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #define ATC260X_CHIP_REV_MAX 31 diff --git a/drivers/mfd/atmel-hlcdc.c b/drivers/mfd/atmel-hlcdc.c index 3c2414ba4b019383dfc2065824978f0e9d2ea67c..20de7f49a8309f51c729aeb99b2aa390be54feb1 100644 --- a/drivers/mfd/atmel-hlcdc.c +++ b/drivers/mfd/atmel-hlcdc.c @@ -83,7 +83,6 @@ static int atmel_hlcdc_probe(struct platform_device *pdev) struct atmel_hlcdc_regmap *hregmap; struct device *dev = &pdev->dev; struct atmel_hlcdc *hlcdc; - struct resource *res; hregmap = devm_kzalloc(dev, sizeof(*hregmap), GFP_KERNEL); if (!hregmap) @@ -93,8 +92,7 @@ static int atmel_hlcdc_probe(struct platform_device *pdev) if (!hlcdc) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hregmap->regs = devm_ioremap_resource(dev, res); + hregmap->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hregmap->regs)) return PTR_ERR(hregmap->regs); diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index c03bc5cda080a9c1d79ab7211d39ed7afd3dd60c..87603eeaa2770574a55401858d8c52b7acff00e5 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -342,7 +342,7 @@ static const struct regmap_config axp152_regmap_config = { .wr_table = &axp152_writeable_table, .volatile_table = &axp152_volatile_table, .max_register = AXP152_PWM1_DUTY_CYCLE, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config axp192_regmap_config = { @@ -360,7 +360,7 @@ static const struct regmap_config axp20x_regmap_config = { .wr_table = &axp20x_writeable_table, .volatile_table = &axp20x_volatile_table, .max_register = AXP20X_OCV(AXP20X_OCV_MAX), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config axp22x_regmap_config = { @@ -369,7 +369,7 @@ static const struct regmap_config axp22x_regmap_config = { .wr_table = &axp22x_writeable_table, .volatile_table = &axp22x_volatile_table, .max_register = AXP22X_BATLOW_THRES1, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config axp288_regmap_config = { @@ -378,7 +378,7 @@ static const struct regmap_config axp288_regmap_config = { .wr_table = &axp288_writeable_table, .volatile_table = &axp288_volatile_table, .max_register = AXP288_FG_TUNE5, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config axp313a_regmap_config = { @@ -396,7 +396,7 @@ static const struct regmap_config axp806_regmap_config = { .wr_table = &axp806_writeable_table, .volatile_table = &axp806_volatile_table, .max_register = AXP806_REG_ADDR_EXT, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static const struct regmap_config axp15060_regmap_config = { @@ -405,7 +405,7 @@ static const struct regmap_config axp15060_regmap_config = { .wr_table = &axp15060_writeable_table, .volatile_table = &axp15060_volatile_table, .max_register = AXP15060_IRQ2_STATE, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; #define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ diff --git a/drivers/mfd/bcm590xx.c b/drivers/mfd/bcm590xx.c index 9f39b46b87f4457b1b00445e7baeb58cb32f6de4..92eede9a5e61b7cec7d43daf59dd1d8de5876a78 100644 --- a/drivers/mfd/bcm590xx.c +++ b/drivers/mfd/bcm590xx.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 92f4dfccc3cc22e013b3326b19caa6575a1cab49..79d393b602bf363d24ca8927656cc7fade5b1d8f 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/cs42l43.c b/drivers/mfd/cs42l43.c index 37b23e9bae823c1b3db4ac912d0b3632175b59a1..7b6d07cbe6fc6f32cb3c850806e1ef8b7257c9b4 100644 --- a/drivers/mfd/cs42l43.c +++ b/drivers/mfd/cs42l43.c @@ -1178,8 +1178,8 @@ static int cs42l43_runtime_resume(struct device *dev) } EXPORT_NS_GPL_DEV_PM_OPS(cs42l43_pm_ops, MFD_CS42L43) = { - SET_SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume) - SET_RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(cs42l43_suspend, cs42l43_resume) + RUNTIME_PM_OPS(cs42l43_runtime_suspend, cs42l43_runtime_resume, NULL) }; MODULE_DESCRIPTION("CS42L43 Core Driver"); diff --git a/drivers/mfd/cs47l15-tables.c b/drivers/mfd/cs47l15-tables.c index 3c77f0a24e9bd14ba257afeaa0cf322053233708..59b005cc1e336c08a053933e0a57722130c4f22d 100644 --- a/drivers/mfd/cs47l15-tables.c +++ b/drivers/mfd/cs47l15-tables.c @@ -1249,7 +1249,7 @@ const struct regmap_config cs47l15_16bit_spi_regmap = { .readable_reg = &cs47l15_16bit_readable_register, .volatile_reg = &cs47l15_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l15_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l15_reg_default), }; @@ -1264,7 +1264,7 @@ const struct regmap_config cs47l15_16bit_i2c_regmap = { .readable_reg = &cs47l15_16bit_readable_register, .volatile_reg = &cs47l15_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l15_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l15_reg_default), }; @@ -1281,7 +1281,7 @@ const struct regmap_config cs47l15_32bit_spi_regmap = { .readable_reg = &cs47l15_32bit_readable_register, .volatile_reg = &cs47l15_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l15_32bit_spi_regmap); @@ -1295,6 +1295,6 @@ const struct regmap_config cs47l15_32bit_i2c_regmap = { .readable_reg = &cs47l15_32bit_readable_register, .volatile_reg = &cs47l15_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l15_32bit_i2c_regmap); diff --git a/drivers/mfd/cs47l24-tables.c b/drivers/mfd/cs47l24-tables.c index c289d92a5c1d666d178fc08dc6cd2ac71f633816..878dfd298a1700141dfab89a0438465a80e39a1b 100644 --- a/drivers/mfd/cs47l24-tables.c +++ b/drivers/mfd/cs47l24-tables.c @@ -1616,7 +1616,7 @@ const struct regmap_config cs47l24_spi_regmap = { .readable_reg = cs47l24_readable_register, .volatile_reg = cs47l24_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l24_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l24_reg_default), }; diff --git a/drivers/mfd/cs47l35-tables.c b/drivers/mfd/cs47l35-tables.c index a0bc6c5100d63293341ea5a3a0dd26b6cfcb6532..274f4b05850a3935d233b743b979eebcdc0f2753 100644 --- a/drivers/mfd/cs47l35-tables.c +++ b/drivers/mfd/cs47l35-tables.c @@ -1498,7 +1498,7 @@ const struct regmap_config cs47l35_16bit_spi_regmap = { .readable_reg = cs47l35_16bit_readable_register, .volatile_reg = cs47l35_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l35_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l35_reg_default), }; @@ -1515,7 +1515,7 @@ const struct regmap_config cs47l35_16bit_i2c_regmap = { .readable_reg = cs47l35_16bit_readable_register, .volatile_reg = cs47l35_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l35_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l35_reg_default), }; @@ -1534,7 +1534,7 @@ const struct regmap_config cs47l35_32bit_spi_regmap = { .readable_reg = cs47l35_32bit_readable_register, .volatile_reg = cs47l35_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l35_32bit_spi_regmap); @@ -1550,6 +1550,6 @@ const struct regmap_config cs47l35_32bit_i2c_regmap = { .readable_reg = cs47l35_32bit_readable_register, .volatile_reg = cs47l35_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l35_32bit_i2c_regmap); diff --git a/drivers/mfd/cs47l85-tables.c b/drivers/mfd/cs47l85-tables.c index 270d8eda3f5f2db885dd250d0a620b8376419887..f397894827ced89072cb835decb5767617ced3b2 100644 --- a/drivers/mfd/cs47l85-tables.c +++ b/drivers/mfd/cs47l85-tables.c @@ -2836,7 +2836,7 @@ const struct regmap_config cs47l85_16bit_spi_regmap = { .readable_reg = cs47l85_16bit_readable_register, .volatile_reg = cs47l85_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l85_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l85_reg_default), }; @@ -2853,7 +2853,7 @@ const struct regmap_config cs47l85_16bit_i2c_regmap = { .readable_reg = cs47l85_16bit_readable_register, .volatile_reg = cs47l85_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l85_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l85_reg_default), }; @@ -2872,7 +2872,7 @@ const struct regmap_config cs47l85_32bit_spi_regmap = { .readable_reg = cs47l85_32bit_readable_register, .volatile_reg = cs47l85_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l85_32bit_spi_regmap); @@ -2888,6 +2888,6 @@ const struct regmap_config cs47l85_32bit_i2c_regmap = { .readable_reg = cs47l85_32bit_readable_register, .volatile_reg = cs47l85_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l85_32bit_i2c_regmap); diff --git a/drivers/mfd/cs47l90-tables.c b/drivers/mfd/cs47l90-tables.c index 7345fc09c0bb6f481296b7bbabd1c8ac45092efb..6f9ceb36c533e8ad7fa1c87d0c6092177b157053 100644 --- a/drivers/mfd/cs47l90-tables.c +++ b/drivers/mfd/cs47l90-tables.c @@ -2539,7 +2539,7 @@ const struct regmap_config cs47l90_16bit_spi_regmap = { .readable_reg = cs47l90_16bit_readable_register, .volatile_reg = cs47l90_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l90_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l90_reg_default), }; @@ -2556,7 +2556,7 @@ const struct regmap_config cs47l90_16bit_i2c_regmap = { .readable_reg = cs47l90_16bit_readable_register, .volatile_reg = cs47l90_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l90_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l90_reg_default), }; @@ -2575,7 +2575,7 @@ const struct regmap_config cs47l90_32bit_spi_regmap = { .readable_reg = cs47l90_32bit_readable_register, .volatile_reg = cs47l90_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l90_32bit_spi_regmap); @@ -2591,6 +2591,6 @@ const struct regmap_config cs47l90_32bit_i2c_regmap = { .readable_reg = cs47l90_32bit_readable_register, .volatile_reg = cs47l90_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l90_32bit_i2c_regmap); diff --git a/drivers/mfd/cs47l92-tables.c b/drivers/mfd/cs47l92-tables.c index f296e355df4da9cd815217d16683573b7e5c4b58..4d9ba865aaf6c8f2bedd3bf88c05450eed8d8a3a 100644 --- a/drivers/mfd/cs47l92-tables.c +++ b/drivers/mfd/cs47l92-tables.c @@ -1890,7 +1890,7 @@ const struct regmap_config cs47l92_16bit_spi_regmap = { .readable_reg = &cs47l92_16bit_readable_register, .volatile_reg = &cs47l92_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l92_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l92_reg_default), }; @@ -1907,7 +1907,7 @@ const struct regmap_config cs47l92_16bit_i2c_regmap = { .readable_reg = &cs47l92_16bit_readable_register, .volatile_reg = &cs47l92_16bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = cs47l92_reg_default, .num_reg_defaults = ARRAY_SIZE(cs47l92_reg_default), }; @@ -1926,7 +1926,7 @@ const struct regmap_config cs47l92_32bit_spi_regmap = { .readable_reg = &cs47l92_32bit_readable_register, .volatile_reg = &cs47l92_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l92_32bit_spi_regmap); @@ -1942,6 +1942,6 @@ const struct regmap_config cs47l92_32bit_i2c_regmap = { .readable_reg = &cs47l92_32bit_readable_register, .volatile_reg = &cs47l92_32bit_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; EXPORT_SYMBOL_GPL(cs47l92_32bit_i2c_regmap); diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 541e2d47677eb9c2dc8b574a9836b4f35a63070b..fd000a21bcba5f4260b41898249a0713e13da35d 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -13,14 +13,11 @@ #include #include #include +#include #include #include -#ifdef CONFIG_OF -#include -#include -#endif /* I2C safe register check */ static inline bool i2c_safe_reg(unsigned char reg) diff --git a/drivers/mfd/da9055-i2c.c b/drivers/mfd/da9055-i2c.c index bbaf4f07f2743851c0430e67baca5ae3d18d86a4..9a5f51b60badac351c35f1a8a075eb4d6c4a0edc 100644 --- a/drivers/mfd/da9055-i2c.c +++ b/drivers/mfd/da9055-i2c.c @@ -11,7 +11,6 @@ #include #include #include -#include #include diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index 48f58b6f5629384d28c722f2e0defaa0815d8731..45da007d3e702edd9ab63ce22c83eb76ca290c1b 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/exynos-lpass.c b/drivers/mfd/exynos-lpass.c index 166cd21088cdd0a0502521c58755fa0dcc586ed4..1506d8d352b19be4f1e13192c5964fb8e81708c6 100644 --- a/drivers/mfd/exynos-lpass.c +++ b/drivers/mfd/exynos-lpass.c @@ -109,14 +109,12 @@ static int exynos_lpass_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct exynos_lpass *lpass; void __iomem *base_top; - struct resource *res; lpass = devm_kzalloc(dev, sizeof(*lpass), GFP_KERNEL); if (!lpass) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base_top = devm_ioremap_resource(dev, res); + base_top = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base_top)) return PTR_ERR(base_top); diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c index cb5cf4a81c06a122d93d4341ace804a4da77502e..a6a890537a1e2803c5de0095056ed5cfca429d06 100644 --- a/drivers/mfd/hi6421-pmic-core.c +++ b/drivers/mfd/hi6421-pmic-core.c @@ -59,7 +59,7 @@ static int hi6421_pmic_probe(struct platform_device *pdev) id = of_match_device(of_hi6421_pmic_match, &pdev->dev); if (!id) return -EINVAL; - type = (enum hi6421_type)id->data; + type = (uintptr_t)id->data; pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL); if (!pmic) diff --git a/drivers/mfd/hi655x-pmic.c b/drivers/mfd/hi655x-pmic.c index a58e42ddcd0c0fc277e9f446c1d5e1326b1688c9..8feae8d8fd9d45daa795c56fb82ec9207b5fde0f 100644 --- a/drivers/mfd/hi655x-pmic.c +++ b/drivers/mfd/hi655x-pmic.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include @@ -100,8 +100,7 @@ static int hi655x_pmic_probe(struct platform_device *pdev) return -ENOMEM; pmic->dev = dev; - pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, pmic->res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -163,7 +162,7 @@ MODULE_DEVICE_TABLE(of, hi655x_pmic_match); static struct platform_driver hi655x_pmic_driver = { .driver = { .name = "hi655x-pmic", - .of_match_table = of_match_ptr(hi655x_pmic_match), + .of_match_table = hi655x_pmic_match, }, .probe = hi655x_pmic_probe, .remove = hi655x_pmic_remove, diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c index 6d3968458e8164c8f3945398d0610715a6ce2a13..c964ea6539aa679d66da3cbce776abfdc7066e4a 100644 --- a/drivers/mfd/ipaq-micro.c +++ b/drivers/mfd/ipaq-micro.c @@ -78,8 +78,6 @@ EXPORT_SYMBOL(ipaq_micro_tx_msg); static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data) { - int i; - dev_dbg(micro->dev, "RX msg: %02x, %d bytes\n", id, len); spin_lock(µ->lock); @@ -131,10 +129,7 @@ static void micro_rx_msg(struct ipaq_micro *micro, u8 id, int len, u8 *data) break; default: dev_err(micro->dev, - "unknown msg %d [%d] ", id, len); - for (i = 0; i < len; ++i) - pr_cont("0x%02x ", data[i]); - pr_cont("\n"); + "unknown msg %d [%d] %*ph\n", id, len, len, data); } spin_unlock(µ->lock); } diff --git a/drivers/mfd/iqs62x.c b/drivers/mfd/iqs62x.c index dfe9cb79e6a14b732bb3d49779cd15d574f845a1..e03b4d38fbb0a61b3935c5b729967cef259e526f 100644 --- a/drivers/mfd/iqs62x.c +++ b/drivers/mfd/iqs62x.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/lochnagar-i2c.c b/drivers/mfd/lochnagar-i2c.c index 3c8843117080ccc8a3185d9cf60cedadd0a95c9e..59092f839d65158762e14be9d91af4c1fd105aee 100644 --- a/drivers/mfd/lochnagar-i2c.c +++ b/drivers/mfd/lochnagar-i2c.c @@ -379,7 +379,7 @@ static int lochnagar_i2c_probe(struct i2c_client *i2c) static struct i2c_driver lochnagar_i2c_driver = { .driver = { .name = "lochnagar", - .of_match_table = of_match_ptr(lochnagar_of_match), + .of_match_table = lochnagar_of_match, .suppress_bind_attrs = true, }, .probe = lochnagar_i2c_probe, diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c index 6639f0fad4ea98495633680cc009a932d1433f81..de7ab7aed3c6c1c2f128dcd2bc604578ba566d4f 100644 --- a/drivers/mfd/lp873x.c +++ b/drivers/mfd/lp873x.c @@ -7,8 +7,8 @@ #include #include +#include #include -#include #include #include diff --git a/drivers/mfd/lp87565.c b/drivers/mfd/lp87565.c index 88ce4d7c50a71c4007a407c340bb0b50aa6a2485..1b7f8349911d1b435d11c81fa22482be90120c54 100644 --- a/drivers/mfd/lp87565.c +++ b/drivers/mfd/lp87565.c @@ -92,7 +92,7 @@ static int lp87565_probe(struct i2c_client *client) of_id = of_match_device(of_lp87565_match_table, &client->dev); if (of_id) - lp87565->dev_type = (enum lp87565_device_type)of_id->data; + lp87565->dev_type = (uintptr_t)of_id->data; i2c_set_clientdata(client, lp87565); diff --git a/drivers/mfd/madera-i2c.c b/drivers/mfd/madera-i2c.c index 0968aa9733ac0f0b2f03682d037e8f01f6e1d274..a404ea26bc798423257840edd9d0f876a18c081e 100644 --- a/drivers/mfd/madera-i2c.c +++ b/drivers/mfd/madera-i2c.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/madera-spi.c b/drivers/mfd/madera-spi.c index da84eb50e53a366669cf878389f4d795af2977a0..ad07ebe29e593a67d3364e83c13b2e75c0ec8922 100644 --- a/drivers/mfd/madera-spi.c +++ b/drivers/mfd/madera-spi.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/max14577.c b/drivers/mfd/max14577.c index 25ed8846b7fbb01d9c753cc13b678e27d9203161..1f4f5002595c0ae43a306217d3483e72b355353a 100644 --- a/drivers/mfd/max14577.c +++ b/drivers/mfd/max14577.c @@ -402,8 +402,7 @@ static int max14577_i2c_probe(struct i2c_client *i2c) of_id = of_match_device(max14577_dt_match, &i2c->dev); if (of_id) - max14577->dev_type = - (enum maxim_device_type)of_id->data; + max14577->dev_type = (uintptr_t)of_id->data; } else { max14577->dev_type = id->driver_data; } diff --git a/drivers/mfd/max77541.c b/drivers/mfd/max77541.c index e147e949c2b31a1b053905923e53946327dc0ac4..10c2e274b4af60125a9302fe0db0657a3a810b9f 100644 --- a/drivers/mfd/max77541.c +++ b/drivers/mfd/max77541.c @@ -173,7 +173,7 @@ static int max77541_probe(struct i2c_client *client) i2c_set_clientdata(client, max77541); max77541->i2c = client; - max77541->id = (enum max7754x_ids)device_get_match_data(dev); + max77541->id = (uintptr_t)device_get_match_data(dev); if (!max77541->id) max77541->id = (enum max7754x_ids)id->driver_data; diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index 5811ed8f484033b967199a871a766acce4a4524f..e63e8e47d908d9c3e972f51b97c9cc6b142781c0 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 01833086ca7dec476295a520113ffe691cdae5a1..91c286c4571cbd5bd58cd9c1e6bcb98ce6d6941b 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -20,7 +20,6 @@ #include #include #include -#include static const struct mfd_cell max77686_devs[] = { { .name = "max77686-pmic", }, diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c index b3689c13a14db6d73ff71b20bf3676836491ec18..fcff0c498c0ff060bb14ea539f0f5aed16aa3e53 100644 --- a/drivers/mfd/max77843.c +++ b/drivers/mfd/max77843.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include static const struct mfd_cell max77843_devs[] = { diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c index 78b5ee688decc6bcae51025cdf61f6f566938872..8bbe7979db914537bfcd5d5ac3f8e362125a02e8 100644 --- a/drivers/mfd/max8907.c +++ b/drivers/mfd/max8907.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 0246bbe80354e91a33ff39d283d69e1f03035518..105d79b9149373d1bde5209b19cd9177c0e4532b 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -17,7 +17,6 @@ #include #include #include -#include static const struct resource bk_resources[] = { { 0x84, 0x84, "mode control", IORESOURCE_REG, }, diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index 94c09a5eab32c3336e5a99f791f3e90a9757404e..110bef71f208cd2c53c2a9c4d5b81a30f8352fdb 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index 33a3ec5464fb6459e431cfcc5e64af332f3600f0..4cc426a6c7679d37e93b37bc31df023e6fc34e24 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/mc13xxx-i2c.c b/drivers/mfd/mc13xxx-i2c.c index de59b498c925e2f19c6df473235f3616b601f7fe..6bc0e755ba346372aabc06d4ca6c665df346497d 100644 --- a/drivers/mfd/mc13xxx-i2c.c +++ b/drivers/mfd/mc13xxx-i2c.c @@ -53,7 +53,6 @@ static const struct regmap_config mc13xxx_regmap_i2c_config = { static int mc13xxx_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); struct mc13xxx *mc13xxx; int ret; @@ -73,13 +72,7 @@ static int mc13xxx_i2c_probe(struct i2c_client *client) return ret; } - if (client->dev.of_node) { - const struct of_device_id *of_id = - of_match_device(mc13xxx_dt_ids, &client->dev); - mc13xxx->variant = of_id->data; - } else { - mc13xxx->variant = (void *)id->driver_data; - } + mc13xxx->variant = i2c_get_match_data(client); return mc13xxx_common_init(&client->dev); } diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c index 389756436af6c878271a907b51c9b0636a20e117..49830b526ee880bf36fda79b6fe54c12ec3d74d6 100644 --- a/drivers/mfd/mt6358-irq.c +++ b/drivers/mfd/mt6358-irq.c @@ -3,6 +3,8 @@ // Copyright (c) 2020 MediaTek Inc. #include +#include +#include #include #include #include @@ -11,9 +13,6 @@ #include #include #include -#include -#include -#include #include #include diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index f6c1f80f94a4513ad34cd3ac3ee96ccec0fbfa04..4449dde05021a02a760037826dabe248860b65c0 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -6,9 +6,10 @@ #include #include +#include #include -#include -#include +#include +#include #include #include #include diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c index 72f923e47752daacb4d5ccb42617938de51cc9df..886745b5b607c3d40ef9b2f44251bb2c94c213dc 100644 --- a/drivers/mfd/mt6397-irq.c +++ b/drivers/mfd/mt6397-irq.c @@ -3,10 +3,9 @@ // Copyright (c) 2019 MediaTek Inc. #include +#include +#include #include -#include -#include -#include #include #include #include diff --git a/drivers/mfd/mxs-lradc.c b/drivers/mfd/mxs-lradc.c index 111d11fd25aad1f0b6178d9c932e4c550c993d35..21f3033d6eb582e6b54fea3817a27068342c42de 100644 --- a/drivers/mfd/mxs-lradc.c +++ b/drivers/mfd/mxs-lradc.c @@ -142,7 +142,7 @@ static int mxs_lradc_probe(struct platform_device *pdev) if (!of_id) return -EINVAL; - lradc->soc = (enum mxs_lradc_id)of_id->data; + lradc->soc = (uintptr_t)of_id->data; lradc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(lradc->clk)) { diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 7f5775109593879086f955f2ab32d4caea851675..78f1bb55dbc0fa06fbac524b3e9912e6aa73e126 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -534,7 +534,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev); struct usbhs_hcd_omap *omap; - struct resource *res; int ret = 0; int i; bool need_logic_fck; @@ -569,8 +568,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) return -ENOMEM; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - omap->uhh_base = devm_ioremap_resource(dev, res); + omap->uhh_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(omap->uhh_base)) return PTR_ERR(omap->uhh_base); diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 69cbc2097911bec50bbefd0fbf3611c48a50759c..906353735c7820965fbfef36c3cb4c45c6f37ea5 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -200,15 +200,13 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) static int usbtll_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct usbtll_omap *tll; void __iomem *base; int i, nch, ver; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index a36f12402987cc6550a785f8b9cc075cc925c059..6e562bab62e4239d21100bb06b0837bb510f3c71 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -18,7 +18,8 @@ #include #include #include -#include +#include +#include static const struct regmap_config palmas_regmap_config[PALMAS_NUM_CLIENTS] = { { diff --git a/drivers/mfd/qcom-pm8008.c b/drivers/mfd/qcom-pm8008.c index 94a8cca1d9551165c7fb572965c475c6d6d46d15..3ac3742f438bd41083c0bec3f828d7a1ef88c666 100644 --- a/drivers/mfd/qcom-pm8008.c +++ b/drivers/mfd/qcom-pm8008.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/qcom-pm8xxx.c b/drivers/mfd/qcom-pm8xxx.c index 9a948df8c28dcf12f378922a2b29697d69d19039..07c531bd1236e5ca2bbd401c5c4cdd4b1656288c 100644 --- a/drivers/mfd/qcom-pm8xxx.c +++ b/drivers/mfd/qcom-pm8xxx.c @@ -103,8 +103,9 @@ static int pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp) { int rc; + unsigned long flags; - spin_lock(&chip->pm_irq_lock); + spin_lock_irqsave(&chip->pm_irq_lock, flags); rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, bp); if (rc) { pr_err("Failed Selecting Block %d rc=%d\n", bp, rc); @@ -116,7 +117,7 @@ pm8xxx_config_irq(struct pm_irq_chip *chip, unsigned int bp, unsigned int cp) if (rc) pr_err("Failed Configuring IRQ rc=%d\n", rc); bail: - spin_unlock(&chip->pm_irq_lock); + spin_unlock_irqrestore(&chip->pm_irq_lock, flags); return rc; } @@ -321,6 +322,7 @@ static int pm8xxx_irq_get_irqchip_state(struct irq_data *d, struct pm_irq_chip *chip = irq_data_get_irq_chip_data(d); unsigned int pmirq = irqd_to_hwirq(d); unsigned int bits; + unsigned long flags; int irq_bit; u8 block; int rc; @@ -331,7 +333,7 @@ static int pm8xxx_irq_get_irqchip_state(struct irq_data *d, block = pmirq / 8; irq_bit = pmirq % 8; - spin_lock(&chip->pm_irq_lock); + spin_lock_irqsave(&chip->pm_irq_lock, flags); rc = regmap_write(chip->regmap, SSBI_REG_ADDR_IRQ_BLK_SEL, block); if (rc) { pr_err("Failed Selecting Block %d rc=%d\n", block, rc); @@ -346,7 +348,7 @@ static int pm8xxx_irq_get_irqchip_state(struct irq_data *d, *state = !!(bits & BIT(irq_bit)); bail: - spin_unlock(&chip->pm_irq_lock); + spin_unlock_irqrestore(&chip->pm_irq_lock, flags); return rc; } diff --git a/drivers/mfd/rave-sp.c b/drivers/mfd/rave-sp.c index 545196c85b5ca9d9f40743ae54737f6b21cd2be0..da50eba1001494e07e36640b7bf311dc2d6362e2 100644 --- a/drivers/mfd/rave-sp.c +++ b/drivers/mfd/rave-sp.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c index e8fc9e2ab1d0d2e27a4d05029a14fa02082f3cd1..11a831e92da83a89f11f29a5e86ec0d8e9227825 100644 --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mfd/rn5t618.c b/drivers/mfd/rn5t618.c index 333fef8729a54468d2f62ccd1af3a4eefd058fff..0fe616b2db8e159d3fc891eedc5ec7cc1444d13c 100644 --- a/drivers/mfd/rn5t618.c +++ b/drivers/mfd/rn5t618.c @@ -277,7 +277,7 @@ static SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops, static struct i2c_driver rn5t618_i2c_driver = { .driver = { .name = "rn5t618", - .of_match_table = of_match_ptr(rn5t618_of_match), + .of_match_table = rn5t618_of_match, .pm = &rn5t618_i2c_dev_pm_ops, }, .probe = rn5t618_i2c_probe, diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c index 93d80a79b90112e9316e0354ca0f137e3ffce8f0..594718f7e8e18ebcf7f0f9e9087166a29d11ea39 100644 --- a/drivers/mfd/rohm-bd71828.c +++ b/drivers/mfd/rohm-bd71828.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c index 0b58ecc7833405e75a3ad1a2929c5118053558c9..4798bdf27afb6c16b20c3e8ba2efc2845e1392d1 100644 --- a/drivers/mfd/rohm-bd718x7.c +++ b/drivers/mfd/rohm-bd718x7.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mfd/rohm-bd9576.c b/drivers/mfd/rohm-bd9576.c index 645673322ec0e62c4ec9cfef0bd31baed7d2827a..bceac7016740db0a5a11530c6b78883fb177c247 100644 --- a/drivers/mfd/rohm-bd9576.c +++ b/drivers/mfd/rohm-bd9576.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mfd/rsmu_i2c.c b/drivers/mfd/rsmu_i2c.c index 26972a5aff4543bcb77b59a981605990864236b0..06d78a1cf1ccbbb47e36348d77666854c9609193 100644 --- a/drivers/mfd/rsmu_i2c.c +++ b/drivers/mfd/rsmu_i2c.c @@ -277,7 +277,7 @@ MODULE_DEVICE_TABLE(of, rsmu_i2c_of_match); static struct i2c_driver rsmu_i2c_driver = { .driver = { .name = "rsmu-i2c", - .of_match_table = of_match_ptr(rsmu_i2c_of_match), + .of_match_table = rsmu_i2c_of_match, }, .probe = rsmu_i2c_probe, .remove = rsmu_i2c_remove, diff --git a/drivers/mfd/rsmu_spi.c b/drivers/mfd/rsmu_spi.c index a4a595bb8d0dfdf7479bfdcb162bd9da30f55f12..ca0a1202c3ce1fb13ec31ce478f3c76e7073d36d 100644 --- a/drivers/mfd/rsmu_spi.c +++ b/drivers/mfd/rsmu_spi.c @@ -262,7 +262,7 @@ MODULE_DEVICE_TABLE(of, rsmu_spi_of_match); static struct spi_driver rsmu_spi_driver = { .driver = { .name = "rsmu-spi", - .of_match_table = of_match_ptr(rsmu_spi_of_match), + .of_match_table = rsmu_spi_of_match, }, .probe = rsmu_spi_probe, .remove = rsmu_spi_remove, diff --git a/drivers/mfd/rt5033.c b/drivers/mfd/rt5033.c index 67b0a228db249b4728a87f045b2ea0560cc99789..7e23ab3d5842c8b4d49e0206e03ba27f9f4e0c5f 100644 --- a/drivers/mfd/rt5033.c +++ b/drivers/mfd/rt5033.c @@ -10,9 +10,9 @@ */ #include +#include #include #include -#include #include #include #include diff --git a/drivers/mfd/rz-mtu3.c b/drivers/mfd/rz-mtu3.c index 04006f4aa70200fc9d07bfa39e14870d04bf5dbb..f3dac4a29a832474b1abbc820072d3ab524e6c5f 100644 --- a/drivers/mfd/rz-mtu3.c +++ b/drivers/mfd/rz-mtu3.c @@ -11,7 +11,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -20,7 +22,7 @@ struct rz_mtu3_priv { void __iomem *mmio; struct reset_control *rstc; - raw_spinlock_t lock; + spinlock_t lock; }; /******* MTU3 registers (original offset is +0x1200) *******/ @@ -174,11 +176,11 @@ void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, u16 offset, struct rz_mtu3_priv *priv = mtu->priv_data; unsigned long tmdr, flags; - raw_spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); tmdr = rz_mtu3_shared_reg_read(ch, offset); __assign_bit(pos, &tmdr, !!val); rz_mtu3_shared_reg_write(ch, offset, tmdr); - raw_spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } EXPORT_SYMBOL_GPL(rz_mtu3_shared_reg_update_bit); @@ -250,16 +252,17 @@ static void rz_mtu3_start_stop_ch(struct rz_mtu3_channel *ch, bool start) u16 offset; u8 bitpos; - /* start stop register shared by multiple timer channels */ - raw_spin_lock_irqsave(&priv->lock, flags); - offset = rz_mtu3_get_tstr_offset(ch); bitpos = rz_mtu3_get_tstr_bit_pos(ch); + + /* start stop register shared by multiple timer channels */ + spin_lock_irqsave(&priv->lock, flags); + tstr = rz_mtu3_shared_reg_read(ch, offset); __assign_bit(bitpos, &tstr, start); rz_mtu3_shared_reg_write(ch, offset, tstr); - raw_spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch) @@ -267,21 +270,18 @@ bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch) struct rz_mtu3 *mtu = dev_get_drvdata(ch->dev->parent); struct rz_mtu3_priv *priv = mtu->priv_data; unsigned long flags, tstr; - bool ret = false; u16 offset; u8 bitpos; - /* start stop register shared by multiple timer channels */ - raw_spin_lock_irqsave(&priv->lock, flags); - offset = rz_mtu3_get_tstr_offset(ch); bitpos = rz_mtu3_get_tstr_bit_pos(ch); - tstr = rz_mtu3_shared_reg_read(ch, offset); - ret = tstr & BIT(bitpos); - raw_spin_unlock_irqrestore(&priv->lock, flags); + /* start stop register shared by multiple timer channels */ + spin_lock_irqsave(&priv->lock, flags); + tstr = rz_mtu3_shared_reg_read(ch, offset); + spin_unlock_irqrestore(&priv->lock, flags); - return ret; + return tstr & BIT(bitpos); } EXPORT_SYMBOL_GPL(rz_mtu3_is_enabled); @@ -349,7 +349,7 @@ static int rz_mtu3_probe(struct platform_device *pdev) return PTR_ERR(ddata->clk); reset_control_deassert(priv->rstc); - raw_spin_lock_init(&priv->lock); + spin_lock_init(&priv->lock); platform_set_drvdata(pdev, ddata); for (i = 0; i < RZ_MTU_NUM_CHANNELS; i++) { diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index d2f631901886915bddf3496a80a25633b71d684a..a6b0d7300b2d387694d13534ffa6fe09ce606de2 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c index d21f32cc784da5b7c95597f45ed60f214d2e1b5e..81e517cdfb27d98cd031e29582bd562a0e51aec4 100644 --- a/drivers/mfd/sprd-sc27xx-spi.c +++ b/drivers/mfd/sprd-sc27xx-spi.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mfd/ssbi.c b/drivers/mfd/ssbi.c index dee89db3471d3f076e5c01ca001fab9c1cb95659..b0b0be483dbf9b1f00128d8b33abb3395bf1f818 100644 --- a/drivers/mfd/ssbi.c +++ b/drivers/mfd/ssbi.c @@ -14,12 +14,12 @@ #include #include #include +#include +#include +#include #include #include #include -#include -#include -#include /* SSBI 2.0 controller registers */ #define SSBI2_CMD 0x0008 diff --git a/drivers/mfd/stm32-lptimer.c b/drivers/mfd/stm32-lptimer.c index fa322f4412c8750524000077722016b911f7f472..b2704a9809c7a2bf6edc45be3b8a76ededf5b124 100644 --- a/drivers/mfd/stm32-lptimer.c +++ b/drivers/mfd/stm32-lptimer.c @@ -9,6 +9,7 @@ #include #include #include +#include #define STM32_LPTIM_MAX_REGISTER 0x3fc diff --git a/drivers/mfd/stm32-timers.c b/drivers/mfd/stm32-timers.c index 44ed2fce0319648c94c783ef8533ebd624861860..732a28db80fadefc1b2be7d02ad56fdeec0d5145 100644 --- a/drivers/mfd/stm32-timers.c +++ b/drivers/mfd/stm32-timers.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define STM32_TIMERS_MAX_REGISTERS 0x3fc @@ -226,8 +227,7 @@ static int stm32_timers_probe(struct platform_device *pdev) if (!ddata) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmio = devm_ioremap_resource(dev, res); + mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(mmio)) return PTR_ERR(mmio); diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index 1d7b401776d1c314d82f953036ee0a0a56b93094..fe018bedab9837dd5a7c9848d244cf1caba0014c 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -87,7 +87,7 @@ stmpe_i2c_probe(struct i2c_client *i2c) dev_info(&i2c->dev, "matching on node name, compatible is preferred\n"); partnum = id->driver_data; } else - partnum = (enum stmpe_partnum)of_id->data; + partnum = (uintptr_t)of_id->data; return stmpe_probe(&i2c_ci, partnum); } diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c index 3cc7492f828ff9646b97d2d885978c0d371bcb7c..c5128fe96cc788a4bb592edabe2417259a99a2d4 100644 --- a/drivers/mfd/stpmic1.c +++ b/drivers/mfd/stpmic1.c @@ -219,7 +219,7 @@ MODULE_DEVICE_TABLE(of, stpmic1_of_match); static struct i2c_driver stpmic1_driver = { .driver = { .name = "stpmic1", - .of_match_table = of_match_ptr(stpmic1_of_match), + .of_match_table = stpmic1_of_match, .pm = pm_sleep_ptr(&stpmic1_pm), }, .probe = stpmic1_probe, diff --git a/drivers/mfd/sun4i-gpadc.c b/drivers/mfd/sun4i-gpadc.c index d1cbea27b1361946f2e38f03d188314f3815e4df..3029d48e982cfd69728749d189ce43a98330c69e 100644 --- a/drivers/mfd/sun4i-gpadc.c +++ b/drivers/mfd/sun4i-gpadc.c @@ -8,8 +8,8 @@ #include #include #include -#include -#include +#include +#include #include #include diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index 16df64e3c0bec0d82818c82d2d41647b4dc1852d..db28eb0c8995f28a3e48aaa331c6e93c74c83d20 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -340,7 +340,7 @@ tc3589x_of_probe(struct device *dev, enum tc3589x_version *version) of_id = of_match_device(tc3589x_match, dev); if (!of_id) return ERR_PTR(-ENODEV); - *version = (enum tc3589x_version) of_id->data; + *version = (uintptr_t) of_id->data; for_each_child_of_node(np, child) { if (of_device_is_compatible(child, "toshiba,tc3589x-gpio")) @@ -483,7 +483,7 @@ static struct i2c_driver tc3589x_driver = { .driver = { .name = "tc3589x", .pm = pm_sleep_ptr(&tc3589x_dev_pm_ops), - .of_match_table = of_match_ptr(tc3589x_match), + .of_match_table = tc3589x_match, }, .probe = tc3589x_probe, .remove = tc3589x_remove, diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c index 4f06adad7b5ef5b8001684a3b9ea2835a0660114..cfc9f88b984202123783050c208819d4b5f97edb 100644 --- a/drivers/mfd/ti-lmu.c +++ b/drivers/mfd/ti-lmu.c @@ -17,7 +17,6 @@ #include #include #include -#include #include struct ti_lmu_data { diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index 07825cfd8aa87469cc46951d375f14ef2593952f..b88eb70c17b353c89907766682ed072c5bff526e 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -201,8 +201,7 @@ static int ti_tscadc_probe(struct platform_device *pdev) else tscadc->irq = err; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res); + tscadc->tscadc_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(tscadc->tscadc_base)) return PTR_ERR(tscadc->tscadc_base); diff --git a/drivers/mfd/tps6507x.c b/drivers/mfd/tps6507x.c index 9716bf703c7aaaeaa53a0b133f423695b118718d..95dafb0e9f00c446dab201486d2e89622ecc0cea 100644 --- a/drivers/mfd/tps6507x.c +++ b/drivers/mfd/tps6507x.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index a35ad70755fb648f01c2485b16be2aee37decda7..9245e11219f3ce0a303cafd8d0de694a792b653f 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #define NUM_INT_REG 2 diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 60599291b3154b0ae4abcc84a07224e07c73e033..029ecc32f0782eaca507f6ec55d6c68cd4023ebe 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 619bf7adb20cad532c172bfad3915629054b5ef0..11e4e52b56bef7c312f5658446ee70da7f2d36b4 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mfd/tps6594-core.c b/drivers/mfd/tps6594-core.c index 15f314833207c4e2c3dfac45ba911801d6bd7e33..0fb9c5cf213a477872d325ed7c242cc492cff718 100644 --- a/drivers/mfd/tps6594-core.c +++ b/drivers/mfd/tps6594-core.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index d85675a4d9a81254b34f802396f957318b313c86..9ce34dfd99b331a2f9d4992f6db0164dfc25c5b9 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -16,8 +16,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 6bba396579914392c79b9568e8bdc9112123e6fa..f77ecc635b6f10b20e6db7a02bcb364c0047e34c 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -1938,7 +1938,7 @@ const struct regmap_config wm5102_i2c_regmap = { .readable_reg = wm5102_readable_register, .volatile_reg = wm5102_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm5102_reg_default, .num_reg_defaults = ARRAY_SIZE(wm5102_reg_default), }; diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index 65b9b1d6daecfdec928b1b47fdea53a01fe68ff0..eba324875afdb55dccce538fbb591c8aec90e137 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -3218,7 +3218,7 @@ const struct regmap_config wm5110_i2c_regmap = { .readable_reg = wm5110_readable_register, .volatile_reg = wm5110_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm5110_reg_default, .num_reg_defaults = ARRAY_SIZE(wm5110_reg_default), }; diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index e86b6a4896a68d70f9bea9b3fd957e382730033c..e7e68929275e7801853b1d28e13d4d0f42594e5c 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -15,8 +15,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 997837f13180603ae0109b67b8b7b551e74d91b6..694ddbbf037287de6659b0980a2ff71fdc58c809 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -36,7 +36,7 @@ static int wm831x_i2c_probe(struct i2c_client *i2c) dev_err(&i2c->dev, "Failed to match device\n"); return -ENODEV; } - type = (enum wm831x_parent)of_id->data; + type = (uintptr_t)of_id->data; } else { type = (enum wm831x_parent)id->driver_data; } diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 7bcddccbf155795e115ac22e8f693c483b8e7112..76be7ef5c9709c83f31be75f7d5cbfaefd9f3a94 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -33,7 +33,7 @@ static int wm831x_spi_probe(struct spi_device *spi) dev_err(&spi->dev, "Failed to match device\n"); return -ENODEV; } - type = (enum wm831x_parent)of_id->data; + type = (uintptr_t)of_id->data; } else { type = (enum wm831x_parent)id->driver_data; } diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 1e4f1694f065721b43dbb09fc3f577e54c9c91df..aba7af6881751a48e5a59559164bed2f8f46b517 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -628,7 +628,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c) if (i2c->dev.of_node) { of_id = of_match_device(wm8994_of_match, &i2c->dev); if (of_id) - wm8994->type = (enum wm8994_type)of_id->data; + wm8994->type = (uintptr_t)of_id->data; } else { wm8994->type = id->driver_data; } diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index cd4fef7df3366256e3b83ce5ddf1708e0c24e44a..ee2ed6773afd9cb6760568172c35025a0c3a4838 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -1238,7 +1238,7 @@ struct regmap_config wm1811_regmap_config = { .reg_bits = 16, .val_bits = 16, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm1811_defaults, .num_reg_defaults = ARRAY_SIZE(wm1811_defaults), @@ -1253,7 +1253,7 @@ struct regmap_config wm8994_regmap_config = { .reg_bits = 16, .val_bits = 16, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8994_defaults, .num_reg_defaults = ARRAY_SIZE(wm8994_defaults), @@ -1268,7 +1268,7 @@ struct regmap_config wm8958_regmap_config = { .reg_bits = 16, .val_bits = 16, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8958_defaults, .num_reg_defaults = ARRAY_SIZE(wm8958_defaults), diff --git a/drivers/mfd/wm8997-tables.c b/drivers/mfd/wm8997-tables.c index 3476787c485e59c62d27bb97310dcc5fefd578a9..288c57b2d21e7a81dcf30c7ea09fb5af8676dae6 100644 --- a/drivers/mfd/wm8997-tables.c +++ b/drivers/mfd/wm8997-tables.c @@ -1523,7 +1523,7 @@ const struct regmap_config wm8997_i2c_regmap = { .readable_reg = wm8997_readable_register, .volatile_reg = wm8997_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8997_reg_default, .num_reg_defaults = ARRAY_SIZE(wm8997_reg_default), }; diff --git a/drivers/mfd/wm8998-tables.c b/drivers/mfd/wm8998-tables.c index 9b34a6d760949db76db99b2395eb6eef671410c6..b3e6e85bee899541f5611d8c52aa172c48e2c34e 100644 --- a/drivers/mfd/wm8998-tables.c +++ b/drivers/mfd/wm8998-tables.c @@ -1556,7 +1556,7 @@ const struct regmap_config wm8998_i2c_regmap = { .readable_reg = wm8998_readable_register, .volatile_reg = wm8998_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = wm8998_reg_default, .num_reg_defaults = ARRAY_SIZE(wm8998_reg_default), }; diff --git a/drivers/misc/cardreader/rts5227.c b/drivers/misc/cardreader/rts5227.c index 3dae5e3a16976f0e229523544396e21670747cae..cd512284bfb399cf10af85592ca5e9af46416e94 100644 --- a/drivers/misc/cardreader/rts5227.c +++ b/drivers/misc/cardreader/rts5227.c @@ -83,63 +83,20 @@ static void rts5227_fetch_vendor_settings(struct rtsx_pcr *pcr) static void rts5227_init_from_cfg(struct rtsx_pcr *pcr) { - struct pci_dev *pdev = pcr->pci; - int l1ss; - u32 lval; struct rtsx_cr_option *option = &pcr->option; - l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); - if (!l1ss) - return; - - pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - if (CHK_PCI_PID(pcr, 0x522A)) { - if (0 == (lval & 0x0F)) - rtsx_pci_enable_oobs_polling(pcr); - else + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) rtsx_pci_disable_oobs_polling(pcr); + else + rtsx_pci_enable_oobs_polling(pcr); } - if (lval & PCI_L1SS_CTL1_ASPM_L1_1) - rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - else - rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_2) - rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - else - rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) - rtsx_set_dev_flag(pcr, PM_L1_1_EN); - else - rtsx_clear_dev_flag(pcr, PM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) - rtsx_set_dev_flag(pcr, PM_L1_2_EN); - else - rtsx_clear_dev_flag(pcr, PM_L1_2_EN); - if (option->ltr_en) { - u16 val; - - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); - if (val & PCI_EXP_DEVCTL2_LTR_EN) { - option->ltr_enabled = true; - option->ltr_active = true; + if (option->ltr_enabled) rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - } else { - option->ltr_enabled = false; - } } - - if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN - | PM_L1_1_EN | PM_L1_2_EN)) - option->force_clkreq_0 = false; - else - option->force_clkreq_0 = true; - } static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) @@ -195,7 +152,7 @@ static int rts5227_extra_init_hw(struct rtsx_pcr *pcr) } } - if (option->force_clkreq_0 && pcr->aspm_mode == ASPM_MODE_CFG) + if (option->force_clkreq_0) rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); else diff --git a/drivers/misc/cardreader/rts5228.c b/drivers/misc/cardreader/rts5228.c index f4ab09439da70c977ea29bbb63e1ae8d80a1f08f..0c7f10bcf6f1222d641b5edd52cfd11c769f314e 100644 --- a/drivers/misc/cardreader/rts5228.c +++ b/drivers/misc/cardreader/rts5228.c @@ -386,59 +386,25 @@ static void rts5228_process_ocp(struct rtsx_pcr *pcr) static void rts5228_init_from_cfg(struct rtsx_pcr *pcr) { - struct pci_dev *pdev = pcr->pci; - int l1ss; - u32 lval; struct rtsx_cr_option *option = &pcr->option; - l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); - if (!l1ss) - return; - - pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - - if (0 == (lval & 0x0F)) - rtsx_pci_enable_oobs_polling(pcr); - else + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) rtsx_pci_disable_oobs_polling(pcr); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_1) - rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - else - rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_2) - rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - else - rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) - rtsx_set_dev_flag(pcr, PM_L1_1_EN); else - rtsx_clear_dev_flag(pcr, PM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) - rtsx_set_dev_flag(pcr, PM_L1_2_EN); - else - rtsx_clear_dev_flag(pcr, PM_L1_2_EN); + rtsx_pci_enable_oobs_polling(pcr); rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0); - if (option->ltr_en) { - u16 val; - pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); - if (val & PCI_EXP_DEVCTL2_LTR_EN) { - option->ltr_enabled = true; - option->ltr_active = true; + if (option->ltr_en) { + if (option->ltr_enabled) rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - } else { - option->ltr_enabled = false; - } } } static int rts5228_extra_init_hw(struct rtsx_pcr *pcr) { + struct rtsx_cr_option *option = &pcr->option; rtsx_pci_write_register(pcr, RTS5228_AUTOLOAD_CFG1, CD_RESUME_EN_MASK, CD_RESUME_EN_MASK); @@ -469,6 +435,17 @@ static int rts5228_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x00); + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); if (pcr->rtd3_en) { diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 47ab72a43256bc2ce1aa6fd3ece81bc50b78ca6d..6c81040e18bef57a4e0d1ac3df75d242f928c56a 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -86,64 +86,22 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) { - struct pci_dev *pdev = pcr->pci; - int l1ss; struct rtsx_cr_option *option = &(pcr->option); - u32 lval; - - l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); - if (!l1ss) - return; - - pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) { - if (0 == (lval & 0x0F)) - rtsx_pci_enable_oobs_polling(pcr); - else + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) rtsx_pci_disable_oobs_polling(pcr); + else + rtsx_pci_enable_oobs_polling(pcr); } - - if (lval & PCI_L1SS_CTL1_ASPM_L1_1) - rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_2) - rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) - rtsx_set_dev_flag(pcr, PM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) - rtsx_set_dev_flag(pcr, PM_L1_2_EN); - if (option->ltr_en) { - u16 val; - - pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &val); - if (val & PCI_EXP_DEVCTL2_LTR_EN) { - option->ltr_enabled = true; - option->ltr_active = true; + if (option->ltr_enabled) rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - } else { - option->ltr_enabled = false; - } } } -static int rts5249_init_from_hw(struct rtsx_pcr *pcr) -{ - struct rtsx_cr_option *option = &(pcr->option); - - if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN - | PM_L1_1_EN | PM_L1_2_EN)) - option->force_clkreq_0 = false; - else - option->force_clkreq_0 = true; - - return 0; -} - static void rts52xa_force_power_down(struct rtsx_pcr *pcr, u8 pm_state, bool runtime) { /* Set relink_time to 0 */ @@ -276,7 +234,6 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) struct rtsx_cr_option *option = &(pcr->option); rts5249_init_from_cfg(pcr); - rts5249_init_from_hw(pcr); rtsx_pci_init_cmd(pcr); @@ -327,11 +284,12 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) } } + /* * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced * to drive low, and we forcibly request clock. */ - if (option->force_clkreq_0 && pcr->aspm_mode == ASPM_MODE_CFG) + if (option->force_clkreq_0) rtsx_pci_write_register(pcr, PETXCFG, FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); else diff --git a/drivers/misc/cardreader/rts5260.c b/drivers/misc/cardreader/rts5260.c index 79b18f6f73a8a83cd6865721f60509a225b8df6e..d2d3a6ccb8f7ddeaba9454355891e1d7c833f4b9 100644 --- a/drivers/misc/cardreader/rts5260.c +++ b/drivers/misc/cardreader/rts5260.c @@ -480,47 +480,19 @@ static void rts5260_pwr_saving_setting(struct rtsx_pcr *pcr) static void rts5260_init_from_cfg(struct rtsx_pcr *pcr) { - struct pci_dev *pdev = pcr->pci; - int l1ss; struct rtsx_cr_option *option = &pcr->option; - u32 lval; - - l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); - if (!l1ss) - return; - - pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_1) - rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_2) - rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) - rtsx_set_dev_flag(pcr, PM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) - rtsx_set_dev_flag(pcr, PM_L1_2_EN); rts5260_pwr_saving_setting(pcr); if (option->ltr_en) { - u16 val; - - pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &val); - if (val & PCI_EXP_DEVCTL2_LTR_EN) { - option->ltr_enabled = true; - option->ltr_active = true; + if (option->ltr_enabled) rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - } else { - option->ltr_enabled = false; - } } } static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) { + struct rtsx_cr_option *option = &pcr->option; /* Set mcu_cnt to 7 to ensure data can be sampled properly */ rtsx_pci_write_register(pcr, 0xFC03, 0x7F, 0x07); @@ -539,6 +511,17 @@ static int rts5260_extra_init_hw(struct rtsx_pcr *pcr) rts5260_init_hw(pcr); + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + rtsx_pci_write_register(pcr, pcr->reg_pm_ctrl3, 0x10, 0x00); return 0; diff --git a/drivers/misc/cardreader/rts5261.c b/drivers/misc/cardreader/rts5261.c index 94af6bf8a25a6f3c36bc3084f5cd95e7926a1fb5..67252512a1329724f2ec964377ecbd0a20885cf7 100644 --- a/drivers/misc/cardreader/rts5261.c +++ b/drivers/misc/cardreader/rts5261.c @@ -454,54 +454,17 @@ static void rts5261_init_from_hw(struct rtsx_pcr *pcr) static void rts5261_init_from_cfg(struct rtsx_pcr *pcr) { - struct pci_dev *pdev = pcr->pci; - int l1ss; - u32 lval; struct rtsx_cr_option *option = &pcr->option; - l1ss = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); - if (!l1ss) - return; - - pci_read_config_dword(pdev, l1ss + PCI_L1SS_CTL1, &lval); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_1) - rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); - else - rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_ASPM_L1_2) - rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); - else - rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) - rtsx_set_dev_flag(pcr, PM_L1_1_EN); - else - rtsx_clear_dev_flag(pcr, PM_L1_1_EN); - - if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) - rtsx_set_dev_flag(pcr, PM_L1_2_EN); - else - rtsx_clear_dev_flag(pcr, PM_L1_2_EN); - - rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0xFF, 0); if (option->ltr_en) { - u16 val; - - pcie_capability_read_word(pdev, PCI_EXP_DEVCTL2, &val); - if (val & PCI_EXP_DEVCTL2_LTR_EN) { - option->ltr_enabled = true; - option->ltr_active = true; + if (option->ltr_enabled) rtsx_set_ltr_latency(pcr, option->ltr_active_latency); - } else { - option->ltr_enabled = false; - } } } static int rts5261_extra_init_hw(struct rtsx_pcr *pcr) { + struct rtsx_cr_option *option = &pcr->option; u32 val; rtsx_pci_write_register(pcr, RTS5261_AUTOLOAD_CFG1, @@ -547,6 +510,17 @@ static int rts5261_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_write_register(pcr, PETXCFG, 0x30, 0x00); + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_write_register(pcr, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + rtsx_pci_write_register(pcr, PWD_SUSPEND_EN, 0xFF, 0xFB); if (pcr->rtd3_en) { diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index a3f4b52bb159f0c1287b692e7484815bddff8614..a30751ad373307701a24ba7808b19f4f5d775fad 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1326,11 +1326,8 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) return err; } - if (pcr->aspm_mode == ASPM_MODE_REG) { + if (pcr->aspm_mode == ASPM_MODE_REG) rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, 0x30, 0x30); - rtsx_pci_write_register(pcr, PETXCFG, - FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); - } /* No CD interrupt if probing driver with card inserted. * So we need to initialize pcr->card_exist here. @@ -1345,7 +1342,9 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) { - int err; + struct rtsx_cr_option *option = &(pcr->option); + int err, l1ss; + u32 lval; u16 cfg_val; u8 val; @@ -1430,6 +1429,48 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) pcr->aspm_enabled = true; } + l1ss = pci_find_ext_capability(pcr->pci, PCI_EXT_CAP_ID_L1SS); + if (l1ss) { + pci_read_config_dword(pcr->pci, l1ss + PCI_L1SS_CTL1, &lval); + + if (lval & PCI_L1SS_CTL1_ASPM_L1_1) + rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); + else + rtsx_clear_dev_flag(pcr, ASPM_L1_1_EN); + + if (lval & PCI_L1SS_CTL1_ASPM_L1_2) + rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); + else + rtsx_clear_dev_flag(pcr, ASPM_L1_2_EN); + + if (lval & PCI_L1SS_CTL1_PCIPM_L1_1) + rtsx_set_dev_flag(pcr, PM_L1_1_EN); + else + rtsx_clear_dev_flag(pcr, PM_L1_1_EN); + + if (lval & PCI_L1SS_CTL1_PCIPM_L1_2) + rtsx_set_dev_flag(pcr, PM_L1_2_EN); + else + rtsx_clear_dev_flag(pcr, PM_L1_2_EN); + + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &cfg_val); + if (cfg_val & PCI_EXP_DEVCTL2_LTR_EN) { + option->ltr_enabled = true; + option->ltr_active = true; + } else { + option->ltr_enabled = false; + } + + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) + option->force_clkreq_0 = false; + else + option->force_clkreq_0 = true; + } else { + option->ltr_enabled = false; + option->force_clkreq_0 = true; + } + if (pcr->ops->fetch_vendor_settings) pcr->ops->fetch_vendor_settings(pcr); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 153fb8d0008ed9643f7bcb32c1e3c83afc02c4ec..df589d9b4d705553ead2b0948fa8d3e19ade13d9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -650,7 +649,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) /* * Valid primary extension versions are: 1.0, 1.1, 1.2, 1.3, 1.4, 1.5 - * see: http://cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19 + * see: http://cs.ozerki.net/zap/pub/axim-x5/docs/cfi_r20.pdf, page 19 * http://www.spansion.com/Support/AppNotes/cfi_100_20011201.pdf * http://www.spansion.com/Support/Datasheets/s29ws-p_00_a12_e.pdf * http://www.spansion.com/Support/Datasheets/S29GL_128S_01GS_00_02_e.pdf diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index a7714e3de887f317f1c618d1a1f4900c9b13ff50..22e73dd6118b937ca1b052bc90e446f620cffadf 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -1599,7 +1599,7 @@ static void doc_unregister_sysfs(struct platform_device *pdev, */ static int flashcontrol_show(struct seq_file *s, void *p) { - struct docg3 *docg3 = (struct docg3 *)s->private; + struct docg3 *docg3 = s->private; u8 fctrl; @@ -1621,7 +1621,7 @@ DEFINE_SHOW_ATTRIBUTE(flashcontrol); static int asic_mode_show(struct seq_file *s, void *p) { - struct docg3 *docg3 = (struct docg3 *)s->private; + struct docg3 *docg3 = s->private; int pctrl, mode; @@ -1658,7 +1658,7 @@ DEFINE_SHOW_ATTRIBUTE(asic_mode); static int device_id_show(struct seq_file *s, void *p) { - struct docg3 *docg3 = (struct docg3 *)s->private; + struct docg3 *docg3 = s->private; int id; mutex_lock(&docg3->cascade->lock); @@ -1672,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(device_id); static int protection_show(struct seq_file *s, void *p) { - struct docg3 *docg3 = (struct docg3 *)s->private; + struct docg3 *docg3 = s->private; int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high; mutex_lock(&docg3->cascade->lock); diff --git a/drivers/mtd/devices/mchp23k256.c b/drivers/mtd/devices/mchp23k256.c index 3a6ea7a6a30c3a62c7627084febc892643adca34..d533475fda159315fa22e0c7e89dcd7d328c9f9f 100644 --- a/drivers/mtd/devices/mchp23k256.c +++ b/drivers/mtd/devices/mchp23k256.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #define MAX_CMD_SIZE 4 diff --git a/drivers/mtd/devices/mchp48l640.c b/drivers/mtd/devices/mchp48l640.c index 40cd5041174c9649c930b9a1985a5fe259f43d5e..f576e6a890e859e7d20aeeb2ede4ca0acf4850fc 100644 --- a/drivers/mtd/devices/mchp48l640.c +++ b/drivers/mtd/devices/mchp48l640.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include struct mchp48_caps { unsigned int size; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 1d3b2a94581f1837d191d292a271baa09526650a..0c1b9330361809d59a3ee534004ef537bc6c40e0 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index cc17133be2972aab47c09a4222c680537939ebf3..0a35e5236ae593753e79e8c078f52c2c7c92dbd9 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -937,7 +937,6 @@ static int spear_smi_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct spear_smi_plat_data *pdata = NULL; struct spear_smi *dev; - struct resource *smi_base; int irq, ret = 0; int i; @@ -975,9 +974,7 @@ static int spear_smi_probe(struct platform_device *pdev) goto err; } - smi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - dev->io_base = devm_ioremap_resource(&pdev->dev, smi_base); + dev->io_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dev->io_base)) { ret = PTR_ERR(dev->io_base); goto err; @@ -996,21 +993,17 @@ static int spear_smi_probe(struct platform_device *pdev) dev->num_flashes = MAX_NUM_FLASH_CHIP; } - dev->clk = devm_clk_get(&pdev->dev, NULL); + dev->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = PTR_ERR(dev->clk); goto err; } - ret = clk_prepare_enable(dev->clk); - if (ret) - goto err; - ret = devm_request_irq(&pdev->dev, irq, spear_smi_int_handler, 0, pdev->name, dev); if (ret) { dev_err(&dev->pdev->dev, "SMI IRQ allocation failed\n"); - goto err_irq; + goto err; } mutex_init(&dev->lock); @@ -1023,14 +1016,11 @@ static int spear_smi_probe(struct platform_device *pdev) ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); if (ret) { dev_err(&dev->pdev->dev, "bank setup failed\n"); - goto err_irq; + goto err; } } return 0; - -err_irq: - clk_disable_unprepare(dev->clk); err: return ret; } @@ -1059,8 +1049,6 @@ static int spear_smi_remove(struct platform_device *pdev) WARN_ON(mtd_device_unregister(&flash->mtd)); } - clk_disable_unprepare(dev->clk); - return 0; } diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 3dbb1aa80bfa7b5c719c76b6630db9b0fadd9461..95530cbbb1e0d2d2444938ed7fb47dd8fcd03812 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2016,7 +2016,6 @@ static int stfsm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct flash_info *info; - struct resource *res; struct stfsm *fsm; int ret; @@ -2033,18 +2032,9 @@ static int stfsm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fsm); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Resource not found\n"); - return -ENODEV; - } - - fsm->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(fsm->base)) { - dev_err(&pdev->dev, - "Failed to reserve memory region %pR\n", res); + fsm->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(fsm->base)) return PTR_ERR(fsm->base); - } fsm->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(fsm->clk)) { diff --git a/drivers/mtd/lpddr/lpddr2_nvm.c b/drivers/mtd/lpddr/lpddr2_nvm.c index e71af4c490969f3e6c02e186cdfbca3f5e0ca53f..f4e5174b2449c1111a756956fdeefb315facb6f7 100644 --- a/drivers/mtd/lpddr/lpddr2_nvm.c +++ b/drivers/mtd/lpddr/lpddr2_nvm.c @@ -412,7 +412,6 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) struct map_info *map; struct mtd_info *mtd; struct resource *add_range; - struct resource *control_regs; struct pcm_int_data *pcm_data; /* Allocate memory control_regs data structures */ @@ -452,8 +451,7 @@ static int lpddr2_nvm_probe(struct platform_device *pdev) simple_map_init(map); /* fill with default methods */ - control_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); - pcm_data->ctl_regs = devm_ioremap_resource(&pdev->dev, control_regs); + pcm_data->ctl_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(pcm_data->ctl_regs)) return PTR_ERR(pcm_data->ctl_regs); diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c index 67a1dbfdd72cee0de73e21629c42698946677692..a1da1c8973c02a7f0a808a62872f00e5b5c6a47e 100644 --- a/drivers/mtd/maps/lantiq-flash.c +++ b/drivers/mtd/maps/lantiq-flash.c @@ -118,11 +118,9 @@ ltq_mtd_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ltq_mtd); - ltq_mtd->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!ltq_mtd->res) { - dev_err(&pdev->dev, "failed to get memory resource\n"); - return -ENOENT; - } + ltq_mtd->map->virt = devm_platform_get_and_ioremap_resource(pdev, 0, <q_mtd->res); + if (IS_ERR(ltq_mtd->map->virt)) + return PTR_ERR(ltq_mtd->map->virt); ltq_mtd->map = devm_kzalloc(&pdev->dev, sizeof(struct map_info), GFP_KERNEL); @@ -131,9 +129,6 @@ ltq_mtd_probe(struct platform_device *pdev) ltq_mtd->map->phys = ltq_mtd->res->start; ltq_mtd->map->size = resource_size(ltq_mtd->res); - ltq_mtd->map->virt = devm_ioremap_resource(&pdev->dev, ltq_mtd->res); - if (IS_ERR(ltq_mtd->map->virt)) - return PTR_ERR(ltq_mtd->map->virt); ltq_mtd->map->name = ltq_map_name; ltq_mtd->map->bankwidth = 2; diff --git a/drivers/mtd/maps/physmap-bt1-rom.c b/drivers/mtd/maps/physmap-bt1-rom.c index 58782cfaf71cf048a059ed1d6249d2955c41c71b..60dccc48f99e01a04d5c7984f9e8585283772ecc 100644 --- a/drivers/mtd/maps/physmap-bt1-rom.c +++ b/drivers/mtd/maps/physmap-bt1-rom.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index c73854da513633a04f66fb76bde8e3b46ae14221..78710fbc8e7f638cdd52b08286b7a6b1bfbffa71 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -508,8 +508,7 @@ static int physmap_flash_probe(struct platform_device *dev) for (i = 0; i < info->nmaps; i++) { struct resource *res; - res = platform_get_resource(dev, IORESOURCE_MEM, i); - info->maps[i].virt = devm_ioremap_resource(&dev->dev, res); + info->maps[i].virt = devm_platform_get_and_ioremap_resource(dev, i, &res); if (IS_ERR(info->maps[i].virt)) { err = PTR_ERR(info->maps[i].virt); goto err_out; diff --git a/drivers/mtd/maps/physmap-gemini.c b/drivers/mtd/maps/physmap-gemini.c index d4a46e159d38f674bb30b3ffba78fe3941e6afe7..9d3b4bf84a1ad7cb340d916b39a281b46af8dc33 100644 --- a/drivers/mtd/maps/physmap-gemini.c +++ b/drivers/mtd/maps/physmap-gemini.c @@ -8,10 +8,10 @@ */ #include #include -#include #include #include #include +#include #include #include #include diff --git a/drivers/mtd/maps/physmap-ixp4xx.c b/drivers/mtd/maps/physmap-ixp4xx.c index 6a054229a8a0ee81c8a02d021f57e0201f4ec73f..c561468f95f6c3f63e6d288465d098d0863250db 100644 --- a/drivers/mtd/maps/physmap-ixp4xx.c +++ b/drivers/mtd/maps/physmap-ixp4xx.c @@ -11,7 +11,7 @@ */ #include #include -#include +#include #include #include #include "physmap-ixp4xx.h" diff --git a/drivers/mtd/maps/physmap-ixp4xx.h b/drivers/mtd/maps/physmap-ixp4xx.h index b0fc49b7f3edf05853f093e299c92208658d91b0..46824c57e58a6013660a092a9f46f58e88a3699d 100644 --- a/drivers/mtd/maps/physmap-ixp4xx.h +++ b/drivers/mtd/maps/physmap-ixp4xx.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#include #include #ifdef CONFIG_MTD_PHYSMAP_IXP4XX diff --git a/drivers/mtd/maps/physmap-versatile.c b/drivers/mtd/maps/physmap-versatile.c index a1b8b7b25f88b2a49383b67b7c4e34f4e0d47599..2e779111bf7949574f37694ecaf8d14059cd3e7e 100644 --- a/drivers/mtd/maps/physmap-versatile.c +++ b/drivers/mtd/maps/physmap-versatile.c @@ -9,9 +9,9 @@ #include #include #include -#include #include #include +#include #include #include #include "physmap-versatile.h" @@ -206,7 +206,7 @@ int of_flash_probe_versatile(struct platform_device *pdev, if (!sysnp) return -ENODEV; - versatile_flashprot = (enum versatile_flashprot)devid->data; + versatile_flashprot = (uintptr_t)devid->data; rmap = syscon_node_to_regmap(sysnp); of_node_put(sysnp); if (IS_ERR(rmap)) diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index cedd8ef9a6bf5a2096df73744e1f6e9eed8df629..4c921dce739664bc8d94f37a58d483b06ef852dc 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -123,8 +123,7 @@ static int platram_probe(struct platform_device *pdev) info->pdata = pdata; /* get the resource for the memory mapping */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->map.virt = devm_ioremap_resource(&pdev->dev, res); + info->map.virt = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(info->map.virt)) { err = PTR_ERR(info->map.virt); goto exit_free; diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 860b19f77090277343e9b59e5a0ca9b2318e1652..2bfdf1b7e18a10fff90e8ac3ae9847a2a77624dd 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index fa476fb4dffb6c9289f8b5678ff4f49c06a2a4a9..9751416c2a91bdbf9033e67f06740324d0f9b40c 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -262,7 +262,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) } if (mtd_type_is_nand(mbd->mtd)) - pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n", + pr_warn_ratelimited("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n", mbd->tr->name, mbd->mtd->name); /* OK, it's not open. Create cache info for it */ diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 66ffc9f1ead2f76aa142f04801fa292de7e8d5e1..ef6299af60e4f063bef9e8e3ca3b92990e07c9f0 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -49,7 +49,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) dev->readonly = 1; if (mtd_type_is_nand(mtd)) - pr_warn("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n", + pr_warn_ratelimited("%s: MTD device '%s' is NAND, please consider using UBI block devices instead.\n", tr->name, mtd->name); if (add_mtd_blktrans_dev(dev)) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index e00b12aa5ec91c32802177128f9b13be251c64c8..9bd661be3ae93d55c70713f5e708e34111737930 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -93,10 +93,39 @@ static void mtd_release(struct device *dev) struct mtd_info *mtd = dev_get_drvdata(dev); dev_t index = MTD_DEVT(mtd->index); + idr_remove(&mtd_idr, mtd->index); + of_node_put(mtd_get_of_node(mtd)); + + if (mtd_is_partition(mtd)) + release_mtd_partition(mtd); + /* remove /dev/mtdXro node */ device_destroy(&mtd_class, index + 1); } +static void mtd_device_release(struct kref *kref) +{ + struct mtd_info *mtd = container_of(kref, struct mtd_info, refcnt); + bool is_partition = mtd_is_partition(mtd); + + debugfs_remove_recursive(mtd->dbg.dfs_dir); + + /* Try to remove the NVMEM provider */ + nvmem_unregister(mtd->nvmem); + + device_unregister(&mtd->dev); + + /* + * Clear dev so mtd can be safely re-registered later if desired. + * Should not be done for partition, + * as it was already destroyed in device_unregister(). + */ + if (!is_partition) + memset(&mtd->dev, 0, sizeof(mtd->dev)); + + module_put(THIS_MODULE); +} + #define MTD_DEVICE_ATTR_RO(name) \ static DEVICE_ATTR(name, 0444, mtd_##name##_show, NULL) @@ -666,7 +695,7 @@ int add_mtd_device(struct mtd_info *mtd) } mtd->index = i; - mtd->usecount = 0; + kref_init(&mtd->refcnt); /* default value if not set by driver */ if (mtd->bitflip_threshold == 0) @@ -779,7 +808,6 @@ int del_mtd_device(struct mtd_info *mtd) { int ret; struct mtd_notifier *not; - struct device_node *mtd_of_node; mutex_lock(&mtd_table_mutex); @@ -793,28 +821,8 @@ int del_mtd_device(struct mtd_info *mtd) list_for_each_entry(not, &mtd_notifiers, list) not->remove(mtd); - if (mtd->usecount) { - printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", - mtd->index, mtd->name, mtd->usecount); - ret = -EBUSY; - } else { - mtd_of_node = mtd_get_of_node(mtd); - debugfs_remove_recursive(mtd->dbg.dfs_dir); - - /* Try to remove the NVMEM provider */ - nvmem_unregister(mtd->nvmem); - - device_unregister(&mtd->dev); - - /* Clear dev so mtd can be safely re-registered later if desired */ - memset(&mtd->dev, 0, sizeof(mtd->dev)); - - idr_remove(&mtd_idr, mtd->index); - of_node_put(mtd_of_node); - - module_put(THIS_MODULE); - ret = 0; - } + kref_put(&mtd->refcnt, mtd_device_release); + ret = 0; out_error: mutex_unlock(&mtd_table_mutex); @@ -1227,25 +1235,27 @@ int __get_mtd_device(struct mtd_info *mtd) struct mtd_info *master = mtd_get_master(mtd); int err; - if (!try_module_get(master->owner)) - return -ENODEV; - if (master->_get_device) { err = master->_get_device(mtd); - - if (err) { - module_put(master->owner); + if (err) return err; - } } - master->usecount++; + if (!try_module_get(master->owner)) { + if (master->_put_device) + master->_put_device(master); + return -ENODEV; + } - while (mtd->parent) { - mtd->usecount++; + while (mtd) { + if (mtd != master) + kref_get(&mtd->refcnt); mtd = mtd->parent; } + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + kref_get(&master->refcnt); + return 0; } EXPORT_SYMBOL_GPL(__get_mtd_device); @@ -1329,18 +1339,23 @@ void __put_mtd_device(struct mtd_info *mtd) { struct mtd_info *master = mtd_get_master(mtd); - while (mtd->parent) { - --mtd->usecount; - BUG_ON(mtd->usecount < 0); - mtd = mtd->parent; + while (mtd) { + /* kref_put() can relese mtd, so keep a reference mtd->parent */ + struct mtd_info *parent = mtd->parent; + + if (mtd != master) + kref_put(&mtd->refcnt, mtd_device_release); + mtd = parent; } - master->usecount--; + if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) + kref_put(&master->refcnt, mtd_device_release); + + module_put(master->owner); + /* must be the last as master can be freed in the _put_device */ if (master->_put_device) master->_put_device(master); - - module_put(master->owner); } EXPORT_SYMBOL_GPL(__put_mtd_device); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index b5eefeabf310d7cb3780d8559e0ab040e549f91c..b014861a06a6f28b50e137274ee93cca8a17f303 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -12,6 +12,7 @@ int __must_check add_mtd_device(struct mtd_info *mtd); int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +void release_mtd_partition(struct mtd_info *mtd); struct mtd_partitions; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index a46affbb037d23ba879d583a2cf9d9fd4b13fab6..23483db8f30c90e17f832c9cb7cab7bdb2610ce6 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -32,6 +32,12 @@ static inline void free_partition(struct mtd_info *mtd) kfree(mtd); } +void release_mtd_partition(struct mtd_info *mtd) +{ + WARN_ON(!list_empty(&mtd->part.node)); + free_partition(mtd); +} + static struct mtd_info *allocate_partition(struct mtd_info *parent, const struct mtd_partition *part, int partno, uint64_t cur_offset) @@ -309,13 +315,11 @@ static int __mtd_del_partition(struct mtd_info *mtd) sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs); + list_del_init(&mtd->part.node); err = del_mtd_device(mtd); if (err) return err; - list_del(&mtd->part.node); - free_partition(mtd); - return 0; } @@ -333,6 +337,7 @@ static int __del_mtd_partitions(struct mtd_info *mtd) __del_mtd_partitions(child); pr_info("Deleting %s MTD partition\n", child->name); + list_del_init(&child->part.node); ret = del_mtd_device(child); if (ret < 0) { pr_err("Error when deleting partition \"%s\" (%d)\n", @@ -340,9 +345,6 @@ static int __del_mtd_partitions(struct mtd_info *mtd) err = ret; continue; } - - list_del(&child->part.node); - free_partition(child); } return err; diff --git a/drivers/mtd/nand/ecc-mxic.c b/drivers/mtd/nand/ecc-mxic.c index 22a760e6024effa2d7c2a4ce893eaa933d0ab326..47e10945b8d27171b5b316d192da51fb6a6f333c 100644 --- a/drivers/mtd/nand/ecc-mxic.c +++ b/drivers/mtd/nand/ecc-mxic.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c index 5250764cedee155804eb18c44ea5e3d59625ff61..8f996e8d61b86b6a0a72697e8d6e21a34a9d944b 100644 --- a/drivers/mtd/nand/ecc.c +++ b/drivers/mtd/nand/ecc.c @@ -95,9 +95,9 @@ #include #include +#include #include #include -#include #include static LIST_HEAD(on_host_hw_engines); diff --git a/drivers/mtd/nand/onenand/onenand_omap2.c b/drivers/mtd/nand/onenand/onenand_omap2.c index ff7af98604df2cd85ba8aa06dc47df589a1dcbec..a12f8f3efd07817f5e7e53c45ca4f09a968a2417 100644 --- a/drivers/mtd/nand/onenand/onenand_omap2.c +++ b/drivers/mtd/nand/onenand/onenand_omap2.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -467,12 +467,6 @@ static int omap2_onenand_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "error getting memory resource\n"); - return -EINVAL; - } - r = of_property_read_u32(np, "reg", &val); if (r) { dev_err(dev, "reg not found in DT\n"); @@ -486,11 +480,11 @@ static int omap2_onenand_probe(struct platform_device *pdev) init_completion(&c->irq_done); init_completion(&c->dma_done); c->gpmc_cs = val; - c->phys_base = res->start; - c->onenand.base = devm_ioremap_resource(dev, res); + c->onenand.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(c->onenand.base)) return PTR_ERR(c->onenand.base); + c->phys_base = res->start; c->int_gpiod = devm_gpiod_get_optional(dev, "int", GPIOD_IN); if (IS_ERR(c->int_gpiod)) { diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c index 92151aa529647688b216b59a2eb9f8717c2aeeb5..fd6890a03d55716af5e0d5378d7a5ddddae402aa 100644 --- a/drivers/mtd/nand/onenand/onenand_samsung.c +++ b/drivers/mtd/nand/onenand/onenand_samsung.c @@ -860,8 +860,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) s3c_onenand_setup(mtd); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - onenand->base = devm_ioremap_resource(&pdev->dev, r); + onenand->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); if (IS_ERR(onenand->base)) return PTR_ERR(onenand->base); @@ -874,8 +873,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) this->options |= ONENAND_SKIP_UNLOCK_CHECK; if (onenand->type != TYPE_S5PC110) { - r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - onenand->ahb_addr = devm_ioremap_resource(&pdev->dev, r); + onenand->ahb_addr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(onenand->ahb_addr)) return PTR_ERR(onenand->ahb_addr); @@ -895,8 +893,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) this->subpagesize = mtd->writesize; } else { /* S5PC110 */ - r = platform_get_resource(pdev, IORESOURCE_MEM, 1); - onenand->dma_addr = devm_ioremap_resource(&pdev->dev, r); + onenand->dma_addr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(onenand->dma_addr)) return PTR_ERR(onenand->dma_addr); diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index b523354dfb00c0beeec82888806045e2b114b1d5..cbf8ae85e1ae0e9bd619fb5ab97c3c41a57d068f 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -160,7 +160,7 @@ config MTD_NAND_MARVELL including: - PXA3xx processors (NFCv1) - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2) - - 64-bit Aramda platforms (7k, 8k) (NFCv2) + - 64-bit Aramda platforms (7k, 8k, ac5) (NFCv2) config MTD_NAND_SLC_LPC32XX tristate "NXP LPC32xx SLC NAND controller" @@ -204,13 +204,6 @@ config MTD_NAND_BCM47XXNFLASH registered by bcma as platform devices. This enables driver for NAND flash memories. For now only BCM4706 is supported. -config MTD_NAND_OXNAS - tristate "Oxford Semiconductor NAND controller" - depends on ARCH_OXNAS || COMPILE_TEST - depends on HAS_IOMEM - help - This enables the NAND flash controller on Oxford Semiconductor SoCs. - config MTD_NAND_MPC5121_NFC tristate "MPC5121 NAND controller" depends on PPC_MPC512x diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile index d93e861d8ba7aead6e87820484dd2bfe791f7a12..25120a4afadac2cefaaf18a2500c6ed9960bf159 100644 --- a/drivers/mtd/nand/raw/Makefile +++ b/drivers/mtd/nand/raw/Makefile @@ -26,7 +26,6 @@ obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o -obj-$(CONFIG_MTD_NAND_OXNAS) += oxnas_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o obj-$(CONFIG_MTD_NAND_FSL_IFC) += fsl_ifc_nand.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o diff --git a/drivers/mtd/nand/raw/ams-delta.c b/drivers/mtd/nand/raw/ams-delta.c index fa621ffa649094435862aa7f54160c47b4a356bb..919816a7aca754987e88f77e76d0666ac6a61fd6 100644 --- a/drivers/mtd/nand/raw/ams-delta.c +++ b/drivers/mtd/nand/raw/ams-delta.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/mtd/nand/raw/arasan-nand-controller.c b/drivers/mtd/nand/raw/arasan-nand-controller.c index 906eef70cb6d9c5b26a0ffca95148e071a596bb8..4621ec549cc79e21d2f7a0281c874917c06c25a8 100644 --- a/drivers/mtd/nand/raw/arasan-nand-controller.c +++ b/drivers/mtd/nand/raw/arasan-nand-controller.c @@ -1440,45 +1440,29 @@ static int anfc_probe(struct platform_device *pdev) anfc_reset(nfc); - nfc->controller_clk = devm_clk_get(&pdev->dev, "controller"); + nfc->controller_clk = devm_clk_get_enabled(&pdev->dev, "controller"); if (IS_ERR(nfc->controller_clk)) return PTR_ERR(nfc->controller_clk); - nfc->bus_clk = devm_clk_get(&pdev->dev, "bus"); + nfc->bus_clk = devm_clk_get_enabled(&pdev->dev, "bus"); if (IS_ERR(nfc->bus_clk)) return PTR_ERR(nfc->bus_clk); - ret = clk_prepare_enable(nfc->controller_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(nfc->bus_clk); - if (ret) - goto disable_controller_clk; - ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); if (ret) - goto disable_bus_clk; + return ret; ret = anfc_parse_cs(nfc); if (ret) - goto disable_bus_clk; + return ret; ret = anfc_chips_init(nfc); if (ret) - goto disable_bus_clk; + return ret; platform_set_drvdata(pdev, nfc); return 0; - -disable_bus_clk: - clk_disable_unprepare(nfc->bus_clk); - -disable_controller_clk: - clk_disable_unprepare(nfc->controller_clk); - - return ret; } static void anfc_remove(struct platform_device *pdev) @@ -1486,9 +1470,6 @@ static void anfc_remove(struct platform_device *pdev) struct arasan_nfc *nfc = platform_get_drvdata(pdev); anfc_chips_cleanup(nfc); - - clk_disable_unprepare(nfc->bus_clk); - clk_disable_unprepare(nfc->controller_clk); } static const struct of_device_id anfc_ids[] = { diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index 81e3d682a8cde430714d23486f6249bf381b8346..3f494f7c7ecbdb8e464d873909dfe70d370ea57e 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -1791,8 +1791,7 @@ atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc) nand->numcs = 1; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nand->cs[0].io.virt = devm_ioremap_resource(dev, res); + nand->cs[0].io.virt = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(nand->cs[0].io.virt)) return PTR_ERR(nand->cs[0].io.virt); diff --git a/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c index 71ddcc611f6e49f68097e5883f8d96d72e87e978..9596629000f41b02e6c2fa7ae49d831822de5d7c 100644 --- a/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/bcm63138_nand.c @@ -61,15 +61,13 @@ static int bcm63138_nand_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct bcm63138_nand_soc *priv; struct brcmnand_soc *soc; - struct resource *res; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; soc = &priv->soc; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base"); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource_byname(pdev, "nand-int-base"); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index 2e9c2e2d9c9f7ce415d339053948e3686edbdec7..440bef477930c2aa9a0a252f0293a9cc76b31dcb 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -272,6 +272,7 @@ struct brcmnand_controller { const unsigned int *page_sizes; unsigned int page_size_shift; unsigned int max_oob; + u32 ecc_level_shift; u32 features; /* for low-power standby/resume only */ @@ -596,6 +597,34 @@ enum { INTFC_CTLR_READY = BIT(31), }; +/*********************************************************************** + * NAND ACC CONTROL bitfield + * + * Some bits have remained constant throughout hardware revision, while + * others have shifted around. + ***********************************************************************/ + +/* Constant for all versions (where supported) */ +enum { + /* See BRCMNAND_HAS_CACHE_MODE */ + ACC_CONTROL_CACHE_MODE = BIT(22), + + /* See BRCMNAND_HAS_PREFETCH */ + ACC_CONTROL_PREFETCH = BIT(23), + + ACC_CONTROL_PAGE_HIT = BIT(24), + ACC_CONTROL_WR_PREEMPT = BIT(25), + ACC_CONTROL_PARTIAL_PAGE = BIT(26), + ACC_CONTROL_RD_ERASED = BIT(27), + ACC_CONTROL_FAST_PGM_RDIN = BIT(28), + ACC_CONTROL_WR_ECC = BIT(30), + ACC_CONTROL_RD_ECC = BIT(31), +}; + +#define ACC_CONTROL_ECC_SHIFT 16 +/* Only for v7.2 */ +#define ACC_CONTROL_ECC_EXT_SHIFT 13 + static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl) { #if IS_ENABLED(CONFIG_MTD_NAND_BRCMNAND_BCMA) @@ -737,6 +766,12 @@ static int brcmnand_revision_init(struct brcmnand_controller *ctrl) else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp")) ctrl->features |= BRCMNAND_HAS_WP; + /* v7.2 has different ecc level shift in the acc register */ + if (ctrl->nand_version == 0x0702) + ctrl->ecc_level_shift = ACC_CONTROL_ECC_EXT_SHIFT; + else + ctrl->ecc_level_shift = ACC_CONTROL_ECC_SHIFT; + return 0; } @@ -931,30 +966,6 @@ static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl) return 0; } -/*********************************************************************** - * NAND ACC CONTROL bitfield - * - * Some bits have remained constant throughout hardware revision, while - * others have shifted around. - ***********************************************************************/ - -/* Constant for all versions (where supported) */ -enum { - /* See BRCMNAND_HAS_CACHE_MODE */ - ACC_CONTROL_CACHE_MODE = BIT(22), - - /* See BRCMNAND_HAS_PREFETCH */ - ACC_CONTROL_PREFETCH = BIT(23), - - ACC_CONTROL_PAGE_HIT = BIT(24), - ACC_CONTROL_WR_PREEMPT = BIT(25), - ACC_CONTROL_PARTIAL_PAGE = BIT(26), - ACC_CONTROL_RD_ERASED = BIT(27), - ACC_CONTROL_FAST_PGM_RDIN = BIT(28), - ACC_CONTROL_WR_ECC = BIT(30), - ACC_CONTROL_RD_ECC = BIT(31), -}; - static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) { if (ctrl->nand_version == 0x0702) @@ -967,18 +978,15 @@ static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl) return GENMASK(4, 0); } -#define NAND_ACC_CONTROL_ECC_SHIFT 16 -#define NAND_ACC_CONTROL_ECC_EXT_SHIFT 13 - static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl) { u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f; - mask <<= NAND_ACC_CONTROL_ECC_SHIFT; + mask <<= ACC_CONTROL_ECC_SHIFT; /* v7.2 includes additional ECC levels */ - if (ctrl->nand_version >= 0x0702) - mask |= 0x7 << NAND_ACC_CONTROL_ECC_EXT_SHIFT; + if (ctrl->nand_version == 0x0702) + mask |= 0x7 << ACC_CONTROL_ECC_EXT_SHIFT; return mask; } @@ -992,8 +1000,8 @@ static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en) if (en) { acc_control |= ecc_flags; /* enable RD/WR ECC */ - acc_control |= host->hwcfg.ecc_level - << NAND_ACC_CONTROL_ECC_SHIFT; + acc_control &= ~brcmnand_ecc_level_mask(ctrl); + acc_control |= host->hwcfg.ecc_level << ctrl->ecc_level_shift; } else { acc_control &= ~ecc_flags; /* disable RD/WR ECC */ acc_control &= ~brcmnand_ecc_level_mask(ctrl); @@ -1072,6 +1080,14 @@ static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl, cpu_relax(); } while (time_after(limit, jiffies)); + /* + * do a final check after time out in case the CPU was busy and the driver + * did not get enough time to perform the polling to avoid false alarms + */ + val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS); + if ((val & mask) == expected_val) + return 0; + dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n", expected_val, val & mask); @@ -1461,19 +1477,33 @@ static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i, const u8 *oob, int sas, int sector_1k) { int tbytes = sas << sector_1k; - int j; + int j, k = 0; + u32 last = 0xffffffff; + u8 *plast = (u8 *)&last; /* Adjust OOB values for 1K sector size */ if (sector_1k && (i & 0x01)) tbytes = max(0, tbytes - (int)ctrl->max_oob); tbytes = min_t(int, tbytes, ctrl->max_oob); - for (j = 0; j < tbytes; j += 4) + /* + * tbytes may not be multiple of words. Make sure we don't read out of + * the boundary and stop at last word. + */ + for (j = 0; (j + 3) < tbytes; j += 4) oob_reg_write(ctrl, j, (oob[j + 0] << 24) | (oob[j + 1] << 16) | (oob[j + 2] << 8) | (oob[j + 3] << 0)); + + /* handle the remaing bytes */ + while (j < tbytes) + plast[k++] = oob[j++]; + + if (tbytes & 0x3) + oob_reg_write(ctrl, (tbytes & ~0x3), (__force u32)cpu_to_be32(last)); + return tbytes; } @@ -1592,7 +1622,17 @@ static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd) dev_dbg(ctrl->dev, "send native cmd %d addr 0x%llx\n", cmd, cmd_addr); - BUG_ON(ctrl->cmd_pending != 0); + /* + * If we came here through _panic_write and there is a pending + * command, try to wait for it. If it times out, rather than + * hitting BUG_ON, just return so we don't crash while crashing. + */ + if (oops_in_progress) { + if (ctrl->cmd_pending && + bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0)) + return; + } else + BUG_ON(ctrl->cmd_pending != 0); ctrl->cmd_pending = cmd; ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); @@ -1626,13 +1666,13 @@ static bool brcmstb_nand_wait_for_completion(struct nand_chip *chip) disable_ctrl_irqs(ctrl); sts = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0); - err = (sts < 0) ? true : false; + err = sts < 0; } else { unsigned long timeo = msecs_to_jiffies( NAND_POLL_STATUS_TIMEOUT_MS); /* wait for completion interrupt */ sts = wait_for_completion_timeout(&ctrl->done, timeo); - err = (sts <= 0) ? true : false; + err = !sts; } return err; @@ -1648,6 +1688,7 @@ static int brcmnand_waitfunc(struct nand_chip *chip) if (ctrl->cmd_pending) err = brcmstb_nand_wait_for_completion(chip); + ctrl->cmd_pending = 0; if (err) { u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START) >> brcmnand_cmd_shift(ctrl); @@ -1656,8 +1697,8 @@ static int brcmnand_waitfunc(struct nand_chip *chip) "timeout waiting for command %#02x\n", cmd); dev_err_ratelimited(ctrl->dev, "intfc status %08x\n", brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS)); + return -ETIMEDOUT; } - ctrl->cmd_pending = 0; return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) & INTFC_FLASH_STATUS; } @@ -2561,7 +2602,7 @@ static int brcmnand_set_cfg(struct brcmnand_host *host, tmp &= ~brcmnand_ecc_level_mask(ctrl); tmp &= ~brcmnand_spare_area_mask(ctrl); if (ctrl->nand_version >= 0x0302) { - tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT; + tmp |= cfg->ecc_level << ctrl->ecc_level_shift; tmp |= cfg->spare_area_size; } nand_writereg(ctrl, acc_control_offs, tmp); @@ -2612,6 +2653,8 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) struct nand_chip *chip = &host->chip; const struct nand_ecc_props *requirements = nanddev_get_ecc_requirements(&chip->base); + struct nand_memory_organization *memorg = + nanddev_get_memorg(&chip->base); struct brcmnand_controller *ctrl = host->ctrl; struct brcmnand_cfg *cfg = &host->hwcfg; char msg[128]; @@ -2633,10 +2676,11 @@ static int brcmnand_setup_dev(struct brcmnand_host *host) if (cfg->spare_area_size > ctrl->max_oob) cfg->spare_area_size = ctrl->max_oob; /* - * Set oobsize to be consistent with controller's spare_area_size, as - * the rest is inaccessible. + * Set mtd and memorg oobsize to be consistent with controller's + * spare_area_size, as the rest is inaccessible. */ mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT); + memorg->oobsize = mtd->oobsize; cfg->device_size = mtd->size; cfg->block_size = mtd->erasesize; @@ -3202,6 +3246,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) ret = brcmnand_init_cs(host, NULL); if (ret) { + if (ret == -EPROBE_DEFER) { + of_node_put(child); + goto err; + } devm_kfree(dev, host); continue; /* Try all chip-selects */ } diff --git a/drivers/mtd/nand/raw/brcmnand/iproc_nand.c b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c index d32950847a62e46421ff7a02a8f254736f1fd36e..089c70fc6edfc06393d90d39653412ba1e3c4dc4 100644 --- a/drivers/mtd/nand/raw/brcmnand/iproc_nand.c +++ b/drivers/mtd/nand/raw/brcmnand/iproc_nand.c @@ -103,7 +103,6 @@ static int iproc_nand_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct iproc_nand_soc *priv; struct brcmnand_soc *soc; - struct resource *res; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -112,13 +111,11 @@ static int iproc_nand_probe(struct platform_device *pdev) spin_lock_init(&priv->idm_lock); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm"); - priv->idm_base = devm_ioremap_resource(dev, res); + priv->idm_base = devm_platform_ioremap_resource_byname(pdev, "iproc-idm"); if (IS_ERR(priv->idm_base)) return PTR_ERR(priv->idm_base); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext"); - priv->ext_base = devm_ioremap_resource(dev, res); + priv->ext_base = devm_platform_ioremap_resource_byname(pdev, "iproc-ext"); if (IS_ERR(priv->ext_base)) return PTR_ERR(priv->ext_base); diff --git a/drivers/mtd/nand/raw/davinci_nand.c b/drivers/mtd/nand/raw/davinci_nand.c index 415d6aaa8255a6b488a77742b58e6edbdcf0e4d0..e75d81cf8c211fa6215d5f9b5d8e85effa78928b 100644 --- a/drivers/mtd/nand/raw/davinci_nand.c +++ b/drivers/mtd/nand/raw/davinci_nand.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/denali_dt.c b/drivers/mtd/nand/raw/denali_dt.c index 915047e3fbc2e2433e0998e1a05d2af8682aa5de..edac8749bb9391adcea02e285cc2cc3ee9601e7e 100644 --- a/drivers/mtd/nand/raw/denali_dt.c +++ b/drivers/mtd/nand/raw/denali_dt.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/fsl_ifc_nand.c b/drivers/mtd/nand/raw/fsl_ifc_nand.c index fa537fee67019baf89af253cf01d84da2cd8e955..20bb1e0cb5ebfde4038a179c17f71cf6ea443d7a 100644 --- a/drivers/mtd/nand/raw/fsl_ifc_nand.c +++ b/drivers/mtd/nand/raw/fsl_ifc_nand.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/fsl_upm.c b/drivers/mtd/nand/raw/fsl_upm.c index 7366e85c09fd987c494fd73aa06b440a0955c199..315e9d2b573d330b935382501d12beb43909989b 100644 --- a/drivers/mtd/nand/raw/fsl_upm.c +++ b/drivers/mtd/nand/raw/fsl_upm.c @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -172,8 +173,7 @@ static int fun_probe(struct platform_device *ofdev) if (!fun) return -ENOMEM; - io_res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); - fun->io_base = devm_ioremap_resource(&ofdev->dev, io_res); + fun->io_base = devm_platform_get_and_ioremap_resource(ofdev, 0, &io_res); if (IS_ERR(fun->io_base)) return PTR_ERR(fun->io_base); diff --git a/drivers/mtd/nand/raw/fsmc_nand.c b/drivers/mtd/nand/raw/fsmc_nand.c index 7b4742420dfcb85a89c538fcef028222a90b136b..811982da355740a58f51ca3d2b8d4583e13aec03 100644 --- a/drivers/mtd/nand/raw/fsmc_nand.c +++ b/drivers/mtd/nand/raw/fsmc_nand.c @@ -1066,16 +1066,12 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->regs_va = base + FSMC_NOR_REG_SIZE + (host->bank * FSMC_NAND_BANK_SZ); - host->clk = devm_clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "failed to fetch block clock\n"); return PTR_ERR(host->clk); } - ret = clk_prepare_enable(host->clk); - if (ret) - return ret; - /* * This device ID is actually a common AMBA ID as used on the * AMBA PrimeCell bus. However it is not a PrimeCell. @@ -1111,7 +1107,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) if (!host->read_dma_chan) { dev_err(&pdev->dev, "Unable to get read dma channel\n"); ret = -ENODEV; - goto disable_clk; + goto disable_fsmc; } host->write_dma_chan = dma_request_channel(mask, filter, NULL); if (!host->write_dma_chan) { @@ -1155,9 +1151,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) release_dma_read_chan: if (host->mode == USE_DMA_ACCESS) dma_release_channel(host->read_dma_chan); -disable_clk: +disable_fsmc: fsmc_nand_disable(host); - clk_disable_unprepare(host->clk); return ret; } @@ -1182,7 +1177,6 @@ static void fsmc_nand_remove(struct platform_device *pdev) dma_release_channel(host->write_dma_chan); dma_release_channel(host->read_dma_chan); } - clk_disable_unprepare(host->clk); } } @@ -1200,9 +1194,14 @@ static int fsmc_nand_suspend(struct device *dev) static int fsmc_nand_resume(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); + int ret; if (host) { - clk_prepare_enable(host->clk); + ret = clk_prepare_enable(host->clk); + if (ret) { + dev_err(dev, "failed to enable clk\n"); + return ret; + } if (host->dev_timings) fsmc_nand_setup(host, host->dev_timings); nand_reset(&host->nand, 0); diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c index 500e7a28d2e417117037aef90409fb5518176791..e71ad2fcec2326dcc5cd93520e4dc54cd3a5133a 100644 --- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include "gpmi-nand.h" diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c index 9054559e52dda6c7dd15b3104f09b5133756b75f..525c34c281b65df0aba78168b3eb13260b14b4fb 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_ecc.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c index b9f135297aa0b4958c51f033e62e14e7720016cc..6748226b8bd12fd989939ca0d6f02bd1e16f0d83 100644 --- a/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c +++ b/drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/raw/intel-nand-controller.c b/drivers/mtd/nand/raw/intel-nand-controller.c index a9909eb081244ed2002237a1ba42304c43cc8831..cb5d88f42297b2a0b45188f8baec981bde4c7c3c 100644 --- a/drivers/mtd/nand/raw/intel-nand-controller.c +++ b/drivers/mtd/nand/raw/intel-nand-controller.c @@ -626,16 +626,10 @@ static int ebu_nand_probe(struct platform_device *pdev) goto err_of_node_put; } - ebu_host->clk = devm_clk_get(dev, NULL); + ebu_host->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(ebu_host->clk)) { ret = dev_err_probe(dev, PTR_ERR(ebu_host->clk), - "failed to get clock\n"); - goto err_of_node_put; - } - - ret = clk_prepare_enable(ebu_host->clk); - if (ret) { - dev_err(dev, "failed to enable clock: %d\n", ret); + "failed to get and enable clock\n"); goto err_of_node_put; } @@ -643,7 +637,7 @@ static int ebu_nand_probe(struct platform_device *pdev) if (IS_ERR(ebu_host->dma_tx)) { ret = dev_err_probe(dev, PTR_ERR(ebu_host->dma_tx), "failed to request DMA tx chan!.\n"); - goto err_disable_unprepare_clk; + goto err_of_node_put; } ebu_host->dma_rx = dma_request_chan(dev, "rx"); @@ -698,8 +692,6 @@ static int ebu_nand_probe(struct platform_device *pdev) nand_cleanup(&ebu_host->chip); err_cleanup_dma: ebu_dma_cleanup(ebu_host); -err_disable_unprepare_clk: - clk_disable_unprepare(ebu_host->clk); err_of_node_put: of_node_put(chip_np); @@ -716,7 +708,6 @@ static void ebu_nand_remove(struct platform_device *pdev) nand_cleanup(&ebu_host->chip); ebu_nand_disable(&ebu_host->chip); ebu_dma_cleanup(ebu_host); - clk_disable_unprepare(ebu_host->clk); } static const struct of_device_id ebu_nand_match[] = { diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index b3136ae6f4e94e4410c646584495213480ef5ce2..488fd452611a66ea70afa3023d1d95cacfdad478 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -695,8 +695,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) host->pdev = pdev; - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->io_base = devm_ioremap_resource(&pdev->dev, rc); + host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &rc); if (IS_ERR(host->io_base)) return PTR_ERR(host->io_base); diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 3139b6107660c540ca2f93523462355eb7f65922..1c5fa855b9f2d3633df77ba113d2f205bffa218f 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -836,8 +836,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) if (!host) return -ENOMEM; - rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->io_base = devm_ioremap_resource(&pdev->dev, rc); + host->io_base = devm_platform_get_and_ioremap_resource(pdev, 0, &rc); if (IS_ERR(host->io_base)) return PTR_ERR(host->io_base); @@ -872,15 +871,12 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) mtd->dev.parent = &pdev->dev; /* Get NAND clock */ - host->clk = devm_clk_get(&pdev->dev, NULL); + host->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(host->clk)) { dev_err(&pdev->dev, "Clock failure\n"); res = -ENOENT; goto enable_wp; } - res = clk_prepare_enable(host->clk); - if (res) - goto enable_wp; /* Set NAND IO addresses and command/ready functions */ chip->legacy.IO_ADDR_R = SLC_DATA(host->io_base); @@ -908,13 +904,13 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) GFP_KERNEL); if (host->data_buf == NULL) { res = -ENOMEM; - goto unprepare_clk; + goto enable_wp; } res = lpc32xx_nand_dma_setup(host); if (res) { res = -EIO; - goto unprepare_clk; + goto enable_wp; } /* Find NAND device */ @@ -935,8 +931,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) nand_cleanup(chip); release_dma: dma_release_channel(host->dma_chan); -unprepare_clk: - clk_disable_unprepare(host->clk); enable_wp: lpc32xx_wp_enable(host); @@ -963,7 +957,6 @@ static void lpc32xx_nand_remove(struct platform_device *pdev) tmp &= ~SLCCFG_CE_LOW; writel(tmp, SLC_CTRL(host->io_base)); - clk_disable_unprepare(host->clk); lpc32xx_wp_enable(host); } diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c index 30c15e4e1cc0d2899bf2fa943c1f7b95cc1d0e79..2c94da7a3b3a45928b67e2978f45458c68f922d9 100644 --- a/drivers/mtd/nand/raw/marvell_nand.c +++ b/drivers/mtd/nand/raw/marvell_nand.c @@ -77,9 +77,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -375,6 +376,7 @@ static inline struct marvell_nand_chip_sel *to_nand_sel(struct marvell_nand_chip * BCH error detection and correction algorithm, * NDCB3 register has been added * @use_dma: Use dma for data transfers + * @max_mode_number: Maximum timing mode supported by the controller */ struct marvell_nfc_caps { unsigned int max_cs_nb; @@ -383,6 +385,7 @@ struct marvell_nfc_caps { bool legacy_of_bindings; bool is_nfcv2; bool use_dma; + unsigned int max_mode_number; }; /** @@ -2376,6 +2379,9 @@ static int marvell_nfc_setup_interface(struct nand_chip *chip, int chipnr, if (IS_ERR(sdr)) return PTR_ERR(sdr); + if (nfc->caps->max_mode_number && nfc->caps->max_mode_number < conf->timings.mode) + return -EOPNOTSUPP; + /* * SDR timings are given in pico-seconds while NFC timings must be * expressed in NAND controller clock cycles, which is half of the @@ -3073,6 +3079,13 @@ static const struct marvell_nfc_caps marvell_armada_8k_nfc_caps = { .is_nfcv2 = true, }; +static const struct marvell_nfc_caps marvell_ac5_caps = { + .max_cs_nb = 2, + .max_rb_nb = 1, + .is_nfcv2 = true, + .max_mode_number = 3, +}; + static const struct marvell_nfc_caps marvell_armada370_nfc_caps = { .max_cs_nb = 4, .max_rb_nb = 2, @@ -3121,6 +3134,10 @@ static const struct of_device_id marvell_nfc_of_ids[] = { .compatible = "marvell,armada-8k-nand-controller", .data = &marvell_armada_8k_nfc_caps, }, + { + .compatible = "marvell,ac5-nand-controller", + .data = &marvell_ac5_caps, + }, { .compatible = "marvell,armada370-nand-controller", .data = &marvell_armada370_nfc_caps, diff --git a/drivers/mtd/nand/raw/meson_nand.c b/drivers/mtd/nand/raw/meson_nand.c index b10011dec1e62c5733f6a8017806cc1cb7374ded..25e3c1cb605e7f95318181616b5470b7cace104d 100644 --- a/drivers/mtd/nand/raw/meson_nand.c +++ b/drivers/mtd/nand/raw/meson_nand.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #define NFC_REG_CMD 0x00 @@ -135,6 +134,7 @@ struct meson_nfc_nand_chip { struct meson_nand_ecc { u32 bch; u32 strength; + u32 size; }; struct meson_nfc_data { @@ -190,7 +190,8 @@ struct meson_nfc { }; enum { - NFC_ECC_BCH8_1K = 2, + NFC_ECC_BCH8_512 = 1, + NFC_ECC_BCH8_1K, NFC_ECC_BCH24_1K, NFC_ECC_BCH30_1K, NFC_ECC_BCH40_1K, @@ -198,15 +199,16 @@ enum { NFC_ECC_BCH60_1K, }; -#define MESON_ECC_DATA(b, s) { .bch = (b), .strength = (s)} +#define MESON_ECC_DATA(b, s, sz) { .bch = (b), .strength = (s), .size = (sz) } static struct meson_nand_ecc meson_ecc[] = { - MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8), - MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24), - MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30), - MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40), - MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50), - MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60), + MESON_ECC_DATA(NFC_ECC_BCH8_512, 8, 512), + MESON_ECC_DATA(NFC_ECC_BCH8_1K, 8, 1024), + MESON_ECC_DATA(NFC_ECC_BCH24_1K, 24, 1024), + MESON_ECC_DATA(NFC_ECC_BCH30_1K, 30, 1024), + MESON_ECC_DATA(NFC_ECC_BCH40_1K, 40, 1024), + MESON_ECC_DATA(NFC_ECC_BCH50_1K, 50, 1024), + MESON_ECC_DATA(NFC_ECC_BCH60_1K, 60, 1024), }; static int meson_nand_calc_ecc_bytes(int step_size, int strength) @@ -224,8 +226,27 @@ static int meson_nand_calc_ecc_bytes(int step_size, int strength) NAND_ECC_CAPS_SINGLE(meson_gxl_ecc_caps, meson_nand_calc_ecc_bytes, 1024, 8, 24, 30, 40, 50, 60); -NAND_ECC_CAPS_SINGLE(meson_axg_ecc_caps, - meson_nand_calc_ecc_bytes, 1024, 8); + +static const int axg_stepinfo_strengths[] = { 8 }; + +static const struct nand_ecc_step_info axg_stepinfo[] = { + { + .stepsize = 1024, + .strengths = axg_stepinfo_strengths, + .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths) + }, + { + .stepsize = 512, + .strengths = axg_stepinfo_strengths, + .nstrengths = ARRAY_SIZE(axg_stepinfo_strengths) + }, +}; + +static const struct nand_ecc_caps meson_axg_ecc_caps = { + .stepinfos = axg_stepinfo, + .nstepinfos = ARRAY_SIZE(axg_stepinfo), + .calc_ecc_bytes = meson_nand_calc_ecc_bytes, +}; static struct meson_nfc_nand_chip *to_meson_nand(struct nand_chip *nand) { @@ -400,9 +421,10 @@ static void meson_nfc_set_data_oob(struct nand_chip *nand, } } -static int meson_nfc_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms, +static int meson_nfc_wait_no_rb_pin(struct nand_chip *nand, int timeout_ms, bool need_cmd_read0) { + struct meson_nfc *nfc = nand_get_controller_data(nand); u32 cmd, cfg; meson_nfc_cmd_idle(nfc, nfc->timing.twb); @@ -414,8 +436,7 @@ static int meson_nfc_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms, writel(cfg, nfc->reg_base + NFC_REG_CFG); reinit_completion(&nfc->completion); - cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_STATUS; - writel(cmd, nfc->reg_base + NFC_REG_CMD); + nand_status_op(nand, NULL); /* use the max erase time as the maximum clock for waiting R/B */ cmd = NFC_CMD_RB | NFC_CMD_RB_INT_NO_PIN | nfc->timing.tbers_max; @@ -425,12 +446,8 @@ static int meson_nfc_wait_no_rb_pin(struct meson_nfc *nfc, int timeout_ms, msecs_to_jiffies(timeout_ms))) return -ETIMEDOUT; - if (need_cmd_read0) { - cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_READ0; - writel(cmd, nfc->reg_base + NFC_REG_CMD); - meson_nfc_drain_cmd(nfc); - meson_nfc_wait_cmd_finish(nfc, CMD_FIFO_EMPTY_TIMEOUT); - } + if (need_cmd_read0) + nand_exit_status_op(nand); return 0; } @@ -463,9 +480,11 @@ static int meson_nfc_wait_rb_pin(struct meson_nfc *nfc, int timeout_ms) return ret; } -static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, +static int meson_nfc_queue_rb(struct nand_chip *nand, int timeout_ms, bool need_cmd_read0) { + struct meson_nfc *nfc = nand_get_controller_data(nand); + if (nfc->no_rb_pin) { /* This mode is used when there is no wired R/B pin. * It works like 'nand_soft_waitrdy()', but instead of @@ -477,7 +496,7 @@ static int meson_nfc_queue_rb(struct meson_nfc *nfc, int timeout_ms, * needed (for all cases except page programming - this * is reason of 'need_cmd_read0' flag). */ - return meson_nfc_wait_no_rb_pin(nfc, timeout_ms, + return meson_nfc_wait_no_rb_pin(nand, timeout_ms, need_cmd_read0); } else { return meson_nfc_wait_rb_pin(nfc, timeout_ms); @@ -687,7 +706,7 @@ static int meson_nfc_rw_cmd_prepare_and_execute(struct nand_chip *nand, if (in) { nfc->cmdfifo.rw.cmd1 = cs | NFC_CMD_CLE | NAND_CMD_READSTART; writel(nfc->cmdfifo.rw.cmd1, nfc->reg_base + NFC_REG_CMD); - meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tR_max), true); + meson_nfc_queue_rb(nand, PSEC_TO_MSEC(sdr->tR_max), true); } else { meson_nfc_cmd_idle(nfc, nfc->timing.tadl); } @@ -733,7 +752,7 @@ static int meson_nfc_write_page_sub(struct nand_chip *nand, cmd = nfc->param.chip_select | NFC_CMD_CLE | NAND_CMD_PAGEPROG; writel(cmd, nfc->reg_base + NFC_REG_CMD); - meson_nfc_queue_rb(nfc, PSEC_TO_MSEC(sdr->tPROG_max), false); + meson_nfc_queue_rb(nand, PSEC_TO_MSEC(sdr->tPROG_max), false); meson_nfc_dma_buffer_release(nand, data_len, info_len, DMA_TO_DEVICE); @@ -1049,7 +1068,7 @@ static int meson_nfc_exec_op(struct nand_chip *nand, break; case NAND_OP_WAITRDY_INSTR: - meson_nfc_queue_rb(nfc, instr->ctx.waitrdy.timeout_ms, + meson_nfc_queue_rb(nand, instr->ctx.waitrdy.timeout_ms, true); if (instr->delay_ns) meson_nfc_cmd_idle(nfc, delay_idle); @@ -1259,7 +1278,8 @@ static int meson_nand_bch_mode(struct nand_chip *nand) return -EINVAL; for (i = 0; i < ARRAY_SIZE(meson_ecc); i++) { - if (meson_ecc[i].strength == nand->ecc.strength) { + if (meson_ecc[i].strength == nand->ecc.strength && + meson_ecc[i].size == nand->ecc.size) { meson_chip->bch_mode = meson_ecc[i].bch; return 0; } diff --git a/drivers/mtd/nand/raw/mpc5121_nfc.c b/drivers/mtd/nand/raw/mpc5121_nfc.c index ab05ee65702c85f5d9c0fc67a0312c3918b7eab1..215610f808f169a0b34bbea7a1b4648295bcff6d 100644 --- a/drivers/mtd/nand/raw/mpc5121_nfc.c +++ b/drivers/mtd/nand/raw/mpc5121_nfc.c @@ -21,10 +21,10 @@ #include #include #include +#include #include -#include #include -#include +#include #include @@ -595,8 +595,6 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) struct nand_chip *chip = mtd_to_nand(mtd); struct mpc5121_nfc_prv *prv = nand_get_controller_data(chip); - clk_disable_unprepare(prv->clk); - if (prv->csreg) iounmap(prv->csreg); } @@ -717,17 +715,12 @@ static int mpc5121_nfc_probe(struct platform_device *op) } /* Enable NFC clock */ - clk = devm_clk_get(dev, "ipg"); + clk = devm_clk_get_enabled(dev, "ipg"); if (IS_ERR(clk)) { - dev_err(dev, "Unable to acquire NFC clock!\n"); + dev_err(dev, "Unable to acquire and enable NFC clock!\n"); retval = PTR_ERR(clk); goto error; } - retval = clk_prepare_enable(clk); - if (retval) { - dev_err(dev, "Unable to enable NFC clock!\n"); - goto error; - } prv->clk = clk; /* Reset NAND Flash controller */ diff --git a/drivers/mtd/nand/raw/mtk_nand.c b/drivers/mtd/nand/raw/mtk_nand.c index b2fa6b2074ab1fe2515fac891e2f1afb7b37a2d5..29c8bddde67ff6df528fe496566eb6927543c53a 100644 --- a/drivers/mtd/nand/raw/mtk_nand.c +++ b/drivers/mtd/nand/raw/mtk_nand.c @@ -16,7 +16,6 @@ #include #include #include -#include #include /* NAND controller register definition */ @@ -1119,32 +1118,6 @@ static irqreturn_t mtk_nfc_irq(int irq, void *id) return IRQ_HANDLED; } -static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk) -{ - int ret; - - ret = clk_prepare_enable(clk->nfi_clk); - if (ret) { - dev_err(dev, "failed to enable nfi clk\n"); - return ret; - } - - ret = clk_prepare_enable(clk->pad_clk); - if (ret) { - dev_err(dev, "failed to enable pad clk\n"); - clk_disable_unprepare(clk->nfi_clk); - return ret; - } - - return 0; -} - -static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk) -{ - clk_disable_unprepare(clk->nfi_clk); - clk_disable_unprepare(clk->pad_clk); -} - static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *oob_region) { @@ -1546,40 +1519,36 @@ static int mtk_nfc_probe(struct platform_device *pdev) goto release_ecc; } - nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk"); + nfc->clk.nfi_clk = devm_clk_get_enabled(dev, "nfi_clk"); if (IS_ERR(nfc->clk.nfi_clk)) { dev_err(dev, "no clk\n"); ret = PTR_ERR(nfc->clk.nfi_clk); goto release_ecc; } - nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk"); + nfc->clk.pad_clk = devm_clk_get_enabled(dev, "pad_clk"); if (IS_ERR(nfc->clk.pad_clk)) { dev_err(dev, "no pad clk\n"); ret = PTR_ERR(nfc->clk.pad_clk); goto release_ecc; } - ret = mtk_nfc_enable_clk(dev, &nfc->clk); - if (ret) - goto release_ecc; - irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -EINVAL; - goto clk_disable; + goto release_ecc; } ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc); if (ret) { dev_err(dev, "failed to request nfi irq\n"); - goto clk_disable; + goto release_ecc; } ret = dma_set_mask(dev, DMA_BIT_MASK(32)); if (ret) { dev_err(dev, "failed to set dma mask\n"); - goto clk_disable; + goto release_ecc; } platform_set_drvdata(pdev, nfc); @@ -1587,14 +1556,11 @@ static int mtk_nfc_probe(struct platform_device *pdev) ret = mtk_nfc_nand_chips_init(dev, nfc); if (ret) { dev_err(dev, "failed to init nand chips\n"); - goto clk_disable; + goto release_ecc; } return 0; -clk_disable: - mtk_nfc_disable_clk(&nfc->clk); - release_ecc: mtk_ecc_release(nfc->ecc); @@ -1619,7 +1585,6 @@ static void mtk_nfc_remove(struct platform_device *pdev) } mtk_ecc_release(nfc->ecc); - mtk_nfc_disable_clk(&nfc->clk); } #ifdef CONFIG_PM_SLEEP @@ -1627,7 +1592,8 @@ static int mtk_nfc_suspend(struct device *dev) { struct mtk_nfc *nfc = dev_get_drvdata(dev); - mtk_nfc_disable_clk(&nfc->clk); + clk_disable_unprepare(nfc->clk.nfi_clk); + clk_disable_unprepare(nfc->clk.pad_clk); return 0; } @@ -1642,9 +1608,18 @@ static int mtk_nfc_resume(struct device *dev) udelay(200); - ret = mtk_nfc_enable_clk(dev, &nfc->clk); - if (ret) + ret = clk_prepare_enable(nfc->clk.nfi_clk); + if (ret) { + dev_err(dev, "failed to enable nfi clk\n"); return ret; + } + + ret = clk_prepare_enable(nfc->clk.pad_clk); + if (ret) { + dev_err(dev, "failed to enable pad clk\n"); + clk_disable_unprepare(nfc->clk.nfi_clk); + return ret; + } /* reset NAND chip if VCC was powered off */ list_for_each_entry(chip, &nfc->chips, node) { diff --git a/drivers/mtd/nand/raw/mxc_nand.c b/drivers/mtd/nand/raw/mxc_nand.c index 3d4b2e8294ea6ba3bf990ca7af264024f1aadc66..003008355b3c29cd046bd6285d312f021b5971b9 100644 --- a/drivers/mtd/nand/raw/mxc_nand.c +++ b/drivers/mtd/nand/raw/mxc_nand.c @@ -20,7 +20,6 @@ #include #include #include -#include #define DRIVER_NAME "mxc_nand" @@ -1696,7 +1695,6 @@ static int mxcnd_probe(struct platform_device *pdev) struct nand_chip *this; struct mtd_info *mtd; struct mxc_nand_host *host; - struct resource *res; int err = 0; /* Allocate memory for MTD device structure and private data */ @@ -1740,17 +1738,15 @@ static int mxcnd_probe(struct platform_device *pdev) this->options |= NAND_KEEP_TIMINGS; if (host->devtype_data->needs_ip) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->regs_ip = devm_ioremap_resource(&pdev->dev, res); + host->regs_ip = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(host->regs_ip)) return PTR_ERR(host->regs_ip); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + host->base = devm_platform_ioremap_resource(pdev, 1); } else { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_platform_ioremap_resource(pdev, 0); } - host->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->base)) return PTR_ERR(host->base); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a6af521832aa41cc158b56ffbab2c2335f7890f7..d4b55155aeae90e8d32917917b56026c758cb5de 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -1885,6 +1885,7 @@ int nand_exit_status_op(struct nand_chip *chip) return 0; } +EXPORT_SYMBOL_GPL(nand_exit_status_op); /** * nand_erase_op - Do an erase operation diff --git a/drivers/mtd/nand/raw/ndfc.c b/drivers/mtd/nand/raw/ndfc.c index 57f3db32122d4d856c60e248ea45f0ddc58ba409..3bb32a7c6d6798473d2ad8d8973608643f737b6e 100644 --- a/drivers/mtd/nand/raw/ndfc.c +++ b/drivers/mtd/nand/raw/ndfc.c @@ -22,8 +22,9 @@ #include #include #include +#include #include -#include +#include #include #define NDFC_MAX_CS 4 diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c index db22b3af16d8a93e70655225b53334b99d476300..c45bef6158e7ac773b0bf0bbe8a3835746a14198 100644 --- a/drivers/mtd/nand/raw/omap2.c +++ b/drivers/mtd/nand/raw/omap2.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include @@ -2219,8 +2219,7 @@ static int omap_nand_probe(struct platform_device *pdev) } } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - vaddr = devm_ioremap_resource(&pdev->dev, res); + vaddr = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(vaddr)) return PTR_ERR(vaddr); diff --git a/drivers/mtd/nand/raw/orion_nand.c b/drivers/mtd/nand/raw/orion_nand.c index 7e0313889b50180cf8b501c256fa2c445ca20688..2951d81614fd0727ca575ab1098bc32c76cf23c7 100644 --- a/drivers/mtd/nand/raw/orion_nand.c +++ b/drivers/mtd/nand/raw/orion_nand.c @@ -169,16 +169,10 @@ static int __init orion_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); /* Not all platforms can gate the clock, so it is optional. */ - info->clk = devm_clk_get_optional(&pdev->dev, NULL); + info->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(info->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(info->clk), - "failed to get clock!\n"); - - ret = clk_prepare_enable(info->clk); - if (ret) { - dev_err(&pdev->dev, "failed to prepare clock!\n"); - return ret; - } + "failed to get and enable clock!\n"); /* * This driver assumes that the default ECC engine should be TYPE_SOFT. @@ -189,19 +183,13 @@ static int __init orion_nand_probe(struct platform_device *pdev) ret = nand_scan(nc, 1); if (ret) - goto no_dev; + return ret; mtd->name = "orion_nand"; ret = mtd_device_register(mtd, board->parts, board->nr_parts); - if (ret) { + if (ret) nand_cleanup(nc); - goto no_dev; - } - - return 0; -no_dev: - clk_disable_unprepare(info->clk); return ret; } @@ -215,8 +203,6 @@ static void orion_nand_remove(struct platform_device *pdev) WARN_ON(ret); nand_cleanup(chip); - - clk_disable_unprepare(info->clk); } #ifdef CONFIG_OF diff --git a/drivers/mtd/nand/raw/oxnas_nand.c b/drivers/mtd/nand/raw/oxnas_nand.c deleted file mode 100644 index e3c9807df1cd82f44fd0a6aee242360ec72a7660..0000000000000000000000000000000000000000 --- a/drivers/mtd/nand/raw/oxnas_nand.c +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Oxford Semiconductor OXNAS NAND driver - - * Copyright (C) 2016 Neil Armstrong - * Heavily based on plat_nand.c : - * Author: Vitaly Wool - * Copyright (C) 2013 Ma Haijun - * Copyright (C) 2012 John Crispin - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Nand commands */ -#define OXNAS_NAND_CMD_ALE BIT(18) -#define OXNAS_NAND_CMD_CLE BIT(19) - -#define OXNAS_NAND_MAX_CHIPS 1 - -struct oxnas_nand_ctrl { - struct nand_controller base; - void __iomem *io_base; - struct clk *clk; - struct nand_chip *chips[OXNAS_NAND_MAX_CHIPS]; - unsigned int nchips; -}; - -static uint8_t oxnas_nand_read_byte(struct nand_chip *chip) -{ - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - return readb(oxnas->io_base); -} - -static void oxnas_nand_read_buf(struct nand_chip *chip, u8 *buf, int len) -{ - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - ioread8_rep(oxnas->io_base, buf, len); -} - -static void oxnas_nand_write_buf(struct nand_chip *chip, const u8 *buf, - int len) -{ - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - iowrite8_rep(oxnas->io_base, buf, len); -} - -/* Single CS command control */ -static void oxnas_nand_cmd_ctrl(struct nand_chip *chip, int cmd, - unsigned int ctrl) -{ - struct oxnas_nand_ctrl *oxnas = nand_get_controller_data(chip); - - if (ctrl & NAND_CLE) - writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_CLE); - else if (ctrl & NAND_ALE) - writeb(cmd, oxnas->io_base + OXNAS_NAND_CMD_ALE); -} - -/* - * Probe for the NAND device. - */ -static int oxnas_nand_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct device_node *nand_np; - struct oxnas_nand_ctrl *oxnas; - struct nand_chip *chip; - struct mtd_info *mtd; - int count = 0; - int err = 0; - int i; - - /* Allocate memory for the device structure (and zero it) */ - oxnas = devm_kzalloc(&pdev->dev, sizeof(*oxnas), - GFP_KERNEL); - if (!oxnas) - return -ENOMEM; - - nand_controller_init(&oxnas->base); - - oxnas->io_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(oxnas->io_base)) - return PTR_ERR(oxnas->io_base); - - oxnas->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(oxnas->clk)) - oxnas->clk = NULL; - - /* Only a single chip node is supported */ - count = of_get_child_count(np); - if (count > 1) - return -EINVAL; - - err = clk_prepare_enable(oxnas->clk); - if (err) - return err; - - device_reset_optional(&pdev->dev); - - for_each_child_of_node(np, nand_np) { - chip = devm_kzalloc(&pdev->dev, sizeof(struct nand_chip), - GFP_KERNEL); - if (!chip) { - err = -ENOMEM; - goto err_release_child; - } - - chip->controller = &oxnas->base; - - nand_set_flash_node(chip, nand_np); - nand_set_controller_data(chip, oxnas); - - mtd = nand_to_mtd(chip); - mtd->dev.parent = &pdev->dev; - mtd->priv = chip; - - chip->legacy.cmd_ctrl = oxnas_nand_cmd_ctrl; - chip->legacy.read_buf = oxnas_nand_read_buf; - chip->legacy.read_byte = oxnas_nand_read_byte; - chip->legacy.write_buf = oxnas_nand_write_buf; - chip->legacy.chip_delay = 30; - - /* Scan to find existence of the device */ - err = nand_scan(chip, 1); - if (err) - goto err_release_child; - - err = mtd_device_register(mtd, NULL, 0); - if (err) - goto err_cleanup_nand; - - oxnas->chips[oxnas->nchips++] = chip; - } - - /* Exit if no chips found */ - if (!oxnas->nchips) { - err = -ENODEV; - goto err_clk_unprepare; - } - - platform_set_drvdata(pdev, oxnas); - - return 0; - -err_cleanup_nand: - nand_cleanup(chip); -err_release_child: - of_node_put(nand_np); - - for (i = 0; i < oxnas->nchips; i++) { - chip = oxnas->chips[i]; - WARN_ON(mtd_device_unregister(nand_to_mtd(chip))); - nand_cleanup(chip); - } - -err_clk_unprepare: - clk_disable_unprepare(oxnas->clk); - return err; -} - -static void oxnas_nand_remove(struct platform_device *pdev) -{ - struct oxnas_nand_ctrl *oxnas = platform_get_drvdata(pdev); - struct nand_chip *chip; - int i; - - for (i = 0; i < oxnas->nchips; i++) { - chip = oxnas->chips[i]; - WARN_ON(mtd_device_unregister(nand_to_mtd(chip))); - nand_cleanup(chip); - } - - clk_disable_unprepare(oxnas->clk); -} - -static const struct of_device_id oxnas_nand_match[] = { - { .compatible = "oxsemi,ox820-nand" }, - {}, -}; -MODULE_DEVICE_TABLE(of, oxnas_nand_match); - -static struct platform_driver oxnas_nand_driver = { - .probe = oxnas_nand_probe, - .remove_new = oxnas_nand_remove, - .driver = { - .name = "oxnas_nand", - .of_match_table = oxnas_nand_match, - }, -}; - -module_platform_driver(oxnas_nand_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Neil Armstrong "); -MODULE_DESCRIPTION("Oxnas NAND driver"); -MODULE_ALIAS("platform:oxnas_nand"); diff --git a/drivers/mtd/nand/raw/pl35x-nand-controller.c b/drivers/mtd/nand/raw/pl35x-nand-controller.c index 28b7bd7e22eb475c0b85fb502dc843b10c27f59b..8da5fee321b57110da7fbc696dea6f5c0d256d71 100644 --- a/drivers/mtd/nand/raw/pl35x-nand-controller.c +++ b/drivers/mtd/nand/raw/pl35x-nand-controller.c @@ -23,9 +23,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index 72d6168d8a1be0c8c7d139a1bcf758d0f387d2db..64499c1b36032c476ce6db4bd3b78ffca53e7f74 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -2,19 +2,19 @@ /* * Copyright (c) 2016, The Linux Foundation. All rights reserved. */ -#include -#include #include -#include -#include +#include +#include #include +#include +#include +#include #include -#include #include +#include #include -#include -#include -#include +#include +#include /* NANDc reg offsets */ #define NAND_FLASH_CMD 0x00 @@ -123,8 +123,8 @@ /* NAND_ERASED_CW_DETECT_CFG bits */ #define ERASED_CW_ECC_MASK 1 #define AUTO_DETECT_RES 0 -#define MASK_ECC (1 << ERASED_CW_ECC_MASK) -#define RESET_ERASED_DET (1 << AUTO_DETECT_RES) +#define MASK_ECC BIT(ERASED_CW_ECC_MASK) +#define RESET_ERASED_DET BIT(AUTO_DETECT_RES) #define ACTIVE_ERASED_DET (0 << AUTO_DETECT_RES) #define CLR_ERASED_PAGE_DET (RESET_ERASED_DET | MASK_ECC) #define SET_ERASED_PAGE_DET (ACTIVE_ERASED_DET | MASK_ECC) @@ -157,6 +157,7 @@ #define OP_PAGE_PROGRAM_WITH_ECC 0x7 #define OP_PROGRAM_PAGE_SPARE 0x9 #define OP_BLOCK_ERASE 0xa +#define OP_CHECK_STATUS 0xc #define OP_FETCH_ID 0xb #define OP_RESET_DEVICE 0xd @@ -211,7 +212,7 @@ nandc_set_reg(chip, reg, \ /* Returns the dma address for reg read buffer */ #define reg_buf_dma_addr(chip, vaddr) \ ((chip)->reg_read_dma + \ - ((uint8_t *)(vaddr) - (uint8_t *)(chip)->reg_read_buf)) + ((u8 *)(vaddr) - (u8 *)(chip)->reg_read_buf)) #define QPIC_PER_CW_CMD_ELEMENTS 32 #define QPIC_PER_CW_CMD_SGL 32 @@ -235,6 +236,8 @@ nandc_set_reg(chip, reg, \ */ #define NAND_ERASED_CW_SET BIT(4) +#define MAX_ADDRESS_CYCLE 5 + /* * This data type corresponds to the BAM transaction which will be used for all * NAND transfers. @@ -382,6 +385,9 @@ struct nandc_regs { * @reg_read_pos: marker for data read in reg_read_buf * * @cmd1/vld: some fixed controller register values + * + * @exec_opwrite: flag to select correct number of code word + * while reading status */ struct qcom_nand_controller { struct device *dev; @@ -432,6 +438,7 @@ struct qcom_nand_controller { int reg_read_pos; u32 cmd1, vld; + bool exec_opwrite; }; /* @@ -447,6 +454,29 @@ struct qcom_nand_boot_partition { u32 page_size; }; +/* + * Qcom op for each exec_op transfer + * + * @data_instr: data instruction pointer + * @data_instr_idx: data instruction index + * @rdy_timeout_ms: wait ready timeout in ms + * @rdy_delay_ns: Additional delay in ns + * @addr1_reg: Address1 register value + * @addr2_reg: Address2 register value + * @cmd_reg: CMD register value + * @flag: flag for misc instruction + */ +struct qcom_op { + const struct nand_op_instr *data_instr; + unsigned int data_instr_idx; + unsigned int rdy_timeout_ms; + unsigned int rdy_delay_ns; + u32 addr1_reg; + u32 addr2_reg; + u32 cmd_reg; + u8 flag; +}; + /* * NAND chip structure * @@ -1273,182 +1303,33 @@ static void config_nand_cw_write(struct nand_chip *chip) write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); } -/* - * the following functions are used within chip->legacy.cmdfunc() to - * perform different NAND_CMD_* commands - */ - -/* sets up descriptors for NAND_CMD_PARAM */ -static int nandc_param(struct qcom_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - /* - * NAND_CMD_PARAM is called before we know much about the FLASH chip - * in use. we configure the controller to perform a raw read of 512 - * bytes to read onfi params - */ - if (nandc->props->qpic_v2) - nandc_set_reg(chip, NAND_FLASH_CMD, OP_PAGE_READ_ONFI_READ | - PAGE_ACC | LAST_PAGE); - else - nandc_set_reg(chip, NAND_FLASH_CMD, OP_PAGE_READ | - PAGE_ACC | LAST_PAGE); - - nandc_set_reg(chip, NAND_ADDR0, 0); - nandc_set_reg(chip, NAND_ADDR1, 0); - nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE - | 512 << UD_SIZE_BYTES - | 5 << NUM_ADDR_CYCLES - | 0 << SPARE_SIZE_BYTES); - nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES - | 0 << CS_ACTIVE_BSY - | 17 << BAD_BLOCK_BYTE_NUM - | 1 << BAD_BLOCK_IN_SPARE_AREA - | 2 << WR_RD_BSY_GAP - | 0 << WIDE_FLASH - | 1 << DEV0_CFG1_ECC_DISABLE); - if (!nandc->props->qpic_v2) - nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); - - /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ - if (!nandc->props->qpic_v2) { - nandc_set_reg(chip, NAND_DEV_CMD_VLD, - (nandc->vld & ~READ_START_VLD)); - nandc_set_reg(chip, NAND_DEV_CMD1, - (nandc->cmd1 & ~(0xFF << READ_ADDR)) - | NAND_CMD_PARAM << READ_ADDR); - } - - nandc_set_reg(chip, NAND_EXEC_CMD, 1); - - if (!nandc->props->qpic_v2) { - nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1); - nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); - } - - nandc_set_read_loc(chip, 0, 0, 0, 512, 1); - - if (!nandc->props->qpic_v2) { - write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); - write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); - } - - nandc->buf_count = 512; - memset(nandc->data_buffer, 0xff, nandc->buf_count); - - config_nand_single_cw_page_read(chip, false, 0); - - read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, - nandc->buf_count, 0); - - /* restore CMD1 and VLD regs */ - if (!nandc->props->qpic_v2) { - write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); - write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); - } - - return 0; -} - -/* sets up descriptors for NAND_CMD_ERASE1 */ -static int erase_block(struct qcom_nand_host *host, int page_addr) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - nandc_set_reg(chip, NAND_FLASH_CMD, - OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE); - nandc_set_reg(chip, NAND_ADDR0, page_addr); - nandc_set_reg(chip, NAND_ADDR1, 0); - nandc_set_reg(chip, NAND_DEV0_CFG0, - host->cfg0_raw & ~(7 << CW_PER_PAGE)); - nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw); - nandc_set_reg(chip, NAND_EXEC_CMD, 1); - nandc_set_reg(chip, NAND_FLASH_STATUS, host->clrflashstatus); - nandc_set_reg(chip, NAND_READ_STATUS, host->clrreadstatus); - - write_reg_dma(nandc, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_DEV0_CFG0, 2, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - write_reg_dma(nandc, NAND_FLASH_STATUS, 1, 0); - write_reg_dma(nandc, NAND_READ_STATUS, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - -/* sets up descriptors for NAND_CMD_READID */ -static int read_id(struct qcom_nand_host *host, int column) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - if (column == -1) - return 0; - - nandc_set_reg(chip, NAND_FLASH_CMD, OP_FETCH_ID); - nandc_set_reg(chip, NAND_ADDR0, column); - nandc_set_reg(chip, NAND_ADDR1, 0); - nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, - nandc->props->is_bam ? 0 : DM_EN); - nandc_set_reg(chip, NAND_EXEC_CMD, 1); - - write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - -/* sets up descriptors for NAND_CMD_RESET */ -static int reset(struct qcom_nand_host *host) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - nandc_set_reg(chip, NAND_FLASH_CMD, OP_RESET_DEVICE); - nandc_set_reg(chip, NAND_EXEC_CMD, 1); - - write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); - write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); - - read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); - - return 0; -} - /* helpers to submit/free our list of dma descriptors */ static int submit_descs(struct qcom_nand_controller *nandc) { - struct desc_info *desc; + struct desc_info *desc, *n; dma_cookie_t cookie = 0; struct bam_transaction *bam_txn = nandc->bam_txn; - int r; + int ret = 0; if (nandc->props->is_bam) { if (bam_txn->rx_sgl_pos > bam_txn->rx_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); - if (r) - return r; + ret = prepare_bam_async_desc(nandc, nandc->rx_chan, 0); + if (ret) + goto err_unmap_free_desc; } if (bam_txn->tx_sgl_pos > bam_txn->tx_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->tx_chan, + ret = prepare_bam_async_desc(nandc, nandc->tx_chan, DMA_PREP_INTERRUPT); - if (r) - return r; + if (ret) + goto err_unmap_free_desc; } if (bam_txn->cmd_sgl_pos > bam_txn->cmd_sgl_start) { - r = prepare_bam_async_desc(nandc, nandc->cmd_chan, + ret = prepare_bam_async_desc(nandc, nandc->cmd_chan, DMA_PREP_CMD); - if (r) - return r; + if (ret) + goto err_unmap_free_desc; } } @@ -1470,19 +1351,17 @@ static int submit_descs(struct qcom_nand_controller *nandc) if (!wait_for_completion_timeout(&bam_txn->txn_done, QPIC_NAND_COMPLETION_TIMEOUT)) - return -ETIMEDOUT; + ret = -ETIMEDOUT; } else { if (dma_sync_wait(nandc->chan, cookie) != DMA_COMPLETE) - return -ETIMEDOUT; + ret = -ETIMEDOUT; } - return 0; -} - -static void free_descs(struct qcom_nand_controller *nandc) -{ - struct desc_info *desc, *n; - +err_unmap_free_desc: + /* + * Unmap the dma sg_list and free the desc allocated by both + * prepare_bam_async_desc() and prep_adm_dma_desc() functions. + */ list_for_each_entry_safe(desc, n, &nandc->desc_list, node) { list_del(&desc->node); @@ -1495,6 +1374,8 @@ static void free_descs(struct qcom_nand_controller *nandc) kfree(desc); } + + return ret; } /* reset the register read buffer for next NAND operation */ @@ -1504,152 +1385,6 @@ static void clear_read_regs(struct qcom_nand_controller *nandc) nandc_read_buffer_sync(nandc, false); } -static void pre_command(struct qcom_nand_host *host, int command) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - nandc->buf_count = 0; - nandc->buf_start = 0; - host->use_ecc = false; - host->last_command = command; - - clear_read_regs(nandc); - - if (command == NAND_CMD_RESET || command == NAND_CMD_READID || - command == NAND_CMD_PARAM || command == NAND_CMD_ERASE1) - clear_bam_transaction(nandc); -} - -/* - * this is called after NAND_CMD_PAGEPROG and NAND_CMD_ERASE1 to set our - * privately maintained status byte, this status byte can be read after - * NAND_CMD_STATUS is called - */ -static void parse_erase_write_errors(struct qcom_nand_host *host, int command) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - int num_cw; - int i; - - num_cw = command == NAND_CMD_PAGEPROG ? ecc->steps : 1; - nandc_read_buffer_sync(nandc, true); - - for (i = 0; i < num_cw; i++) { - u32 flash_status = le32_to_cpu(nandc->reg_read_buf[i]); - - if (flash_status & FS_MPU_ERR) - host->status &= ~NAND_STATUS_WP; - - if (flash_status & FS_OP_ERR || (i == (num_cw - 1) && - (flash_status & - FS_DEVICE_STS_ERR))) - host->status |= NAND_STATUS_FAIL; - } -} - -static void post_command(struct qcom_nand_host *host, int command) -{ - struct nand_chip *chip = &host->chip; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - switch (command) { - case NAND_CMD_READID: - nandc_read_buffer_sync(nandc, true); - memcpy(nandc->data_buffer, nandc->reg_read_buf, - nandc->buf_count); - break; - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - parse_erase_write_errors(host, command); - break; - default: - break; - } -} - -/* - * Implements chip->legacy.cmdfunc. It's only used for a limited set of - * commands. The rest of the commands wouldn't be called by upper layers. - * For example, NAND_CMD_READOOB would never be called because we have our own - * versions of read_oob ops for nand_ecc_ctrl. - */ -static void qcom_nandc_command(struct nand_chip *chip, unsigned int command, - int column, int page_addr) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - bool wait = false; - int ret = 0; - - pre_command(host, command); - - switch (command) { - case NAND_CMD_RESET: - ret = reset(host); - wait = true; - break; - - case NAND_CMD_READID: - nandc->buf_count = 4; - ret = read_id(host, column); - wait = true; - break; - - case NAND_CMD_PARAM: - ret = nandc_param(host); - wait = true; - break; - - case NAND_CMD_ERASE1: - ret = erase_block(host, page_addr); - wait = true; - break; - - case NAND_CMD_READ0: - /* we read the entire page for now */ - WARN_ON(column != 0); - - host->use_ecc = true; - set_address(host, 0, page_addr); - update_rw_regs(host, ecc->steps, true, 0); - break; - - case NAND_CMD_SEQIN: - WARN_ON(column != 0); - set_address(host, 0, page_addr); - break; - - case NAND_CMD_PAGEPROG: - case NAND_CMD_STATUS: - case NAND_CMD_NONE: - default: - break; - } - - if (ret) { - dev_err(nandc->dev, "failure executing command %d\n", - command); - free_descs(nandc); - return; - } - - if (wait) { - ret = submit_descs(nandc); - if (ret) - dev_err(nandc->dev, - "failure submitting descs for command %d\n", - command); - } - - free_descs(nandc); - - post_command(host, command); -} - /* * when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read * an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS. @@ -1736,6 +1471,9 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, int raw_cw = cw; nand_read_page_op(chip, page, 0, NULL, 0); + nandc->buf_count = 0; + nandc->buf_start = 0; + clear_read_regs(nandc); host->use_ecc = false; if (nandc->props->qpic_v2) @@ -1786,7 +1524,6 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip, read_data_dma(nandc, reg_off, oob_buf + oob_size1, oob_size2, 0); ret = submit_descs(nandc); - free_descs(nandc); if (ret) { dev_err(nandc->dev, "failure to read raw cw %d\n", cw); return ret; @@ -1819,7 +1556,7 @@ check_for_erased_page(struct qcom_nand_host *host, u8 *data_buf, struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; u8 *cw_data_buf, *cw_oob_buf; - int cw, data_size, oob_size, ret = 0; + int cw, data_size, oob_size, ret; if (!data_buf) data_buf = nand_get_data_buf(chip); @@ -2040,8 +1777,6 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf, } ret = submit_descs(nandc); - free_descs(nandc); - if (ret) { dev_err(nandc->dev, "failure to read page/oob\n"); return ret; @@ -2080,8 +1815,6 @@ static int copy_last_cw(struct qcom_nand_host *host, int page) if (ret) dev_err(nandc->dev, "failed to copy last codeword\n"); - free_descs(nandc); - return ret; } @@ -2149,27 +1882,35 @@ static void qcom_nandc_codeword_fixup(struct qcom_nand_host *host, int page) } /* implements ecc->read_page() */ -static int qcom_nandc_read_page(struct nand_chip *chip, uint8_t *buf, +static int qcom_nandc_read_page(struct nand_chip *chip, u8 *buf, int oob_required, int page) { struct qcom_nand_host *host = to_qcom_nand_host(chip); struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; u8 *data_buf, *oob_buf = NULL; if (host->nr_boot_partitions) qcom_nandc_codeword_fixup(host, page); nand_read_page_op(chip, page, 0, NULL, 0); - data_buf = buf; - oob_buf = oob_required ? chip->oob_poi : NULL; - - clear_bam_transaction(nandc); + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = true; + clear_read_regs(nandc); + set_address(host, 0, page); + update_rw_regs(host, ecc->steps, true, 0); + + data_buf = buf; + oob_buf = oob_required ? chip->oob_poi : NULL; + + clear_bam_transaction(nandc); return read_page_ecc(host, data_buf, oob_buf, page); } /* implements ecc->read_page_raw() */ -static int qcom_nandc_read_page_raw(struct nand_chip *chip, uint8_t *buf, +static int qcom_nandc_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -2215,7 +1956,7 @@ static int qcom_nandc_read_oob(struct nand_chip *chip, int page) } /* implements ecc->write_page() */ -static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, +static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf, int oob_required, int page) { struct qcom_nand_host *host = to_qcom_nand_host(chip); @@ -2229,6 +1970,9 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, nand_prog_page_begin_op(chip, page, 0, NULL, 0); + set_address(host, 0, page); + nandc->buf_count = 0; + nandc->buf_start = 0; clear_read_regs(nandc); clear_bam_transaction(nandc); @@ -2251,7 +1995,6 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, oob_size = ecc->bytes; } - write_data_dma(nandc, FLASH_BUF_ACC, data_buf, data_size, i == (ecc->steps - 1) ? NAND_BAM_NO_EOT : 0); @@ -2276,20 +2019,17 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const uint8_t *buf, } ret = submit_descs(nandc); - if (ret) + if (ret) { dev_err(nandc->dev, "failure to write page\n"); + return ret; + } - free_descs(nandc); - - if (!ret) - ret = nand_prog_page_end_op(chip); - - return ret; + return nand_prog_page_end_op(chip); } /* implements ecc->write_page_raw() */ static int qcom_nandc_write_page_raw(struct nand_chip *chip, - const uint8_t *buf, int oob_required, + const u8 *buf, int oob_required, int page) { struct mtd_info *mtd = nand_to_mtd(chip); @@ -2352,15 +2092,12 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip, } ret = submit_descs(nandc); - if (ret) + if (ret) { dev_err(nandc->dev, "failure to write raw page\n"); + return ret; + } - free_descs(nandc); - - if (!ret) - ret = nand_prog_page_end_op(chip); - - return ret; + return nand_prog_page_end_op(chip); } /* @@ -2404,12 +2141,9 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page) config_nand_cw_write(chip); ret = submit_descs(nandc); - - free_descs(nandc); - if (ret) { dev_err(nandc->dev, "failure to write oob\n"); - return -EIO; + return ret; } return nand_prog_page_end_op(chip); @@ -2483,73 +2217,12 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs) config_nand_cw_write(chip); ret = submit_descs(nandc); - - free_descs(nandc); - if (ret) { dev_err(nandc->dev, "failure to update BBM\n"); - return -EIO; - } - - return nand_prog_page_end_op(chip); -} - -/* - * the three functions below implement chip->legacy.read_byte(), - * chip->legacy.read_buf() and chip->legacy.write_buf() respectively. these - * aren't used for reading/writing page data, they are used for smaller data - * like reading id, status etc - */ -static uint8_t qcom_nandc_read_byte(struct nand_chip *chip) -{ - struct qcom_nand_host *host = to_qcom_nand_host(chip); - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - u8 *buf = nandc->data_buffer; - u8 ret = 0x0; - - if (host->last_command == NAND_CMD_STATUS) { - ret = host->status; - - host->status = NAND_STATUS_READY | NAND_STATUS_WP; - return ret; } - if (nandc->buf_start < nandc->buf_count) - ret = buf[nandc->buf_start++]; - - return ret; -} - -static void qcom_nandc_read_buf(struct nand_chip *chip, uint8_t *buf, int len) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); - - memcpy(buf, nandc->data_buffer + nandc->buf_start, real_len); - nandc->buf_start += real_len; -} - -static void qcom_nandc_write_buf(struct nand_chip *chip, const uint8_t *buf, - int len) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - int real_len = min_t(size_t, len, nandc->buf_count - nandc->buf_start); - - memcpy(nandc->data_buffer + nandc->buf_start, buf, real_len); - - nandc->buf_start += real_len; -} - -/* we support only one external chip for now */ -static void qcom_nandc_select_chip(struct nand_chip *chip, int chipnr) -{ - struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - - if (chipnr <= 0) - return; - - dev_warn(nandc->dev, "invalid chip select\n"); + return nand_prog_page_end_op(chip); } /* @@ -2660,7 +2333,7 @@ static int qcom_nand_ooblayout_ecc(struct mtd_info *mtd, int section, } static int qcom_nand_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) + struct mtd_oob_region *oobregion) { struct nand_chip *chip = mtd_to_nand(mtd); struct qcom_nand_host *host = to_qcom_nand_host(chip); @@ -2685,6 +2358,7 @@ qcom_nandc_calc_ecc_bytes(int step_size, int strength) { return strength == 4 ? 12 : 16; } + NAND_ECC_CAPS_SINGLE(qcom_nandc_ecc_caps, qcom_nandc_calc_ecc_bytes, NANDC_STEP_SIZE, 4, 8); @@ -2867,8 +2541,479 @@ static int qcom_nand_attach_chip(struct nand_chip *chip) return 0; } +static int qcom_op_cmd_mapping(struct nand_chip *chip, u8 opcode, + struct qcom_op *q_op) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + int cmd; + + switch (opcode) { + case NAND_CMD_RESET: + cmd = OP_RESET_DEVICE; + break; + case NAND_CMD_READID: + cmd = OP_FETCH_ID; + break; + case NAND_CMD_PARAM: + if (nandc->props->qpic_v2) + cmd = OP_PAGE_READ_ONFI_READ; + else + cmd = OP_PAGE_READ; + break; + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + cmd = OP_BLOCK_ERASE; + break; + case NAND_CMD_STATUS: + cmd = OP_CHECK_STATUS; + break; + case NAND_CMD_PAGEPROG: + cmd = OP_PROGRAM_PAGE; + q_op->flag = OP_PROGRAM_PAGE; + nandc->exec_opwrite = true; + break; + case NAND_CMD_READ0: + case NAND_CMD_READSTART: + if (host->use_ecc) + cmd = OP_PAGE_READ_WITH_ECC; + else + cmd = OP_PAGE_READ; + break; + default: + dev_err(nandc->dev, "Opcode not supported: %u\n", opcode); + return -EOPNOTSUPP; + } + + return cmd; +} + +/* NAND framework ->exec_op() hooks and related helpers */ +static int qcom_parse_instructions(struct nand_chip *chip, + const struct nand_subop *subop, + struct qcom_op *q_op) +{ + const struct nand_op_instr *instr = NULL; + unsigned int op_id; + int i, ret; + + for (op_id = 0; op_id < subop->ninstrs; op_id++) { + unsigned int offset, naddrs; + const u8 *addrs; + + instr = &subop->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + ret = qcom_op_cmd_mapping(chip, instr->ctx.cmd.opcode, q_op); + if (ret < 0) + return ret; + + q_op->cmd_reg = ret; + q_op->rdy_delay_ns = instr->delay_ns; + break; + + case NAND_OP_ADDR_INSTR: + offset = nand_subop_get_addr_start_off(subop, op_id); + naddrs = nand_subop_get_num_addr_cyc(subop, op_id); + addrs = &instr->ctx.addr.addrs[offset]; + + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++) + q_op->addr1_reg |= addrs[i] << (i * 8); + + if (naddrs > 4) + q_op->addr2_reg |= addrs[4]; + + q_op->rdy_delay_ns = instr->delay_ns; + break; + + case NAND_OP_DATA_IN_INSTR: + q_op->data_instr = instr; + q_op->data_instr_idx = op_id; + q_op->rdy_delay_ns = instr->delay_ns; + fallthrough; + case NAND_OP_DATA_OUT_INSTR: + q_op->rdy_delay_ns = instr->delay_ns; + break; + + case NAND_OP_WAITRDY_INSTR: + q_op->rdy_timeout_ms = instr->ctx.waitrdy.timeout_ms; + q_op->rdy_delay_ns = instr->delay_ns; + break; + } + } + + return 0; +} + +static void qcom_delay_ns(unsigned int ns) +{ + if (!ns) + return; + + if (ns < 10000) + ndelay(ns); + else + udelay(DIV_ROUND_UP(ns, 1000)); +} + +static int qcom_wait_rdy_poll(struct nand_chip *chip, unsigned int time_ms) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + unsigned long start = jiffies + msecs_to_jiffies(time_ms); + u32 flash; + + nandc_read_buffer_sync(nandc, true); + + do { + flash = le32_to_cpu(nandc->reg_read_buf[0]); + if (flash & FS_READY_BSY_N) + return 0; + cpu_relax(); + } while (time_after(start, jiffies)); + + dev_err(nandc->dev, "Timeout waiting for device to be ready:0x%08x\n", flash); + + return -ETIMEDOUT; +} + +static int qcom_read_status_exec(struct nand_chip *chip, + const struct nand_subop *subop) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct qcom_op q_op = {}; + const struct nand_op_instr *instr = NULL; + unsigned int op_id = 0; + unsigned int len = 0; + int ret, num_cw, i; + u32 flash_status; + + host->status = NAND_STATUS_READY | NAND_STATUS_WP; + + ret = qcom_parse_instructions(chip, subop, &q_op); + if (ret) + return ret; + + num_cw = nandc->exec_opwrite ? ecc->steps : 1; + nandc->exec_opwrite = false; + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting status descriptor\n"); + goto err_out; + } + + nandc_read_buffer_sync(nandc, true); + + for (i = 0; i < num_cw; i++) { + flash_status = le32_to_cpu(nandc->reg_read_buf[i]); + + if (flash_status & FS_MPU_ERR) + host->status &= ~NAND_STATUS_WP; + + if (flash_status & FS_OP_ERR || + (i == (num_cw - 1) && (flash_status & FS_DEVICE_STS_ERR))) + host->status |= NAND_STATUS_FAIL; + } + + flash_status = host->status; + instr = q_op.data_instr; + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + memcpy(instr->ctx.data.buf.in, &flash_status, len); + +err_out: + return ret; +} + +static int qcom_read_id_type_exec(struct nand_chip *chip, const struct nand_subop *subop) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_op q_op = {}; + const struct nand_op_instr *instr = NULL; + unsigned int op_id = 0; + unsigned int len = 0; + int ret; + + ret = qcom_parse_instructions(chip, subop, &q_op); + if (ret) + return ret; + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); + nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); + nandc_set_reg(chip, NAND_FLASH_CHIP_SELECT, + nandc->props->is_bam ? 0 : DM_EN); + + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, 4, NAND_BAM_NEXT_SGL); + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + + read_reg_dma(nandc, NAND_READ_ID, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting read id descriptor\n"); + goto err_out; + } + + instr = q_op.data_instr; + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + + nandc_read_buffer_sync(nandc, true); + memcpy(instr->ctx.data.buf.in, nandc->reg_read_buf, len); + +err_out: + return ret; +} + +static int qcom_misc_cmd_type_exec(struct nand_chip *chip, const struct nand_subop *subop) +{ + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_op q_op = {}; + int ret; + int instrs = 1; + + ret = qcom_parse_instructions(chip, subop, &q_op); + if (ret) + return ret; + + if (q_op.flag == OP_PROGRAM_PAGE) { + goto wait_rdy; + } else if (q_op.cmd_reg == OP_BLOCK_ERASE) { + q_op.cmd_reg |= PAGE_ACC | LAST_PAGE; + nandc_set_reg(chip, NAND_ADDR0, q_op.addr1_reg); + nandc_set_reg(chip, NAND_ADDR1, q_op.addr2_reg); + nandc_set_reg(chip, NAND_DEV0_CFG0, + host->cfg0_raw & ~(7 << CW_PER_PAGE)); + nandc_set_reg(chip, NAND_DEV0_CFG1, host->cfg1_raw); + instrs = 3; + } else { + return 0; + } + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + write_reg_dma(nandc, NAND_FLASH_CMD, instrs, NAND_BAM_NEXT_SGL); + (q_op.cmd_reg == OP_BLOCK_ERASE) ? write_reg_dma(nandc, NAND_DEV0_CFG0, + 2, NAND_BAM_NEXT_SGL) : read_reg_dma(nandc, + NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + write_reg_dma(nandc, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); + read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL); + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting misc descriptor\n"); + goto err_out; + } + +wait_rdy: + qcom_delay_ns(q_op.rdy_delay_ns); + ret = qcom_wait_rdy_poll(chip, q_op.rdy_timeout_ms); + +err_out: + return ret; +} + +static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_subop *subop) +{ + struct qcom_nand_host *host = to_qcom_nand_host(chip); + struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); + struct qcom_op q_op = {}; + const struct nand_op_instr *instr = NULL; + unsigned int op_id = 0; + unsigned int len = 0; + int ret; + + ret = qcom_parse_instructions(chip, subop, &q_op); + if (ret) + return ret; + + q_op.cmd_reg |= PAGE_ACC | LAST_PAGE; + + nandc->buf_count = 0; + nandc->buf_start = 0; + host->use_ecc = false; + clear_read_regs(nandc); + clear_bam_transaction(nandc); + + nandc_set_reg(chip, NAND_FLASH_CMD, q_op.cmd_reg); + + nandc_set_reg(chip, NAND_ADDR0, 0); + nandc_set_reg(chip, NAND_ADDR1, 0); + nandc_set_reg(chip, NAND_DEV0_CFG0, 0 << CW_PER_PAGE + | 512 << UD_SIZE_BYTES + | 5 << NUM_ADDR_CYCLES + | 0 << SPARE_SIZE_BYTES); + nandc_set_reg(chip, NAND_DEV0_CFG1, 7 << NAND_RECOVERY_CYCLES + | 0 << CS_ACTIVE_BSY + | 17 << BAD_BLOCK_BYTE_NUM + | 1 << BAD_BLOCK_IN_SPARE_AREA + | 2 << WR_RD_BSY_GAP + | 0 << WIDE_FLASH + | 1 << DEV0_CFG1_ECC_DISABLE); + if (!nandc->props->qpic_v2) + nandc_set_reg(chip, NAND_EBI2_ECC_BUF_CFG, 1 << ECC_CFG_ECC_DISABLE); + + /* configure CMD1 and VLD for ONFI param probing in QPIC v1 */ + if (!nandc->props->qpic_v2) { + nandc_set_reg(chip, NAND_DEV_CMD_VLD, + (nandc->vld & ~READ_START_VLD)); + nandc_set_reg(chip, NAND_DEV_CMD1, + (nandc->cmd1 & ~(0xFF << READ_ADDR)) + | NAND_CMD_PARAM << READ_ADDR); + } + + nandc_set_reg(chip, NAND_EXEC_CMD, 1); + + if (!nandc->props->qpic_v2) { + nandc_set_reg(chip, NAND_DEV_CMD1_RESTORE, nandc->cmd1); + nandc_set_reg(chip, NAND_DEV_CMD_VLD_RESTORE, nandc->vld); + } + + instr = q_op.data_instr; + op_id = q_op.data_instr_idx; + len = nand_subop_get_data_len(subop, op_id); + + nandc_set_read_loc(chip, 0, 0, 0, len, 1); + + if (!nandc->props->qpic_v2) { + write_reg_dma(nandc, NAND_DEV_CMD_VLD, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD1, 1, NAND_BAM_NEXT_SGL); + } + + nandc->buf_count = len; + memset(nandc->data_buffer, 0xff, nandc->buf_count); + + config_nand_single_cw_page_read(chip, false, 0); + + read_data_dma(nandc, FLASH_BUF_ACC, nandc->data_buffer, + nandc->buf_count, 0); + + /* restore CMD1 and VLD regs */ + if (!nandc->props->qpic_v2) { + write_reg_dma(nandc, NAND_DEV_CMD1_RESTORE, 1, 0); + write_reg_dma(nandc, NAND_DEV_CMD_VLD_RESTORE, 1, NAND_BAM_NEXT_SGL); + } + + ret = submit_descs(nandc); + if (ret) { + dev_err(nandc->dev, "failure in submitting param page descriptor\n"); + goto err_out; + } + + ret = qcom_wait_rdy_poll(chip, q_op.rdy_timeout_ms); + if (ret) + goto err_out; + + memcpy(instr->ctx.data.buf.in, nandc->data_buffer, len); + +err_out: + return ret; +} + +static const struct nand_op_parser qcom_op_parser = NAND_OP_PARSER( + NAND_OP_PARSER_PATTERN( + qcom_read_id_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYCLE), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 8)), + NAND_OP_PARSER_PATTERN( + qcom_read_status_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 1)), + NAND_OP_PARSER_PATTERN( + qcom_param_page_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(false, MAX_ADDRESS_CYCLE), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true), + NAND_OP_PARSER_PAT_DATA_IN_ELEM(false, 512)), + NAND_OP_PARSER_PATTERN( + qcom_misc_cmd_type_exec, + NAND_OP_PARSER_PAT_CMD_ELEM(false), + NAND_OP_PARSER_PAT_ADDR_ELEM(true, MAX_ADDRESS_CYCLE), + NAND_OP_PARSER_PAT_CMD_ELEM(true), + NAND_OP_PARSER_PAT_WAITRDY_ELEM(false)), + ); + +static int qcom_check_op(struct nand_chip *chip, + const struct nand_operation *op) +{ + const struct nand_op_instr *instr; + int op_id; + + for (op_id = 0; op_id < op->ninstrs; op_id++) { + instr = &op->instrs[op_id]; + + switch (instr->type) { + case NAND_OP_CMD_INSTR: + if (instr->ctx.cmd.opcode != NAND_CMD_RESET && + instr->ctx.cmd.opcode != NAND_CMD_READID && + instr->ctx.cmd.opcode != NAND_CMD_PARAM && + instr->ctx.cmd.opcode != NAND_CMD_ERASE1 && + instr->ctx.cmd.opcode != NAND_CMD_ERASE2 && + instr->ctx.cmd.opcode != NAND_CMD_STATUS && + instr->ctx.cmd.opcode != NAND_CMD_PAGEPROG && + instr->ctx.cmd.opcode != NAND_CMD_READ0 && + instr->ctx.cmd.opcode != NAND_CMD_READSTART) + return -EOPNOTSUPP; + break; + default: + break; + } + } + + return 0; +} + +static int qcom_nand_exec_op(struct nand_chip *chip, + const struct nand_operation *op, bool check_only) +{ + if (check_only) + return qcom_check_op(chip, op); + + return nand_op_parser_exec_op(chip, &qcom_op_parser, op, check_only); +} + static const struct nand_controller_ops qcom_nandc_ops = { .attach_chip = qcom_nand_attach_chip, + .exec_op = qcom_nand_exec_op, }; static void qcom_nandc_unalloc(struct qcom_nand_controller *nandc) @@ -2912,19 +3057,17 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) */ nandc->buf_size = 532; - nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, - GFP_KERNEL); + nandc->data_buffer = devm_kzalloc(nandc->dev, nandc->buf_size, GFP_KERNEL); if (!nandc->data_buffer) return -ENOMEM; - nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), - GFP_KERNEL); + nandc->regs = devm_kzalloc(nandc->dev, sizeof(*nandc->regs), GFP_KERNEL); if (!nandc->regs) return -ENOMEM; - nandc->reg_read_buf = devm_kcalloc(nandc->dev, - MAX_REG_RD, sizeof(*nandc->reg_read_buf), - GFP_KERNEL); + nandc->reg_read_buf = devm_kcalloc(nandc->dev, MAX_REG_RD, + sizeof(*nandc->reg_read_buf), + GFP_KERNEL); if (!nandc->reg_read_buf) return -ENOMEM; @@ -2969,7 +3112,7 @@ static int qcom_nandc_alloc(struct qcom_nand_controller *nandc) /* * Initially allocate BAM transaction to read ONFI param page. * After detecting all the devices, this BAM transaction will - * be freed and the next BAM tranasction will be allocated with + * be freed and the next BAM transaction will be allocated with * maximum codeword size */ nandc->max_cwperpage = 1; @@ -3135,14 +3278,6 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc, mtd->owner = THIS_MODULE; mtd->dev.parent = dev; - chip->legacy.cmdfunc = qcom_nandc_command; - chip->legacy.select_chip = qcom_nandc_select_chip; - chip->legacy.read_byte = qcom_nandc_read_byte; - chip->legacy.read_buf = qcom_nandc_read_buf; - chip->legacy.write_buf = qcom_nandc_write_buf; - chip->legacy.set_features = nand_get_set_features_notsupp; - chip->legacy.get_features = nand_get_set_features_notsupp; - /* * the bad block marker is readable only when we read the last codeword * of a page with ECC disabled. currently, the nand_base and nand_bbt diff --git a/drivers/mtd/nand/raw/rockchip-nand-controller.c b/drivers/mtd/nand/raw/rockchip-nand-controller.c index 5a04680342c327c403b353c8eabcf94f97b8cc40..5bc90ffa721f0d7c685da2d769e3afd3f62a4d38 100644 --- a/drivers/mtd/nand/raw/rockchip-nand-controller.c +++ b/drivers/mtd/nand/raw/rockchip-nand-controller.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/s3c2410.c b/drivers/mtd/nand/raw/s3c2410.c index ac80aaf5b4e304f5faed7cabeff7099324482cf1..3d3d5c9814ffc6cea96f4bc29bfdd752780d59b5 100644 --- a/drivers/mtd/nand/raw/s3c2410.c +++ b/drivers/mtd/nand/raw/s3c2410.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/raw/sh_flctl.c b/drivers/mtd/nand/raw/sh_flctl.c index 63bf20c4171972ce2f0515661d7de51ec8ee1835..3e5df75cbc98237548fdfc411c32569a985cf07b 100644 --- a/drivers/mtd/nand/raw/sh_flctl.c +++ b/drivers/mtd/nand/raw/sh_flctl.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -1124,8 +1123,7 @@ static int flctl_probe(struct platform_device *pdev) if (!flctl) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - flctl->reg = devm_ioremap_resource(&pdev->dev, res); + flctl->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(flctl->reg)) return PTR_ERR(flctl->reg); flctl->fifo = res->start + 0x24; /* FLDTFIFO */ diff --git a/drivers/mtd/nand/raw/socrates_nand.c b/drivers/mtd/nand/raw/socrates_nand.c index a8b720ffe9e843b30cd15bcaa9b6a1c72d684173..76d50eb9f1db50c6e7ac0fa682ea84fe2830b761 100644 --- a/drivers/mtd/nand/raw/socrates_nand.c +++ b/drivers/mtd/nand/raw/socrates_nand.c @@ -8,8 +8,9 @@ #include #include #include +#include #include -#include +#include #include #define FPGA_NAND_CMD_MASK (0x7 << 28) diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c index 10c11cecac0803a6ce87793a9598b3c9c0d9f875..88811139aaf5b9966a790c403d3fb8519350a74f 100644 --- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c @@ -1922,8 +1922,8 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) if (!(nfc->cs_assigned & BIT(chip_cs))) continue; - res = platform_get_resource(pdev, IORESOURCE_MEM, mem_region); - nfc->data_base[chip_cs] = devm_ioremap_resource(dev, res); + nfc->data_base[chip_cs] = devm_platform_get_and_ioremap_resource(pdev, + mem_region, &res); if (IS_ERR(nfc->data_base[chip_cs])) return PTR_ERR(nfc->data_base[chip_cs]); @@ -1951,21 +1951,17 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) init_completion(&nfc->complete); - nfc->clk = devm_clk_get(nfc->cdev, NULL); - if (IS_ERR(nfc->clk)) + nfc->clk = devm_clk_get_enabled(nfc->cdev, NULL); + if (IS_ERR(nfc->clk)) { + dev_err(dev, "can not get and enable the clock\n"); return PTR_ERR(nfc->clk); - - ret = clk_prepare_enable(nfc->clk); - if (ret) { - dev_err(dev, "can not enable the clock\n"); - return ret; } rstc = devm_reset_control_get(dev, NULL); if (IS_ERR(rstc)) { ret = PTR_ERR(rstc); if (ret == -EPROBE_DEFER) - goto err_clk_disable; + return ret; } else { reset_control_assert(rstc); reset_control_deassert(rstc); @@ -2018,9 +2014,6 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev) sg_free_table(&nfc->dma_data_sg); sg_free_table(&nfc->dma_ecc_sg); -err_clk_disable: - clk_disable_unprepare(nfc->clk); - return ret; } @@ -2045,8 +2038,6 @@ static void stm32_fmc2_nfc_remove(struct platform_device *pdev) sg_free_table(&nfc->dma_data_sg); sg_free_table(&nfc->dma_ecc_sg); - clk_disable_unprepare(nfc->clk); - stm32_fmc2_nfc_wp_enable(nand); } diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 9884304634f68a4a242cec3a81114f58c9d98ebf..9abf38049d35f3a25e22a5cd82f440e88a2c9cdd 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -2087,8 +2086,7 @@ static int sunxi_nfc_probe(struct platform_device *pdev) nand_controller_init(&nfc->controller); INIT_LIST_HEAD(&nfc->chips); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - nfc->regs = devm_ioremap_resource(dev, r); + nfc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r); if (IS_ERR(nfc->regs)) return PTR_ERR(nfc->regs); @@ -2096,37 +2094,26 @@ static int sunxi_nfc_probe(struct platform_device *pdev) if (irq < 0) return irq; - nfc->ahb_clk = devm_clk_get(dev, "ahb"); + nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb"); if (IS_ERR(nfc->ahb_clk)) { dev_err(dev, "failed to retrieve ahb clk\n"); return PTR_ERR(nfc->ahb_clk); } - ret = clk_prepare_enable(nfc->ahb_clk); - if (ret) - return ret; - - nfc->mod_clk = devm_clk_get(dev, "mod"); + nfc->mod_clk = devm_clk_get_enabled(dev, "mod"); if (IS_ERR(nfc->mod_clk)) { dev_err(dev, "failed to retrieve mod clk\n"); - ret = PTR_ERR(nfc->mod_clk); - goto out_ahb_clk_unprepare; + return PTR_ERR(nfc->mod_clk); } - ret = clk_prepare_enable(nfc->mod_clk); - if (ret) - goto out_ahb_clk_unprepare; - nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb"); - if (IS_ERR(nfc->reset)) { - ret = PTR_ERR(nfc->reset); - goto out_mod_clk_unprepare; - } + if (IS_ERR(nfc->reset)) + return PTR_ERR(nfc->reset); ret = reset_control_deassert(nfc->reset); if (ret) { dev_err(dev, "reset err %d\n", ret); - goto out_mod_clk_unprepare; + return ret; } nfc->caps = of_device_get_match_data(&pdev->dev); @@ -2165,10 +2152,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev) dma_release_channel(nfc->dmac); out_ahb_reset_reassert: reset_control_assert(nfc->reset); -out_mod_clk_unprepare: - clk_disable_unprepare(nfc->mod_clk); -out_ahb_clk_unprepare: - clk_disable_unprepare(nfc->ahb_clk); return ret; } @@ -2183,8 +2166,6 @@ static void sunxi_nfc_remove(struct platform_device *pdev) if (nfc->dmac) dma_release_channel(nfc->dmac); - clk_disable_unprepare(nfc->mod_clk); - clk_disable_unprepare(nfc->ahb_clk); } static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = { diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c index 86522048e2714ada70704c208f3e97e6721723c4..3f783b8f76c968c249e866d14b0deff5788e721d 100644 --- a/drivers/mtd/nand/raw/vf610_nfc.c +++ b/drivers/mtd/nand/raw/vf610_nfc.c @@ -827,30 +827,24 @@ static int vf610_nfc_probe(struct platform_device *pdev) mtd->name = DRV_NAME; irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -EINVAL; + if (irq < 0) + return irq; nfc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(nfc->regs)) return PTR_ERR(nfc->regs); - nfc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(nfc->clk)) + nfc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(nfc->clk)) { + dev_err(nfc->dev, "Unable to get and enable clock!\n"); return PTR_ERR(nfc->clk); - - err = clk_prepare_enable(nfc->clk); - if (err) { - dev_err(nfc->dev, "Unable to enable clock!\n"); - return err; } of_id = of_match_device(vf610_nfc_dt_ids, &pdev->dev); - if (!of_id) { - err = -ENODEV; - goto err_disable_clk; - } + if (!of_id) + return -ENODEV; - nfc->variant = (enum vf610_nfc_variant)of_id->data; + nfc->variant = (uintptr_t)of_id->data; for_each_available_child_of_node(nfc->dev->of_node, child) { if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) { @@ -858,9 +852,8 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (nand_get_flash_node(chip)) { dev_err(nfc->dev, "Only one NAND chip supported!\n"); - err = -EINVAL; of_node_put(child); - goto err_disable_clk; + return -EINVAL; } nand_set_flash_node(chip, child); @@ -869,8 +862,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) if (!nand_get_flash_node(chip)) { dev_err(nfc->dev, "NAND chip sub-node missing!\n"); - err = -ENODEV; - goto err_disable_clk; + return -ENODEV; } chip->options |= NAND_NO_SUBPAGE_WRITE; @@ -880,7 +872,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, nfc); if (err) { dev_err(nfc->dev, "Error requesting IRQ!\n"); - goto err_disable_clk; + return err; } vf610_nfc_preinit_controller(nfc); @@ -892,7 +884,7 @@ static int vf610_nfc_probe(struct platform_device *pdev) /* Scan the NAND chip */ err = nand_scan(chip, 1); if (err) - goto err_disable_clk; + return err; platform_set_drvdata(pdev, nfc); @@ -904,8 +896,6 @@ static int vf610_nfc_probe(struct platform_device *pdev) err_cleanup_nand: nand_cleanup(chip); -err_disable_clk: - clk_disable_unprepare(nfc->clk); return err; } @@ -918,7 +908,6 @@ static void vf610_nfc_remove(struct platform_device *pdev) ret = mtd_device_unregister(nand_to_mtd(chip)); WARN_ON(ret); nand_cleanup(chip); - clk_disable_unprepare(nfc->clk); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mtd/nand/raw/xway_nand.c b/drivers/mtd/nand/raw/xway_nand.c index 6b1e2a2bba15c1ab71a881395aa4c2849a4669cd..51d802a165edf0fe5f51c4fbc57e0c8bb350cd50 100644 --- a/drivers/mtd/nand/raw/xway_nand.c +++ b/drivers/mtd/nand/raw/xway_nand.c @@ -7,7 +7,8 @@ #include #include -#include +#include +#include #include diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c index 1a3ffb982335809a451db1df84e260d7a5181e02..31c439a557b1840067a8eca69244362bf926b05f 100644 --- a/drivers/mtd/nand/spi/esmt.c +++ b/drivers/mtd/nand/spi/esmt.c @@ -121,6 +121,15 @@ static const struct spinand_info esmt_c8_spinand_table[] = { &update_cache_variants), 0, SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), + SPINAND_INFO("F50D2G41KA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51), + NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), }; static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index cfd7c3b26dc45a4cbd9f2f93b490225450258fba..987710e09441adefbf238948e759fec4d049b126 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -511,6 +511,26 @@ static const struct spinand_info gigadevice_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ5RExxH", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), + SPINAND_INFO("GD5F1GQ4RExxH", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xc9), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(4, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5, + &write_cache_variants, + &update_cache_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout, + gd5fxgq4uexxg_ecc_get_status)), }; static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index a80427c131216641bde9b2ae411c429716fb2a60..bbbcaa87c0bc78576ff9ca0b88deb4fa81703276 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -266,6 +266,39 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 1Gb (1st generation) */ + SPINAND_INFO("TC58NYG0S3HBAI4", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xA1), + NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_variants, + &update_cache_variants), + 0, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 4Gb (1st generation) */ + SPINAND_INFO("TH58NYG2S3HBAI4", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xAC), + NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 2, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), + /* 1.8V 8Gb (1st generation) */ + SPINAND_INFO("TH58NYG3S0HBAI6", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xA3), + NAND_MEMORG(1, 4096, 256, 64, 4096, 80, 1, 1, 1), + NAND_ECCREQ(8, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, + &write_cache_x4_variants, + &update_cache_x4_variants), + SPINAND_HAS_QE_BIT, + SPINAND_ECCINFO(&tx58cxgxsxraix_ooblayout, + tx58cxgxsxraix_ecc_get_status)), }; static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { diff --git a/drivers/mtd/spi-nor/atmel.c b/drivers/mtd/spi-nor/atmel.c index 656dd80a0be794e924933b144504550313c8437c..58968c1e7d2f8ee668641bae12945bb885e4bbcb 100644 --- a/drivers/mtd/spi-nor/atmel.c +++ b/drivers/mtd/spi-nor/atmel.c @@ -48,9 +48,11 @@ static const struct spi_nor_locking_ops at25fs_nor_locking_ops = { .is_locked = at25fs_nor_is_locked, }; -static void at25fs_nor_late_init(struct spi_nor *nor) +static int at25fs_nor_late_init(struct spi_nor *nor) { nor->params->locking_ops = &at25fs_nor_locking_ops; + + return 0; } static const struct spi_nor_fixups at25fs_nor_fixups = { @@ -149,9 +151,11 @@ static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = { .is_locked = atmel_nor_is_global_protected, }; -static void atmel_nor_global_protection_late_init(struct spi_nor *nor) +static int atmel_nor_global_protection_late_init(struct spi_nor *nor) { nor->params->locking_ops = &atmel_nor_global_protection_ops; + + return 0; } static const struct spi_nor_fixups atmel_nor_global_protection_fixups = { diff --git a/drivers/mtd/spi-nor/controllers/nxp-spifi.c b/drivers/mtd/spi-nor/controllers/nxp-spifi.c index 794c7b7d5c92b2b5ea0491fd92764d23c6e63a03..5d8f47ab146fd9b9388c2cda1454564cba52f920 100644 --- a/drivers/mtd/spi-nor/controllers/nxp-spifi.c +++ b/drivers/mtd/spi-nor/controllers/nxp-spifi.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -395,30 +394,18 @@ static int nxp_spifi_probe(struct platform_device *pdev) if (IS_ERR(spifi->flash_base)) return PTR_ERR(spifi->flash_base); - spifi->clk_spifi = devm_clk_get(&pdev->dev, "spifi"); + spifi->clk_spifi = devm_clk_get_enabled(&pdev->dev, "spifi"); if (IS_ERR(spifi->clk_spifi)) { - dev_err(&pdev->dev, "spifi clock not found\n"); + dev_err(&pdev->dev, "spifi clock not found or unable to enable\n"); return PTR_ERR(spifi->clk_spifi); } - spifi->clk_reg = devm_clk_get(&pdev->dev, "reg"); + spifi->clk_reg = devm_clk_get_enabled(&pdev->dev, "reg"); if (IS_ERR(spifi->clk_reg)) { - dev_err(&pdev->dev, "reg clock not found\n"); + dev_err(&pdev->dev, "reg clock not found or unable to enable\n"); return PTR_ERR(spifi->clk_reg); } - ret = clk_prepare_enable(spifi->clk_reg); - if (ret) { - dev_err(&pdev->dev, "unable to enable reg clock\n"); - return ret; - } - - ret = clk_prepare_enable(spifi->clk_spifi); - if (ret) { - dev_err(&pdev->dev, "unable to enable spifi clock\n"); - goto dis_clk_reg; - } - spifi->dev = &pdev->dev; platform_set_drvdata(pdev, spifi); @@ -431,24 +418,17 @@ static int nxp_spifi_probe(struct platform_device *pdev) flash_np = of_get_next_available_child(pdev->dev.of_node, NULL); if (!flash_np) { dev_err(&pdev->dev, "no SPI flash device to configure\n"); - ret = -ENODEV; - goto dis_clks; + return -ENODEV; } ret = nxp_spifi_setup_flash(spifi, flash_np); of_node_put(flash_np); if (ret) { dev_err(&pdev->dev, "unable to setup flash chip\n"); - goto dis_clks; + return ret; } return 0; - -dis_clks: - clk_disable_unprepare(spifi->clk_spifi); -dis_clk_reg: - clk_disable_unprepare(spifi->clk_reg); - return ret; } static int nxp_spifi_remove(struct platform_device *pdev) @@ -456,8 +436,6 @@ static int nxp_spifi_remove(struct platform_device *pdev) struct nxp_spifi *spifi = platform_get_drvdata(pdev); mtd_device_unregister(&spifi->nor.mtd); - clk_disable_unprepare(spifi->clk_spifi); - clk_disable_unprepare(spifi->clk_reg); return 0; } diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 5f29fac8669a301e8010566eee02edfdcdb66620..1b0c6770c14e46419d9362bc7111128873e71780 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -870,21 +870,22 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1) ret = spi_nor_read_cr(nor, &sr_cr[1]); if (ret) return ret; - } else if (nor->params->quad_enable) { + } else if (spi_nor_get_protocol_width(nor->read_proto) == 4 && + spi_nor_get_protocol_width(nor->write_proto) == 4 && + nor->params->quad_enable) { /* * If the Status Register 2 Read command (35h) is not * supported, we should at least be sure we don't * change the value of the SR2 Quad Enable bit. * - * We can safely assume that when the Quad Enable method is - * set, the value of the QE bit is one, as a consequence of the - * nor->params->quad_enable() call. + * When the Quad Enable method is set and the buswidth is 4, we + * can safely assume that the value of the QE bit is one, as a + * consequence of the nor->params->quad_enable() call. * - * We can safely assume that the Quad Enable bit is present in - * the Status Register 2 at BIT(1). According to the JESD216 - * revB standard, BFPT DWORDS[15], bits 22:20, the 16-bit - * Write Status (01h) command is available just for the cases - * in which the QE bit is described in SR2 at BIT(1). + * According to the JESD216 revB standard, BFPT DWORDS[15], + * bits 22:20, the 16-bit Write Status (01h) command is + * available just for the cases in which the QE bit is + * described in SR2 at BIT(1). */ sr_cr[1] = SR2_QUAD_EN_BIT1; } else { @@ -2844,6 +2845,9 @@ static void spi_nor_init_flags(struct spi_nor *nor) if (of_property_read_bool(np, "broken-flash-reset")) nor->flags |= SNOR_F_BROKEN_RESET; + if (of_property_read_bool(np, "no-wp")) + nor->flags |= SNOR_F_NO_WP; + if (flags & SPI_NOR_SWP_IS_VOLATILE) nor->flags |= SNOR_F_SWP_IS_VOLATILE; @@ -2897,16 +2901,23 @@ static void spi_nor_init_fixup_flags(struct spi_nor *nor) * SFDP standard, or where SFDP tables are not defined at all. * Will replace the spi_nor_manufacturer_init_params() method. */ -static void spi_nor_late_init_params(struct spi_nor *nor) +static int spi_nor_late_init_params(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; + int ret; if (nor->manufacturer && nor->manufacturer->fixups && - nor->manufacturer->fixups->late_init) - nor->manufacturer->fixups->late_init(nor); + nor->manufacturer->fixups->late_init) { + ret = nor->manufacturer->fixups->late_init(nor); + if (ret) + return ret; + } - if (nor->info->fixups && nor->info->fixups->late_init) - nor->info->fixups->late_init(nor); + if (nor->info->fixups && nor->info->fixups->late_init) { + ret = nor->info->fixups->late_init(nor); + if (ret) + return ret; + } /* Default method kept for backward compatibility. */ if (!params->set_4byte_addr_mode) @@ -2924,6 +2935,8 @@ static void spi_nor_late_init_params(struct spi_nor *nor) if (nor->info->n_banks > 1) params->bank_size = div64_u64(params->size, nor->info->n_banks); + + return 0; } /** @@ -3082,22 +3095,20 @@ static int spi_nor_init_params(struct spi_nor *nor) spi_nor_init_params_deprecated(nor); } - spi_nor_late_init_params(nor); - - return 0; + return spi_nor_late_init_params(nor); } -/** spi_nor_octal_dtr_enable() - enable Octal DTR I/O if needed +/** spi_nor_set_octal_dtr() - enable or disable Octal DTR I/O. * @nor: pointer to a 'struct spi_nor' * @enable: whether to enable or disable Octal DTR * * Return: 0 on success, -errno otherwise. */ -static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +static int spi_nor_set_octal_dtr(struct spi_nor *nor, bool enable) { int ret; - if (!nor->params->octal_dtr_enable) + if (!nor->params->set_octal_dtr) return 0; if (!(nor->read_proto == SNOR_PROTO_8_8_8_DTR && @@ -3107,7 +3118,7 @@ static int spi_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) if (!(nor->flags & SNOR_F_IO_MODE_EN_VOLATILE)) return 0; - ret = nor->params->octal_dtr_enable(nor, enable); + ret = nor->params->set_octal_dtr(nor, enable); if (ret) return ret; @@ -3168,7 +3179,7 @@ static int spi_nor_init(struct spi_nor *nor) { int err; - err = spi_nor_octal_dtr_enable(nor, true); + err = spi_nor_set_octal_dtr(nor, true); if (err) { dev_dbg(nor->dev, "octal mode not supported\n"); return err; @@ -3270,7 +3281,7 @@ static int spi_nor_suspend(struct mtd_info *mtd) int ret; /* Disable octal DTR mode if we enabled it. */ - ret = spi_nor_octal_dtr_enable(nor, false); + ret = spi_nor_set_octal_dtr(nor, false); if (ret) dev_err(nor->dev, "suspend() failed\n"); diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 4fb5ff09c63a94d4afa4f928fb306837d7220d28..9217379b9cfefd2c8b8e4637c85a262b87238f0f 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -132,6 +132,7 @@ enum spi_nor_option_flags { SNOR_F_SWP_IS_VOLATILE = BIT(13), SNOR_F_RWW = BIT(14), SNOR_F_ECC = BIT(15), + SNOR_F_NO_WP = BIT(16), }; struct spi_nor_read_command { @@ -363,7 +364,7 @@ struct spi_nor_otp { * @erase_map: the erase map parsed from the SFDP Sector Map Parameter * Table. * @otp: SPI NOR OTP info. - * @octal_dtr_enable: enables SPI NOR octal DTR mode. + * @set_octal_dtr: enables or disables SPI NOR octal DTR mode. * @quad_enable: enables SPI NOR quad mode. * @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode. * @convert_addr: converts an absolute address into something the flash @@ -377,6 +378,7 @@ struct spi_nor_otp { * than reading the status register to indicate they * are ready for a new command * @locking_ops: SPI NOR locking methods. + * @priv: flash's private data. */ struct spi_nor_flash_parameter { u64 bank_size; @@ -397,7 +399,7 @@ struct spi_nor_flash_parameter { struct spi_nor_erase_map erase_map; struct spi_nor_otp otp; - int (*octal_dtr_enable)(struct spi_nor *nor, bool enable); + int (*set_octal_dtr)(struct spi_nor *nor, bool enable); int (*quad_enable)(struct spi_nor *nor); int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable); u32 (*convert_addr)(struct spi_nor *nor, u32 addr); @@ -405,6 +407,7 @@ struct spi_nor_flash_parameter { int (*ready)(struct spi_nor *nor); const struct spi_nor_locking_ops *locking_ops; + void *priv; }; /** @@ -431,7 +434,7 @@ struct spi_nor_fixups { const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt); int (*post_sfdp)(struct spi_nor *nor); - void (*late_init)(struct spi_nor *nor); + int (*late_init)(struct spi_nor *nor); }; /** diff --git a/drivers/mtd/spi-nor/debugfs.c b/drivers/mtd/spi-nor/debugfs.c index e11536fffe0f462285e756aaf2df0e3c73fcb7ce..6e163cb5b478c8e6f05fdbe608caa78f51356f38 100644 --- a/drivers/mtd/spi-nor/debugfs.c +++ b/drivers/mtd/spi-nor/debugfs.c @@ -27,6 +27,7 @@ static const char *const snor_f_names[] = { SNOR_F_NAME(SWP_IS_VOLATILE), SNOR_F_NAME(RWW), SNOR_F_NAME(ECC), + SNOR_F_NAME(NO_WP), }; #undef SNOR_F_NAME diff --git a/drivers/mtd/spi-nor/issi.c b/drivers/mtd/spi-nor/issi.c index 400e2b42f45af2f2bdb2f83f5f8fb8c1bc96b5ac..accdf7aa2bfdea90dab2c1cb3a5b92cc564ddb90 100644 --- a/drivers/mtd/spi-nor/issi.c +++ b/drivers/mtd/spi-nor/issi.c @@ -29,7 +29,7 @@ static const struct spi_nor_fixups is25lp256_fixups = { .post_bfpt = is25lp256_post_bfpt_fixups, }; -static void pm25lv_nor_late_init(struct spi_nor *nor) +static int pm25lv_nor_late_init(struct spi_nor *nor) { struct spi_nor_erase_map *map = &nor->params->erase_map; int i; @@ -38,6 +38,8 @@ static void pm25lv_nor_late_init(struct spi_nor *nor) for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) if (map->erase_type[i].size == 4096) map->erase_type[i].opcode = SPINOR_OP_BE_4K_PMC; + + return 0; } static const struct spi_nor_fixups pm25lv_nor_fixups = { diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 04888258e89148bd07f6747b0209e5465f32c881..eb149e517c1fe7ac458db1f2f08a58d556fd1f8f 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -110,10 +110,12 @@ static void macronix_nor_default_init(struct spi_nor *nor) nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable; } -static void macronix_nor_late_init(struct spi_nor *nor) +static int macronix_nor_late_init(struct spi_nor *nor) { if (!nor->params->set_4byte_addr_mode) nor->params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_en4b_ex4b; + + return 0; } static const struct spi_nor_fixups macronix_nor_fixups = { diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c index 4b919756a205582ae500f72bddf1849a8145ea55..6ad080c52ab5d6734dd80d5a58409da5357e4164 100644 --- a/drivers/mtd/spi-nor/micron-st.c +++ b/drivers/mtd/spi-nor/micron-st.c @@ -120,7 +120,7 @@ static int micron_st_nor_octal_dtr_dis(struct spi_nor *nor) return 0; } -static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable) { return enable ? micron_st_nor_octal_dtr_en(nor) : micron_st_nor_octal_dtr_dis(nor); @@ -128,7 +128,7 @@ static int micron_st_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) static void mt35xu512aba_default_init(struct spi_nor *nor) { - nor->params->octal_dtr_enable = micron_st_nor_octal_dtr_enable; + nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr; } static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor) @@ -429,7 +429,7 @@ static void micron_st_nor_default_init(struct spi_nor *nor) nor->params->quad_enable = NULL; } -static void micron_st_nor_late_init(struct spi_nor *nor) +static int micron_st_nor_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; @@ -438,6 +438,8 @@ static void micron_st_nor_late_init(struct spi_nor *nor) if (!params->set_4byte_addr_mode) params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b; + + return 0; } static const struct spi_nor_fixups micron_st_nor_fixups = { diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c index 15f9a80c10b9bd58096d95214b921dee5c72f48f..709822fced867f82659b482c718b846261d44c02 100644 --- a/drivers/mtd/spi-nor/spansion.c +++ b/drivers/mtd/spi-nor/spansion.c @@ -4,14 +4,19 @@ * Copyright (C) 2014, Freescale Semiconductor, Inc. */ +#include +#include +#include #include #include "core.h" /* flash_info mfr_flag. Used to clear sticky prorietary SR bits. */ #define USE_CLSR BIT(0) +#define USE_CLPEF BIT(1) #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ +#define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_REG_CYPRESS_VREG 0x00800000 @@ -19,21 +24,16 @@ #define SPINOR_REG_CYPRESS_STR1V \ (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_STR1) #define SPINOR_REG_CYPRESS_CFR1 0x2 -#define SPINOR_REG_CYPRESS_CFR1V \ - (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR1) #define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */ #define SPINOR_REG_CYPRESS_CFR2 0x3 #define SPINOR_REG_CYPRESS_CFR2V \ (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR2) +#define SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK GENMASK(3, 0) #define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb #define SPINOR_REG_CYPRESS_CFR2_ADRBYT BIT(7) #define SPINOR_REG_CYPRESS_CFR3 0x4 -#define SPINOR_REG_CYPRESS_CFR3V \ - (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR3) #define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */ #define SPINOR_REG_CYPRESS_CFR5 0x6 -#define SPINOR_REG_CYPRESS_CFR5V \ - (SPINOR_REG_CYPRESS_VREG + SPINOR_REG_CYPRESS_CFR5) #define SPINOR_REG_CYPRESS_CFR5_BIT6 BIT(6) #define SPINOR_REG_CYPRESS_CFR5_DDR BIT(1) #define SPINOR_REG_CYPRESS_CFR5_OPI BIT(0) @@ -57,22 +57,32 @@ SPI_MEM_OP_DUMMY(ndummy, 0), \ SPI_MEM_OP_DATA_IN(1, buf, 0)) -#define SPANSION_CLSR_OP \ - SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CLSR, 0), \ +#define SPANSION_OP(opcode) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) +/** + * struct spansion_nor_params - Spansion private parameters. + * @clsr: Clear Status Register or Clear Program and Erase Failure Flag + * opcode. + */ +struct spansion_nor_params { + u8 clsr; +}; + /** * spansion_nor_clear_sr() - Clear the Status Register. * @nor: pointer to 'struct spi_nor'. */ static void spansion_nor_clear_sr(struct spi_nor *nor) { + const struct spansion_nor_params *priv_params = nor->params->priv; int ret; if (nor->spimem) { - struct spi_mem_op op = SPANSION_CLSR_OP; + struct spi_mem_op op = SPANSION_OP(priv_params->clsr); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); @@ -88,11 +98,17 @@ static void spansion_nor_clear_sr(struct spi_nor *nor) static int cypress_nor_sr_ready_and_clear_reg(struct spi_nor *nor, u64 addr) { + struct spi_nor_flash_parameter *params = nor->params; struct spi_mem_op op = - CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, addr, + CYPRESS_NOR_RD_ANY_REG_OP(params->addr_mode_nbytes, addr, 0, nor->bouncebuf); int ret; + if (nor->reg_proto == SNOR_PROTO_8_8_8_DTR) { + op.dummy.nbytes = params->rdsr_dummy; + op.data.nbytes = 2; + } + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); if (ret) return ret; @@ -141,18 +157,26 @@ static int cypress_nor_sr_ready_and_clear(struct spi_nor *nor) return 1; } -static int cypress_nor_octal_dtr_en(struct spi_nor *nor) +static int cypress_nor_set_memlat(struct spi_nor *nor, u64 addr) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; int ret; u8 addr_mode_nbytes = nor->params->addr_mode_nbytes; + op = (struct spi_mem_op) + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode_nbytes, addr, 0, buf); + + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); + if (ret) + return ret; + /* Use 24 dummy cycles for memory array reads. */ - *buf = SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24; + *buf &= ~SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK; + *buf |= FIELD_PREP(SPINOR_REG_CYPRESS_CFR2_MEMLAT_MASK, + SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24); op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR2V, 1, buf); + CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, addr, 1, buf); ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); if (ret) @@ -160,15 +184,41 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor) nor->read_dummy = 24; + return 0; +} + +static int cypress_nor_set_octal_dtr_bits(struct spi_nor *nor, u64 addr) +{ + struct spi_mem_op op; + u8 *buf = nor->bouncebuf; + /* Set the octal and DTR enable bits. */ buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_EN; op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR5V, 1, buf); + CYPRESS_NOR_WR_ANY_REG_OP(nor->params->addr_mode_nbytes, + addr, 1, buf); - ret = spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); - if (ret) - return ret; + return spi_nor_write_any_volatile_reg(nor, &op, nor->reg_proto); +} + +static int cypress_nor_octal_dtr_en(struct spi_nor *nor) +{ + const struct spi_nor_flash_parameter *params = nor->params; + u8 *buf = nor->bouncebuf; + u64 addr; + int i, ret; + + for (i = 0; i < params->n_dice; i++) { + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR2; + ret = cypress_nor_set_memlat(nor, addr); + if (ret) + return ret; + + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5; + ret = cypress_nor_set_octal_dtr_bits(nor, addr); + if (ret) + return ret; + } /* Read flash ID to make sure the switch was successful. */ ret = spi_nor_read_id(nor, nor->addr_nbytes, 3, buf, @@ -184,11 +234,10 @@ static int cypress_nor_octal_dtr_en(struct spi_nor *nor) return 0; } -static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) +static int cypress_nor_set_single_spi_bits(struct spi_nor *nor, u64 addr) { struct spi_mem_op op; u8 *buf = nor->bouncebuf; - int ret; /* * The register is 1-byte wide, but 1-byte transactions are not allowed @@ -198,11 +247,23 @@ static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) buf[0] = SPINOR_REG_CYPRESS_CFR5_OCT_DTR_DS; buf[1] = 0; op = (struct spi_mem_op) - CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, - SPINOR_REG_CYPRESS_CFR5V, 2, buf); - ret = spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); - if (ret) - return ret; + CYPRESS_NOR_WR_ANY_REG_OP(nor->addr_nbytes, addr, 2, buf); + return spi_nor_write_any_volatile_reg(nor, &op, SNOR_PROTO_8_8_8_DTR); +} + +static int cypress_nor_octal_dtr_dis(struct spi_nor *nor) +{ + const struct spi_nor_flash_parameter *params = nor->params; + u8 *buf = nor->bouncebuf; + u64 addr; + int i, ret; + + for (i = 0; i < params->n_dice; i++) { + addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR5; + ret = cypress_nor_set_single_spi_bits(nor, addr); + if (ret) + return ret; + } /* Read flash ID to make sure the switch was successful. */ ret = spi_nor_read_id(nor, 0, 0, buf, SNOR_PROTO_1_1_1); @@ -283,10 +344,6 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) u8 i; int ret; - if (!params->n_dice) - return cypress_nor_quad_enable_volatile_reg(nor, - SPINOR_REG_CYPRESS_CFR1V); - for (i = 0; i < params->n_dice; i++) { addr = params->vreg_offset[i] + SPINOR_REG_CYPRESS_CFR1; ret = cypress_nor_quad_enable_volatile_reg(nor, addr); @@ -408,28 +465,17 @@ static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor) return 0; } -static int cypress_nor_get_page_size_single_chip(struct spi_nor *nor) -{ - struct spi_mem_op op = - CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, - SPINOR_REG_CYPRESS_CFR3V, 0, - nor->bouncebuf); - int ret; - - ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); - if (ret) - return ret; - - if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR3_PGSZ) - nor->params->page_size = 512; - else - nor->params->page_size = 256; - - return 0; -} - - -static int cypress_nor_get_page_size_mcp(struct spi_nor *nor) +/** + * cypress_nor_get_page_size() - Get flash page size configuration. + * @nor: pointer to a 'struct spi_nor' + * + * The BFPT table advertises a 512B or 256B page size depending on part but the + * page size is actually configurable (with the default being 256B). Read from + * CFR3V[4] and set the correct size. + * + * Return: 0 on success, -errno otherwise. + */ +static int cypress_nor_get_page_size(struct spi_nor *nor) { struct spi_mem_op op = CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes, @@ -459,23 +505,6 @@ static int cypress_nor_get_page_size_mcp(struct spi_nor *nor) return 0; } -/** - * cypress_nor_get_page_size() - Get flash page size configuration. - * @nor: pointer to a 'struct spi_nor' - * - * The BFPT table advertises a 512B or 256B page size depending on part but the - * page size is actually configurable (with the default being 256B). Read from - * CFR3V[4] and set the correct size. - * - * Return: 0 on success, -errno otherwise. - */ -static int cypress_nor_get_page_size(struct spi_nor *nor) -{ - if (nor->params->n_dice) - return cypress_nor_get_page_size_mcp(nor); - return cypress_nor_get_page_size_single_chip(nor); -} - static void cypress_nor_ecc_init(struct spi_nor *nor) { /* @@ -512,25 +541,39 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, if (nor->bouncebuf[0]) return -ENODEV; - return cypress_nor_get_page_size(nor); + return 0; } static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; + /* + * S25FS256T does not define the SCCR map, but we would like to use the + * same code base for both single and multi chip package devices, thus + * set the vreg_offset and n_dice to be able to do so. + */ + params->vreg_offset = devm_kmalloc(nor->dev, sizeof(u32), GFP_KERNEL); + if (!params->vreg_offset) + return -ENOMEM; + + params->vreg_offset[0] = SPINOR_REG_CYPRESS_VREG; + params->n_dice = 1; + /* PP_1_1_4_4B is supported but missing in 4BAIT. */ params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4; spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_1_1_4], SPINOR_OP_PP_1_1_4_4B, SNOR_PROTO_1_1_4); - return 0; + return cypress_nor_get_page_size(nor); } -static void s25fs256t_late_init(struct spi_nor *nor) +static int s25fs256t_late_init(struct spi_nor *nor) { cypress_nor_ecc_init(nor); + + return 0; } static struct spi_nor_fixups s25fs256t_fixups = { @@ -558,10 +601,20 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor, static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor) { - struct spi_nor_erase_type *erase_type = - nor->params->erase_map.erase_type; + struct spi_nor_flash_parameter *params = nor->params; + struct spi_nor_erase_type *erase_type = params->erase_map.erase_type; unsigned int i; + if (!params->n_dice || !params->vreg_offset) { + dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n", + __func__); + return -EOPNOTSUPP; + } + + /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ + if (params->size == SZ_256M) + params->n_dice = 2; + /* * In some parts, 3byte erase opcodes are advertised by 4BAIT. * Convert them to 4byte erase opcodes. @@ -579,25 +632,19 @@ static int s25hx_t_post_sfdp_fixup(struct spi_nor *nor) } } - /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ - if (nor->params->size == SZ_256M) - nor->params->n_dice = 2; - return cypress_nor_get_page_size(nor); } -static void s25hx_t_late_init(struct spi_nor *nor) +static int s25hx_t_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; /* Fast Read 4B requires mode cycles */ params->reads[SNOR_CMD_READ_FAST].num_mode_clocks = 8; - + params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); - /* Replace ready() with multi die version */ - if (params->n_dice) - params->ready = cypress_nor_sr_ready_and_clear; + return 0; } static struct spi_nor_fixups s25hx_t_fixups = { @@ -607,7 +654,7 @@ static struct spi_nor_fixups s25hx_t_fixups = { }; /** - * cypress_nor_octal_dtr_enable() - Enable octal DTR on Cypress flashes. + * cypress_nor_set_octal_dtr() - Enable or disable octal DTR on Cypress flashes. * @nor: pointer to a 'struct spi_nor' * @enable: whether to enable or disable Octal DTR * @@ -616,7 +663,7 @@ static struct spi_nor_fixups s25hx_t_fixups = { * * Return: 0 on success, -errno otherwise. */ -static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) +static int cypress_nor_set_octal_dtr(struct spi_nor *nor, bool enable) { return enable ? cypress_nor_octal_dtr_en(nor) : cypress_nor_octal_dtr_dis(nor); @@ -624,22 +671,34 @@ static int cypress_nor_octal_dtr_enable(struct spi_nor *nor, bool enable) static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor) { + struct spi_nor_flash_parameter *params = nor->params; + + if (!params->n_dice || !params->vreg_offset) { + dev_err(nor->dev, "%s failed. The volatile register offset could not be retrieved from SFDP.\n", + __func__); + return -EOPNOTSUPP; + } + + /* The 2 Gb parts duplicate info and advertise 4 dice instead of 2. */ + if (params->size == SZ_256M) + params->n_dice = 2; + /* * On older versions of the flash the xSPI Profile 1.0 table has the * 8D-8D-8D Fast Read opcode as 0x00. But it actually should be 0xEE. */ - if (nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) - nor->params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = + if (params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode == 0) + params->reads[SNOR_CMD_READ_8_8_8_DTR].opcode = SPINOR_OP_CYPRESS_RD_FAST; /* This flash is also missing the 4-byte Page Program opcode bit. */ - spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP], + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP], SPINOR_OP_PP_4B, SNOR_PROTO_1_1_1); /* * Since xSPI Page Program opcode is backward compatible with * Legacy SPI, use Legacy SPI opcode there as well. */ - spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR], + spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP_8_8_8_DTR], SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR); /* @@ -647,7 +706,7 @@ static int s28hx_t_post_sfdp_fixup(struct spi_nor *nor) * address bytes needed for Read Status Register command as 0 but the * actual value for that is 4. */ - nor->params->rdsr_addr_nbytes = 4; + params->rdsr_addr_nbytes = 4; return cypress_nor_get_page_size(nor); } @@ -656,19 +715,18 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - int ret; - - ret = cypress_nor_set_addr_mode_nbytes(nor); - if (ret) - return ret; - - return 0; + return cypress_nor_set_addr_mode_nbytes(nor); } -static void s28hx_t_late_init(struct spi_nor *nor) +static int s28hx_t_late_init(struct spi_nor *nor) { - nor->params->octal_dtr_enable = cypress_nor_octal_dtr_enable; + struct spi_nor_flash_parameter *params = nor->params; + + params->set_octal_dtr = cypress_nor_set_octal_dtr; + params->ready = cypress_nor_sr_ready_and_clear; cypress_nor_ecc_init(nor); + + return 0; } static const struct spi_nor_fixups s28hx_t_fixups = { @@ -792,47 +850,59 @@ static const struct flash_info spansion_nor_parts[] = { FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, { "s25fs256t", INFO6(0x342b19, 0x0f0890, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) .fixups = &s25fs256t_fixups }, - { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 256 * 1024, 256) + { "s25hl512t", INFO6(0x342a1a, 0x0f0390, 0, 0) PARSE_SFDP - MFR_FLAGS(USE_CLSR) + MFR_FLAGS(USE_CLPEF) .fixups = &s25hx_t_fixups }, - { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 256 * 1024, 512) + { "s25hl01gt", INFO6(0x342a1b, 0x0f0390, 0, 0) PARSE_SFDP - MFR_FLAGS(USE_CLSR) + MFR_FLAGS(USE_CLPEF) .fixups = &s25hx_t_fixups }, { "s25hl02gt", INFO6(0x342a1c, 0x0f0090, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) FLAGS(NO_CHIP_ERASE) .fixups = &s25hx_t_fixups }, - { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 256 * 1024, 256) + { "s25hs512t", INFO6(0x342b1a, 0x0f0390, 0, 0) PARSE_SFDP - MFR_FLAGS(USE_CLSR) + MFR_FLAGS(USE_CLPEF) .fixups = &s25hx_t_fixups }, - { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 256 * 1024, 512) + { "s25hs01gt", INFO6(0x342b1b, 0x0f0390, 0, 0) PARSE_SFDP - MFR_FLAGS(USE_CLSR) + MFR_FLAGS(USE_CLPEF) .fixups = &s25hx_t_fixups }, { "s25hs02gt", INFO6(0x342b1c, 0x0f0090, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) FLAGS(NO_CHIP_ERASE) .fixups = &s25hx_t_fixups }, { "cy15x104q", INFO6(0x042cc2, 0x7f7f7f, 512 * 1024, 1) FLAGS(SPI_NOR_NO_ERASE) }, - { "s28hl512t", INFO(0x345a1a, 0, 256 * 1024, 256) + { "s28hl512t", INFO(0x345a1a, 0, 0, 0) + PARSE_SFDP + MFR_FLAGS(USE_CLPEF) + .fixups = &s28hx_t_fixups, + }, + { "s28hl01gt", INFO(0x345a1b, 0, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) .fixups = &s28hx_t_fixups, }, - { "s28hl01gt", INFO(0x345a1b, 0, 256 * 1024, 512) + { "s28hs512t", INFO(0x345b1a, 0, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) .fixups = &s28hx_t_fixups, }, - { "s28hs512t", INFO(0x345b1a, 0, 256 * 1024, 256) + { "s28hs01gt", INFO(0x345b1b, 0, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) .fixups = &s28hx_t_fixups, }, - { "s28hs01gt", INFO(0x345b1b, 0, 256 * 1024, 512) + { "s28hs02gt", INFO(0x345b1c, 0, 0, 0) PARSE_SFDP + MFR_FLAGS(USE_CLPEF) .fixups = &s28hx_t_fixups, }, }; @@ -876,17 +946,35 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor) return !(nor->bouncebuf[0] & SR_WIP); } -static void spansion_nor_late_init(struct spi_nor *nor) +static int spansion_nor_late_init(struct spi_nor *nor) { - if (nor->params->size > SZ_16M) { + struct spi_nor_flash_parameter *params = nor->params; + struct spansion_nor_params *priv_params; + u8 mfr_flags = nor->info->mfr_flags; + + if (params->size > SZ_16M) { nor->flags |= SNOR_F_4B_OPCODES; /* No small sector erase for 4-byte command set */ nor->erase_opcode = SPINOR_OP_SE; nor->mtd.erasesize = nor->info->sector_size; } - if (nor->info->mfr_flags & USE_CLSR) - nor->params->ready = spansion_nor_sr_ready_and_clear; + if (mfr_flags & (USE_CLSR | USE_CLPEF)) { + priv_params = devm_kmalloc(nor->dev, sizeof(*priv_params), + GFP_KERNEL); + if (!priv_params) + return -ENOMEM; + + if (mfr_flags & USE_CLSR) + priv_params->clsr = SPINOR_OP_CLSR; + else if (mfr_flags & USE_CLPEF) + priv_params->clsr = SPINOR_OP_CLPEF; + + params->priv = priv_params; + params->ready = spansion_nor_sr_ready_and_clear; + } + + return 0; } static const struct spi_nor_fixups spansion_nor_fixups = { diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c index 688eb20c763e9b636386fe8bf4a1a9509f78b244..197d2c1101ed5e37e3556edd3d2b8768e99b0040 100644 --- a/drivers/mtd/spi-nor/sst.c +++ b/drivers/mtd/spi-nor/sst.c @@ -49,9 +49,11 @@ static const struct spi_nor_locking_ops sst26vf_nor_locking_ops = { .is_locked = sst26vf_nor_is_locked, }; -static void sst26vf_nor_late_init(struct spi_nor *nor) +static int sst26vf_nor_late_init(struct spi_nor *nor) { nor->params->locking_ops = &sst26vf_nor_locking_ops; + + return 0; } static const struct spi_nor_fixups sst26vf_nor_fixups = { @@ -111,6 +113,10 @@ static const struct flash_info sst_nor_parts[] = { SPI_NOR_QUAD_READ) }, { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) }, + { "sst26vf032b", INFO(0xbf2642, 0, 0, 0) + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) + PARSE_SFDP + .fixups = &sst26vf_nor_fixups }, { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128) FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) @@ -203,10 +209,12 @@ static int sst_nor_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } -static void sst_nor_late_init(struct spi_nor *nor) +static int sst_nor_late_init(struct spi_nor *nor) { if (nor->info->mfr_flags & SST_WRITE) nor->mtd._write = sst_nor_write; + + return 0; } static const struct spi_nor_fixups sst_nor_fixups = { diff --git a/drivers/mtd/spi-nor/swp.c b/drivers/mtd/spi-nor/swp.c index 0ba716e84377fbf79ee276a5e4e83c242098731d..5ab9d532486081c8a3c1a81fc458678f866457e8 100644 --- a/drivers/mtd/spi-nor/swp.c +++ b/drivers/mtd/spi-nor/swp.c @@ -214,8 +214,13 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) status_new = (status_old & ~mask & ~tb_mask) | val; - /* Disallow further writes if WP pin is asserted */ - status_new |= SR_SRWD; + /* + * Disallow further writes if WP# pin is neither left floating nor + * wrongly tied to GND (that includes internal pull-downs). + * WP# pin hard strapped to GND can be a valid use case. + */ + if (!(nor->flags & SNOR_F_NO_WP)) + status_new |= SR_SRWD; if (!use_top) status_new |= tb_mask; diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 834d6ba5ce703dfdfe57498793df2c65df9e9666..cd99c9a1c568844ca6ef3f307928fe116c448899 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -120,8 +120,9 @@ static const struct flash_info winbond_nor_parts[] = { NO_SFDP_FLAGS(SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16) NO_SFDP_FLAGS(SECT_4K) }, - { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256) - NO_SFDP_FLAGS(SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 0, 0) + PARSE_SFDP + FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB) }, { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) .fixups = &w25q256_fixups }, @@ -216,7 +217,7 @@ static const struct spi_nor_otp_ops winbond_nor_otp_ops = { .is_locked = spi_nor_otp_is_locked_sr2, }; -static void winbond_nor_late_init(struct spi_nor *nor) +static int winbond_nor_late_init(struct spi_nor *nor) { struct spi_nor_flash_parameter *params = nor->params; @@ -232,6 +233,8 @@ static void winbond_nor_late_init(struct spi_nor *nor) * from BFPT, if any. */ params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode; + + return 0; } static const struct spi_nor_fixups winbond_nor_fixups = { diff --git a/drivers/mtd/spi-nor/xilinx.c b/drivers/mtd/spi-nor/xilinx.c index 7175de8aa336a20298db2277ca5fca45e83b9ecf..00d53eae5ee83c634c232a45a086a0cffa8a7d87 100644 --- a/drivers/mtd/spi-nor/xilinx.c +++ b/drivers/mtd/spi-nor/xilinx.c @@ -155,10 +155,12 @@ static int xilinx_nor_setup(struct spi_nor *nor, return 0; } -static void xilinx_nor_late_init(struct spi_nor *nor) +static int xilinx_nor_late_init(struct spi_nor *nor) { nor->params->setup = xilinx_nor_setup; nor->params->ready = xilinx_nor_sr_ready; + + return 0; } static const struct spi_nor_fixups xilinx_nor_fixups = { diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 8b91a55ec0d28827d57fbfb42d405402f738ab71..8ee51e49fced559d3a5e561956103cb53d6fcf50 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -894,6 +894,13 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, return -EINVAL; } + /* UBI cannot work on flashes with zero erasesize. */ + if (!mtd->erasesize) { + pr_err("ubi: refuse attaching mtd%d - zero erasesize flash is not supported\n", + mtd->index); + return -EINVAL; + } + if (ubi_num == UBI_DEV_NUM_AUTO) { /* Search for an empty slot in the @ubi_devices array */ for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 6673122266b7a0b51c98dc6b86488794fe44933b..42db7679c3606840df19045a46ec2f7148a8ffcb 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -2335,13 +2335,27 @@ static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; - if (dev->chip_id == KSZ8830_CHIP_ID) { + switch (dev->chip_id) { + case KSZ8830_CHIP_ID: /* Silicon Errata Sheet (DS80000830A): * Port 1 does not work with LinkMD Cable-Testing. * Port 1 does not respond to received PAUSE control frames. */ if (!port) return MICREL_KSZ8_P1_ERRATA; + break; + case KSZ9477_CHIP_ID: + /* KSZ9477 Errata DS80000754C + * + * Module 4: Energy Efficient Ethernet (EEE) feature select must + * be manually disabled + * The EEE feature is enabled by default, but it is not fully + * operational. It must be manually disabled through register + * controls. If not disabled, the PHY ports can auto-negotiate + * to enable EEE, and this feature can cause link drops when + * linked to another device supporting EEE. + */ + return MICREL_NO_EEE; } return 0; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 52a99d8bada076620312683ddeaf0f44e62db1fc..ab434a77b059a5adda955e319900b80a19298851 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2958,14 +2958,16 @@ static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) * from the wrong location resulting in the switch booting * to wrong mode and inoperable. */ - mv88e6xxx_g1_wait_eeprom_done(chip); + if (chip->info->ops->get_eeprom) + mv88e6xxx_g2_eeprom_wait(chip); gpiod_set_value_cansleep(gpiod, 1); usleep_range(10000, 20000); gpiod_set_value_cansleep(gpiod, 0); usleep_range(10000, 20000); - mv88e6xxx_g1_wait_eeprom_done(chip); + if (chip->info->ops->get_eeprom) + mv88e6xxx_g2_eeprom_wait(chip); } } diff --git a/drivers/net/dsa/mv88e6xxx/global1.c b/drivers/net/dsa/mv88e6xxx/global1.c index 2fa55a6435910fd5d9a9c42dd28b93285b2181ba..174c773b38c2bd252c3107174886a497b3fab71e 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.c +++ b/drivers/net/dsa/mv88e6xxx/global1.c @@ -75,37 +75,6 @@ static int mv88e6xxx_g1_wait_init_ready(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_wait_bit(chip, MV88E6XXX_G1_STS, bit, 1); } -void mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip) -{ - const unsigned long timeout = jiffies + 1 * HZ; - u16 val; - int err; - - /* Wait up to 1 second for the switch to finish reading the - * EEPROM. - */ - while (time_before(jiffies, timeout)) { - err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &val); - if (err) { - dev_err(chip->dev, "Error reading status"); - return; - } - - /* If the switch is still resetting, it may not - * respond on the bus, and so MDIO read returns - * 0xffff. Differentiate between that, and waiting for - * the EEPROM to be done by bit 0 being set. - */ - if (val != 0xffff && - val & BIT(MV88E6XXX_G1_STS_IRQ_EEPROM_DONE)) - return; - - usleep_range(1000, 2000); - } - - dev_err(chip->dev, "Timeout waiting for EEPROM done"); -} - /* Offset 0x01: Switch MAC Address Register Bytes 0 & 1 * Offset 0x02: Switch MAC Address Register Bytes 2 & 3 * Offset 0x03: Switch MAC Address Register Bytes 4 & 5 diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index c99ddd117fe6e13fa00ea29e8fe91534608794d5..1095261f5b490a8bc708f3d7a513ca633ae0080c 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -282,7 +282,6 @@ int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr); int mv88e6185_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6352_g1_reset(struct mv88e6xxx_chip *chip); int mv88e6250_g1_reset(struct mv88e6xxx_chip *chip); -void mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_enable(struct mv88e6xxx_chip *chip); int mv88e6185_g1_ppu_disable(struct mv88e6xxx_chip *chip); diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 937a01f2ba75ebe0e89e419da93a0b937352cb68..b2b5f6ba438f46f2e8b39625a08e5eaae2d63d03 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -340,7 +340,7 @@ int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) * Offset 0x15: EEPROM Addr (for 8-bit data access) */ -static int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) +int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip) { int bit = __bf_shf(MV88E6XXX_G2_EEPROM_CMD_BUSY); int err; diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 7e091965582b75254b3dd6b0fed781a77f637d75..d9434f7cae538bb887271f144f0414ae8027becc 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -365,6 +365,7 @@ int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip); int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, int port); +int mv88e6xxx_g2_eeprom_wait(struct mv88e6xxx_chip *chip); extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; extern const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops; diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index dee35ba924ad2f5fe97be4865be825393f42bc58..8c66d3bf61f028167a98fd7025ba6b0f7b3b431d 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -132,6 +132,8 @@ struct sja1105_info { int max_frame_mem; int num_ports; bool multiple_cascade_ports; + /* Every {port, TXQ} has its own CBS shaper */ + bool fixed_cbs_mapping; enum dsa_tag_protocol tag_proto; const struct sja1105_dynamic_table_ops *dyn_ops; const struct sja1105_table_ops *static_ops; @@ -264,6 +266,8 @@ struct sja1105_private { * the switch doesn't confuse them with one another. */ struct mutex mgmt_lock; + /* Serializes accesses to the FDB */ + struct mutex fdb_lock; /* PTP two-step TX timestamp ID, and its serialization lock */ spinlock_t ts_id_lock; u8 ts_id; diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 7729d3f8b7f50a1a8fd85f1b5f34381817228812..984c0e604e8de23fcb940bb443c10d94595433c3 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -1175,18 +1175,15 @@ const struct sja1105_dynamic_table_ops sja1110_dyn_ops[BLK_IDX_MAX_DYN] = { static int sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, - struct sja1105_dyn_cmd *cmd, - const struct sja1105_dynamic_table_ops *ops) + const struct sja1105_dynamic_table_ops *ops, + void *entry, bool check_valident, + bool check_errors) { u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {}; + struct sja1105_dyn_cmd cmd = {}; int rc; - /* We don't _need_ to read the full entry, just the command area which - * is a fixed SJA1105_SIZE_DYN_CMD. But our cmd_packing() API expects a - * buffer that contains the full entry too. Additionally, our API - * doesn't really know how many bytes into the buffer does the command - * area really begin. So just read back the whole entry. - */ + /* Read back the whole entry + command structure. */ rc = sja1105_xfer_buf(priv, SPI_READ, ops->addr, packed_buf, ops->packed_size); if (rc) @@ -1195,11 +1192,25 @@ sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, /* Unpack the command structure, and return it to the caller in case it * needs to perform further checks on it (VALIDENT). */ - memset(cmd, 0, sizeof(*cmd)); - ops->cmd_packing(packed_buf, cmd, UNPACK); + ops->cmd_packing(packed_buf, &cmd, UNPACK); /* Hardware hasn't cleared VALID => still working on it */ - return cmd->valid ? -EAGAIN : 0; + if (cmd.valid) + return -EAGAIN; + + if (check_valident && !cmd.valident && !(ops->access & OP_VALID_ANYWAY)) + return -ENOENT; + + if (check_errors && cmd.errors) + return -EINVAL; + + /* Don't dereference possibly NULL pointer - maybe caller + * only wanted to see whether the entry existed or not. + */ + if (entry) + ops->entry_packing(packed_buf, entry, UNPACK); + + return 0; } /* Poll the dynamic config entry's control area until the hardware has @@ -1208,16 +1219,19 @@ sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, */ static int sja1105_dynamic_config_wait_complete(struct sja1105_private *priv, - struct sja1105_dyn_cmd *cmd, - const struct sja1105_dynamic_table_ops *ops) + const struct sja1105_dynamic_table_ops *ops, + void *entry, bool check_valident, + bool check_errors) { - int rc; - - return read_poll_timeout(sja1105_dynamic_config_poll_valid, - rc, rc != -EAGAIN, - SJA1105_DYNAMIC_CONFIG_SLEEP_US, - SJA1105_DYNAMIC_CONFIG_TIMEOUT_US, - false, priv, cmd, ops); + int err, rc; + + err = read_poll_timeout(sja1105_dynamic_config_poll_valid, + rc, rc != -EAGAIN, + SJA1105_DYNAMIC_CONFIG_SLEEP_US, + SJA1105_DYNAMIC_CONFIG_TIMEOUT_US, + false, priv, ops, entry, check_valident, + check_errors); + return err < 0 ? err : rc; } /* Provides read access to the settings through the dynamic interface @@ -1286,25 +1300,14 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv, mutex_lock(&priv->dynamic_config_lock); rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf, ops->packed_size); - if (rc < 0) { - mutex_unlock(&priv->dynamic_config_lock); - return rc; - } - - rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops); - mutex_unlock(&priv->dynamic_config_lock); if (rc < 0) - return rc; + goto out; - if (!cmd.valident && !(ops->access & OP_VALID_ANYWAY)) - return -ENOENT; + rc = sja1105_dynamic_config_wait_complete(priv, ops, entry, true, false); +out: + mutex_unlock(&priv->dynamic_config_lock); - /* Don't dereference possibly NULL pointer - maybe caller - * only wanted to see whether the entry existed or not. - */ - if (entry) - ops->entry_packing(packed_buf, entry, UNPACK); - return 0; + return rc; } int sja1105_dynamic_config_write(struct sja1105_private *priv, @@ -1356,22 +1359,14 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv, mutex_lock(&priv->dynamic_config_lock); rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf, ops->packed_size); - if (rc < 0) { - mutex_unlock(&priv->dynamic_config_lock); - return rc; - } - - rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops); - mutex_unlock(&priv->dynamic_config_lock); if (rc < 0) - return rc; + goto out; - cmd = (struct sja1105_dyn_cmd) {0}; - ops->cmd_packing(packed_buf, &cmd, UNPACK); - if (cmd.errors) - return -EINVAL; + rc = sja1105_dynamic_config_wait_complete(priv, ops, NULL, false, true); +out: + mutex_unlock(&priv->dynamic_config_lock); - return 0; + return rc; } static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 331bb1c6676aca8940aafcc956ef8769ccca5ae3..1a367e64bc3b1d35a708c47b34fcf8ce76d015a0 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1798,6 +1798,7 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, struct dsa_db db) { struct sja1105_private *priv = ds->priv; + int rc; if (!vid) { switch (db.type) { @@ -1812,12 +1813,16 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, } } - return priv->info->fdb_add_cmd(ds, port, addr, vid); + mutex_lock(&priv->fdb_lock); + rc = priv->info->fdb_add_cmd(ds, port, addr, vid); + mutex_unlock(&priv->fdb_lock); + + return rc; } -static int sja1105_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) +static int __sja1105_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct sja1105_private *priv = ds->priv; @@ -1837,6 +1842,20 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port, return priv->info->fdb_del_cmd(ds, port, addr, vid); } +static int sja1105_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + mutex_lock(&priv->fdb_lock); + rc = __sja1105_fdb_del(ds, port, addr, vid, db); + mutex_unlock(&priv->fdb_lock); + + return rc; +} + static int sja1105_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { @@ -1868,13 +1887,14 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, if (!(l2_lookup.destports & BIT(port))) continue; - /* We need to hide the FDB entry for unknown multicast */ - if (l2_lookup.macaddr == SJA1105_UNKNOWN_MULTICAST && - l2_lookup.mask_macaddr == SJA1105_UNKNOWN_MULTICAST) - continue; - u64_to_ether_addr(l2_lookup.macaddr, macaddr); + /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" + * only wants to see unicast + */ + if (is_multicast_ether_addr(macaddr)) + continue; + /* We need to hide the dsa_8021q VLANs from the user. */ if (vid_is_dsa_8021q(l2_lookup.vlanid)) l2_lookup.vlanid = 0; @@ -1898,6 +1918,8 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) }; int i; + mutex_lock(&priv->fdb_lock); + for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { struct sja1105_l2_lookup_entry l2_lookup = {0}; u8 macaddr[ETH_ALEN]; @@ -1911,7 +1933,7 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) if (rc) { dev_err(ds->dev, "Failed to read FDB: %pe\n", ERR_PTR(rc)); - return; + break; } if (!(l2_lookup.destports & BIT(port))) @@ -1923,14 +1945,16 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) u64_to_ether_addr(l2_lookup.macaddr, macaddr); - rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); + rc = __sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); if (rc) { dev_err(ds->dev, "Failed to delete FDB entry %pM vid %lld: %pe\n", macaddr, l2_lookup.vlanid, ERR_PTR(rc)); - return; + break; } } + + mutex_unlock(&priv->fdb_lock); } static int sja1105_mdb_add(struct dsa_switch *ds, int port, @@ -2115,11 +2139,36 @@ static void sja1105_bridge_leave(struct dsa_switch *ds, int port, } #define BYTES_PER_KBIT (1000LL / 8) +/* Port 0 (the uC port) does not have CBS shapers */ +#define SJA1110_FIXED_CBS(port, prio) ((((port) - 1) * SJA1105_NUM_TC) + (prio)) + +static int sja1105_find_cbs_shaper(struct sja1105_private *priv, + int port, int prio) +{ + int i; + + if (priv->info->fixed_cbs_mapping) { + i = SJA1110_FIXED_CBS(port, prio); + if (i >= 0 && i < priv->info->num_cbs_shapers) + return i; + + return -1; + } + + for (i = 0; i < priv->info->num_cbs_shapers; i++) + if (priv->cbs[i].port == port && priv->cbs[i].prio == prio) + return i; + + return -1; +} static int sja1105_find_unused_cbs_shaper(struct sja1105_private *priv) { int i; + if (priv->info->fixed_cbs_mapping) + return -1; + for (i = 0; i < priv->info->num_cbs_shapers; i++) if (!priv->cbs[i].idle_slope && !priv->cbs[i].send_slope) return i; @@ -2150,14 +2199,20 @@ static int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, { struct sja1105_private *priv = ds->priv; struct sja1105_cbs_entry *cbs; + s64 port_transmit_rate_kbps; int index; if (!offload->enable) return sja1105_delete_cbs_shaper(priv, port, offload->queue); - index = sja1105_find_unused_cbs_shaper(priv); - if (index < 0) - return -ENOSPC; + /* The user may be replacing an existing shaper */ + index = sja1105_find_cbs_shaper(priv, port, offload->queue); + if (index < 0) { + /* That isn't the case - see if we can allocate a new one */ + index = sja1105_find_unused_cbs_shaper(priv); + if (index < 0) + return -ENOSPC; + } cbs = &priv->cbs[index]; cbs->port = port; @@ -2167,9 +2222,17 @@ static int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, */ cbs->credit_hi = offload->hicredit; cbs->credit_lo = abs(offload->locredit); - /* User space is in kbits/sec, hardware in bytes/sec */ - cbs->idle_slope = offload->idleslope * BYTES_PER_KBIT; - cbs->send_slope = abs(offload->sendslope * BYTES_PER_KBIT); + /* User space is in kbits/sec, while the hardware in bytes/sec times + * link speed. Since the given offload->sendslope is good only for the + * current link speed anyway, and user space is likely to reprogram it + * when that changes, don't even bother to track the port's link speed, + * but deduce the port transmit rate from idleslope - sendslope. + */ + port_transmit_rate_kbps = offload->idleslope - offload->sendslope; + cbs->idle_slope = div_s64(offload->idleslope * BYTES_PER_KBIT, + port_transmit_rate_kbps); + cbs->send_slope = div_s64(abs(offload->sendslope * BYTES_PER_KBIT), + port_transmit_rate_kbps); /* Convert the negative values from 64-bit 2's complement * to 32-bit 2's complement (for the case of 0x80000000 whose * negative is still negative). @@ -2234,6 +2297,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv, int rc, i; s64 now; + mutex_lock(&priv->fdb_lock); mutex_lock(&priv->mgmt_lock); mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; @@ -2346,6 +2410,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv, goto out; out: mutex_unlock(&priv->mgmt_lock); + mutex_unlock(&priv->fdb_lock); return rc; } @@ -2915,7 +2980,9 @@ static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, { struct sja1105_l2_lookup_entry *l2_lookup; struct sja1105_table *table; - int match; + int match, rc; + + mutex_lock(&priv->fdb_lock); table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; l2_lookup = table->entries; @@ -2928,7 +2995,8 @@ static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, if (match == table->entry_count) { NL_SET_ERR_MSG_MOD(extack, "Could not find FDB entry for unknown multicast"); - return -ENOSPC; + rc = -ENOSPC; + goto out; } if (flags.val & BR_MCAST_FLOOD) @@ -2936,10 +3004,13 @@ static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, else l2_lookup[match].destports &= ~BIT(to); - return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, - l2_lookup[match].index, - &l2_lookup[match], - true); + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup[match].index, + &l2_lookup[match], true); +out: + mutex_unlock(&priv->fdb_lock); + + return rc; } static int sja1105_port_pre_bridge_flags(struct dsa_switch *ds, int port, @@ -3309,6 +3380,7 @@ static int sja1105_probe(struct spi_device *spi) mutex_init(&priv->ptp_data.lock); mutex_init(&priv->dynamic_config_lock); mutex_init(&priv->mgmt_lock); + mutex_init(&priv->fdb_lock); spin_lock_init(&priv->ts_id_lock); rc = sja1105_parse_dt(priv); diff --git a/drivers/net/dsa/sja1105/sja1105_spi.c b/drivers/net/dsa/sja1105/sja1105_spi.c index 5ce29c8057a4181510abcb7397cc5ddc3a45d84c..834b5c1b4db0cbeb180d0c6118d589e4a5bba292 100644 --- a/drivers/net/dsa/sja1105/sja1105_spi.c +++ b/drivers/net/dsa/sja1105/sja1105_spi.c @@ -781,6 +781,7 @@ const struct sja1105_info sja1110a_info = { .tag_proto = DSA_TAG_PROTO_SJA1110, .can_limit_mcast_flood = true, .multiple_cascade_ports = true, + .fixed_cbs_mapping = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .max_frame_mem = SJA1110_MAX_FRAME_MEMORY, @@ -831,6 +832,7 @@ const struct sja1105_info sja1110b_info = { .tag_proto = DSA_TAG_PROTO_SJA1110, .can_limit_mcast_flood = true, .multiple_cascade_ports = true, + .fixed_cbs_mapping = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .max_frame_mem = SJA1110_MAX_FRAME_MEMORY, @@ -881,6 +883,7 @@ const struct sja1105_info sja1110c_info = { .tag_proto = DSA_TAG_PROTO_SJA1110, .can_limit_mcast_flood = true, .multiple_cascade_ports = true, + .fixed_cbs_mapping = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .max_frame_mem = SJA1110_MAX_FRAME_MEMORY, @@ -931,6 +934,7 @@ const struct sja1105_info sja1110d_info = { .tag_proto = DSA_TAG_PROTO_SJA1110, .can_limit_mcast_flood = true, .multiple_cascade_ports = true, + .fixed_cbs_mapping = true, .ptp_ts_bits = 32, .ptpegr_ts_bytes = 8, .max_frame_mem = SJA1110_MAX_FRAME_MEMORY, diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c index 1c009b485188d4cfcf2c0dc0ff5faf8c31a1855e..ca66b747b7c5d160de5e9f70eff26ef394b67c3e 100644 --- a/drivers/net/ethernet/adi/adin1110.c +++ b/drivers/net/ethernet/adi/adin1110.c @@ -1385,7 +1385,7 @@ static int adin1110_fdb_add(struct adin1110_port_priv *port_priv, return -ENOMEM; other_port = priv->ports[!port_priv->nr]; - port_rules = adin1110_port_rules(port_priv, false, true); + port_rules = adin1110_port_rules(other_port, false, true); eth_broadcast_addr(mask); return adin1110_write_mac_address(other_port, mac_nr, (u8 *)fdb->addr, diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index ad32ca81f7ef41365dda25dd0778e60bab2d5406..f955bde10cf90ad6375f9b97fcffc36ba174a358 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1833,6 +1833,9 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi, return work_done; error: + if (xdp_flags & ENA_XDP_REDIRECT) + xdp_do_flush(); + adapter = netdev_priv(rx_ring->netdev); if (rc == -ENOSPC) { diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.c b/drivers/net/ethernet/broadcom/asp2/bcmasp.c index d63d321f3e7b0124e19093add27cfc252a1f7f71..41a6098eb0c2f610dfd657c639d59fdd25c1615c 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.c @@ -528,13 +528,16 @@ void bcmasp_netfilt_suspend(struct bcmasp_intf *intf) ASP_RX_FILTER_BLK_CTRL); } -void bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, - u32 *rule_cnt) +int bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, + u32 *rule_cnt) { struct bcmasp_priv *priv = intf->parent; int j = 0, i; for (i = 0; i < NUM_NET_FILTERS; i++) { + if (j == *rule_cnt) + return -EMSGSIZE; + if (!priv->net_filters[i].claimed || priv->net_filters[i].port != intf->port) continue; @@ -548,6 +551,8 @@ void bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, } *rule_cnt = j; + + return 0; } int bcmasp_netfilt_get_active(struct bcmasp_intf *intf) @@ -1300,6 +1305,7 @@ static int bcmasp_probe(struct platform_device *pdev) if (!intf) { dev_err(dev, "Cannot create eth interface %d\n", i); bcmasp_remove_intfs(priv); + of_node_put(intf_node); goto of_put_exit; } list_add_tail(&intf->list, &priv->intfs); diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.h b/drivers/net/ethernet/broadcom/asp2/bcmasp.h index 5b512f7f5e9499e569458a9d36f6963bc7cf7c43..ec90add6b03e28889d61e53e9eab6d072ce0f1f6 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.h +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.h @@ -577,8 +577,8 @@ void bcmasp_netfilt_release(struct bcmasp_intf *intf, int bcmasp_netfilt_get_active(struct bcmasp_intf *intf); -void bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, - u32 *rule_cnt); +int bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, + u32 *rule_cnt); void bcmasp_netfilt_suspend(struct bcmasp_intf *intf); diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c index c4f1604d5ab39e442397b0e85b88424c0c301d48..ce6a3d56fb2395baab61779210ab0b26bd6da659 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c @@ -335,7 +335,7 @@ static int bcmasp_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, err = bcmasp_flow_get(intf, cmd); break; case ETHTOOL_GRXCLSRLALL: - bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); + err = bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); cmd->data = NUM_NET_FILTERS; break; default: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 5cc0dbe12132727fb286a41d117a8091c346ec53..7551aa8068f8f794fc9beb9e6cbdf5bd8faaee18 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -2614,6 +2614,7 @@ static int bnxt_poll_nitroa0(struct napi_struct *napi, int budget) struct rx_cmp_ext *rxcmp1; u32 cp_cons, tmp_raw_cons; u32 raw_cons = cpr->cp_raw_cons; + bool flush_xdp = false; u32 rx_pkts = 0; u8 event = 0; @@ -2648,6 +2649,8 @@ static int bnxt_poll_nitroa0(struct napi_struct *napi, int budget) rx_pkts++; else if (rc == -EBUSY) /* partial completion */ break; + if (event & BNXT_REDIRECT_EVENT) + flush_xdp = true; } else if (unlikely(TX_CMP_TYPE(txcmp) == CMPL_BASE_TYPE_HWRM_DONE)) { bnxt_hwrm_handler(bp, txcmp); @@ -2667,6 +2670,8 @@ static int bnxt_poll_nitroa0(struct napi_struct *napi, int budget) if (event & BNXT_AGG_EVENT) bnxt_db_write(bp, &rxr->rx_agg_db, rxr->rx_agg_prod); + if (flush_xdp) + xdp_do_flush(); if (!bnxt_has_work(bp, cpr) && rx_pkts < budget) { napi_complete_done(napi, rx_pkts); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index f178ed9899a9ed53393ebd181efcf507c0105100..3ae8e8af8ab338669703363e504fda1d31df970f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -3721,6 +3721,60 @@ struct hwrm_func_backing_store_qcaps_v2_output { u8 valid; }; +/* hwrm_func_dbr_pacing_qcfg_input (size:128b/16B) */ +struct hwrm_func_dbr_pacing_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; +}; + +/* hwrm_func_dbr_pacing_qcfg_output (size:512b/64B) */ +struct hwrm_func_dbr_pacing_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 flags; +#define FUNC_DBR_PACING_QCFG_RESP_FLAGS_DBR_NQ_EVENT_ENABLED 0x1UL + u8 unused_0[7]; + __le32 dbr_stat_db_fifo_reg; +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_MASK 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_SFT 0 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_PCIE_CFG 0x0UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_GRC 0x1UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_BAR0 0x2UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_BAR1 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_LAST \ + FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SPACE_BAR1 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_MASK 0xfffffffcUL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_STAT_DB_FIFO_REG_ADDR_SFT 2 + __le32 dbr_stat_db_fifo_reg_watermark_mask; + u8 dbr_stat_db_fifo_reg_watermark_shift; + u8 unused_1[3]; + __le32 dbr_stat_db_fifo_reg_fifo_room_mask; + u8 dbr_stat_db_fifo_reg_fifo_room_shift; + u8 unused_2[3]; + __le32 dbr_throttling_aeq_arm_reg; +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_MASK 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_SFT 0 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_PCIE_CFG 0x0UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_GRC 0x1UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_BAR0 0x2UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_BAR1 0x3UL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_LAST \ + FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SPACE_BAR1 +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_MASK 0xfffffffcUL +#define FUNC_DBR_PACING_QCFG_RESP_DBR_THROTTLING_AEQ_ARM_REG_ADDR_SFT 2 + u8 dbr_throttling_aeq_arm_reg_val; + u8 unused_3[7]; + __le32 primary_nq_id; + __le32 pacing_threshold; + u8 unused_4[7]; + u8 valid; +}; + /* hwrm_func_drv_if_change_input (size:192b/24B) */ struct hwrm_func_drv_if_change_input { __le16 req_type; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index 852eb449ccae222c74e147de17e1aeec63b90ca7..6ba2b9398633307f992431ce1c852d2693396eb3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -345,7 +345,7 @@ static void bnxt_set_edev_info(struct bnxt_en_dev *edev, struct bnxt *bp) edev->hw_ring_stats_size = bp->hw_ring_stats_size; edev->pf_port_id = bp->pf.port_id; edev->en_state = bp->state; - + edev->bar0 = bp->bar0; edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp); } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h index 80cbc4b6130aa85270f36332908f6d3d157423a7..6ff77f082e6c744626b0c6ed227d69acec1e81f3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h @@ -81,6 +81,7 @@ struct bnxt_en_dev { * mode only. Will be * updated in resume. */ + void __iomem *bar0; }; static inline bool bnxt_ulp_registered(struct bnxt_en_dev *edev) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 31f664ee4d778507833fe468ee3390a43692e38e..b940dcd3ace681d6dd4cba431600f89d9a3913a6 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -756,8 +756,6 @@ static void macb_mac_link_up(struct phylink_config *config, if (rx_pause) ctrl |= MACB_BIT(PAE); - macb_set_tx_clk(bp, speed); - /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ @@ -777,6 +775,9 @@ static void macb_mac_link_up(struct phylink_config *config, spin_unlock_irqrestore(&bp->lock, flags); + if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) + macb_set_tx_clk(bp, speed); + /* Enable Rx and Tx; Enable PTP unicast */ ctrl = macb_readl(bp, NCR); if (gem_has_ptp(bp)) diff --git a/drivers/net/ethernet/engleder/tsnep_ethtool.c b/drivers/net/ethernet/engleder/tsnep_ethtool.c index 716815dad7d2190c29a44603e1ef342d985c1d2f..65ec1abc944211471f5eef3d564718c2c6b566fd 100644 --- a/drivers/net/ethernet/engleder/tsnep_ethtool.c +++ b/drivers/net/ethernet/engleder/tsnep_ethtool.c @@ -300,10 +300,8 @@ static void tsnep_ethtool_get_channels(struct net_device *netdev, { struct tsnep_adapter *adapter = netdev_priv(netdev); - ch->max_rx = adapter->num_rx_queues; - ch->max_tx = adapter->num_tx_queues; - ch->rx_count = adapter->num_rx_queues; - ch->tx_count = adapter->num_tx_queues; + ch->max_combined = adapter->num_queues; + ch->combined_count = adapter->num_queues; } static int tsnep_ethtool_get_ts_info(struct net_device *netdev, diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c index f61bd89734c588e7c5314859f7245c7e081bba87..8b992dc9bb52b499b5ced0cb24d408e705fb45b0 100644 --- a/drivers/net/ethernet/engleder/tsnep_main.c +++ b/drivers/net/ethernet/engleder/tsnep_main.c @@ -87,8 +87,11 @@ static irqreturn_t tsnep_irq(int irq, void *arg) /* handle TX/RX queue 0 interrupt */ if ((active & adapter->queue[0].irq_mask) != 0) { - tsnep_disable_irq(adapter, adapter->queue[0].irq_mask); - napi_schedule(&adapter->queue[0].napi); + if (napi_schedule_prep(&adapter->queue[0].napi)) { + tsnep_disable_irq(adapter, adapter->queue[0].irq_mask); + /* schedule after masking to avoid races */ + __napi_schedule(&adapter->queue[0].napi); + } } return IRQ_HANDLED; @@ -99,8 +102,11 @@ static irqreturn_t tsnep_irq_txrx(int irq, void *arg) struct tsnep_queue *queue = arg; /* handle TX/RX queue interrupt */ - tsnep_disable_irq(queue->adapter, queue->irq_mask); - napi_schedule(&queue->napi); + if (napi_schedule_prep(&queue->napi)) { + tsnep_disable_irq(queue->adapter, queue->irq_mask); + /* schedule after masking to avoid races */ + __napi_schedule(&queue->napi); + } return IRQ_HANDLED; } @@ -1728,6 +1734,10 @@ static int tsnep_poll(struct napi_struct *napi, int budget) if (queue->tx) complete = tsnep_tx_poll(queue->tx, budget); + /* handle case where we are called by netpoll with a budget of 0 */ + if (unlikely(budget <= 0)) + return budget; + if (queue->rx) { done = queue->rx->xsk_pool ? tsnep_rx_poll_zc(queue->rx, napi, budget) : diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c b/drivers/net/ethernet/freescale/enetc/enetc_pf.c index e0a4cb7e3f501732ea1001fb96474dfb57590718..c153dc083aff0fe618563d2161cdf8ae90b6682c 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c @@ -1402,7 +1402,7 @@ static void enetc_fixup_clear_rss_rfs(struct pci_dev *pdev) return; si = enetc_psi_create(pdev); - if (si) + if (!IS_ERR(si)) enetc_psi_destroy(pdev); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, ENETC_DEV_ID_PF, diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c index ea0e38b4d9e9f6c41441729d6ae5693186f03040..f281e42a7ef9680e2049a185782a37d402c7f886 100644 --- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c +++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c @@ -570,7 +570,10 @@ static int gve_rx_append_frags(struct napi_struct *napi, if (!skb) return -1; - skb_shinfo(rx->ctx.skb_tail)->frag_list = skb; + if (rx->ctx.skb_tail == rx->ctx.skb_head) + skb_shinfo(rx->ctx.skb_head)->frag_list = skb; + else + rx->ctx.skb_tail->next = skb; rx->ctx.skb_tail = skb; num_frags = 0; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index a4b43bcd2f0c9c80ed96d529f988231dd456abe0..aaf1f42624a79b609e617b2700b0af82a2c5d81b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -814,6 +814,7 @@ struct hnae3_tc_info { u8 max_tc; /* Total number of TCs */ u8 num_tc; /* Total number of enabled TCs */ bool mqprio_active; + bool dcb_ets_active; }; #define HNAE3_MAX_DSCP 64 diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index f276b5ecb431fc7adade443d122cb5c13a3c53f3..b8508533878bea377eb055fcaa29b330f03c2626 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -1045,6 +1045,7 @@ hns3_dbg_dev_specs(struct hnae3_handle *h, char *buf, int len, int *pos) struct hnae3_ae_dev *ae_dev = pci_get_drvdata(h->pdev); struct hnae3_dev_specs *dev_specs = &ae_dev->dev_specs; struct hnae3_knic_private_info *kinfo = &h->kinfo; + struct net_device *dev = kinfo->netdev; *pos += scnprintf(buf + *pos, len - *pos, "dev_spec:\n"); *pos += scnprintf(buf + *pos, len - *pos, "MAC entry num: %u\n", @@ -1087,6 +1088,9 @@ hns3_dbg_dev_specs(struct hnae3_handle *h, char *buf, int len, int *pos) dev_specs->mc_mac_size); *pos += scnprintf(buf + *pos, len - *pos, "MAC statistics number: %u\n", dev_specs->mac_stats_num); + *pos += scnprintf(buf + *pos, len - *pos, + "TX timeout threshold: %d seconds\n", + dev->watchdog_timeo / HZ); } static int hns3_dbg_dev_info(struct hnae3_handle *h, char *buf, int len) @@ -1411,9 +1415,9 @@ int hns3_dbg_init(struct hnae3_handle *handle) return 0; out: - mutex_destroy(&handle->dbgfs_lock); debugfs_remove_recursive(handle->hnae3_dbgfs); handle->hnae3_dbgfs = NULL; + mutex_destroy(&handle->dbgfs_lock); return ret; } @@ -1421,6 +1425,9 @@ void hns3_dbg_uninit(struct hnae3_handle *handle) { u32 i; + debugfs_remove_recursive(handle->hnae3_dbgfs); + handle->hnae3_dbgfs = NULL; + for (i = 0; i < ARRAY_SIZE(hns3_dbg_cmd); i++) if (handle->dbgfs_buf[i]) { kvfree(handle->dbgfs_buf[i]); @@ -1428,8 +1435,6 @@ void hns3_dbg_uninit(struct hnae3_handle *handle) } mutex_destroy(&handle->dbgfs_lock); - debugfs_remove_recursive(handle->hnae3_dbgfs); - handle->hnae3_dbgfs = NULL; } void hns3_dbg_register_debugfs(const char *debugfs_dir_name) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index eac2d05732415c3948239919c0431aaad09edb08..cf50368441b783df43cb308745d49fa58a69674c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -2103,8 +2103,12 @@ static void hns3_tx_doorbell(struct hns3_enet_ring *ring, int num, */ if (test_bit(HNS3_NIC_STATE_TX_PUSH_ENABLE, &priv->state) && num && !ring->pending_buf && num <= HNS3_MAX_PUSH_BD_NUM && doorbell) { + /* This smp_store_release() pairs with smp_load_aquire() in + * hns3_nic_reclaim_desc(). Ensure that the BD valid bit + * is updated. + */ + smp_store_release(&ring->last_to_use, ring->next_to_use); hns3_tx_push_bd(ring, num); - WRITE_ONCE(ring->last_to_use, ring->next_to_use); return; } @@ -2115,6 +2119,11 @@ static void hns3_tx_doorbell(struct hns3_enet_ring *ring, int num, return; } + /* This smp_store_release() pairs with smp_load_aquire() in + * hns3_nic_reclaim_desc(). Ensure that the BD valid bit is updated. + */ + smp_store_release(&ring->last_to_use, ring->next_to_use); + if (ring->tqp->mem_base) hns3_tx_mem_doorbell(ring); else @@ -2122,7 +2131,6 @@ static void hns3_tx_doorbell(struct hns3_enet_ring *ring, int num, ring->tqp->io_base + HNS3_RING_TX_RING_TAIL_REG); ring->pending_buf = 0; - WRITE_ONCE(ring->last_to_use, ring->next_to_use); } static void hns3_tsyn(struct net_device *netdev, struct sk_buff *skb, @@ -3308,8 +3316,6 @@ static void hns3_set_default_feature(struct net_device *netdev) netdev->priv_flags |= IFF_UNICAST_FLT; - netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; - netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | @@ -3347,6 +3353,15 @@ static void hns3_set_default_feature(struct net_device *netdev) NETIF_F_HW_TC); netdev->hw_enc_features |= netdev->vlan_features | NETIF_F_TSO_MANGLEID; + + /* The device_version V3 hardware can't offload the checksum for IP in + * GRE packets, but can do it for NvGRE. So default to disable the + * checksum and GSO offload for GRE. + */ + if (ae_dev->dev_version > HNAE3_DEVICE_VERSION_V2) { + netdev->features &= ~NETIF_F_GSO_GRE; + netdev->features &= ~NETIF_F_GSO_GRE_CSUM; + } } static int hns3_alloc_buffer(struct hns3_enet_ring *ring, @@ -3563,9 +3578,8 @@ static void hns3_reuse_buffer(struct hns3_enet_ring *ring, int i) static bool hns3_nic_reclaim_desc(struct hns3_enet_ring *ring, int *bytes, int *pkts, int budget) { - /* pair with ring->last_to_use update in hns3_tx_doorbell(), - * smp_store_release() is not used in hns3_tx_doorbell() because - * the doorbell operation already have the needed barrier operation. + /* This smp_load_acquire() pairs with smp_store_release() in + * hns3_tx_doorbell(). */ int ltu = smp_load_acquire(&ring->last_to_use); int ntc = ring->next_to_clean; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index 36858a72d771e13af621ca354a4fcc739197a025..682239f33082b0b27b7ef866e15220f88b177fc3 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -773,7 +773,9 @@ static int hns3_get_link_ksettings(struct net_device *netdev, hns3_get_ksettings(h, cmd); break; case HNAE3_MEDIA_TYPE_FIBER: - if (module_type == HNAE3_MODULE_TYPE_CR) + if (module_type == HNAE3_MODULE_TYPE_UNKNOWN) + cmd->base.port = PORT_OTHER; + else if (module_type == HNAE3_MODULE_TYPE_CR) cmd->base.port = PORT_DA; else cmd->base.port = PORT_FIBRE; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c index fad5a5ff3cda54f5dfd4717a362d83cbac9e6358..b98301e205f7fff69b21f36a96e93ec358a371c1 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c @@ -259,7 +259,7 @@ static int hclge_ieee_setets(struct hnae3_handle *h, struct ieee_ets *ets) int ret; if (!(hdev->dcbx_cap & DCB_CAP_DCBX_VER_IEEE) || - hdev->flag & HCLGE_FLAG_MQPRIO_ENABLE) + h->kinfo.tc_info.mqprio_active) return -EINVAL; ret = hclge_ets_validate(hdev, ets, &num_tc, &map_changed); @@ -275,10 +275,7 @@ static int hclge_ieee_setets(struct hnae3_handle *h, struct ieee_ets *ets) } hclge_tm_schd_info_update(hdev, num_tc); - if (num_tc > 1) - hdev->flag |= HCLGE_FLAG_DCB_ENABLE; - else - hdev->flag &= ~HCLGE_FLAG_DCB_ENABLE; + h->kinfo.tc_info.dcb_ets_active = num_tc > 1; ret = hclge_ieee_ets_to_tm_info(hdev, ets); if (ret) @@ -487,7 +484,7 @@ static u8 hclge_getdcbx(struct hnae3_handle *h) struct hclge_vport *vport = hclge_get_vport(h); struct hclge_dev *hdev = vport->back; - if (hdev->flag & HCLGE_FLAG_MQPRIO_ENABLE) + if (h->kinfo.tc_info.mqprio_active) return 0; return hdev->dcbx_cap; @@ -611,7 +608,8 @@ static int hclge_setup_tc(struct hnae3_handle *h, if (!test_bit(HCLGE_STATE_NIC_REGISTERED, &hdev->state)) return -EBUSY; - if (hdev->flag & HCLGE_FLAG_DCB_ENABLE) + kinfo = &vport->nic.kinfo; + if (kinfo->tc_info.dcb_ets_active) return -EINVAL; ret = hclge_mqprio_qopt_check(hdev, mqprio_qopt); @@ -625,7 +623,6 @@ static int hclge_setup_tc(struct hnae3_handle *h, if (ret) return ret; - kinfo = &vport->nic.kinfo; memcpy(&old_tc_info, &kinfo->tc_info, sizeof(old_tc_info)); hclge_sync_mqprio_qopt(&kinfo->tc_info, mqprio_qopt); kinfo->tc_info.mqprio_active = tc > 0; @@ -634,13 +631,6 @@ static int hclge_setup_tc(struct hnae3_handle *h, if (ret) goto err_out; - hdev->flag &= ~HCLGE_FLAG_DCB_ENABLE; - - if (tc > 1) - hdev->flag |= HCLGE_FLAG_MQPRIO_ENABLE; - else - hdev->flag &= ~HCLGE_FLAG_MQPRIO_ENABLE; - return hclge_notify_init_up(hdev); err_out: diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c index f01a7a9ee02ca149e05096fbbf47abc2d3f231e9..ff3f8f424ad9004bbd549bc32cc43abca3ea2610 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_debugfs.c @@ -1519,7 +1519,7 @@ static int hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, bool sel_x, struct hclge_desc desc[3]; int pos = 0; int ret, i; - u32 *req; + __le32 *req; hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_FD_TCAM_OP, true); desc[0].flag |= cpu_to_le16(HCLGE_COMM_CMD_FLAG_NEXT); @@ -1544,22 +1544,22 @@ static int hclge_dbg_fd_tcam_read(struct hclge_dev *hdev, bool sel_x, tcam_msg.loc); /* tcam_data0 ~ tcam_data1 */ - req = (u32 *)req1->tcam_data; + req = (__le32 *)req1->tcam_data; for (i = 0; i < 2; i++) pos += scnprintf(tcam_buf + pos, HCLGE_DBG_TCAM_BUF_SIZE - pos, - "%08x\n", *req++); + "%08x\n", le32_to_cpu(*req++)); /* tcam_data2 ~ tcam_data7 */ - req = (u32 *)req2->tcam_data; + req = (__le32 *)req2->tcam_data; for (i = 0; i < 6; i++) pos += scnprintf(tcam_buf + pos, HCLGE_DBG_TCAM_BUF_SIZE - pos, - "%08x\n", *req++); + "%08x\n", le32_to_cpu(*req++)); /* tcam_data8 ~ tcam_data12 */ - req = (u32 *)req3->tcam_data; + req = (__le32 *)req3->tcam_data; for (i = 0; i < 5; i++) pos += scnprintf(tcam_buf + pos, HCLGE_DBG_TCAM_BUF_SIZE - pos, - "%08x\n", *req++); + "%08x\n", le32_to_cpu(*req++)); return ret; } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 0f50dba6cc4780792b88b077df493daf89225434..c42574e297476bdd1209e4a3090345c9a4e13b3c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -3564,9 +3564,14 @@ static u32 hclge_check_event_cause(struct hclge_dev *hdev, u32 *clearval) static void hclge_clear_event_cause(struct hclge_dev *hdev, u32 event_type, u32 regclr) { +#define HCLGE_IMP_RESET_DELAY 5 + switch (event_type) { case HCLGE_VECTOR0_EVENT_PTP: case HCLGE_VECTOR0_EVENT_RST: + if (regclr == BIT(HCLGE_VECTOR0_IMPRESET_INT_B)) + mdelay(HCLGE_IMP_RESET_DELAY); + hclge_write_dev(&hdev->hw, HCLGE_MISC_RESET_STS_REG, regclr); break; case HCLGE_VECTOR0_EVENT_MBX: @@ -7348,6 +7353,12 @@ static int hclge_del_cls_flower(struct hnae3_handle *handle, ret = hclge_fd_tcam_config(hdev, HCLGE_FD_STAGE_1, true, rule->location, NULL, false); if (ret) { + /* if tcam config fail, set rule state to TO_DEL, + * so the rule will be deleted when periodic + * task being scheduled. + */ + hclge_update_fd_list(hdev, HCLGE_FD_TO_DEL, rule->location, NULL); + set_bit(HCLGE_STATE_FD_TBL_CHANGED, &hdev->state); spin_unlock_bh(&hdev->fd_rule_lock); return ret; } @@ -8824,7 +8835,7 @@ static void hclge_update_overflow_flags(struct hclge_vport *vport, if (mac_type == HCLGE_MAC_ADDR_UC) { if (is_all_added) vport->overflow_promisc_flags &= ~HNAE3_OVERFLOW_UPE; - else + else if (hclge_is_umv_space_full(vport, true)) vport->overflow_promisc_flags |= HNAE3_OVERFLOW_UPE; } else { if (is_all_added) @@ -11026,6 +11037,7 @@ static void hclge_get_mdix_mode(struct hnae3_handle *handle, static void hclge_info_show(struct hclge_dev *hdev) { + struct hnae3_handle *handle = &hdev->vport->nic; struct device *dev = &hdev->pdev->dev; dev_info(dev, "PF info begin:\n"); @@ -11042,9 +11054,9 @@ static void hclge_info_show(struct hclge_dev *hdev) dev_info(dev, "This is %s PF\n", hdev->flag & HCLGE_FLAG_MAIN ? "main" : "not main"); dev_info(dev, "DCB %s\n", - hdev->flag & HCLGE_FLAG_DCB_ENABLE ? "enable" : "disable"); + handle->kinfo.tc_info.dcb_ets_active ? "enable" : "disable"); dev_info(dev, "MQPRIO %s\n", - hdev->flag & HCLGE_FLAG_MQPRIO_ENABLE ? "enable" : "disable"); + handle->kinfo.tc_info.mqprio_active ? "enable" : "disable"); dev_info(dev, "Default tx spare buffer size: %u\n", hdev->tx_spare_buf_size); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index ec233ec57222aa8e76adcda2bce6e941b9e4129b..7bc2049b723daa387aba2a083dc526a2e68083ba 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -919,8 +919,6 @@ struct hclge_dev { #define HCLGE_FLAG_MAIN BIT(0) #define HCLGE_FLAG_DCB_CAPABLE BIT(1) -#define HCLGE_FLAG_DCB_ENABLE BIT(2) -#define HCLGE_FLAG_MQPRIO_ENABLE BIT(3) u32 flag; u32 pkt_buf_size; /* Total pf buf size for tx/rx */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 7a2f9233d69548d1a5be39ccb449c3c2708e2950..a4d68fb216fb92ae4a23b24d323dade62676abea 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -1855,7 +1855,8 @@ static void hclgevf_periodic_service_task(struct hclgevf_dev *hdev) unsigned long delta = round_jiffies_relative(HZ); struct hnae3_handle *handle = &hdev->nic; - if (test_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state)) + if (test_bit(HCLGEVF_STATE_RST_FAIL, &hdev->state) || + test_bit(HCLGE_COMM_STATE_CMD_DISABLE, &hdev->hw.hw.comm_state)) return; if (time_is_after_jiffies(hdev->last_serv_processed + HZ)) { diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 9406237c461e0c3664a40231d4cd6939002c7db4..f81a43d2cdfcd92c46c69e2c7e866912657ae07e 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -456,9 +456,6 @@ int hinic_set_vlan_fliter(struct hinic_dev *nic_dev, u32 en) u16 out_size = sizeof(vlan_filter); int err; - if (!hwdev) - return -EINVAL; - vlan_filter.func_idx = HINIC_HWIF_FUNC_IDX(hwif); vlan_filter.enable = en; diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 832a2ae019509cbd0bde6dd87be9fd78906b4a1a..a8d79ee350f8dacd623109eebdf5e1ccaea03c76 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1303,24 +1303,23 @@ static void ibmveth_rx_csum_helper(struct sk_buff *skb, * the user space for finding a flow. During this process, OVS computes * checksum on the first packet when CHECKSUM_PARTIAL flag is set. * - * So, re-compute TCP pseudo header checksum when configured for - * trunk mode. + * So, re-compute TCP pseudo header checksum. */ + if (iph_proto == IPPROTO_TCP) { struct tcphdr *tcph = (struct tcphdr *)(skb->data + iphlen); + if (tcph->check == 0x0000) { /* Recompute TCP pseudo header checksum */ - if (adapter->is_active_trunk) { - tcphdrlen = skb->len - iphlen; - if (skb_proto == ETH_P_IP) - tcph->check = - ~csum_tcpudp_magic(iph->saddr, - iph->daddr, tcphdrlen, iph_proto, 0); - else if (skb_proto == ETH_P_IPV6) - tcph->check = - ~csum_ipv6_magic(&iph6->saddr, - &iph6->daddr, tcphdrlen, iph_proto, 0); - } + tcphdrlen = skb->len - iphlen; + if (skb_proto == ETH_P_IP) + tcph->check = + ~csum_tcpudp_magic(iph->saddr, + iph->daddr, tcphdrlen, iph_proto, 0); + else if (skb_proto == ETH_P_IPV6) + tcph->check = + ~csum_ipv6_magic(&iph6->saddr, + &iph6->daddr, tcphdrlen, iph_proto, 0); /* Setup SKB fields for checksum offload */ skb_partial_csum_set(skb, iphlen, offsetof(struct tcphdr, check)); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 8ea1a238dcefe1145833fc387444d8ebbdea4bb3..d3d6415553ed671bd433bf15173564abd5f3f30c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -4475,9 +4475,7 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, goto error_pvid; i40e_vlan_stripping_enable(vsi); - i40e_vc_reset_vf(vf, true); - /* During reset the VF got a new VSI, so refresh a pointer. */ - vsi = pf->vsi[vf->lan_vsi_idx]; + /* Locked once because multiple functions below iterate list */ spin_lock_bh(&vsi->mac_filter_hash_lock); @@ -4563,6 +4561,10 @@ int i40e_ndo_set_vf_port_vlan(struct net_device *netdev, int vf_id, */ vf->port_vlan_id = le16_to_cpu(vsi->info.pvid); + i40e_vc_reset_vf(vf, true); + /* During reset the VF got a new VSI, so refresh a pointer. */ + vsi = pf->vsi[vf->lan_vsi_idx]; + ret = i40e_config_vf_promiscuous_mode(vf, vsi->id, allmulti, alluni); if (ret) { dev_err(&pf->pdev->dev, "Unable to config vf promiscuous mode\n"); diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h index 85fba85fbb232bf869a988892f90084d92b3629a..e110ba3461857b0abf54d872df9a55ef282f00e6 100644 --- a/drivers/net/ethernet/intel/iavf/iavf.h +++ b/drivers/net/ethernet/intel/iavf/iavf.h @@ -521,7 +521,7 @@ void iavf_down(struct iavf_adapter *adapter); int iavf_process_config(struct iavf_adapter *adapter); int iavf_parse_vf_resource_msg(struct iavf_adapter *adapter); void iavf_schedule_reset(struct iavf_adapter *adapter, u64 flags); -void iavf_schedule_request_stats(struct iavf_adapter *adapter); +void iavf_schedule_aq_request(struct iavf_adapter *adapter, u64 flags); void iavf_schedule_finish_config(struct iavf_adapter *adapter); void iavf_reset(struct iavf_adapter *adapter); void iavf_set_ethtool_ops(struct net_device *netdev); diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c index a34303ad057d005c7289ec161562ed916b5d7827..90397293525f7169a7bb15ae2c4866d5ffd30fdc 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c @@ -362,7 +362,7 @@ static void iavf_get_ethtool_stats(struct net_device *netdev, unsigned int i; /* Explicitly request stats refresh */ - iavf_schedule_request_stats(adapter); + iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_REQUEST_STATS); iavf_add_ethtool_stats(&data, adapter, iavf_gstrings_stats); diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c index 7b300c86ceda733a94222472a77aa3e9d8e68b5d..6a2e6d64bc3a5436348b902f3d55767ad2d8194c 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_main.c +++ b/drivers/net/ethernet/intel/iavf/iavf_main.c @@ -314,15 +314,13 @@ void iavf_schedule_reset(struct iavf_adapter *adapter, u64 flags) } /** - * iavf_schedule_request_stats - Set the flags and schedule statistics request + * iavf_schedule_aq_request - Set the flags and schedule aq request * @adapter: board private structure - * - * Sets IAVF_FLAG_AQ_REQUEST_STATS flag so iavf_watchdog_task() will explicitly - * request and refresh ethtool stats + * @flags: requested aq flags **/ -void iavf_schedule_request_stats(struct iavf_adapter *adapter) +void iavf_schedule_aq_request(struct iavf_adapter *adapter, u64 flags) { - adapter->aq_required |= IAVF_FLAG_AQ_REQUEST_STATS; + adapter->aq_required |= flags; mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0); } @@ -823,7 +821,7 @@ iavf_vlan_filter *iavf_add_vlan(struct iavf_adapter *adapter, list_add_tail(&f->list, &adapter->vlan_filter_list); f->state = IAVF_VLAN_ADD; adapter->num_vlan_filters++; - adapter->aq_required |= IAVF_FLAG_AQ_ADD_VLAN_FILTER; + iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_ADD_VLAN_FILTER); } clearout: @@ -845,7 +843,7 @@ static void iavf_del_vlan(struct iavf_adapter *adapter, struct iavf_vlan vlan) f = iavf_find_vlan(adapter, vlan); if (f) { f->state = IAVF_VLAN_REMOVE; - adapter->aq_required |= IAVF_FLAG_AQ_DEL_VLAN_FILTER; + iavf_schedule_aq_request(adapter, IAVF_FLAG_AQ_DEL_VLAN_FILTER); } spin_unlock_bh(&adapter->mac_vlan_list_lock); @@ -1421,7 +1419,8 @@ void iavf_down(struct iavf_adapter *adapter) iavf_clear_fdir_filters(adapter); iavf_clear_adv_rss_conf(adapter); - if (!(adapter->flags & IAVF_FLAG_PF_COMMS_FAILED)) { + if (!(adapter->flags & IAVF_FLAG_PF_COMMS_FAILED) && + !(test_bit(__IAVF_IN_REMOVE_TASK, &adapter->crit_section))) { /* cancel any current operation */ adapter->current_op = VIRTCHNL_OP_UNKNOWN; /* Schedule operations to close down the HW. Don't wait diff --git a/drivers/net/ethernet/intel/ice/ice_virtchnl.c b/drivers/net/ethernet/intel/ice/ice_virtchnl.c index b03426ac932bc1910e7f644240feeb6c92588144..db97353efd067f496150bdf0a2213fa3b1755414 100644 --- a/drivers/net/ethernet/intel/ice/ice_virtchnl.c +++ b/drivers/net/ethernet/intel/ice/ice_virtchnl.c @@ -2617,12 +2617,14 @@ static int ice_vc_query_rxdid(struct ice_vf *vf) goto err; } - /* Read flexiflag registers to determine whether the - * corresponding RXDID is configured and supported or not. - * Since Legacy 16byte descriptor format is not supported, - * start from Legacy 32byte descriptor. + /* RXDIDs supported by DDP package can be read from the register + * to get the supported RXDID bitmap. But the legacy 32byte RXDID + * is not listed in DDP package, add it in the bitmap manually. + * Legacy 16byte descriptor is not supported. */ - for (i = ICE_RXDID_LEGACY_1; i < ICE_FLEX_DESC_RXDID_MAX_NUM; i++) { + rxdid->supported_rxdids |= BIT(ICE_RXDID_LEGACY_1); + + for (i = ICE_RXDID_FLEX_NIC; i < ICE_FLEX_DESC_RXDID_MAX_NUM; i++) { regval = rd32(hw, GLFLXP_RXDID_FLAGS(i, 0)); if ((regval >> GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_S) & GLFLXP_RXDID_FLAGS_FLEXIFLAG_4N_M) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 015b781441149b0d03fed5a340b2323083b4eba8..a2b759531cb7ba44720f000018597d5222bec900 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -34,11 +34,11 @@ struct igb_adapter; /* TX/RX descriptor defines */ #define IGB_DEFAULT_TXD 256 #define IGB_DEFAULT_TX_WORK 128 -#define IGB_MIN_TXD 80 +#define IGB_MIN_TXD 64 #define IGB_MAX_TXD 4096 #define IGB_DEFAULT_RXD 256 -#define IGB_MIN_RXD 80 +#define IGB_MIN_RXD 64 #define IGB_MAX_RXD 4096 #define IGB_DEFAULT_ITR 3 /* dynamic */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1ab787ed254df35ea738752d779f35fab54e4183..76b34cee1da3c8d9a18d3254be5b88f79882ff53 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3827,8 +3827,11 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs, bool reinit) } /* only call pci_enable_sriov() if no VFs are allocated already */ - if (!old_vfs) + if (!old_vfs) { err = pci_enable_sriov(pdev, adapter->vfs_allocated_count); + if (err) + goto err_out; + } goto out; @@ -3933,8 +3936,9 @@ static void igb_probe_vfs(struct igb_adapter *adapter) struct pci_dev *pdev = adapter->pdev; struct e1000_hw *hw = &adapter->hw; - /* Virtualization features not supported on i210 family. */ - if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) + /* Virtualization features not supported on i210 and 82580 family. */ + if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211) || + (hw->mac.type == e1000_82580)) return; /* Of the below we really only want the effect of getting diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index 57d39ee00b58540aaf7ea919951a51f8fd30a934..7b83678ba83a695cfea6eaafbcac8efdda148415 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -39,11 +39,11 @@ enum latency_range { /* Tx/Rx descriptor defines */ #define IGBVF_DEFAULT_TXD 256 #define IGBVF_MAX_TXD 4096 -#define IGBVF_MIN_TXD 80 +#define IGBVF_MIN_TXD 64 #define IGBVF_DEFAULT_RXD 256 #define IGBVF_MAX_RXD 4096 -#define IGBVF_MIN_RXD 80 +#define IGBVF_MIN_RXD 64 #define IGBVF_MIN_ITR_USECS 10 /* 100000 irq/sec */ #define IGBVF_MAX_ITR_USECS 10000 /* 100 irq/sec */ diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 8ebe6999a528cd44a77f9e83b5babe5dc675af31..f48f82d5e274b1dc55052d8a86824d42db8264a7 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -379,11 +379,11 @@ static inline u32 igc_rss_type(const union igc_adv_rx_desc *rx_desc) /* TX/RX descriptor defines */ #define IGC_DEFAULT_TXD 256 #define IGC_DEFAULT_TX_WORK 128 -#define IGC_MIN_TXD 80 +#define IGC_MIN_TXD 64 #define IGC_MAX_TXD 4096 #define IGC_DEFAULT_RXD 256 -#define IGC_MIN_RXD 80 +#define IGC_MIN_RXD 64 #define IGC_MAX_RXD 4096 /* Supported Rx Buffer Sizes */ diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 93bce729be76a69c562ee5523bd521466356168b..7ab6dd58e40013614a9174686200841393530e30 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -868,6 +868,18 @@ static void igc_ethtool_get_stats(struct net_device *netdev, spin_unlock(&adapter->stats64_lock); } +static int igc_ethtool_get_previous_rx_coalesce(struct igc_adapter *adapter) +{ + return (adapter->rx_itr_setting <= 3) ? + adapter->rx_itr_setting : adapter->rx_itr_setting >> 2; +} + +static int igc_ethtool_get_previous_tx_coalesce(struct igc_adapter *adapter) +{ + return (adapter->tx_itr_setting <= 3) ? + adapter->tx_itr_setting : adapter->tx_itr_setting >> 2; +} + static int igc_ethtool_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec, struct kernel_ethtool_coalesce *kernel_coal, @@ -875,17 +887,8 @@ static int igc_ethtool_get_coalesce(struct net_device *netdev, { struct igc_adapter *adapter = netdev_priv(netdev); - if (adapter->rx_itr_setting <= 3) - ec->rx_coalesce_usecs = adapter->rx_itr_setting; - else - ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2; - - if (!(adapter->flags & IGC_FLAG_QUEUE_PAIRS)) { - if (adapter->tx_itr_setting <= 3) - ec->tx_coalesce_usecs = adapter->tx_itr_setting; - else - ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2; - } + ec->rx_coalesce_usecs = igc_ethtool_get_previous_rx_coalesce(adapter); + ec->tx_coalesce_usecs = igc_ethtool_get_previous_tx_coalesce(adapter); return 0; } @@ -910,8 +913,12 @@ static int igc_ethtool_set_coalesce(struct net_device *netdev, ec->tx_coalesce_usecs == 2) return -EINVAL; - if ((adapter->flags & IGC_FLAG_QUEUE_PAIRS) && ec->tx_coalesce_usecs) + if ((adapter->flags & IGC_FLAG_QUEUE_PAIRS) && + ec->tx_coalesce_usecs != igc_ethtool_get_previous_tx_coalesce(adapter)) { + NL_SET_ERR_MSG_MOD(extack, + "Queue Pair mode enabled, both Rx and Tx coalescing controlled by rx-usecs"); return -EINVAL; + } /* If ITR is disabled, disable DMAC */ if (ec->rx_coalesce_usecs == 0) { diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 293b4571768384d2116dcc4a66c368e842436926..98de34d0ce07e1a6915f5eb5befee3a619572d9b 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -6491,7 +6491,7 @@ static int igc_xdp_xmit(struct net_device *dev, int num_frames, struct igc_ring *ring; int i, drops; - if (unlikely(test_bit(__IGC_DOWN, &adapter->state))) + if (unlikely(!netif_carrier_ok(dev))) return -ENETDOWN; if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 0310af851086bbd5cf7253c2b52d04423de52c14..9339edbd90821af008ded99335d017145fcaeb13 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -979,6 +979,7 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, u32 tsync_tx_ctl = IXGBE_TSYNCTXCTL_ENABLED; u32 tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED; u32 tsync_rx_mtrl = PTP_EV_PORT << 16; + u32 aflags = adapter->flags; bool is_l2 = false; u32 regval; @@ -996,20 +997,20 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, case HWTSTAMP_FILTER_NONE: tsync_rx_ctl = 0; tsync_rx_mtrl = 0; - adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG; - adapter->flags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG; - adapter->flags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: @@ -1023,8 +1024,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2; is_l2 = true; config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - adapter->flags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_NTP_ALL: @@ -1035,7 +1036,7 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, if (hw->mac.type >= ixgbe_mac_X550) { tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_ALL; config->rx_filter = HWTSTAMP_FILTER_ALL; - adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; + aflags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; break; } fallthrough; @@ -1046,8 +1047,6 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, * Delay_Req messages and hardware does not support * timestamping all packets => return error */ - adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); config->rx_filter = HWTSTAMP_FILTER_NONE; return -ERANGE; } @@ -1079,8 +1078,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, IXGBE_TSYNCRXCTL_TYPE_ALL | IXGBE_TSYNCRXCTL_TSIP_UT_EN; config->rx_filter = HWTSTAMP_FILTER_ALL; - adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; - adapter->flags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER; + aflags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; + aflags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER; is_l2 = true; break; default: @@ -1113,6 +1112,9 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, IXGBE_WRITE_FLUSH(hw); + /* configure adapter flags only when HW is actually configured */ + adapter->flags = aflags; + /* clear TX/RX time stamp registers, just to be sure */ ixgbe_ptp_clear_tx_timestamp(adapter); IXGBE_READ_REG(hw, IXGBE_RXSTMPH); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index eb74ccddb4409762cbc01cf24b9bb53a95dd250d..21c3f9b015c85de6e94e9f36bf6f84df261ae295 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5586,6 +5586,11 @@ static int mvpp2_ethtool_get_rxnfc(struct net_device *dev, break; case ETHTOOL_GRXCLSRLALL: for (i = 0; i < MVPP2_N_RFS_ENTRIES_PER_FLOW; i++) { + if (loc == info->rule_cnt) { + ret = -EMSGSIZE; + break; + } + if (port->rfs_rules[i]) rules[loc++] = i; } diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c index 4424de2ffd70ca6762cfd1666ac326163bc9527e..dbc518ff827688a72ad2e13a3648d8c0c482d3f5 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_main.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_main.c @@ -734,13 +734,13 @@ static netdev_tx_t octep_start_xmit(struct sk_buff *skb, dma_map_sg_err: if (si > 0) { dma_unmap_single(iq->dev, sglist[0].dma_ptr[0], - sglist[0].len[0], DMA_TO_DEVICE); - sglist[0].len[0] = 0; + sglist[0].len[3], DMA_TO_DEVICE); + sglist[0].len[3] = 0; } while (si > 1) { dma_unmap_page(iq->dev, sglist[si >> 2].dma_ptr[si & 3], - sglist[si >> 2].len[si & 3], DMA_TO_DEVICE); - sglist[si >> 2].len[si & 3] = 0; + sglist[si >> 2].len[3 - (si & 3)], DMA_TO_DEVICE); + sglist[si >> 2].len[3 - (si & 3)] = 0; si--; } tx_buffer->gather = 0; diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_tx.c b/drivers/net/ethernet/marvell/octeon_ep/octep_tx.c index 5a520d37bea02459815f868f889053949c4d3ce3..d0adb82d65c31eddccdb6b56522ce91e3d021fee 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_tx.c +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_tx.c @@ -69,12 +69,12 @@ int octep_iq_process_completions(struct octep_iq *iq, u16 budget) compl_sg++; dma_unmap_single(iq->dev, tx_buffer->sglist[0].dma_ptr[0], - tx_buffer->sglist[0].len[0], DMA_TO_DEVICE); + tx_buffer->sglist[0].len[3], DMA_TO_DEVICE); i = 1; /* entry 0 is main skb, unmapped above */ while (frags--) { dma_unmap_page(iq->dev, tx_buffer->sglist[i >> 2].dma_ptr[i & 3], - tx_buffer->sglist[i >> 2].len[i & 3], DMA_TO_DEVICE); + tx_buffer->sglist[i >> 2].len[3 - (i & 3)], DMA_TO_DEVICE); i++; } @@ -131,13 +131,13 @@ static void octep_iq_free_pending(struct octep_iq *iq) dma_unmap_single(iq->dev, tx_buffer->sglist[0].dma_ptr[0], - tx_buffer->sglist[0].len[0], + tx_buffer->sglist[0].len[3], DMA_TO_DEVICE); i = 1; /* entry 0 is main skb, unmapped above */ while (frags--) { dma_unmap_page(iq->dev, tx_buffer->sglist[i >> 2].dma_ptr[i & 3], - tx_buffer->sglist[i >> 2].len[i & 3], DMA_TO_DEVICE); + tx_buffer->sglist[i >> 2].len[3 - (i & 3)], DMA_TO_DEVICE); i++; } diff --git a/drivers/net/ethernet/marvell/octeon_ep/octep_tx.h b/drivers/net/ethernet/marvell/octeon_ep/octep_tx.h index 2ef57980eb47b8ee44c22c40acb9d18355f58956..21e75ff9f5e71165be04cdefd72678bd5911a663 100644 --- a/drivers/net/ethernet/marvell/octeon_ep/octep_tx.h +++ b/drivers/net/ethernet/marvell/octeon_ep/octep_tx.h @@ -17,7 +17,21 @@ #define TX_BUFTYPE_NET_SG 2 #define NUM_TX_BUFTYPES 3 -/* Hardware format for Scatter/Gather list */ +/* Hardware format for Scatter/Gather list + * + * 63 48|47 32|31 16|15 0 + * ----------------------------------------- + * | Len 0 | Len 1 | Len 2 | Len 3 | + * ----------------------------------------- + * | Ptr 0 | + * ----------------------------------------- + * | Ptr 1 | + * ----------------------------------------- + * | Ptr 2 | + * ----------------------------------------- + * | Ptr 3 | + * ----------------------------------------- + */ struct octep_tx_sglist_desc { u16 len[4]; dma_addr_t dma_ptr[4]; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index c2f68678e947e8019aa53a13c290080ebc488d0a..23c2f2ed2fb83294f4345cf55e9b5252d280b376 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -846,6 +846,21 @@ static int nix_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block, return 0; } +static void nix_get_aq_req_smq(struct rvu *rvu, struct nix_aq_enq_req *req, + u16 *smq, u16 *smq_mask) +{ + struct nix_cn10k_aq_enq_req *aq_req; + + if (!is_rvu_otx2(rvu)) { + aq_req = (struct nix_cn10k_aq_enq_req *)req; + *smq = aq_req->sq.smq; + *smq_mask = aq_req->sq_mask.smq; + } else { + *smq = req->sq.smq; + *smq_mask = req->sq_mask.smq; + } +} + static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw, struct nix_aq_enq_req *req, struct nix_aq_enq_rsp *rsp) @@ -857,6 +872,7 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw, struct rvu_block *block; struct admin_queue *aq; struct rvu_pfvf *pfvf; + u16 smq, smq_mask; void *ctx, *mask; bool ena; u64 cfg; @@ -928,13 +944,14 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw, if (rc) return rc; + nix_get_aq_req_smq(rvu, req, &smq, &smq_mask); /* Check if SQ pointed SMQ belongs to this PF/VF or not */ if (req->ctype == NIX_AQ_CTYPE_SQ && ((req->op == NIX_AQ_INSTOP_INIT && req->sq.ena) || (req->op == NIX_AQ_INSTOP_WRITE && - req->sq_mask.ena && req->sq_mask.smq && req->sq.ena))) { + req->sq_mask.ena && req->sq.ena && smq_mask))) { if (!is_valid_txschq(rvu, blkaddr, NIX_TXSCH_LVL_SMQ, - pcifunc, req->sq.smq)) + pcifunc, smq)) return NIX_AF_ERR_AQ_ENQUEUE; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c index 826f691de2595bb67ab7e5499deacd9c481aab26..a4a258da8dd59aca6d6832ca0ca77d9d091d38f8 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c @@ -107,12 +107,13 @@ int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura) } #define NPA_MAX_BURST 16 -void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) +int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) { struct otx2_nic *pfvf = dev; + int cnt = cq->pool_ptrs; u64 ptrs[NPA_MAX_BURST]; - int num_ptrs = 1; dma_addr_t bufptr; + int num_ptrs = 1; /* Refill pool with new buffers */ while (cq->pool_ptrs) { @@ -131,6 +132,7 @@ void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) num_ptrs = 1; } } + return cnt - cq->pool_ptrs; } void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h index 8ae96815865e6a2ca03878daa694d5cb5da2cd88..c1861f7de254500a659892fcbd1bed835cff2d98 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h @@ -24,7 +24,7 @@ static inline int mtu_to_dwrr_weight(struct otx2_nic *pfvf, int mtu) return weight; } -void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); +int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx); int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura); int cn10k_lmtst_init(struct otx2_nic *pfvf); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 8511906cb4e2d300034cecfb41648f723acd3546..997fedac3a98fdfeadf0f481805477ce295638db 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -574,20 +574,8 @@ int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, int otx2_alloc_buffer(struct otx2_nic *pfvf, struct otx2_cq_queue *cq, dma_addr_t *dma) { - if (unlikely(__otx2_alloc_rbuf(pfvf, cq->rbpool, dma))) { - struct refill_work *work; - struct delayed_work *dwork; - - work = &pfvf->refill_wrk[cq->cq_idx]; - dwork = &work->pool_refill_work; - /* Schedule a task if no other task is running */ - if (!cq->refill_task_sched) { - cq->refill_task_sched = true; - schedule_delayed_work(dwork, - msecs_to_jiffies(100)); - } + if (unlikely(__otx2_alloc_rbuf(pfvf, cq->rbpool, dma))) return -ENOMEM; - } return 0; } @@ -1082,39 +1070,20 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) static void otx2_pool_refill_task(struct work_struct *work) { struct otx2_cq_queue *cq; - struct otx2_pool *rbpool; struct refill_work *wrk; - int qidx, free_ptrs = 0; struct otx2_nic *pfvf; - dma_addr_t bufptr; + int qidx; wrk = container_of(work, struct refill_work, pool_refill_work.work); pfvf = wrk->pf; qidx = wrk - pfvf->refill_wrk; cq = &pfvf->qset.cq[qidx]; - rbpool = cq->rbpool; - free_ptrs = cq->pool_ptrs; - while (cq->pool_ptrs) { - if (otx2_alloc_rbuf(pfvf, rbpool, &bufptr)) { - /* Schedule a WQ if we fails to free atleast half of the - * pointers else enable napi for this RQ. - */ - if (!((free_ptrs - cq->pool_ptrs) > free_ptrs / 2)) { - struct delayed_work *dwork; - - dwork = &wrk->pool_refill_work; - schedule_delayed_work(dwork, - msecs_to_jiffies(100)); - } else { - cq->refill_task_sched = false; - } - return; - } - pfvf->hw_ops->aura_freeptr(pfvf, qidx, bufptr + OTX2_HEAD_ROOM); - cq->pool_ptrs--; - } cq->refill_task_sched = false; + + local_bh_disable(); + napi_schedule(wrk->napi); + local_bh_enable(); } int otx2_config_nix_queues(struct otx2_nic *pfvf) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 4c6032ee7800de07e646bc3ec4f2f955a6ed5824..c04a8ee53a82f1711ea62164b764b92a9d2244cc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -302,6 +302,7 @@ struct flr_work { struct refill_work { struct delayed_work pool_refill_work; struct otx2_nic *pf; + struct napi_struct *napi; }; /* PTPv2 originTimestamp structure */ @@ -370,7 +371,7 @@ struct dev_hw_ops { int (*sq_aq_init)(void *dev, u16 qidx, u16 sqb_aura); void (*sqe_flush)(void *dev, struct otx2_snd_queue *sq, int size, int qidx); - void (*refill_pool_ptrs)(void *dev, struct otx2_cq_queue *cq); + int (*refill_pool_ptrs)(void *dev, struct otx2_cq_queue *cq); void (*aura_freeptr)(void *dev, int aura, u64 buf); }; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 70b9065f7d10188180b8aa16e2b37c85d1ff7176..6daf4d58c25d6379c5e78689d8d91b301927a186 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1943,6 +1943,10 @@ int otx2_stop(struct net_device *netdev) netif_tx_disable(netdev); + for (wrk = 0; wrk < pf->qset.cq_cnt; wrk++) + cancel_delayed_work_sync(&pf->refill_wrk[wrk].pool_refill_work); + devm_kfree(pf->dev, pf->refill_wrk); + otx2_free_hw_resources(pf); otx2_free_cints(pf, pf->hw.cint_cnt); otx2_disable_napi(pf); @@ -1950,9 +1954,6 @@ int otx2_stop(struct net_device *netdev) for (qidx = 0; qidx < netdev->num_tx_queues; qidx++) netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx)); - for (wrk = 0; wrk < pf->qset.cq_cnt; wrk++) - cancel_delayed_work_sync(&pf->refill_wrk[wrk].pool_refill_work); - devm_kfree(pf->dev, pf->refill_wrk); kfree(qset->sq); kfree(qset->cq); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index e369baf115301df919a2b315e3709d82f931e4a0..53b2a4ef52985271ab6d0ee67c779af878876ba0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -29,7 +29,8 @@ static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, struct bpf_prog *prog, struct nix_cqe_rx_s *cqe, - struct otx2_cq_queue *cq); + struct otx2_cq_queue *cq, + bool *need_xdp_flush); static int otx2_nix_cq_op_status(struct otx2_nic *pfvf, struct otx2_cq_queue *cq) @@ -337,7 +338,7 @@ static bool otx2_check_rcv_errors(struct otx2_nic *pfvf, static void otx2_rcv_pkt_handler(struct otx2_nic *pfvf, struct napi_struct *napi, struct otx2_cq_queue *cq, - struct nix_cqe_rx_s *cqe) + struct nix_cqe_rx_s *cqe, bool *need_xdp_flush) { struct nix_rx_parse_s *parse = &cqe->parse; struct nix_rx_sg_s *sg = &cqe->sg; @@ -353,7 +354,7 @@ static void otx2_rcv_pkt_handler(struct otx2_nic *pfvf, } if (pfvf->xdp_prog) - if (otx2_xdp_rcv_pkt_handler(pfvf, pfvf->xdp_prog, cqe, cq)) + if (otx2_xdp_rcv_pkt_handler(pfvf, pfvf->xdp_prog, cqe, cq, need_xdp_flush)) return; skb = napi_get_frags(napi); @@ -388,6 +389,7 @@ static int otx2_rx_napi_handler(struct otx2_nic *pfvf, struct napi_struct *napi, struct otx2_cq_queue *cq, int budget) { + bool need_xdp_flush = false; struct nix_cqe_rx_s *cqe; int processed_cqe = 0; @@ -409,13 +411,15 @@ static int otx2_rx_napi_handler(struct otx2_nic *pfvf, cq->cq_head++; cq->cq_head &= (cq->cqe_cnt - 1); - otx2_rcv_pkt_handler(pfvf, napi, cq, cqe); + otx2_rcv_pkt_handler(pfvf, napi, cq, cqe, &need_xdp_flush); cqe->hdr.cqe_type = NIX_XQE_TYPE_INVALID; cqe->sg.seg_addr = 0x00; processed_cqe++; cq->pend_cqe--; } + if (need_xdp_flush) + xdp_do_flush(); /* Free CQEs to HW */ otx2_write64(pfvf, NIX_LF_CQ_OP_DOOR, @@ -424,9 +428,10 @@ static int otx2_rx_napi_handler(struct otx2_nic *pfvf, return processed_cqe; } -void otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) +int otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) { struct otx2_nic *pfvf = dev; + int cnt = cq->pool_ptrs; dma_addr_t bufptr; while (cq->pool_ptrs) { @@ -435,6 +440,8 @@ void otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) otx2_aura_freeptr(pfvf, cq->cq_idx, bufptr + OTX2_HEAD_ROOM); cq->pool_ptrs--; } + + return cnt - cq->pool_ptrs; } static int otx2_tx_napi_handler(struct otx2_nic *pfvf, @@ -521,6 +528,7 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) struct otx2_cq_queue *cq; struct otx2_qset *qset; struct otx2_nic *pfvf; + int filled_cnt = -1; cq_poll = container_of(napi, struct otx2_cq_poll, napi); pfvf = (struct otx2_nic *)cq_poll->dev; @@ -541,7 +549,7 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) } if (rx_cq && rx_cq->pool_ptrs) - pfvf->hw_ops->refill_pool_ptrs(pfvf, rx_cq); + filled_cnt = pfvf->hw_ops->refill_pool_ptrs(pfvf, rx_cq); /* Clear the IRQ */ otx2_write64(pfvf, NIX_LF_CINTX_INT(cq_poll->cint_idx), BIT_ULL(0)); @@ -561,9 +569,25 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) otx2_config_irq_coalescing(pfvf, i); } - /* Re-enable interrupts */ - otx2_write64(pfvf, NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), - BIT_ULL(0)); + if (unlikely(!filled_cnt)) { + struct refill_work *work; + struct delayed_work *dwork; + + work = &pfvf->refill_wrk[cq->cq_idx]; + dwork = &work->pool_refill_work; + /* Schedule a task if no other task is running */ + if (!cq->refill_task_sched) { + work->napi = napi; + cq->refill_task_sched = true; + schedule_delayed_work(dwork, + msecs_to_jiffies(100)); + } + } else { + /* Re-enable interrupts */ + otx2_write64(pfvf, + NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), + BIT_ULL(0)); + } } return workdone; } @@ -1334,7 +1358,8 @@ bool otx2_xdp_sq_append_pkt(struct otx2_nic *pfvf, u64 iova, int len, u16 qidx) static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, struct bpf_prog *prog, struct nix_cqe_rx_s *cqe, - struct otx2_cq_queue *cq) + struct otx2_cq_queue *cq, + bool *need_xdp_flush) { unsigned char *hard_start, *data; int qidx = cq->cq_idx; @@ -1371,8 +1396,10 @@ static bool otx2_xdp_rcv_pkt_handler(struct otx2_nic *pfvf, otx2_dma_unmap_page(pfvf, iova, pfvf->rbsize, DMA_FROM_DEVICE); - if (!err) + if (!err) { + *need_xdp_flush = true; return true; + } put_page(page); break; default: diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index 9e3bfbe5c480946da799ae02adff5e489afb0b10..a82ffca8ce1b1bcf9b9fc0b9d0707da79d3ac3e1 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -170,6 +170,6 @@ void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx); void otx2_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx); -void otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); -void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); +int otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); +int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); #endif /* OTX2_TXRX_H */ diff --git a/drivers/net/ethernet/marvell/sky2.h b/drivers/net/ethernet/marvell/sky2.h index ddec1627f1a7bdcbd573400c51141de4f499a3e6..8d0bacf4e49ccceb2331f14ac2f21cba485940e9 100644 --- a/drivers/net/ethernet/marvell/sky2.h +++ b/drivers/net/ethernet/marvell/sky2.h @@ -2195,7 +2195,7 @@ struct rx_ring_info { struct sk_buff *skb; dma_addr_t data_addr; DEFINE_DMA_UNMAP_LEN(data_size); - dma_addr_t frag_addr[ETH_JUMBO_MTU >> PAGE_SHIFT]; + dma_addr_t frag_addr[ETH_JUMBO_MTU >> PAGE_SHIFT ?: 1]; }; enum flow_control { diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 6ad42e3b488f7b4f0a8abeb419285a7f1906f18d..20afe79f380a240c55bb6c3d06a0bd614046f70c 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -2005,11 +2005,11 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, u8 *data, *new_data; struct mtk_rx_dma_v2 *rxd, trxd; int done = 0, bytes = 0; + dma_addr_t dma_addr = DMA_MAPPING_ERROR; while (done < budget) { unsigned int pktlen, *rxdcsum; struct net_device *netdev; - dma_addr_t dma_addr; u32 hash, reason; int mac = 0; @@ -2186,7 +2186,8 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, else rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); - if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA) && + likely(dma_addr != DMA_MAPPING_ERROR)) rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr); ring->calc_idx = idx; @@ -2994,6 +2995,9 @@ static int mtk_hwlro_get_fdir_all(struct net_device *dev, int i; for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + if (mac->hwlro_ip[i]) { rule_locs[cnt] = i; cnt++; @@ -3167,8 +3171,8 @@ static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth) eth->rx_events++; if (likely(napi_schedule_prep(ð->rx_napi))) { - __napi_schedule(ð->rx_napi); mtk_rx_irq_disable(eth, eth->soc->txrx.rx_irq_done_mask); + __napi_schedule(ð->rx_napi); } return IRQ_HANDLED; @@ -3180,8 +3184,8 @@ static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth) eth->tx_events++; if (likely(napi_schedule_prep(ð->tx_napi))) { - __napi_schedule(ð->tx_napi); mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); + __napi_schedule(ð->tx_napi); } return IRQ_HANDLED; diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index a70a5417c1734b7e3126147cc66c2a0444205727..a4efbeb162084134d5c43457c20d289bad33a138 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -214,9 +214,11 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, dsa_port = mtk_flow_get_dsa_port(&dev); if (dev == eth->netdev[0]) - pse_port = 1; + pse_port = PSE_GDM1_PORT; else if (dev == eth->netdev[1]) - pse_port = 2; + pse_port = PSE_GDM2_PORT; + else if (dev == eth->netdev[2]) + pse_port = PSE_GDM3_PORT; else return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c index 92d3952dfa8b78fc067e515cd1c78b9662fea038..feeb41693c176eaeb805251d328610c23b4707d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/ct.c @@ -17,8 +17,10 @@ tc_act_parse_ct(struct mlx5e_tc_act_parse_state *parse_state, if (err) return err; - if (mlx5e_is_eswitch_flow(parse_state->flow)) + if (mlx5e_is_eswitch_flow(parse_state->flow)) { attr->esw_attr->split_count = attr->esw_attr->out_count; + parse_state->if_count = 0; + } attr->flags |= MLX5_ATTR_FLAG_CT; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c index 291193f7120d5f68f982f75ccbb1e7238db1999b..f63402c480280c735dac1e219b14f4ca8ed0b5b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/mirred.c @@ -294,6 +294,7 @@ parse_mirred_ovs_master(struct mlx5e_tc_act_parse_state *parse_state, if (err) return err; + parse_state->if_count = 0; esw_attr->out_count++; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c index 3b272bbf4c53806ffc5559126ebe44491bc4d906..368a95fa77d3263faa2749c2c33e61ddbbc1aa91 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/pedit.c @@ -98,8 +98,10 @@ tc_act_parse_pedit(struct mlx5e_tc_act_parse_state *parse_state, attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; - if (ns_type == MLX5_FLOW_NAMESPACE_FDB) + if (ns_type == MLX5_FLOW_NAMESPACE_FDB) { esw_attr->split_count = esw_attr->out_count; + parse_state->if_count = 0; + } return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c index ad09a8a5f36e0441f53cc039c27da72c4091b0a5..2d1d4a04501b47b85613cfc876d694b64e78442f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/redirect_ingress.c @@ -66,6 +66,7 @@ tc_act_parse_redirect_ingress(struct mlx5e_tc_act_parse_state *parse_state, if (err) return err; + parse_state->if_count = 0; esw_attr->out_count++; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c index c8a3eaf189f6ace825aff2c9641e61a0285ddb91..a13c5e707b83cfe62593cae080a3b880172fb6b8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan.c @@ -166,6 +166,7 @@ tc_act_parse_vlan(struct mlx5e_tc_act_parse_state *parse_state, return err; esw_attr->split_count = esw_attr->out_count; + parse_state->if_count = 0; return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c index 310b992307607cc86a626c834195506308d64694..f17575b09788db241a3a39e0ca75a8a17c925346 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/vlan_mangle.c @@ -65,8 +65,10 @@ tc_act_parse_vlan_mangle(struct mlx5e_tc_act_parse_state *parse_state, if (err) return err; - if (ns_type == MLX5_FLOW_NAMESPACE_FDB) + if (ns_type == MLX5_FLOW_NAMESPACE_FDB) { attr->esw_attr->split_count = attr->esw_attr->out_count; + parse_state->if_count = 0; + } return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 318083690fcd26a8134bbbc56edb6ef3dd3eabf2..c24828b688ac0aa049f3518aefdf21251da4e8d9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -3936,6 +3936,7 @@ parse_tc_actions(struct mlx5e_tc_act_parse_state *parse_state, } i_split = i + 1; + parse_state->if_count = 0; list_add(&attr->list, &flow->attrs); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 6cd7d6497e10996f891774d5406f10dfce2ba889..d4cde655506323deac75918aa1429b8e6ce4d721 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1276,12 +1276,19 @@ int mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, enum mlx5_eswitch_vport_event enabled_events) { + bool pf_needed; int ret; + pf_needed = mlx5_core_is_ecpf_esw_manager(esw->dev) || + esw->mode == MLX5_ESWITCH_LEGACY; + /* Enable PF vport */ - ret = mlx5_eswitch_load_pf_vf_vport(esw, MLX5_VPORT_PF, enabled_events); - if (ret) - return ret; + if (pf_needed) { + ret = mlx5_eswitch_load_pf_vf_vport(esw, MLX5_VPORT_PF, + enabled_events); + if (ret) + return ret; + } /* Enable external host PF HCA */ ret = host_pf_enable_hca(esw->dev); @@ -1317,7 +1324,8 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw, ecpf_err: host_pf_disable_hca(esw->dev); pf_hca_err: - mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_PF); + if (pf_needed) + mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_PF); return ret; } @@ -1335,7 +1343,10 @@ void mlx5_eswitch_disable_pf_vf_vports(struct mlx5_eswitch *esw) } host_pf_disable_hca(esw->dev); - mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_PF); + + if (mlx5_core_is_ecpf_esw_manager(esw->dev) || + esw->mode == MLX5_ESWITCH_LEGACY) + mlx5_eswitch_unload_pf_vf_vport(esw, MLX5_VPORT_PF); } static void mlx5_eswitch_get_devlink_param(struct mlx5_eswitch *esw) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 752fb0dfb111b27075cd1ead2688d83bb6d1bac7..b296ac52a43974fc17e372a403a981a9220ab273 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3216,26 +3216,47 @@ esw_vport_destroy_offloads_acl_tables(struct mlx5_eswitch *esw, esw_acl_ingress_ofld_cleanup(esw, vport); } -static int esw_create_uplink_offloads_acl_tables(struct mlx5_eswitch *esw) +static int esw_create_offloads_acl_tables(struct mlx5_eswitch *esw) { - struct mlx5_vport *vport; + struct mlx5_vport *uplink, *manager; + int ret; - vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_UPLINK); - if (IS_ERR(vport)) - return PTR_ERR(vport); + uplink = mlx5_eswitch_get_vport(esw, MLX5_VPORT_UPLINK); + if (IS_ERR(uplink)) + return PTR_ERR(uplink); + + ret = esw_vport_create_offloads_acl_tables(esw, uplink); + if (ret) + return ret; + + manager = mlx5_eswitch_get_vport(esw, esw->manager_vport); + if (IS_ERR(manager)) { + ret = PTR_ERR(manager); + goto err_manager; + } - return esw_vport_create_offloads_acl_tables(esw, vport); + ret = esw_vport_create_offloads_acl_tables(esw, manager); + if (ret) + goto err_manager; + + return 0; + +err_manager: + esw_vport_destroy_offloads_acl_tables(esw, uplink); + return ret; } -static void esw_destroy_uplink_offloads_acl_tables(struct mlx5_eswitch *esw) +static void esw_destroy_offloads_acl_tables(struct mlx5_eswitch *esw) { struct mlx5_vport *vport; - vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_UPLINK); - if (IS_ERR(vport)) - return; + vport = mlx5_eswitch_get_vport(esw, esw->manager_vport); + if (!IS_ERR(vport)) + esw_vport_destroy_offloads_acl_tables(esw, vport); - esw_vport_destroy_offloads_acl_tables(esw, vport); + vport = mlx5_eswitch_get_vport(esw, MLX5_VPORT_UPLINK); + if (!IS_ERR(vport)) + esw_vport_destroy_offloads_acl_tables(esw, vport); } int mlx5_eswitch_reload_reps(struct mlx5_eswitch *esw) @@ -3280,7 +3301,7 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw) } esw->fdb_table.offloads.indir = indir; - err = esw_create_uplink_offloads_acl_tables(esw); + err = esw_create_offloads_acl_tables(esw); if (err) goto create_acl_err; @@ -3321,7 +3342,7 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw) create_restore_err: esw_destroy_offloads_table(esw); create_offloads_err: - esw_destroy_uplink_offloads_acl_tables(esw); + esw_destroy_offloads_acl_tables(esw); create_acl_err: mlx5_esw_indir_table_destroy(esw->fdb_table.offloads.indir); create_indir_err: @@ -3337,7 +3358,7 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) esw_destroy_offloads_fdb_tables(esw); esw_destroy_restore_table(esw); esw_destroy_offloads_table(esw); - esw_destroy_uplink_offloads_acl_tables(esw); + esw_destroy_offloads_acl_tables(esw); mlx5_esw_indir_table_destroy(esw->fdb_table.offloads.indir); mutex_destroy(&esw->fdb_table.offloads.vports.lock); } diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 329e374b9539c9ac684149e441b6fd1b6c4dadf8..43ba71e82260c1db39a91c64c4b3f9e68e0a5539 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -46,6 +46,7 @@ config LAN743X tristate "LAN743x support" depends on PCI depends on PTP_1588_CLOCK_OPTIONAL + select PHYLIB select FIXED_PHY select CRC16 select CRC32 diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 300fe1a93dce81f78463d0e96c99b7eeea97ee7a..ef980e4e5bc2fa22fce9a064116a3393b50df107 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1021,18 +1021,32 @@ static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri, list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { newckf = kmemdup(ckf, sizeof(*newckf), GFP_KERNEL); if (!newckf) - return ERR_PTR(-ENOMEM); + goto err; list_add_tail(&newckf->ctrl.list, &duprule->data.keyfields); } list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { newcaf = kmemdup(caf, sizeof(*newcaf), GFP_KERNEL); if (!newcaf) - return ERR_PTR(-ENOMEM); + goto err; list_add_tail(&newcaf->ctrl.list, &duprule->data.actionfields); } return duprule; + +err: + list_for_each_entry_safe(ckf, newckf, &duprule->data.keyfields, ctrl.list) { + list_del(&ckf->ctrl.list); + kfree(ckf); + } + + list_for_each_entry_safe(caf, newcaf, &duprule->data.actionfields, ctrl.list) { + list_del(&caf->ctrl.list); + kfree(caf); + } + + kfree(duprule); + return ERR_PTR(-ENOMEM); } static void vcap_apply_width(u8 *dst, int width, int bytes) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c index c07f25e791c7633a7cd1f4c297976cb41e6bb451..fe4e166de8a0458ffa509f2238a45ae33920743a 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api_kunit.c @@ -243,10 +243,9 @@ static void vcap_test_api_init(struct vcap_admin *admin) } /* Helper function to create a rule of a specific size */ -static struct vcap_rule * -test_vcap_xn_rule_creator(struct kunit *test, int cid, enum vcap_user user, - u16 priority, - int id, int size, int expected_addr) +static void test_vcap_xn_rule_creator(struct kunit *test, int cid, + enum vcap_user user, u16 priority, + int id, int size, int expected_addr) { struct vcap_rule *rule; struct vcap_rule_internal *ri; @@ -311,7 +310,7 @@ test_vcap_xn_rule_creator(struct kunit *test, int cid, enum vcap_user user, ret = vcap_add_rule(rule); KUNIT_EXPECT_EQ(test, 0, ret); KUNIT_EXPECT_EQ(test, expected_addr, ri->addr); - return rule; + vcap_free_rule(rule); } /* Prepare testing rule deletion */ @@ -995,6 +994,16 @@ static void vcap_api_encode_rule_actionset_test(struct kunit *test) KUNIT_EXPECT_EQ(test, (u32)0x00000000, actwords[11]); } +static void vcap_free_ckf(struct vcap_rule *rule) +{ + struct vcap_client_keyfield *ckf, *next_ckf; + + list_for_each_entry_safe(ckf, next_ckf, &rule->keyfields, ctrl.list) { + list_del(&ckf->ctrl.list); + kfree(ckf); + } +} + static void vcap_api_rule_add_keyvalue_test(struct kunit *test) { struct vcap_admin admin = { @@ -1027,6 +1036,7 @@ static void vcap_api_rule_add_keyvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value); KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask); + vcap_free_ckf(rule); INIT_LIST_HEAD(&rule->keyfields); ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, VCAP_BIT_1); @@ -1039,6 +1049,7 @@ static void vcap_api_rule_add_keyvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.value); KUNIT_EXPECT_EQ(test, 0x1, kf->data.u1.mask); + vcap_free_ckf(rule); INIT_LIST_HEAD(&rule->keyfields); ret = vcap_rule_add_key_bit(rule, VCAP_KF_LOOKUP_FIRST_IS, @@ -1052,6 +1063,7 @@ static void vcap_api_rule_add_keyvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, kf->ctrl.type); KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.value); KUNIT_EXPECT_EQ(test, 0x0, kf->data.u1.mask); + vcap_free_ckf(rule); INIT_LIST_HEAD(&rule->keyfields); ret = vcap_rule_add_key_u32(rule, VCAP_KF_TYPE, 0x98765432, 0xff00ffab); @@ -1064,6 +1076,7 @@ static void vcap_api_rule_add_keyvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, kf->ctrl.type); KUNIT_EXPECT_EQ(test, 0x98765432, kf->data.u32.value); KUNIT_EXPECT_EQ(test, 0xff00ffab, kf->data.u32.mask); + vcap_free_ckf(rule); INIT_LIST_HEAD(&rule->keyfields); ret = vcap_rule_add_key_u128(rule, VCAP_KF_L3_IP6_SIP, &dip); @@ -1078,6 +1091,18 @@ static void vcap_api_rule_add_keyvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, dip.value[idx], kf->data.u128.value[idx]); for (idx = 0; idx < ARRAY_SIZE(dip.mask); ++idx) KUNIT_EXPECT_EQ(test, dip.mask[idx], kf->data.u128.mask[idx]); + vcap_free_ckf(rule); +} + +static void vcap_free_caf(struct vcap_rule *rule) +{ + struct vcap_client_actionfield *caf, *next_caf; + + list_for_each_entry_safe(caf, next_caf, + &rule->actionfields, ctrl.list) { + list_del(&caf->ctrl.list); + kfree(caf); + } } static void vcap_api_rule_add_actionvalue_test(struct kunit *test) @@ -1105,6 +1130,7 @@ static void vcap_api_rule_add_actionvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value); + vcap_free_caf(rule); INIT_LIST_HEAD(&rule->actionfields); ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_1); @@ -1116,6 +1142,7 @@ static void vcap_api_rule_add_actionvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); KUNIT_EXPECT_EQ(test, 0x1, af->data.u1.value); + vcap_free_caf(rule); INIT_LIST_HEAD(&rule->actionfields); ret = vcap_rule_add_action_bit(rule, VCAP_AF_POLICE_ENA, VCAP_BIT_ANY); @@ -1127,6 +1154,7 @@ static void vcap_api_rule_add_actionvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_AF_POLICE_ENA, af->ctrl.action); KUNIT_EXPECT_EQ(test, VCAP_FIELD_BIT, af->ctrl.type); KUNIT_EXPECT_EQ(test, 0x0, af->data.u1.value); + vcap_free_caf(rule); INIT_LIST_HEAD(&rule->actionfields); ret = vcap_rule_add_action_u32(rule, VCAP_AF_TYPE, 0x98765432); @@ -1138,6 +1166,7 @@ static void vcap_api_rule_add_actionvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_AF_TYPE, af->ctrl.action); KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type); KUNIT_EXPECT_EQ(test, 0x98765432, af->data.u32.value); + vcap_free_caf(rule); INIT_LIST_HEAD(&rule->actionfields); ret = vcap_rule_add_action_u32(rule, VCAP_AF_MASK_MODE, 0xaabbccdd); @@ -1149,6 +1178,7 @@ static void vcap_api_rule_add_actionvalue_test(struct kunit *test) KUNIT_EXPECT_EQ(test, VCAP_AF_MASK_MODE, af->ctrl.action); KUNIT_EXPECT_EQ(test, VCAP_FIELD_U32, af->ctrl.type); KUNIT_EXPECT_EQ(test, 0xaabbccdd, af->data.u32.value); + vcap_free_caf(rule); } static void vcap_api_rule_find_keyset_basic_test(struct kunit *test) @@ -1408,6 +1438,10 @@ static void vcap_api_encode_rule_test(struct kunit *test) ret = list_empty(&is2_admin.rules); KUNIT_EXPECT_EQ(test, false, ret); KUNIT_EXPECT_EQ(test, 0, ret); + + vcap_enable_lookups(&test_vctrl, &test_netdev, 0, 0, + rule->cookie, false); + vcap_free_rule(rule); /* Check that the rule has been freed: tricky to access since this @@ -1418,6 +1452,8 @@ static void vcap_api_encode_rule_test(struct kunit *test) KUNIT_EXPECT_EQ(test, true, ret); ret = list_empty(&rule->actionfields); KUNIT_EXPECT_EQ(test, true, ret); + + vcap_del_rule(&test_vctrl, &test_netdev, id); } static void vcap_api_set_rule_counter_test(struct kunit *test) @@ -1561,6 +1597,11 @@ static void vcap_api_rule_insert_in_order_test(struct kunit *test) test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 20, 400, 6, 774); test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 30, 300, 3, 771); test_vcap_xn_rule_creator(test, 10000, VCAP_USER_QOS, 40, 200, 2, 768); + + vcap_del_rule(&test_vctrl, &test_netdev, 200); + vcap_del_rule(&test_vctrl, &test_netdev, 300); + vcap_del_rule(&test_vctrl, &test_netdev, 400); + vcap_del_rule(&test_vctrl, &test_netdev, 500); } static void vcap_api_rule_insert_reverse_order_test(struct kunit *test) @@ -1619,6 +1660,11 @@ static void vcap_api_rule_insert_reverse_order_test(struct kunit *test) ++idx; } KUNIT_EXPECT_EQ(test, 768, admin.last_used_addr); + + vcap_del_rule(&test_vctrl, &test_netdev, 500); + vcap_del_rule(&test_vctrl, &test_netdev, 400); + vcap_del_rule(&test_vctrl, &test_netdev, 300); + vcap_del_rule(&test_vctrl, &test_netdev, 200); } static void vcap_api_rule_remove_at_end_test(struct kunit *test) @@ -1819,6 +1865,9 @@ static void vcap_api_rule_remove_in_front_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 786, test_init_start); KUNIT_EXPECT_EQ(test, 8, test_init_count); KUNIT_EXPECT_EQ(test, 794, admin.last_used_addr); + + vcap_del_rule(&test_vctrl, &test_netdev, 200); + vcap_del_rule(&test_vctrl, &test_netdev, 300); } static struct kunit_case vcap_api_rule_remove_test_cases[] = { diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 4a16ebff3d1d532a2a79f982b297a8bfd71f1fb4..48ea4aeeea5d4c95f98317d8c712b0c7fc353c13 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -91,63 +91,137 @@ static unsigned int mana_checksum_info(struct sk_buff *skb) return 0; } +static void mana_add_sge(struct mana_tx_package *tp, struct mana_skb_head *ash, + int sg_i, dma_addr_t da, int sge_len, u32 gpa_mkey) +{ + ash->dma_handle[sg_i] = da; + ash->size[sg_i] = sge_len; + + tp->wqe_req.sgl[sg_i].address = da; + tp->wqe_req.sgl[sg_i].mem_key = gpa_mkey; + tp->wqe_req.sgl[sg_i].size = sge_len; +} + static int mana_map_skb(struct sk_buff *skb, struct mana_port_context *apc, - struct mana_tx_package *tp) + struct mana_tx_package *tp, int gso_hs) { struct mana_skb_head *ash = (struct mana_skb_head *)skb->head; + int hsg = 1; /* num of SGEs of linear part */ struct gdma_dev *gd = apc->ac->gdma_dev; + int skb_hlen = skb_headlen(skb); + int sge0_len, sge1_len = 0; struct gdma_context *gc; struct device *dev; skb_frag_t *frag; dma_addr_t da; + int sg_i; int i; gc = gd->gdma_context; dev = gc->dev; - da = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); + if (gso_hs && gso_hs < skb_hlen) { + sge0_len = gso_hs; + sge1_len = skb_hlen - gso_hs; + } else { + sge0_len = skb_hlen; + } + + da = dma_map_single(dev, skb->data, sge0_len, DMA_TO_DEVICE); if (dma_mapping_error(dev, da)) return -ENOMEM; - ash->dma_handle[0] = da; - ash->size[0] = skb_headlen(skb); + mana_add_sge(tp, ash, 0, da, sge0_len, gd->gpa_mkey); - tp->wqe_req.sgl[0].address = ash->dma_handle[0]; - tp->wqe_req.sgl[0].mem_key = gd->gpa_mkey; - tp->wqe_req.sgl[0].size = ash->size[0]; + if (sge1_len) { + sg_i = 1; + da = dma_map_single(dev, skb->data + sge0_len, sge1_len, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, da)) + goto frag_err; + + mana_add_sge(tp, ash, sg_i, da, sge1_len, gd->gpa_mkey); + hsg = 2; + } for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + sg_i = hsg + i; + frag = &skb_shinfo(skb)->frags[i]; da = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), DMA_TO_DEVICE); - if (dma_mapping_error(dev, da)) goto frag_err; - ash->dma_handle[i + 1] = da; - ash->size[i + 1] = skb_frag_size(frag); - - tp->wqe_req.sgl[i + 1].address = ash->dma_handle[i + 1]; - tp->wqe_req.sgl[i + 1].mem_key = gd->gpa_mkey; - tp->wqe_req.sgl[i + 1].size = ash->size[i + 1]; + mana_add_sge(tp, ash, sg_i, da, skb_frag_size(frag), + gd->gpa_mkey); } return 0; frag_err: - for (i = i - 1; i >= 0; i--) - dma_unmap_page(dev, ash->dma_handle[i + 1], ash->size[i + 1], + for (i = sg_i - 1; i >= hsg; i--) + dma_unmap_page(dev, ash->dma_handle[i], ash->size[i], DMA_TO_DEVICE); - dma_unmap_single(dev, ash->dma_handle[0], ash->size[0], DMA_TO_DEVICE); + for (i = hsg - 1; i >= 0; i--) + dma_unmap_single(dev, ash->dma_handle[i], ash->size[i], + DMA_TO_DEVICE); return -ENOMEM; } +/* Handle the case when GSO SKB linear length is too large. + * MANA NIC requires GSO packets to put only the packet header to SGE0. + * So, we need 2 SGEs for the skb linear part which contains more than the + * header. + * Return a positive value for the number of SGEs, or a negative value + * for an error. + */ +static int mana_fix_skb_head(struct net_device *ndev, struct sk_buff *skb, + int gso_hs) +{ + int num_sge = 1 + skb_shinfo(skb)->nr_frags; + int skb_hlen = skb_headlen(skb); + + if (gso_hs < skb_hlen) { + num_sge++; + } else if (gso_hs > skb_hlen) { + if (net_ratelimit()) + netdev_err(ndev, + "TX nonlinear head: hs:%d, skb_hlen:%d\n", + gso_hs, skb_hlen); + + return -EINVAL; + } + + return num_sge; +} + +/* Get the GSO packet's header size */ +static int mana_get_gso_hs(struct sk_buff *skb) +{ + int gso_hs; + + if (skb->encapsulation) { + gso_hs = skb_inner_tcp_all_headers(skb); + } else { + if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) { + gso_hs = skb_transport_offset(skb) + + sizeof(struct udphdr); + } else { + gso_hs = skb_tcp_all_headers(skb); + } + } + + return gso_hs; +} + netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) { enum mana_tx_pkt_format pkt_fmt = MANA_SHORT_PKT_FMT; struct mana_port_context *apc = netdev_priv(ndev); + int gso_hs = 0; /* zero for non-GSO pkts */ u16 txq_idx = skb_get_queue_mapping(skb); struct gdma_dev *gd = apc->ac->gdma_dev; bool ipv4 = false, ipv6 = false; @@ -159,7 +233,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct mana_txq *txq; struct mana_cq *cq; int err, len; - u16 ihs; if (unlikely(!apc->port_is_up)) goto tx_drop; @@ -209,19 +282,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) pkg.wqe_req.client_data_unit = 0; pkg.wqe_req.num_sge = 1 + skb_shinfo(skb)->nr_frags; - WARN_ON_ONCE(pkg.wqe_req.num_sge > MAX_TX_WQE_SGL_ENTRIES); - - if (pkg.wqe_req.num_sge <= ARRAY_SIZE(pkg.sgl_array)) { - pkg.wqe_req.sgl = pkg.sgl_array; - } else { - pkg.sgl_ptr = kmalloc_array(pkg.wqe_req.num_sge, - sizeof(struct gdma_sge), - GFP_ATOMIC); - if (!pkg.sgl_ptr) - goto tx_drop_count; - - pkg.wqe_req.sgl = pkg.sgl_ptr; - } if (skb->protocol == htons(ETH_P_IP)) ipv4 = true; @@ -229,6 +289,26 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) ipv6 = true; if (skb_is_gso(skb)) { + int num_sge; + + gso_hs = mana_get_gso_hs(skb); + + num_sge = mana_fix_skb_head(ndev, skb, gso_hs); + if (num_sge > 0) + pkg.wqe_req.num_sge = num_sge; + else + goto tx_drop_count; + + u64_stats_update_begin(&tx_stats->syncp); + if (skb->encapsulation) { + tx_stats->tso_inner_packets++; + tx_stats->tso_inner_bytes += skb->len - gso_hs; + } else { + tx_stats->tso_packets++; + tx_stats->tso_bytes += skb->len - gso_hs; + } + u64_stats_update_end(&tx_stats->syncp); + pkg.tx_oob.s_oob.is_outer_ipv4 = ipv4; pkg.tx_oob.s_oob.is_outer_ipv6 = ipv6; @@ -252,28 +332,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); } - - if (skb->encapsulation) { - ihs = skb_inner_tcp_all_headers(skb); - u64_stats_update_begin(&tx_stats->syncp); - tx_stats->tso_inner_packets++; - tx_stats->tso_inner_bytes += skb->len - ihs; - u64_stats_update_end(&tx_stats->syncp); - } else { - if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4) { - ihs = skb_transport_offset(skb) + sizeof(struct udphdr); - } else { - ihs = skb_tcp_all_headers(skb); - if (ipv6_has_hopopt_jumbo(skb)) - ihs -= sizeof(struct hop_jumbo_hdr); - } - - u64_stats_update_begin(&tx_stats->syncp); - tx_stats->tso_packets++; - tx_stats->tso_bytes += skb->len - ihs; - u64_stats_update_end(&tx_stats->syncp); - } - } else if (skb->ip_summed == CHECKSUM_PARTIAL) { csum_type = mana_checksum_info(skb); @@ -296,11 +354,25 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev) } else { /* Can't do offload of this type of checksum */ if (skb_checksum_help(skb)) - goto free_sgl_ptr; + goto tx_drop_count; } } - if (mana_map_skb(skb, apc, &pkg)) { + WARN_ON_ONCE(pkg.wqe_req.num_sge > MAX_TX_WQE_SGL_ENTRIES); + + if (pkg.wqe_req.num_sge <= ARRAY_SIZE(pkg.sgl_array)) { + pkg.wqe_req.sgl = pkg.sgl_array; + } else { + pkg.sgl_ptr = kmalloc_array(pkg.wqe_req.num_sge, + sizeof(struct gdma_sge), + GFP_ATOMIC); + if (!pkg.sgl_ptr) + goto tx_drop_count; + + pkg.wqe_req.sgl = pkg.sgl_ptr; + } + + if (mana_map_skb(skb, apc, &pkg, gso_hs)) { u64_stats_update_begin(&tx_stats->syncp); tx_stats->mana_map_err++; u64_stats_update_end(&tx_stats->syncp); @@ -1258,11 +1330,16 @@ static void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc) struct mana_skb_head *ash = (struct mana_skb_head *)skb->head; struct gdma_context *gc = apc->ac->gdma_dev->gdma_context; struct device *dev = gc->dev; - int i; + int hsg, i; - dma_unmap_single(dev, ash->dma_handle[0], ash->size[0], DMA_TO_DEVICE); + /* Number of SGEs of linear part */ + hsg = (skb_is_gso(skb) && skb_headlen(skb) > ash->size[0]) ? 2 : 1; - for (i = 1; i < skb_shinfo(skb)->nr_frags + 1; i++) + for (i = 0; i < hsg; i++) + dma_unmap_single(dev, ash->dma_handle[i], ash->size[i], + DMA_TO_DEVICE); + + for (i = hsg; i < skb_shinfo(skb)->nr_frags + hsg; i++) dma_unmap_page(dev, ash->dma_handle[i], ash->size[i], DMA_TO_DEVICE); } @@ -1317,19 +1394,23 @@ static void mana_poll_tx_cq(struct mana_cq *cq) case CQE_TX_VPORT_IDX_OUT_OF_RANGE: case CQE_TX_VPORT_DISABLED: case CQE_TX_VLAN_TAGGING_VIOLATION: - WARN_ONCE(1, "TX: CQE error %d: ignored.\n", - cqe_oob->cqe_hdr.cqe_type); + if (net_ratelimit()) + netdev_err(ndev, "TX: CQE error %d\n", + cqe_oob->cqe_hdr.cqe_type); + apc->eth_stats.tx_cqe_err++; break; default: - /* If the CQE type is unexpected, log an error, assert, - * and go through the error path. + /* If the CQE type is unknown, log an error, + * and still free the SKB, update tail, etc. */ - WARN_ONCE(1, "TX: Unexpected CQE type %d: HW BUG?\n", - cqe_oob->cqe_hdr.cqe_type); + if (net_ratelimit()) + netdev_err(ndev, "TX: unknown CQE type %d\n", + cqe_oob->cqe_hdr.cqe_type); + apc->eth_stats.tx_cqe_unknown_type++; - return; + break; } if (WARN_ON_ONCE(txq->gdma_txq_id != completions[i].wq_num)) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h index 6aac98bcb9f4b9f2e16503acc28590ce598779f4..aae4131f146a88fb9b447b80f2dd4617211bed64 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h +++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h @@ -187,6 +187,7 @@ typedef void (*ionic_desc_cb)(struct ionic_queue *q, struct ionic_desc_info *desc_info, struct ionic_cq_info *cq_info, void *cb_arg); +#define IONIC_MAX_BUF_LEN ((u16)-1) #define IONIC_PAGE_SIZE PAGE_SIZE #define IONIC_PAGE_SPLIT_SZ (PAGE_SIZE / 2) #define IONIC_PAGE_GFP_MASK (GFP_ATOMIC | __GFP_NOWARN |\ diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c index 26798fc635dbddf04947fb585ab2df4e26c7452f..44466e8c5d77bb819e783135c33651af5f2b9e79 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c @@ -207,7 +207,8 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, return NULL; } - frag_len = min_t(u16, len, IONIC_PAGE_SIZE - buf_info->page_offset); + frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, + IONIC_PAGE_SIZE - buf_info->page_offset)); len -= frag_len; dma_sync_single_for_cpu(dev, @@ -452,7 +453,8 @@ void ionic_rx_fill(struct ionic_queue *q) /* fill main descriptor - buf[0] */ desc->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); - frag_len = min_t(u16, len, IONIC_PAGE_SIZE - buf_info->page_offset); + frag_len = min_t(u16, len, min_t(u32, IONIC_MAX_BUF_LEN, + IONIC_PAGE_SIZE - buf_info->page_offset)); desc->len = cpu_to_le16(frag_len); remain_len -= frag_len; buf_info++; @@ -471,7 +473,9 @@ void ionic_rx_fill(struct ionic_queue *q) } sg_elem->addr = cpu_to_le64(buf_info->dma_addr + buf_info->page_offset); - frag_len = min_t(u16, remain_len, IONIC_PAGE_SIZE - buf_info->page_offset); + frag_len = min_t(u16, remain_len, min_t(u32, IONIC_MAX_BUF_LEN, + IONIC_PAGE_SIZE - + buf_info->page_offset)); sg_elem->len = cpu_to_le16(frag_len); remain_len -= frag_len; buf_info++; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.h b/drivers/net/ethernet/qlogic/qed/qed_ll2.h index 0bfc375161ed658b0166fef560016d6a1072603d..a174c6fc626acb0f0a6f255108bf0e24fd52418f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.h @@ -110,9 +110,9 @@ struct qed_ll2_info { enum core_tx_dest tx_dest; u8 tx_stats_en; bool main_func_queue; + struct qed_ll2_cbs cbs; struct qed_ll2_rx_queue rx_queue; struct qed_ll2_tx_queue tx_queue; - struct qed_ll2_cbs cbs; }; extern const struct qed_ll2_ops qed_ll2_ops_pass; diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index 6083b1c8e4fb9ad3c8f890678f12a5126da4269a..fc01ad3f340d69b744eecfb3021ae5dee7b036fe 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -4,6 +4,7 @@ * Copyright (C) 2022 Renesas Electronics Corporation */ +#include #include #include #include @@ -799,6 +800,7 @@ static int rswitch_poll(struct napi_struct *napi, int budget) struct net_device *ndev = napi->dev; struct rswitch_private *priv; struct rswitch_device *rdev; + unsigned long flags; int quota = budget; rdev = netdev_priv(ndev); @@ -816,10 +818,12 @@ static int rswitch_poll(struct napi_struct *napi, int budget) netif_wake_subqueue(ndev, 0); - napi_complete(napi); - - rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); - rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); + if (napi_complete_done(napi, budget - quota)) { + spin_lock_irqsave(&priv->lock, flags); + rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); + rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); + spin_unlock_irqrestore(&priv->lock, flags); + } out: return budget - quota; @@ -835,8 +839,10 @@ static void rswitch_queue_interrupt(struct net_device *ndev) struct rswitch_device *rdev = netdev_priv(ndev); if (napi_schedule_prep(&rdev->napi)) { + spin_lock(&rdev->priv->lock); rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); + spin_unlock(&rdev->priv->lock); __napi_schedule(&rdev->napi); } } @@ -1044,7 +1050,7 @@ static void rswitch_rmac_setting(struct rswitch_etha *etha, const u8 *mac) static void rswitch_etha_enable_mii(struct rswitch_etha *etha) { rswitch_modify(etha->addr, MPIC, MPIC_PSMCS_MASK | MPIC_PSMHT_MASK, - MPIC_PSMCS(0x05) | MPIC_PSMHT(0x06)); + MPIC_PSMCS(etha->psmcs) | MPIC_PSMHT(0x06)); rswitch_modify(etha->addr, MPSM, 0, MPSM_MFF_C45); } @@ -1440,14 +1446,17 @@ static void rswitch_ether_port_deinit_all(struct rswitch_private *priv) static int rswitch_open(struct net_device *ndev) { struct rswitch_device *rdev = netdev_priv(ndev); + unsigned long flags; phy_start(ndev->phydev); napi_enable(&rdev->napi); netif_start_queue(ndev); + spin_lock_irqsave(&rdev->priv->lock, flags); rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true); rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true); + spin_unlock_irqrestore(&rdev->priv->lock, flags); if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS)) iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE); @@ -1461,6 +1470,7 @@ static int rswitch_stop(struct net_device *ndev) { struct rswitch_device *rdev = netdev_priv(ndev); struct rswitch_gwca_ts_info *ts_info, *ts_info2; + unsigned long flags; netif_tx_stop_all_queues(ndev); bitmap_clear(rdev->priv->opened_ports, rdev->port, 1); @@ -1476,8 +1486,10 @@ static int rswitch_stop(struct net_device *ndev) kfree(ts_info); } + spin_lock_irqsave(&rdev->priv->lock, flags); rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); + spin_unlock_irqrestore(&rdev->priv->lock, flags); phy_stop(ndev->phydev); napi_disable(&rdev->napi); @@ -1682,6 +1694,12 @@ static void rswitch_etha_init(struct rswitch_private *priv, int index) etha->index = index; etha->addr = priv->addr + RSWITCH_ETHA_OFFSET + index * RSWITCH_ETHA_SIZE; etha->coma_addr = priv->addr; + + /* MPIC.PSMCS = (clk [MHz] / (MDC frequency [MHz] * 2) - 1. + * Calculating PSMCS value as MDC frequency = 2.5MHz. So, multiply + * both the numerator and the denominator by 10. + */ + etha->psmcs = clk_get_rate(priv->clk) / 100000 / (25 * 2) - 1; } static int rswitch_device_alloc(struct rswitch_private *priv, int index) @@ -1887,6 +1905,11 @@ static int renesas_eth_sw_probe(struct platform_device *pdev) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + spin_lock_init(&priv->lock); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); attr = soc_device_match(rswitch_soc_no_speed_change); if (attr) diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h index 54f397effbc6b1ee17f669f6411776634a4ff586..04f49a7a58435600c6a5f2aa801755e7f4906142 100644 --- a/drivers/net/ethernet/renesas/rswitch.h +++ b/drivers/net/ethernet/renesas/rswitch.h @@ -915,6 +915,7 @@ struct rswitch_etha { bool external_phy; struct mii_bus *mii; phy_interface_t phy_interface; + u32 psmcs; u8 mac_addr[MAX_ADDR_LEN]; int link; int speed; @@ -1011,6 +1012,9 @@ struct rswitch_private { struct rswitch_etha etha[RSWITCH_NUM_PORTS]; struct rswitch_mfwd mfwd; + spinlock_t lock; /* lock interrupt registers' control */ + struct clk *clk; + bool etha_no_runtime_change; bool gwca_halt; }; diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 2375cef577e44e2db689a24bb50f9d37cdcce33a..f77a2d3ef37ecaa566be2458df36b56499984818 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -359,26 +359,36 @@ static bool efx_do_xdp(struct efx_nic *efx, struct efx_channel *channel, /* Handle a received packet. Second half: Touches packet payload. */ void __efx_rx_packet(struct efx_channel *channel) { + struct efx_rx_queue *rx_queue = efx_channel_get_rx_queue(channel); struct efx_nic *efx = channel->efx; struct efx_rx_buffer *rx_buf = - efx_rx_buffer(&channel->rx_queue, channel->rx_pkt_index); + efx_rx_buffer(rx_queue, channel->rx_pkt_index); u8 *eh = efx_rx_buf_va(rx_buf); /* Read length from the prefix if necessary. This already * excludes the length of the prefix itself. */ - if (rx_buf->flags & EFX_RX_PKT_PREFIX_LEN) + if (rx_buf->flags & EFX_RX_PKT_PREFIX_LEN) { rx_buf->len = le16_to_cpup((__le16 *) (eh + efx->rx_packet_len_offset)); + /* A known issue may prevent this being filled in; + * if that happens, just drop the packet. + * Must do that in the driver since passing a zero-length + * packet up to the stack may cause a crash. + */ + if (unlikely(!rx_buf->len)) { + efx_free_rx_buffers(rx_queue, rx_buf, + channel->rx_pkt_n_frags); + channel->n_rx_frm_trunc++; + goto out; + } + } /* If we're in loopback test, then pass the packet directly to the * loopback layer, and free the rx_buf here */ if (unlikely(efx->loopback_selftest)) { - struct efx_rx_queue *rx_queue; - efx_loopback_rx_packet(efx, eh, rx_buf->len); - rx_queue = efx_channel_get_rx_queue(channel); efx_free_rx_buffers(rx_queue, rx_buf, channel->rx_pkt_n_frags); goto out; diff --git a/drivers/net/ethernet/sfc/tc.c b/drivers/net/ethernet/sfc/tc.c index 047322b04d4f52725c01a3db3e3e3788f9fb5e02..834f000ba1c49e6b192a2d34068e1d299e5ff6a8 100644 --- a/drivers/net/ethernet/sfc/tc.c +++ b/drivers/net/ethernet/sfc/tc.c @@ -136,6 +136,8 @@ static struct efx_tc_mac_pedit_action *efx_tc_flower_get_mac(struct efx_nic *efx if (old) { /* don't need our new entry */ kfree(ped); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return ERR_CAST(old); if (!refcount_inc_not_zero(&old->ref)) return ERR_PTR(-EAGAIN); /* existing entry found, ref taken */ @@ -602,6 +604,8 @@ static int efx_tc_flower_record_encap_match(struct efx_nic *efx, kfree(encap); if (pseudo) /* don't need our new pseudo either */ efx_tc_flower_release_encap_match(efx, pseudo); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return PTR_ERR(old); /* check old and new em_types are compatible */ switch (old->type) { case EFX_TC_EM_DIRECT: @@ -700,6 +704,8 @@ static struct efx_tc_recirc_id *efx_tc_get_recirc_id(struct efx_nic *efx, if (old) { /* don't need our new entry */ kfree(rid); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return ERR_CAST(old); if (!refcount_inc_not_zero(&old->ref)) return ERR_PTR(-EAGAIN); /* existing entry found */ @@ -1482,7 +1488,10 @@ static int efx_tc_flower_replace_foreign(struct efx_nic *efx, old = rhashtable_lookup_get_insert_fast(&efx->tc->match_action_ht, &rule->linkage, efx_tc_match_action_ht_params); - if (old) { + if (IS_ERR(old)) { + rc = PTR_ERR(old); + goto release; + } else if (old) { netif_dbg(efx, drv, efx->net_dev, "Ignoring already-offloaded rule (cookie %lx)\n", tc->cookie); @@ -1697,7 +1706,10 @@ static int efx_tc_flower_replace_lhs(struct efx_nic *efx, old = rhashtable_lookup_get_insert_fast(&efx->tc->lhs_rule_ht, &rule->linkage, efx_tc_lhs_rule_ht_params); - if (old) { + if (IS_ERR(old)) { + rc = PTR_ERR(old); + goto release; + } else if (old) { netif_dbg(efx, drv, efx->net_dev, "Already offloaded rule (cookie %lx)\n", tc->cookie); rc = -EEXIST; @@ -1858,7 +1870,10 @@ static int efx_tc_flower_replace(struct efx_nic *efx, old = rhashtable_lookup_get_insert_fast(&efx->tc->match_action_ht, &rule->linkage, efx_tc_match_action_ht_params); - if (old) { + if (IS_ERR(old)) { + rc = PTR_ERR(old); + goto release; + } else if (old) { netif_dbg(efx, drv, efx->net_dev, "Already offloaded rule (cookie %lx)\n", tc->cookie); NL_SET_ERR_MSG_MOD(extack, "Rule already offloaded"); diff --git a/drivers/net/ethernet/sfc/tc_conntrack.c b/drivers/net/ethernet/sfc/tc_conntrack.c index 8e06bfbcbea17cc5c3984d37a41725e61c895433..44bb57670340dae06cad31fde1deaa60a1ae6e90 100644 --- a/drivers/net/ethernet/sfc/tc_conntrack.c +++ b/drivers/net/ethernet/sfc/tc_conntrack.c @@ -298,7 +298,10 @@ static int efx_tc_ct_replace(struct efx_tc_ct_zone *ct_zone, old = rhashtable_lookup_get_insert_fast(&efx->tc->ct_ht, &conn->linkage, efx_tc_ct_ht_params); - if (old) { + if (IS_ERR(old)) { + rc = PTR_ERR(old); + goto release; + } else if (old) { netif_dbg(efx, drv, efx->net_dev, "Already offloaded conntrack (cookie %lx)\n", tc->cookie); rc = -EEXIST; @@ -482,6 +485,8 @@ struct efx_tc_ct_zone *efx_tc_ct_register_zone(struct efx_nic *efx, u16 zone, if (old) { /* don't need our new entry */ kfree(ct_zone); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return ERR_CAST(old); if (!refcount_inc_not_zero(&old->ref)) return ERR_PTR(-EAGAIN); /* existing entry found */ diff --git a/drivers/net/ethernet/sfc/tc_counters.c b/drivers/net/ethernet/sfc/tc_counters.c index 0fafb47ea0824b450de43520430e41816aab9d12..c4408842432363d02bcbd64d5ab4807a444ae15e 100644 --- a/drivers/net/ethernet/sfc/tc_counters.c +++ b/drivers/net/ethernet/sfc/tc_counters.c @@ -236,6 +236,8 @@ struct efx_tc_counter_index *efx_tc_flower_get_counter_index( if (old) { /* don't need our new entry */ kfree(ctr); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return ERR_CAST(old); if (!refcount_inc_not_zero(&old->ref)) return ERR_PTR(-EAGAIN); /* existing entry found */ diff --git a/drivers/net/ethernet/sfc/tc_encap_actions.c b/drivers/net/ethernet/sfc/tc_encap_actions.c index 7e8bcdb222ad18b4f175d5312613402d169dfbcd..87443f9dfd22b8b82564efc31bb0d8497d3757a1 100644 --- a/drivers/net/ethernet/sfc/tc_encap_actions.c +++ b/drivers/net/ethernet/sfc/tc_encap_actions.c @@ -132,6 +132,8 @@ static int efx_bind_neigh(struct efx_nic *efx, /* don't need our new entry */ put_net_track(neigh->net, &neigh->ns_tracker); kfree(neigh); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return PTR_ERR(old); if (!refcount_inc_not_zero(&old->ref)) return -EAGAIN; /* existing entry found, ref taken */ @@ -640,6 +642,8 @@ struct efx_tc_encap_action *efx_tc_flower_create_encap_md( if (old) { /* don't need our new entry */ kfree(encap); + if (IS_ERR(old)) /* oh dear, it's actually an error */ + return ERR_CAST(old); if (!refcount_inc_not_zero(&old->ref)) return ERR_PTR(-EAGAIN); /* existing entry found, ref taken */ diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 403cb397d4d36ca6c841b19efb733468422616cf..1e996c29043dcbdc40bfb1e030f93b70d989e4a4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -70,7 +70,7 @@ struct stmmac_txq_stats { u64 tx_tso_frames; u64 tx_tso_nfrags; struct u64_stats_sync syncp; -}; +} ____cacheline_aligned_in_smp; struct stmmac_rxq_stats { u64 rx_bytes; @@ -79,7 +79,7 @@ struct stmmac_rxq_stats { u64 rx_normal_irq_n; u64 napi_poll; struct u64_stats_sync syncp; -}; +} ____cacheline_aligned_in_smp; /* Extra statistic and debug information exposed by ethtool */ struct stmmac_extra_stats { @@ -202,6 +202,9 @@ struct stmmac_extra_stats { unsigned long mtl_est_hlbf; unsigned long mtl_est_btre; unsigned long mtl_est_btrlm; + /* per queue statistics */ + struct stmmac_txq_stats txq_stats[MTL_MAX_TX_QUEUES]; + struct stmmac_rxq_stats rxq_stats[MTL_MAX_RX_QUEUES]; unsigned long rx_dropped; unsigned long rx_errors; unsigned long tx_dropped; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c index 26ea8c687881cce9de2dbc1a3778f969a4838076..a0e276783e65f7f37be7e8e9bbb94a0ad2ccec18 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c @@ -104,6 +104,7 @@ struct stm32_ops { int (*parse_data)(struct stm32_dwmac *dwmac, struct device *dev); u32 syscfg_eth_mask; + bool clk_rx_enable_in_suspend; }; static int stm32_dwmac_init(struct plat_stmmacenet_data *plat_dat) @@ -121,7 +122,8 @@ static int stm32_dwmac_init(struct plat_stmmacenet_data *plat_dat) if (ret) return ret; - if (!dwmac->dev->power.is_suspended) { + if (!dwmac->ops->clk_rx_enable_in_suspend || + !dwmac->dev->power.is_suspended) { ret = clk_prepare_enable(dwmac->clk_rx); if (ret) { clk_disable_unprepare(dwmac->clk_tx); @@ -513,7 +515,8 @@ static struct stm32_ops stm32mp1_dwmac_data = { .suspend = stm32mp1_suspend, .resume = stm32mp1_resume, .parse_data = stm32mp1_parse_data, - .syscfg_eth_mask = SYSCFG_MP1_ETH_MASK + .syscfg_eth_mask = SYSCFG_MP1_ETH_MASK, + .clk_rx_enable_in_suspend = true }; static const struct of_device_id stm32_dwmac_match[] = { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c index 01e77368eef1c1845b3c9b8fb610b62d61bd84dc..465ff1fd478554963cf339999c1768825b81aa97 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c @@ -441,8 +441,8 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; int ret = 0; u32 v; @@ -455,9 +455,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, if (v & EMAC_TX_INT) { ret |= handle_tx; - u64_stats_update_begin(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_normal_irq_n++; - u64_stats_update_end(&tx_q->txq_stats.syncp); + u64_stats_update_begin(&txq_stats->syncp); + txq_stats->tx_normal_irq_n++; + u64_stats_update_end(&txq_stats->syncp); } if (v & EMAC_TX_DMA_STOP_INT) @@ -479,9 +479,9 @@ static int sun8i_dwmac_dma_interrupt(struct stmmac_priv *priv, if (v & EMAC_RX_INT) { ret |= handle_rx; - u64_stats_update_begin(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_normal_irq_n++; - u64_stats_update_end(&rx_q->rxq_stats.syncp); + u64_stats_update_begin(&rxq_stats->syncp); + rxq_stats->rx_normal_irq_n++; + u64_stats_update_end(&rxq_stats->syncp); } if (v & EMAC_RX_BUF_UA_INT) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c index 980e5f8a37ec520ebb4116e812b2cd70afa1c22f..9470d3fd2dede2bb436c05f6a92d87824c2db733 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c @@ -171,8 +171,8 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, const struct dwmac4_addrs *dwmac4_addrs = priv->plat->dwmac4_addrs; u32 intr_status = readl(ioaddr + DMA_CHAN_STATUS(dwmac4_addrs, chan)); u32 intr_en = readl(ioaddr + DMA_CHAN_INTR_ENA(dwmac4_addrs, chan)); - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; int ret = 0; if (dir == DMA_DIR_RX) @@ -201,15 +201,15 @@ int dwmac4_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, } /* TX/RX NORMAL interrupts */ if (likely(intr_status & DMA_CHAN_STATUS_RI)) { - u64_stats_update_begin(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_normal_irq_n++; - u64_stats_update_end(&rx_q->rxq_stats.syncp); + u64_stats_update_begin(&rxq_stats->syncp); + rxq_stats->rx_normal_irq_n++; + u64_stats_update_end(&rxq_stats->syncp); ret |= handle_rx; } if (likely(intr_status & DMA_CHAN_STATUS_TI)) { - u64_stats_update_begin(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_normal_irq_n++; - u64_stats_update_end(&tx_q->txq_stats.syncp); + u64_stats_update_begin(&txq_stats->syncp); + txq_stats->tx_normal_irq_n++; + u64_stats_update_end(&txq_stats->syncp); ret |= handle_tx; } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index aaa09b16b016f21a12725f053296086ac21b0111..7907d62d343759d661e00452198ef8e6cfef3601 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -162,8 +162,8 @@ static void show_rx_process_state(unsigned int status) int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; int ret = 0; /* read the status register (CSR5) */ u32 intr_status = readl(ioaddr + DMA_STATUS); @@ -215,16 +215,16 @@ int dwmac_dma_interrupt(struct stmmac_priv *priv, void __iomem *ioaddr, u32 value = readl(ioaddr + DMA_INTR_ENA); /* to schedule NAPI on real RIE event. */ if (likely(value & DMA_INTR_ENA_RIE)) { - u64_stats_update_begin(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_normal_irq_n++; - u64_stats_update_end(&rx_q->rxq_stats.syncp); + u64_stats_update_begin(&rxq_stats->syncp); + rxq_stats->rx_normal_irq_n++; + u64_stats_update_end(&rxq_stats->syncp); ret |= handle_rx; } } if (likely(intr_status & DMA_STATUS_TI)) { - u64_stats_update_begin(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_normal_irq_n++; - u64_stats_update_end(&tx_q->txq_stats.syncp); + u64_stats_update_begin(&txq_stats->syncp); + txq_stats->tx_normal_irq_n++; + u64_stats_update_end(&txq_stats->syncp); ret |= handle_tx; } if (unlikely(intr_status & DMA_STATUS_ERI)) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c index fa69d64a86943160449d6ac8012d82544104f126..3cde695fec91bd7592e23e725517f0cccee08a42 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c @@ -337,8 +337,8 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, struct stmmac_extra_stats *x, u32 chan, u32 dir) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[chan]; - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[chan]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[chan]; u32 intr_status = readl(ioaddr + XGMAC_DMA_CH_STATUS(chan)); u32 intr_en = readl(ioaddr + XGMAC_DMA_CH_INT_EN(chan)); int ret = 0; @@ -367,15 +367,15 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv, /* TX/RX NORMAL interrupts */ if (likely(intr_status & XGMAC_NIS)) { if (likely(intr_status & XGMAC_RI)) { - u64_stats_update_begin(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_normal_irq_n++; - u64_stats_update_end(&rx_q->rxq_stats.syncp); + u64_stats_update_begin(&rxq_stats->syncp); + rxq_stats->rx_normal_irq_n++; + u64_stats_update_end(&rxq_stats->syncp); ret |= handle_rx; } if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) { - u64_stats_update_begin(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_normal_irq_n++; - u64_stats_update_end(&tx_q->txq_stats.syncp); + u64_stats_update_begin(&txq_stats->syncp); + txq_stats->tx_normal_irq_n++; + u64_stats_update_end(&txq_stats->syncp); ret |= handle_tx; } } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 3401e888a9f6860b835a115a80102d01ea44ebff..cd7a9768de5f12960cd88167a5483fbc24cf06d8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -78,7 +78,6 @@ struct stmmac_tx_queue { dma_addr_t dma_tx_phy; dma_addr_t tx_tail_addr; u32 mss; - struct stmmac_txq_stats txq_stats; }; struct stmmac_rx_buffer { @@ -123,7 +122,6 @@ struct stmmac_rx_queue { unsigned int len; unsigned int error; } state; - struct stmmac_rxq_stats rxq_stats; }; struct stmmac_channel { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index b7ac7abecdd3518001378039f63d9f0624bc9fd9..6aa5c0556d2203fcc2edd0307974f6e0d40ffccc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -548,14 +548,14 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data) pos = data; for (q = 0; q < tx_cnt; q++) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[q]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[q]; struct stmmac_txq_stats snapshot; data = pos; do { - start = u64_stats_fetch_begin(&tx_q->txq_stats.syncp); - snapshot = tx_q->txq_stats; - } while (u64_stats_fetch_retry(&tx_q->txq_stats.syncp, start)); + start = u64_stats_fetch_begin(&txq_stats->syncp); + snapshot = *txq_stats; + } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); p = (char *)&snapshot + offsetof(struct stmmac_txq_stats, tx_pkt_n); for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) { @@ -566,14 +566,14 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data) pos = data; for (q = 0; q < rx_cnt; q++) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[q]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[q]; struct stmmac_rxq_stats snapshot; data = pos; do { - start = u64_stats_fetch_begin(&rx_q->rxq_stats.syncp); - snapshot = rx_q->rxq_stats; - } while (u64_stats_fetch_retry(&rx_q->rxq_stats.syncp, start)); + start = u64_stats_fetch_begin(&rxq_stats->syncp); + snapshot = *rxq_stats; + } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); p = (char *)&snapshot + offsetof(struct stmmac_rxq_stats, rx_pkt_n); for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) { @@ -637,14 +637,14 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, pos = j; for (i = 0; i < rx_queues_count; i++) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[i]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[i]; struct stmmac_rxq_stats snapshot; j = pos; do { - start = u64_stats_fetch_begin(&rx_q->rxq_stats.syncp); - snapshot = rx_q->rxq_stats; - } while (u64_stats_fetch_retry(&rx_q->rxq_stats.syncp, start)); + start = u64_stats_fetch_begin(&rxq_stats->syncp); + snapshot = *rxq_stats; + } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); data[j++] += snapshot.rx_pkt_n; data[j++] += snapshot.rx_normal_irq_n; @@ -654,14 +654,14 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, pos = j; for (i = 0; i < tx_queues_count; i++) { - struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[i]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[i]; struct stmmac_txq_stats snapshot; j = pos; do { - start = u64_stats_fetch_begin(&tx_q->txq_stats.syncp); - snapshot = tx_q->txq_stats; - } while (u64_stats_fetch_retry(&tx_q->txq_stats.syncp, start)); + start = u64_stats_fetch_begin(&txq_stats->syncp); + snapshot = *txq_stats; + } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); data[j++] += snapshot.tx_pkt_n; data[j++] += snapshot.tx_normal_irq_n; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 9a3182b9e767c566e2b0f89a0d21e74fb56e06af..ed1a5a31a4914a8255c5735ddba0db7a973cf625 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2426,6 +2426,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) { struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue); struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; struct xsk_buff_pool *pool = tx_q->xsk_pool; unsigned int entry = tx_q->cur_tx; struct dma_desc *tx_desc = NULL; @@ -2505,9 +2506,9 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, priv->dma_conf.dma_tx_size); entry = tx_q->cur_tx; } - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_set_ic_bit += tx_set_ic_bit; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->tx_set_ic_bit += tx_set_ic_bit; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); if (tx_desc) { stmmac_flush_tx_descriptors(priv, queue); @@ -2547,6 +2548,7 @@ static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan) static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) { struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; unsigned int bytes_compl = 0, pkts_compl = 0; unsigned int entry, xmits = 0, count = 0; u32 tx_packets = 0, tx_errors = 0; @@ -2704,15 +2706,13 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) /* We still have pending packets, let's call for a new scheduling */ if (tx_q->dirty_tx != tx_q->cur_tx) - hrtimer_start(&tx_q->txtimer, - STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), - HRTIMER_MODE_REL); + stmmac_tx_timer_arm(priv, queue); - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_packets += tx_packets; - tx_q->txq_stats.tx_pkt_n += tx_packets; - tx_q->txq_stats.tx_clean++; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->tx_packets += tx_packets; + txq_stats->tx_pkt_n += tx_packets; + txq_stats->tx_clean++; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); priv->xstats.tx_errors += tx_errors; @@ -2995,9 +2995,13 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) { struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + u32 tx_coal_timer = priv->tx_coal_timer[queue]; + + if (!tx_coal_timer) + return; hrtimer_start(&tx_q->txtimer, - STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), + STMMAC_COAL_TIMER(tx_coal_timer), HRTIMER_MODE_REL); } @@ -4112,6 +4116,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) int nfrags = skb_shinfo(skb)->nr_frags; u32 queue = skb_get_queue_mapping(skb); unsigned int first_entry, tx_packets; + struct stmmac_txq_stats *txq_stats; int tmp_pay_len = 0, first_tx; struct stmmac_tx_queue *tx_q; bool has_vlan, set_ic; @@ -4122,6 +4127,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) int i; tx_q = &priv->dma_conf.tx_queue[queue]; + txq_stats = &priv->xstats.txq_stats[queue]; first_tx = tx_q->cur_tx; /* Compute header lengths */ @@ -4280,13 +4286,13 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_bytes += skb->len; - tx_q->txq_stats.tx_tso_frames++; - tx_q->txq_stats.tx_tso_nfrags += nfrags; + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->tx_bytes += skb->len; + txq_stats->tx_tso_frames++; + txq_stats->tx_tso_nfrags += nfrags; if (set_ic) - tx_q->txq_stats.tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + txq_stats->tx_set_ic_bit++; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); if (priv->sarc_type) stmmac_set_desc_sarc(priv, first, priv->sarc_type); @@ -4357,6 +4363,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) u32 queue = skb_get_queue_mapping(skb); int nfrags = skb_shinfo(skb)->nr_frags; int gso = skb_shinfo(skb)->gso_type; + struct stmmac_txq_stats *txq_stats; struct dma_edesc *tbs_desc = NULL; struct dma_desc *desc, *first; struct stmmac_tx_queue *tx_q; @@ -4366,6 +4373,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) dma_addr_t des; tx_q = &priv->dma_conf.tx_queue[queue]; + txq_stats = &priv->xstats.txq_stats[queue]; first_tx = tx_q->cur_tx; if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en) @@ -4517,11 +4525,11 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, queue)); } - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_bytes += skb->len; + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->tx_bytes += skb->len; if (set_ic) - tx_q->txq_stats.tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + txq_stats->tx_set_ic_bit++; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); if (priv->sarc_type) stmmac_set_desc_sarc(priv, first, priv->sarc_type); @@ -4728,6 +4736,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, struct xdp_frame *xdpf, bool dma_map) { + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue]; struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; unsigned int entry = tx_q->cur_tx; struct dma_desc *tx_desc; @@ -4787,9 +4796,9 @@ static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, unsigned long flags; tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, tx_desc); - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.tx_set_ic_bit++; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->tx_set_ic_bit++; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); } stmmac_enable_dma_transmission(priv, priv->ioaddr); @@ -4934,7 +4943,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, struct dma_desc *p, struct dma_desc *np, struct xdp_buff *xdp) { - struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; struct stmmac_channel *ch = &priv->channel[queue]; unsigned int len = xdp->data_end - xdp->data; enum pkt_hash_types hash_type; @@ -4964,10 +4973,10 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, skb_record_rx_queue(skb, queue); napi_gro_receive(&ch->rxtx_napi, skb); - flags = u64_stats_update_begin_irqsave(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_pkt_n++; - rx_q->rxq_stats.rx_bytes += len; - u64_stats_update_end_irqrestore(&rx_q->rxq_stats.syncp, flags); + flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); + rxq_stats->rx_pkt_n++; + rxq_stats->rx_bytes += len; + u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); } static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) @@ -5040,6 +5049,7 @@ static struct stmmac_xdp_buff *xsk_buff_to_stmmac_ctx(struct xdp_buff *xdp) static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) { + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; unsigned int count = 0, error = 0, len = 0; int dirty = stmmac_rx_dirty(priv, queue); @@ -5203,9 +5213,9 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) stmmac_finalize_xdp_rx(priv, xdp_status); - flags = u64_stats_update_begin_irqsave(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_pkt_n += count; - u64_stats_update_end_irqrestore(&rx_q->rxq_stats.syncp, flags); + flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); + rxq_stats->rx_pkt_n += count; + u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); priv->xstats.rx_dropped += rx_dropped; priv->xstats.rx_errors += rx_errors; @@ -5233,6 +5243,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) { u32 rx_errors = 0, rx_dropped = 0, rx_bytes = 0, rx_packets = 0; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue]; struct stmmac_channel *ch = &priv->channel[queue]; unsigned int count = 0, error = 0, len = 0; @@ -5494,11 +5505,11 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) stmmac_rx_refill(priv, queue); - flags = u64_stats_update_begin_irqsave(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.rx_packets += rx_packets; - rx_q->rxq_stats.rx_bytes += rx_bytes; - rx_q->rxq_stats.rx_pkt_n += count; - u64_stats_update_end_irqrestore(&rx_q->rxq_stats.syncp, flags); + flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); + rxq_stats->rx_packets += rx_packets; + rxq_stats->rx_bytes += rx_bytes; + rxq_stats->rx_pkt_n += count; + u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); priv->xstats.rx_dropped += rx_dropped; priv->xstats.rx_errors += rx_errors; @@ -5511,15 +5522,15 @@ static int stmmac_napi_poll_rx(struct napi_struct *napi, int budget) struct stmmac_channel *ch = container_of(napi, struct stmmac_channel, rx_napi); struct stmmac_priv *priv = ch->priv_data; - struct stmmac_rx_queue *rx_q; + struct stmmac_rxq_stats *rxq_stats; u32 chan = ch->index; unsigned long flags; int work_done; - rx_q = &priv->dma_conf.rx_queue[chan]; - flags = u64_stats_update_begin_irqsave(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.napi_poll++; - u64_stats_update_end_irqrestore(&rx_q->rxq_stats.syncp, flags); + rxq_stats = &priv->xstats.rxq_stats[chan]; + flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); + rxq_stats->napi_poll++; + u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); work_done = stmmac_rx(priv, budget, chan); if (work_done < budget && napi_complete_done(napi, work_done)) { @@ -5538,15 +5549,15 @@ static int stmmac_napi_poll_tx(struct napi_struct *napi, int budget) struct stmmac_channel *ch = container_of(napi, struct stmmac_channel, tx_napi); struct stmmac_priv *priv = ch->priv_data; - struct stmmac_tx_queue *tx_q; + struct stmmac_txq_stats *txq_stats; u32 chan = ch->index; unsigned long flags; int work_done; - tx_q = &priv->dma_conf.tx_queue[chan]; - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.napi_poll++; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + txq_stats = &priv->xstats.txq_stats[chan]; + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->napi_poll++; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); work_done = stmmac_tx_clean(priv, budget, chan); work_done = min(work_done, budget); @@ -5568,20 +5579,20 @@ static int stmmac_napi_poll_rxtx(struct napi_struct *napi, int budget) container_of(napi, struct stmmac_channel, rxtx_napi); struct stmmac_priv *priv = ch->priv_data; int rx_done, tx_done, rxtx_done; - struct stmmac_rx_queue *rx_q; - struct stmmac_tx_queue *tx_q; + struct stmmac_rxq_stats *rxq_stats; + struct stmmac_txq_stats *txq_stats; u32 chan = ch->index; unsigned long flags; - rx_q = &priv->dma_conf.rx_queue[chan]; - flags = u64_stats_update_begin_irqsave(&rx_q->rxq_stats.syncp); - rx_q->rxq_stats.napi_poll++; - u64_stats_update_end_irqrestore(&rx_q->rxq_stats.syncp, flags); + rxq_stats = &priv->xstats.rxq_stats[chan]; + flags = u64_stats_update_begin_irqsave(&rxq_stats->syncp); + rxq_stats->napi_poll++; + u64_stats_update_end_irqrestore(&rxq_stats->syncp, flags); - tx_q = &priv->dma_conf.tx_queue[chan]; - flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); - tx_q->txq_stats.napi_poll++; - u64_stats_update_end_irqrestore(&tx_q->txq_stats.syncp, flags); + txq_stats = &priv->xstats.txq_stats[chan]; + flags = u64_stats_update_begin_irqsave(&txq_stats->syncp); + txq_stats->napi_poll++; + u64_stats_update_end_irqrestore(&txq_stats->syncp, flags); tx_done = stmmac_tx_clean(priv, budget, chan); tx_done = min(tx_done, budget); @@ -5991,33 +6002,6 @@ static irqreturn_t stmmac_msi_intr_rx(int irq, void *data) return IRQ_HANDLED; } -#ifdef CONFIG_NET_POLL_CONTROLLER -/* Polling receive - used by NETCONSOLE and other diagnostic tools - * to allow network I/O with interrupts disabled. - */ -static void stmmac_poll_controller(struct net_device *dev) -{ - struct stmmac_priv *priv = netdev_priv(dev); - int i; - - /* If adapter is down, do nothing */ - if (test_bit(STMMAC_DOWN, &priv->state)) - return; - - if (priv->plat->flags & STMMAC_FLAG_MULTI_MSI_EN) { - for (i = 0; i < priv->plat->rx_queues_to_use; i++) - stmmac_msi_intr_rx(0, &priv->dma_conf.rx_queue[i]); - - for (i = 0; i < priv->plat->tx_queues_to_use; i++) - stmmac_msi_intr_tx(0, &priv->dma_conf.tx_queue[i]); - } else { - disable_irq(dev->irq); - stmmac_interrupt(dev->irq, dev); - enable_irq(dev->irq); - } -} -#endif - /** * stmmac_ioctl - Entry point for the Ioctl * @dev: Device pointer. @@ -6924,7 +6908,7 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 int q; for (q = 0; q < tx_cnt; q++) { - struct stmmac_txq_stats *txq_stats = &priv->dma_conf.tx_queue[q].txq_stats; + struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[q]; u64 tx_packets; u64 tx_bytes; @@ -6939,7 +6923,7 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 } for (q = 0; q < rx_cnt; q++) { - struct stmmac_rxq_stats *rxq_stats = &priv->dma_conf.rx_queue[q].rxq_stats; + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[q]; u64 rx_packets; u64 rx_bytes; @@ -6978,9 +6962,6 @@ static const struct net_device_ops stmmac_netdev_ops = { .ndo_get_stats64 = stmmac_get_stats64, .ndo_setup_tc = stmmac_setup_tc, .ndo_select_queue = stmmac_select_queue, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = stmmac_poll_controller, -#endif .ndo_set_mac_address = stmmac_set_mac_address, .ndo_vlan_rx_add_vid = stmmac_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = stmmac_vlan_rx_kill_vid, @@ -7340,9 +7321,9 @@ int stmmac_dvr_probe(struct device *device, priv->dev = ndev; for (i = 0; i < MTL_MAX_RX_QUEUES; i++) - u64_stats_init(&priv->dma_conf.rx_queue[i].rxq_stats.syncp); + u64_stats_init(&priv->xstats.rxq_stats[i].syncp); for (i = 0; i < MTL_MAX_TX_QUEUES; i++) - u64_stats_init(&priv->dma_conf.tx_queue[i].txq_stats.syncp); + u64_stats_init(&priv->xstats.txq_stats[i].syncp); stmmac_set_ethtool_ops(ndev); priv->pause = pause; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 35f4b1484029ba4b9d6d87260b831fa3df7953d8..2f0678f15fb7e71f42a5727a4e36887592b3fbf2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -419,9 +419,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) return ERR_PTR(phy_mode); plat->phy_interface = phy_mode; - plat->mac_interface = stmmac_of_get_mac_mode(np); - if (plat->mac_interface < 0) - plat->mac_interface = plat->phy_interface; + rc = stmmac_of_get_mac_mode(np); + plat->mac_interface = rc < 0 ? plat->phy_interface : rc; /* Some wrapper drivers still rely on phy_node. Let's save it while * they are not converted to phylink. */ @@ -902,7 +901,7 @@ static int __maybe_unused stmmac_pltfr_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); int ret; - ret = stmmac_pltfr_init(pdev, priv->plat->bsp_priv); + ret = stmmac_pltfr_init(pdev, priv->plat); if (ret) return ret; diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 88b5b1b47779a54c6970bc29bac47bfe35c35fbe..0a3346650e03117ba0e1ba8df98cfaf4024c5c67 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -199,6 +199,7 @@ config TI_ICSSG_PRUETH config TI_ICSS_IEP tristate "TI PRU ICSS IEP driver" + depends on PTP_1588_CLOCK_OPTIONAL depends on TI_PRUSS default TI_PRUSS help diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c index bea6fc0f324ce9a42e8ced4bd3a12a770e20f7ea..24120605502f9e8b7d3161d066902e2b8edc8a3f 100644 --- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c +++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c @@ -1747,9 +1747,10 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common) } tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); - if (tx_chn->irq <= 0) { + if (tx_chn->irq < 0) { dev_err(dev, "Failed to get tx dma irq %d\n", tx_chn->irq); + ret = tx_chn->irq; goto err; } diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c index 410612f43cbd0a5c979c50f021a4e6dff63a58a5..4914d0ef58e9b969dc82401be4922a0d13b4e9ad 100644 --- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c +++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c @@ -316,12 +316,12 @@ static int prueth_init_tx_chns(struct prueth_emac *emac) goto fail; } - tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); - if (tx_chn->irq <= 0) { - ret = -EINVAL; + ret = k3_udma_glue_tx_get_irq(tx_chn->tx_chn); + if (ret < 0) { netdev_err(ndev, "failed to get tx irq\n"); goto fail; } + tx_chn->irq = ret; snprintf(tx_chn->name, sizeof(tx_chn->name), "%s-tx%d", dev_name(dev), tx_chn->id); diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index c3f30663070f9496e6f574ea83e189ae85cfb6de..b7e151439c4886f851ba87e27a34e33cd834069a 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -1330,8 +1330,7 @@ static struct crypto_aead *macsec_alloc_tfm(char *key, int key_len, int icv_len) struct crypto_aead *tfm; int ret; - /* Pick a sync gcm(aes) cipher to ensure order is preserved. */ - tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC); + tfm = crypto_alloc_aead("gcm(aes)", 0, 0); if (IS_ERR(tfm)) return tfm; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index b6d7981b2d1ee7f295b9ed9346a1f77489311360..927d3d54658ef825f57cadfc21e31c1fca0896d4 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -1800,9 +1800,6 @@ static const struct ksz9477_errata_write ksz9477_errata_writes[] = { /* Transmit waveform amplitude can be improved (1000BASE-T, 100BASE-TX, 10BASE-Te) */ {0x1c, 0x04, 0x00d0}, - /* Energy Efficient Ethernet (EEE) feature select must be manually disabled */ - {0x07, 0x3c, 0x0000}, - /* Register settings are required to meet data sheet supply current specifications */ {0x1c, 0x13, 0x6eff}, {0x1c, 0x14, 0xe6ff}, @@ -1847,6 +1844,12 @@ static int ksz9477_config_init(struct phy_device *phydev) return err; } + /* According to KSZ9477 Errata DS80000754C (Module 4) all EEE modes + * in this switch shall be regarded as broken. + */ + if (phydev->dev_flags & MICREL_NO_EEE) + phydev->eee_broken_modes = -1; + err = genphy_restart_aneg(phydev); if (err) return err; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index e8b94580194e719184527cf8d2fc9e138afdebb2..508d9a392ab182c4809019e73fd2b658fd1d66de 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -2115,7 +2115,12 @@ static const struct ethtool_ops team_ethtool_ops = { static void team_setup_by_port(struct net_device *dev, struct net_device *port_dev) { - dev->header_ops = port_dev->header_ops; + struct team *team = netdev_priv(dev); + + if (port_dev->type == ARPHRD_ETHER) + dev->header_ops = team->header_ops_cache; + else + dev->header_ops = port_dev->header_ops; dev->type = port_dev->type; dev->hard_header_len = port_dev->hard_header_len; dev->needed_headroom = port_dev->needed_headroom; @@ -2162,8 +2167,11 @@ static int team_dev_type_check_change(struct net_device *dev, static void team_setup(struct net_device *dev) { + struct team *team = netdev_priv(dev); + ether_setup(dev); dev->max_mtu = ETH_MAX_MTU; + team->header_ops_cache = dev->header_ops; dev->netdev_ops = &team_netdev_ops; dev->ethtool_ops = &team_ethtool_ops; diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 0c1e8970ee589fde200ba494f79ed48ce499ae7a..0a53ec293d0408b591467baca93e1e1ff0999191 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -1049,12 +1049,11 @@ static bool tbnet_xmit_csum_and_map(struct tbnet *net, struct sk_buff *skb, *tucso = ~csum_tcpudp_magic(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr, 0, ip_hdr(skb)->protocol, 0); - } else if (skb_is_gso_v6(skb)) { + } else if (skb_is_gso(skb) && skb_is_gso_v6(skb)) { tucso = dest + ((void *)&(tcp_hdr(skb)->check) - data); *tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, 0, IPPROTO_TCP, 0); - return false; } else if (protocol == htons(ETH_P_IPV6)) { tucso = dest + skb_checksum_start_offset(skb) + skb->csum_offset; *tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 332c853ca99b30e2279df7d14d60d04055829a57..0c13d9950cd85502e35c1c26da43d95582a08314 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -2636,6 +2636,9 @@ static int r8152_poll(struct napi_struct *napi, int budget) struct r8152 *tp = container_of(napi, struct r8152, napi); int work_done; + if (!budget) + return 0; + work_done = rx_bottom(tp, budget); if (work_done < budget) { diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 5d6454fedb3f17707f7cd8a3b963da08e67c76c9..78ad2da3ee29b225f41eddc90b08d43257d01e8f 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -90,7 +90,9 @@ static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index, ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, &buf, 4); - if (unlikely(ret < 0)) { + if (unlikely(ret < 4)) { + ret = ret < 0 ? ret : -ENODATA; + netdev_warn(dev->net, "Failed to read reg index 0x%08x: %d\n", index, ret); return ret; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index d43e62ebc2fcd5d30f4717454ab5c9d089502d15..0deefd1573cf264e50a6451fd307c2174aef4d4c 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -344,6 +344,7 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) { struct veth_priv *rcv_priv, *priv = netdev_priv(dev); struct veth_rq *rq = NULL; + int ret = NETDEV_TX_OK; struct net_device *rcv; int length = skb->len; bool use_napi = false; @@ -378,11 +379,12 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) } else { drop: atomic64_inc(&priv->dropped); + ret = NET_XMIT_DROP; } rcu_read_unlock(); - return NETDEV_TX_OK; + return ret; } static u64 veth_stats_tx(struct net_device *dev, u64 *packets, u64 *bytes) @@ -1444,6 +1446,8 @@ static int veth_open(struct net_device *dev) netif_carrier_on(peer); } + veth_set_xdp_features(dev); + return 0; } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 231ad91a919d1a7abf8f37aafc1bd9823b9ce5d7..fe7f314d65c971fcad826fa0b3e44a956ef88940 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -132,6 +132,14 @@ struct virtnet_interrupt_coalesce { u32 max_usecs; }; +/* The dma information of pages allocated at a time. */ +struct virtnet_rq_dma { + dma_addr_t addr; + u32 ref; + u16 len; + u16 need_sync; +}; + /* Internal representation of a send virtqueue */ struct send_queue { /* Virtqueue associated with this send _queue */ @@ -185,6 +193,12 @@ struct receive_queue { char name[16]; struct xdp_rxq_info xdp_rxq; + + /* Record the last dma info to free after new pages is allocated. */ + struct virtnet_rq_dma *last_dma; + + /* Do dma by self */ + bool do_dma; }; /* This structure can contain rss message with maximum settings for indirection table and keysize @@ -580,6 +594,156 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi, return skb; } +static void virtnet_rq_unmap(struct receive_queue *rq, void *buf, u32 len) +{ + struct page *page = virt_to_head_page(buf); + struct virtnet_rq_dma *dma; + void *head; + int offset; + + head = page_address(page); + + dma = head; + + --dma->ref; + + if (dma->ref) { + if (dma->need_sync && len) { + offset = buf - (head + sizeof(*dma)); + + virtqueue_dma_sync_single_range_for_cpu(rq->vq, dma->addr, offset, + len, DMA_FROM_DEVICE); + } + + return; + } + + virtqueue_dma_unmap_single_attrs(rq->vq, dma->addr, dma->len, + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + put_page(page); +} + +static void *virtnet_rq_get_buf(struct receive_queue *rq, u32 *len, void **ctx) +{ + void *buf; + + buf = virtqueue_get_buf_ctx(rq->vq, len, ctx); + if (buf && rq->do_dma) + virtnet_rq_unmap(rq, buf, *len); + + return buf; +} + +static void *virtnet_rq_detach_unused_buf(struct receive_queue *rq) +{ + void *buf; + + buf = virtqueue_detach_unused_buf(rq->vq); + if (buf && rq->do_dma) + virtnet_rq_unmap(rq, buf, 0); + + return buf; +} + +static void virtnet_rq_init_one_sg(struct receive_queue *rq, void *buf, u32 len) +{ + struct virtnet_rq_dma *dma; + dma_addr_t addr; + u32 offset; + void *head; + + if (!rq->do_dma) { + sg_init_one(rq->sg, buf, len); + return; + } + + head = page_address(rq->alloc_frag.page); + + offset = buf - head; + + dma = head; + + addr = dma->addr - sizeof(*dma) + offset; + + sg_init_table(rq->sg, 1); + rq->sg[0].dma_address = addr; + rq->sg[0].length = len; +} + +static void *virtnet_rq_alloc(struct receive_queue *rq, u32 size, gfp_t gfp) +{ + struct page_frag *alloc_frag = &rq->alloc_frag; + struct virtnet_rq_dma *dma; + void *buf, *head; + dma_addr_t addr; + + if (unlikely(!skb_page_frag_refill(size, alloc_frag, gfp))) + return NULL; + + head = page_address(alloc_frag->page); + + if (rq->do_dma) { + dma = head; + + /* new pages */ + if (!alloc_frag->offset) { + if (rq->last_dma) { + /* Now, the new page is allocated, the last dma + * will not be used. So the dma can be unmapped + * if the ref is 0. + */ + virtnet_rq_unmap(rq, rq->last_dma, 0); + rq->last_dma = NULL; + } + + dma->len = alloc_frag->size - sizeof(*dma); + + addr = virtqueue_dma_map_single_attrs(rq->vq, dma + 1, + dma->len, DMA_FROM_DEVICE, 0); + if (virtqueue_dma_mapping_error(rq->vq, addr)) + return NULL; + + dma->addr = addr; + dma->need_sync = virtqueue_dma_need_sync(rq->vq, addr); + + /* Add a reference to dma to prevent the entire dma from + * being released during error handling. This reference + * will be freed after the pages are no longer used. + */ + get_page(alloc_frag->page); + dma->ref = 1; + alloc_frag->offset = sizeof(*dma); + + rq->last_dma = dma; + } + + ++dma->ref; + } + + buf = head + alloc_frag->offset; + + get_page(alloc_frag->page); + alloc_frag->offset += size; + + return buf; +} + +static void virtnet_rq_set_premapped(struct virtnet_info *vi) +{ + int i; + + /* disable for big mode */ + if (!vi->mergeable_rx_bufs && vi->big_packets) + return; + + for (i = 0; i < vi->max_queue_pairs; i++) { + if (virtqueue_set_dma_premapped(vi->rq[i].vq)) + continue; + + vi->rq[i].do_dma = true; + } +} + static void free_old_xmit_skbs(struct send_queue *sq, bool in_napi) { unsigned int len; @@ -935,7 +1099,7 @@ static struct page *xdp_linearize_page(struct receive_queue *rq, void *buf; int off; - buf = virtqueue_get_buf(rq->vq, &buflen); + buf = virtnet_rq_get_buf(rq, &buflen, NULL); if (unlikely(!buf)) goto err_buf; @@ -1155,7 +1319,7 @@ static void mergeable_buf_free(struct receive_queue *rq, int num_buf, int len; while (num_buf-- > 1) { - buf = virtqueue_get_buf(rq->vq, &len); + buf = virtnet_rq_get_buf(rq, &len, NULL); if (unlikely(!buf)) { pr_debug("%s: rx error: %d buffers missing\n", dev->name, num_buf); @@ -1263,7 +1427,7 @@ static int virtnet_build_xdp_buff_mrg(struct net_device *dev, return -EINVAL; while (--*num_buf > 0) { - buf = virtqueue_get_buf_ctx(rq->vq, &len, &ctx); + buf = virtnet_rq_get_buf(rq, &len, &ctx); if (unlikely(!buf)) { pr_debug("%s: rx error: %d buffers out of %d missing\n", dev->name, *num_buf, @@ -1492,7 +1656,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, while (--num_buf) { int num_skb_frags; - buf = virtqueue_get_buf_ctx(rq->vq, &len, &ctx); + buf = virtnet_rq_get_buf(rq, &len, &ctx); if (unlikely(!buf)) { pr_debug("%s: rx error: %d buffers out of %d missing\n", dev->name, num_buf, @@ -1651,7 +1815,6 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq, static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, gfp_t gfp) { - struct page_frag *alloc_frag = &rq->alloc_frag; char *buf; unsigned int xdp_headroom = virtnet_get_headroom(vi); void *ctx = (void *)(unsigned long)xdp_headroom; @@ -1660,17 +1823,21 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, len = SKB_DATA_ALIGN(len) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - if (unlikely(!skb_page_frag_refill(len, alloc_frag, gfp))) + + buf = virtnet_rq_alloc(rq, len, gfp); + if (unlikely(!buf)) return -ENOMEM; - buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset; - get_page(alloc_frag->page); - alloc_frag->offset += len; - sg_init_one(rq->sg, buf + VIRTNET_RX_PAD + xdp_headroom, - vi->hdr_len + GOOD_PACKET_LEN); + virtnet_rq_init_one_sg(rq, buf + VIRTNET_RX_PAD + xdp_headroom, + vi->hdr_len + GOOD_PACKET_LEN); + err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); - if (err < 0) + if (err < 0) { + if (rq->do_dma) + virtnet_rq_unmap(rq, buf, 0); put_page(virt_to_head_page(buf)); + } + return err; } @@ -1747,23 +1914,22 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, unsigned int headroom = virtnet_get_headroom(vi); unsigned int tailroom = headroom ? sizeof(struct skb_shared_info) : 0; unsigned int room = SKB_DATA_ALIGN(headroom + tailroom); - char *buf; + unsigned int len, hole; void *ctx; + char *buf; int err; - unsigned int len, hole; /* Extra tailroom is needed to satisfy XDP's assumption. This * means rx frags coalescing won't work, but consider we've * disabled GSO for XDP, it won't be a big issue. */ len = get_mergeable_buf_len(rq, &rq->mrg_avg_pkt_len, room); - if (unlikely(!skb_page_frag_refill(len + room, alloc_frag, gfp))) + + buf = virtnet_rq_alloc(rq, len + room, gfp); + if (unlikely(!buf)) return -ENOMEM; - buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset; buf += headroom; /* advance address leaving hole at front of pkt */ - get_page(alloc_frag->page); - alloc_frag->offset += len + room; hole = alloc_frag->size - alloc_frag->offset; if (hole < len + room) { /* To avoid internal fragmentation, if there is very likely not @@ -1777,11 +1943,15 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, alloc_frag->offset += hole; } - sg_init_one(rq->sg, buf, len); + virtnet_rq_init_one_sg(rq, buf, len); + ctx = mergeable_len_to_ctx(len + room, headroom); err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); - if (err < 0) + if (err < 0) { + if (rq->do_dma) + virtnet_rq_unmap(rq, buf, 0); put_page(virt_to_head_page(buf)); + } return err; } @@ -1902,13 +2072,13 @@ static int virtnet_receive(struct receive_queue *rq, int budget, void *ctx; while (stats.packets < budget && - (buf = virtqueue_get_buf_ctx(rq->vq, &len, &ctx))) { + (buf = virtnet_rq_get_buf(rq, &len, &ctx))) { receive_buf(vi, rq, buf, len, ctx, xdp_xmit, &stats); stats.packets++; } } else { while (stats.packets < budget && - (buf = virtqueue_get_buf(rq->vq, &len)) != NULL) { + (buf = virtnet_rq_get_buf(rq, &len, NULL)) != NULL) { receive_buf(vi, rq, buf, len, NULL, xdp_xmit, &stats); stats.packets++; } @@ -3808,8 +3978,11 @@ static void free_receive_page_frags(struct virtnet_info *vi) { int i; for (i = 0; i < vi->max_queue_pairs; i++) - if (vi->rq[i].alloc_frag.page) + if (vi->rq[i].alloc_frag.page) { + if (vi->rq[i].do_dma && vi->rq[i].last_dma) + virtnet_rq_unmap(&vi->rq[i], vi->rq[i].last_dma, 0); put_page(vi->rq[i].alloc_frag.page); + } } static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf) @@ -3846,9 +4019,10 @@ static void free_unused_bufs(struct virtnet_info *vi) } for (i = 0; i < vi->max_queue_pairs; i++) { - struct virtqueue *vq = vi->rq[i].vq; - while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) - virtnet_rq_free_unused_buf(vq, buf); + struct receive_queue *rq = &vi->rq[i]; + + while ((buf = virtnet_rq_detach_unused_buf(rq)) != NULL) + virtnet_rq_free_unused_buf(rq->vq, buf); cond_resched(); } } @@ -4022,6 +4196,8 @@ static int init_vqs(struct virtnet_info *vi) if (ret) goto err_free; + virtnet_rq_set_premapped(vi); + cpus_read_lock(); virtnet_set_affinity(vi); cpus_read_unlock(); diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index e463f59e95c2535cef645939b156de7ae428ceb2..5b5597073b004c911f88b472748447804594c752 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -4331,6 +4331,10 @@ static size_t vxlan_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_TX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_RX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LOCALBYPASS */ + nla_total_size(0) + /* IFLA_VXLAN_GBP */ + nla_total_size(0) + /* IFLA_VXLAN_GPE */ + nla_total_size(0) + /* IFLA_VXLAN_REMCSUM_NOPARTIAL */ + nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_VNIFILTER */ 0; } diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c index 47c2ad7a3e429ae03fc732024728b113fe77d87e..fd50bb313b92448d6de580271d5cf76a0f717d15 100644 --- a/drivers/net/wan/fsl_ucc_hdlc.c +++ b/drivers/net/wan/fsl_ucc_hdlc.c @@ -34,6 +34,8 @@ #define TDM_PPPOHT_SLIC_MAXIN #define RX_BD_ERRORS (R_CD_S | R_OV_S | R_CR_S | R_AB_S | R_NO_S | R_LG_S) +static int uhdlc_close(struct net_device *dev); + static struct ucc_tdm_info utdm_primary_info = { .uf_info = { .tsa = 0, @@ -708,6 +710,7 @@ static int uhdlc_open(struct net_device *dev) hdlc_device *hdlc = dev_to_hdlc(dev); struct ucc_hdlc_private *priv = hdlc->priv; struct ucc_tdm *utdm = priv->utdm; + int rc = 0; if (priv->hdlc_busy != 1) { if (request_irq(priv->ut_info->uf_info.irq, @@ -731,10 +734,13 @@ static int uhdlc_open(struct net_device *dev) napi_enable(&priv->napi); netdev_reset_queue(dev); netif_start_queue(dev); - hdlc_open(dev); + + rc = hdlc_open(dev); + if (rc) + uhdlc_close(dev); } - return 0; + return rc; } static void uhdlc_memclean(struct ucc_hdlc_private *priv) @@ -824,6 +830,8 @@ static int uhdlc_close(struct net_device *dev) netdev_reset_queue(dev); priv->hdlc_busy = 0; + hdlc_close(dev); + return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index bece26741d3a350a6e6eb08bf80c1478a4781751..611d1a6aabb9e5f9cf6787e23434665df0819e67 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -442,7 +442,12 @@ struct brcmf_scan_params_v2_le { * fixed parameter portion is assumed, otherwise * ssid in the fixed portion is ignored */ - __le16 channel_list[1]; /* list of chanspecs */ + union { + __le16 padding; /* Reserve space for at least 1 entry for abort + * which uses an on stack brcmf_scan_params_v2_le + */ + DECLARE_FLEX_ARRAY(__le16, channel_list); /* chanspecs */ + }; }; struct brcmf_scan_results { @@ -702,7 +707,7 @@ struct brcmf_sta_info_le { struct brcmf_chanspec_list { __le32 count; /* # of entries */ - __le32 element[1]; /* variable length uint32 list */ + __le32 element[]; /* variable length uint32 list */ }; /* diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index f5e08988dc7bfb24a2eecfb84917630427e6b0da..06d6f7f6643089a63ffbb458ab7f3be69aebc2b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -310,9 +310,9 @@ struct iwl_fw_ini_fifo_hdr { struct iwl_fw_ini_error_dump_range { __le32 range_data_size; union { - __le32 internal_base_addr; - __le64 dram_base_addr; - __le32 page_num; + __le32 internal_base_addr __packed; + __le64 dram_base_addr __packed; + __le32 page_num __packed; struct iwl_fw_ini_fifo_hdr fifo_hdr; struct iwl_cmd_header fw_pkt_hdr; }; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 1f5db65a088d3b657dde7344b3bb2202358f760d..1d5ee4330f29f334475886c6b6e466dff2f65c44 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -802,7 +802,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm) mvm->nvm_data->bands[0].n_channels = 1; mvm->nvm_data->bands[0].n_bitrates = 1; mvm->nvm_data->bands[0].bitrates = - (void *)((u8 *)mvm->nvm_data->channels + 1); + (void *)(mvm->nvm_data->channels + 1); mvm->nvm_data->bands[0].bitrates->hw_value = 10; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 8b6c641772ee6f697529cbd5eb9ff373b6639f22..b719843e94576e35e7ce2270136c15d5756ce4e4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -731,73 +731,78 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, mvmvif->associated = vif->cfg.assoc; - if (!(changes & BSS_CHANGED_ASSOC)) - return; - - if (vif->cfg.assoc) { - /* clear statistics to get clean beacon counter */ - iwl_mvm_request_statistics(mvm, true); - iwl_mvm_sf_update(mvm, vif, false); - iwl_mvm_power_vif_assoc(mvm, vif); - - for_each_mvm_vif_valid_link(mvmvif, i) { - memset(&mvmvif->link[i]->beacon_stats, 0, - sizeof(mvmvif->link[i]->beacon_stats)); + if (changes & BSS_CHANGED_ASSOC) { + if (vif->cfg.assoc) { + /* clear statistics to get clean beacon counter */ + iwl_mvm_request_statistics(mvm, true); + iwl_mvm_sf_update(mvm, vif, false); + iwl_mvm_power_vif_assoc(mvm, vif); + + for_each_mvm_vif_valid_link(mvmvif, i) { + memset(&mvmvif->link[i]->beacon_stats, 0, + sizeof(mvmvif->link[i]->beacon_stats)); + + if (vif->p2p) { + iwl_mvm_update_smps(mvm, vif, + IWL_MVM_SMPS_REQ_PROT, + IEEE80211_SMPS_DYNAMIC, i); + } + + rcu_read_lock(); + link_conf = rcu_dereference(vif->link_conf[i]); + if (link_conf && !link_conf->dtim_period) + protect = true; + rcu_read_unlock(); + } - if (vif->p2p) { - iwl_mvm_update_smps(mvm, vif, - IWL_MVM_SMPS_REQ_PROT, - IEEE80211_SMPS_DYNAMIC, i); + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + protect) { + /* If we're not restarting and still haven't + * heard a beacon (dtim period unknown) then + * make sure we still have enough minimum time + * remaining in the time event, since the auth + * might actually have taken quite a while + * (especially for SAE) and so the remaining + * time could be small without us having heard + * a beacon yet. + */ + iwl_mvm_protect_assoc(mvm, vif, 0); } - rcu_read_lock(); - link_conf = rcu_dereference(vif->link_conf[i]); - if (link_conf && !link_conf->dtim_period) - protect = true; - rcu_read_unlock(); - } + iwl_mvm_sf_update(mvm, vif, false); + + /* FIXME: need to decide about misbehaving AP handling */ + iwl_mvm_power_vif_assoc(mvm, vif); + } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { + iwl_mvm_mei_host_disassociated(mvm); - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && - protect) { - /* If we're not restarting and still haven't - * heard a beacon (dtim period unknown) then - * make sure we still have enough minimum time - * remaining in the time event, since the auth - * might actually have taken quite a while - * (especially for SAE) and so the remaining - * time could be small without us having heard - * a beacon yet. + /* If update fails - SF might be running in associated + * mode while disassociated - which is forbidden. */ - iwl_mvm_protect_assoc(mvm, vif, 0); + ret = iwl_mvm_sf_update(mvm, vif, false); + WARN_ONCE(ret && + !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, + &mvm->status), + "Failed to update SF upon disassociation\n"); + + /* If we get an assert during the connection (after the + * station has been added, but before the vif is set + * to associated), mac80211 will re-add the station and + * then configure the vif. Since the vif is not + * associated, we would remove the station here and + * this would fail the recovery. + */ + iwl_mvm_mld_vif_delete_all_stas(mvm, vif); } - iwl_mvm_sf_update(mvm, vif, false); - - /* FIXME: need to decide about misbehaving AP handling */ - iwl_mvm_power_vif_assoc(mvm, vif); - } else if (iwl_mvm_mld_vif_have_valid_ap_sta(mvmvif)) { - iwl_mvm_mei_host_disassociated(mvm); - - /* If update fails - SF might be running in associated - * mode while disassociated - which is forbidden. - */ - ret = iwl_mvm_sf_update(mvm, vif, false); - WARN_ONCE(ret && - !test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, - &mvm->status), - "Failed to update SF upon disassociation\n"); - - /* If we get an assert during the connection (after the - * station has been added, but before the vif is set - * to associated), mac80211 will re-add the station and - * then configure the vif. Since the vif is not - * associated, we would remove the station here and - * this would fail the recovery. - */ - iwl_mvm_mld_vif_delete_all_stas(mvm, vif); + iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); } - iwl_mvm_bss_info_changed_station_assoc(mvm, vif, changes); + if (changes & BSS_CHANGED_PS) { + ret = iwl_mvm_power_update_mac(mvm); + if (ret) + IWL_ERR(mvm, "failed to update power mode\n"); + } } static void diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index c1d9ce75346882033495d1fbf3e42a59a0662ff2..3cbe2c0b8d6bcddd5416eae3d0c3524e0e149843 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -2342,7 +2342,7 @@ iwl_mvm_scan_umac_fill_general_p_v12(struct iwl_mvm *mvm, if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2) gp->num_of_fragments[SCAN_HB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; - if (version < 12) { + if (version < 16) { gp->scan_start_mac_or_link_id = scan_vif->id; } else { struct iwl_mvm_vif_link_info *link_info; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 36d70d589aedda6511f3274cacc411cf5b483b38..898dca3936435cb162a61aa776e6999eed7f112f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1612,6 +1612,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); memset(&info->status, 0, sizeof(info->status)); + info->flags &= ~(IEEE80211_TX_STAT_ACK | IEEE80211_TX_STAT_TX_FILTERED); /* inform mac80211 about what happened with the frame */ switch (status & TX_STATUS_MSK) { @@ -1964,6 +1965,8 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, */ if (!is_flush) info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; } /* diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index 391793a16adca3d40817ef1674219cbb43ca6d14..10690e82358b892f04327d41981d5eb7f5791309 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -918,9 +918,17 @@ void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv, mwifiex_dbg_dump(priv->adapter, EVT_D, "RXBA_SYNC event:", event_buf, len); - while (tlv_buf_left >= sizeof(*tlv_rxba)) { + while (tlv_buf_left > sizeof(*tlv_rxba)) { tlv_type = le16_to_cpu(tlv_rxba->header.type); tlv_len = le16_to_cpu(tlv_rxba->header.len); + if (size_add(sizeof(tlv_rxba->header), tlv_len) > tlv_buf_left) { + mwifiex_dbg(priv->adapter, WARN, + "TLV size (%zu) overflows event_buf buf_left=%d\n", + size_add(sizeof(tlv_rxba->header), tlv_len), + tlv_buf_left); + return; + } + if (tlv_type != TLV_TYPE_RXBA_SYNC) { mwifiex_dbg(priv->adapter, ERROR, "Wrong TLV id=0x%x\n", tlv_type); @@ -929,6 +937,14 @@ void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv, tlv_seq_num = le16_to_cpu(tlv_rxba->seq_num); tlv_bitmap_len = le16_to_cpu(tlv_rxba->bitmap_len); + if (size_add(sizeof(*tlv_rxba), tlv_bitmap_len) > tlv_buf_left) { + mwifiex_dbg(priv->adapter, WARN, + "TLV size (%zu) overflows event_buf buf_left=%d\n", + size_add(sizeof(*tlv_rxba), tlv_bitmap_len), + tlv_buf_left); + return; + } + mwifiex_dbg(priv->adapter, INFO, "%pM tid=%d seq_num=%d bitmap_len=%d\n", tlv_rxba->mac, tlv_rxba->tid, tlv_seq_num, @@ -965,8 +981,8 @@ void mwifiex_11n_rxba_sync_event(struct mwifiex_private *priv, } } - tlv_buf_left -= (sizeof(*tlv_rxba) + tlv_len); - tmp = (u8 *)tlv_rxba + tlv_len + sizeof(*tlv_rxba); + tlv_buf_left -= (sizeof(tlv_rxba->header) + tlv_len); + tmp = (u8 *)tlv_rxba + sizeof(tlv_rxba->header) + tlv_len; tlv_rxba = (struct mwifiex_ie_types_rxba_sync *)tmp; } } diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index f2168fac95ed1a82aa89fe53b9deb8ccae4e4bd3..8e6db904e5b2d895bb9aaad6a2431c1be2c2cc4c 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -779,7 +779,7 @@ struct mwifiex_ie_types_rxba_sync { u8 reserved; __le16 seq_num; __le16 bitmap_len; - u8 bitmap[1]; + u8 bitmap[]; } __packed; struct chan_band_param_set { diff --git a/drivers/net/wireless/marvell/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c index 65420ad6741679ec7c2cd8d24004c1ab1f8334db..257737137cd70fb1ed28737db7acafd364378464 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_rx.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c @@ -86,7 +86,8 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; - if (sizeof(*rx_pkt_hdr) + rx_pkt_off > skb->len) { + if (sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rfc1042_header) + + rx_pkt_off > skb->len) { mwifiex_dbg(priv->adapter, ERROR, "wrong rx packet offset: len=%d, rx_pkt_off=%d\n", skb->len, rx_pkt_off); @@ -95,12 +96,13 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, return -1; } - if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, - sizeof(bridge_tunnel_header))) || - (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, - sizeof(rfc1042_header)) && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { + if (sizeof(*rx_pkt_hdr) + rx_pkt_off <= skb->len && + ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX))) { /* * Replace the 803 header and rfc1042 header (llc/snap) with an * EthernetII header, keep the src/dst and snap_type diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 05d9ab3ce819bbeb92f0457a5e36400c8ecf039e..dc8f4e157eb291bb0a38820c1bd1040a8356ee6f 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -93,13 +93,13 @@ __mt76_get_rxwi(struct mt76_dev *dev) { struct mt76_txwi_cache *t = NULL; - spin_lock(&dev->wed_lock); + spin_lock_bh(&dev->wed_lock); if (!list_empty(&dev->rxwi_cache)) { t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache, list); list_del(&t->list); } - spin_unlock(&dev->wed_lock); + spin_unlock_bh(&dev->wed_lock); return t; } @@ -145,9 +145,9 @@ mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) if (!t) return; - spin_lock(&dev->wed_lock); + spin_lock_bh(&dev->wed_lock); list_add(&t->list, &dev->rxwi_cache); - spin_unlock(&dev->wed_lock); + spin_unlock_bh(&dev->wed_lock); } EXPORT_SYMBOL_GPL(mt76_put_rxwi); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c index 0acabba2d1a50c5653f5866108998dac9f046632..5d402cf2951cb35c2aa5bd1dde0ca20a142a56dc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c @@ -131,15 +131,8 @@ u8 mt76x02_get_lna_gain(struct mt76x02_dev *dev, s8 *lna_2g, s8 *lna_5g, struct ieee80211_channel *chan) { - u16 val; u8 lna; - val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1); - if (val & MT_EE_NIC_CONF_1_LNA_EXT_2G) - *lna_2g = 0; - if (val & MT_EE_NIC_CONF_1_LNA_EXT_5G) - memset(lna_5g, 0, sizeof(s8) * 3); - if (chan->band == NL80211_BAND_2GHZ) lna = *lna_2g; else if (chan->hw_value <= 64) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c index d5809408d1d3720fbfb7f7396242b5e5cae1301a..8c01855885ce3949a16e63fe4169db3345a62044 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -256,7 +256,8 @@ void mt76x2_read_rx_gain(struct mt76x02_dev *dev) struct ieee80211_channel *chan = dev->mphy.chandef.chan; int channel = chan->hw_value; s8 lna_5g[3], lna_2g; - u8 lna; + bool use_lna; + u8 lna = 0; u16 val; if (chan->band == NL80211_BAND_2GHZ) @@ -275,7 +276,15 @@ void mt76x2_read_rx_gain(struct mt76x02_dev *dev) dev->cal.rx.mcu_gain |= (lna_5g[1] & 0xff) << 16; dev->cal.rx.mcu_gain |= (lna_5g[2] & 0xff) << 24; - lna = mt76x02_get_lna_gain(dev, &lna_2g, lna_5g, chan); + val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1); + if (chan->band == NL80211_BAND_2GHZ) + use_lna = !(val & MT_EE_NIC_CONF_1_LNA_EXT_2G); + else + use_lna = !(val & MT_EE_NIC_CONF_1_LNA_EXT_5G); + + if (use_lna) + lna = mt76x02_get_lna_gain(dev, &lna_2g, lna_5g, chan); + dev->cal.rx.lna_gain = mt76x02_sign_extend(lna, 8); } EXPORT_SYMBOL_GPL(mt76x2_read_rx_gain); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.h b/drivers/net/wireless/realtek/rtw88/rtw8723d.h index 3642a2c7f80c984dc2111880b7ac3729cdaaea5a..2434e2480cbe27acb67b0d35514a6243727c44e8 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.h +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.h @@ -46,6 +46,7 @@ struct rtw8723du_efuse { u8 vender_id[2]; /* 0x100 */ u8 product_id[2]; /* 0x102 */ u8 usb_option; /* 0x104 */ + u8 res5[2]; /* 0x105 */ u8 mac_addr[ETH_ALEN]; /* 0x107 */ }; diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index dca25a0c2f33ccbd3ae88a2b4ae1b9ffdffaf72a..3ae4b41c59ac3d64c77fe9479c303756e0b441bb 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -336,6 +336,7 @@ MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match); #ifdef CONFIG_ACPI static const struct acpi_device_id acpi_id[] = { { "NXP1001" }, + { "NXP1002" }, { "NXP7471" }, { } }; diff --git a/drivers/ntb/hw/amd/ntb_hw_amd.c b/drivers/ntb/hw/amd/ntb_hw_amd.c index 4940b6301d833e5bb655884e90e1208bfb6dfd30..d687e8c2cc78dcd65d26c20c1e2f69ec85c6a37a 100644 --- a/drivers/ntb/hw/amd/ntb_hw_amd.c +++ b/drivers/ntb/hw/amd/ntb_hw_amd.c @@ -941,13 +941,10 @@ static void ndev_init_debugfs(struct amd_ntb_dev *ndev) ndev->debugfs_dir = debugfs_create_dir(pci_name(ndev->ntb.pdev), debugfs_dir); - if (IS_ERR(ndev->debugfs_dir)) - ndev->debugfs_info = NULL; - else - ndev->debugfs_info = - debugfs_create_file("info", S_IRUSR, - ndev->debugfs_dir, ndev, - &amd_ntb_debugfs_info); + ndev->debugfs_info = + debugfs_create_file("info", S_IRUSR, + ndev->debugfs_dir, ndev, + &amd_ntb_debugfs_info); } } diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 2abd2235bbcabfc8d6e63281db0b1979c90688fb..f9e7847a378e771ce2fee12eaf17319272cf96a9 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -909,7 +909,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, return 0; } -static void ntb_qp_link_down_reset(struct ntb_transport_qp *qp) +static void ntb_qp_link_context_reset(struct ntb_transport_qp *qp) { qp->link_is_up = false; qp->active = false; @@ -932,6 +932,13 @@ static void ntb_qp_link_down_reset(struct ntb_transport_qp *qp) qp->tx_async = 0; } +static void ntb_qp_link_down_reset(struct ntb_transport_qp *qp) +{ + ntb_qp_link_context_reset(qp); + if (qp->remote_rx_info) + qp->remote_rx_info->entry = qp->rx_max_entry - 1; +} + static void ntb_qp_link_cleanup(struct ntb_transport_qp *qp) { struct ntb_transport_ctx *nt = qp->transport; @@ -1174,7 +1181,7 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, qp->ndev = nt->ndev; qp->client_ready = false; qp->event_handler = NULL; - ntb_qp_link_down_reset(qp); + ntb_qp_link_context_reset(qp); if (mw_num < qp_count % mw_count) num_qps_mw = qp_count / mw_count + 1; @@ -1894,7 +1901,7 @@ static void ntb_async_tx(struct ntb_transport_qp *qp, static int ntb_process_tx(struct ntb_transport_qp *qp, struct ntb_queue_entry *entry) { - if (qp->tx_index == qp->remote_rx_info->entry) { + if (!ntb_transport_tx_free_entry(qp)) { qp->tx_ring_full++; return -EAGAIN; } @@ -2276,9 +2283,13 @@ int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, struct ntb_queue_entry *entry; int rc; - if (!qp || !qp->link_is_up || !len) + if (!qp || !len) return -EINVAL; + /* If the qp link is down already, just ignore. */ + if (!qp->link_is_up) + return 0; + entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q); if (!entry) { qp->tx_err_no_buf++; @@ -2418,7 +2429,7 @@ unsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp) unsigned int head = qp->tx_index; unsigned int tail = qp->remote_rx_info->entry; - return tail > head ? tail - head : qp->tx_max_entry + tail - head; + return tail >= head ? tail - head : qp->tx_max_entry + tail - head; } EXPORT_SYMBOL_GPL(ntb_transport_tx_free_entry); diff --git a/drivers/ntb/test/ntb_perf.c b/drivers/ntb/test/ntb_perf.c index 65e1e5cf1b29a6dd0b0573e263f273d7644c2754..553f1f46bc664ff92295320001cabe7266010c88 100644 --- a/drivers/ntb/test/ntb_perf.c +++ b/drivers/ntb/test/ntb_perf.c @@ -1355,7 +1355,7 @@ static void perf_setup_dbgfs(struct perf_ctx *perf) struct pci_dev *pdev = perf->ntb->pdev; perf->dbgfs_dir = debugfs_create_dir(pci_name(pdev), perf_dbgfs_topdir); - if (!perf->dbgfs_dir) { + if (IS_ERR(perf->dbgfs_dir)) { dev_warn(&perf->ntb->dev, "DebugFS unsupported\n"); return; } diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index eeeb4b1c97d2c20918ec7ef98bc75ad8bd599c9b..641cb7e05a47b27783ce8594be2d8e520939e4d9 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -370,16 +370,9 @@ static ssize_t tool_fn_write(struct tool_ctx *tc, if (*offp) return 0; - buf = kmalloc(size + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, size)) { - kfree(buf); - return -EFAULT; - } - - buf[size] = 0; + buf = memdup_user_nul(ubuf, size); + if (IS_ERR(buf)) + return PTR_ERR(buf); n = sscanf(buf, "%c %lli", &cmd, &bits); @@ -1495,8 +1488,6 @@ static void tool_setup_dbgfs(struct tool_ctx *tc) tc->dbgfs_dir = debugfs_create_dir(dev_name(&tc->ntb->dev), tool_dbgfs_topdir); - if (!tc->dbgfs_dir) - return; debugfs_create_file("port", 0600, tc->dbgfs_dir, tc, &tool_port_fops); diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index f3a01b79148cb1e3246d759cf0bdab809270eb2a..21783aa2ee8e18f64154f479a4b6800eccecde32 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2245,25 +2245,8 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl) else ctrl->ctrl_config = NVME_CC_CSS_NVM; - if (ctrl->cap & NVME_CAP_CRMS_CRWMS) { - u32 crto; - - ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CRTO, &crto); - if (ret) { - dev_err(ctrl->device, "Reading CRTO failed (%d)\n", - ret); - return ret; - } - - if (ctrl->cap & NVME_CAP_CRMS_CRIMS) { - ctrl->ctrl_config |= NVME_CC_CRIME; - timeout = NVME_CRTO_CRIMT(crto); - } else { - timeout = NVME_CRTO_CRWMT(crto); - } - } else { - timeout = NVME_CAP_TIMEOUT(ctrl->cap); - } + if (ctrl->cap & NVME_CAP_CRMS_CRWMS && ctrl->cap & NVME_CAP_CRMS_CRIMS) + ctrl->ctrl_config |= NVME_CC_CRIME; ctrl->ctrl_config |= (NVME_CTRL_PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT; ctrl->ctrl_config |= NVME_CC_AMS_RR | NVME_CC_SHN_NONE; @@ -2277,6 +2260,39 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl) if (ret) return ret; + /* CAP value may change after initial CC write */ + ret = ctrl->ops->reg_read64(ctrl, NVME_REG_CAP, &ctrl->cap); + if (ret) + return ret; + + timeout = NVME_CAP_TIMEOUT(ctrl->cap); + if (ctrl->cap & NVME_CAP_CRMS_CRWMS) { + u32 crto, ready_timeout; + + ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CRTO, &crto); + if (ret) { + dev_err(ctrl->device, "Reading CRTO failed (%d)\n", + ret); + return ret; + } + + /* + * CRTO should always be greater or equal to CAP.TO, but some + * devices are known to get this wrong. Use the larger of the + * two values. + */ + if (ctrl->ctrl_config & NVME_CC_CRIME) + ready_timeout = NVME_CRTO_CRIMT(crto); + else + ready_timeout = NVME_CRTO_CRWMT(crto); + + if (ready_timeout < timeout) + dev_warn_once(ctrl->device, "bad crto:%x cap:%llx\n", + crto, ctrl->cap); + else + timeout = ready_timeout; + } + ctrl->ctrl_config |= NVME_CC_ENABLE; ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config); if (ret) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 1cd2bf82319a988021cbb04d311ebee3dc017bed..a15b37750d6e931b0a875058cda6868034a642d9 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -1924,7 +1924,7 @@ char *nvme_fc_io_getuuid(struct nvmefc_fcp_req *req) struct nvme_fc_fcp_op *op = fcp_req_to_fcp_op(req); struct request *rq = op->rq; - if (!IS_ENABLED(CONFIG_BLK_CGROUP_FC_APPID) || !rq->bio) + if (!IS_ENABLED(CONFIG_BLK_CGROUP_FC_APPID) || !rq || !rq->bio) return NULL; return blkcg_get_fc_appid(rq->bio); } diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c index 316f3e4ca7cc60da613e5f246f9e5a861d60e14f..8df73a0b3980cde3f150b66672db7965eda9a328 100644 --- a/drivers/nvme/host/hwmon.c +++ b/drivers/nvme/host/hwmon.c @@ -187,7 +187,7 @@ static umode_t nvme_hwmon_is_visible(const void *_data, return 0; } -static const struct hwmon_channel_info *nvme_hwmon_info[] = { +static const struct hwmon_channel_info *const nvme_hwmon_info[] = { HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 2f57da12d9836dd1af3beceb4d61e9e73ceb5930..347cb5daebc3cedefc09dac49a10960f00aa1ead 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2916,9 +2916,6 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev, struct nvme_dev *dev; int ret = -ENOMEM; - if (node == NUMA_NO_NODE) - set_dev_node(&pdev->dev, first_memory_node); - dev = kzalloc_node(sizeof(*dev), GFP_KERNEL, node); if (!dev) return ERR_PTR(-ENOMEM); diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 868aa4de2e4c4ef7bcfc3c704ae10791f1725ff9..cd92d7ddf5ed15f677d9d954a43d064659ce2a36 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -348,7 +348,7 @@ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) while (length) { u32 iov_len = min_t(u32, length, sg->length - sg_offset); - bvec_set_page(iov, sg_page(sg), sg->length, + bvec_set_page(iov, sg_page(sg), iov_len, sg->offset + sg_offset); length -= iov_len; diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 0a3483e247a8932ca8ffe6ecbf0047f0c5472960..f63250c650cafdae49c7b558d47528a1af632689 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -890,13 +890,13 @@ int of_changeset_action(struct of_changeset *ocs, unsigned long action, { struct of_changeset_entry *ce; + if (WARN_ON(action >= ARRAY_SIZE(action_names))) + return -EINVAL; + ce = kzalloc(sizeof(*ce), GFP_KERNEL); if (!ce) return -ENOMEM; - if (WARN_ON(action >= ARRAY_SIZE(action_names))) - return -EINVAL; - /* get a reference to the node */ ce->action = action; ce->np = of_node_get(np); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index dfb6fb962fc705ee1032d334233f89367707797d..a9a292d6d59b263ebcc7005511c01b42dbdc06f1 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -45,8 +45,8 @@ struct target { /** * struct fragment - info about fragment nodes in overlay expanded device tree - * @target: target of the overlay operation * @overlay: pointer to the __overlay__ node + * @target: target of the overlay operation */ struct fragment { struct device_node *overlay; diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 509a4072d50afd7560a63c06b27089480a5b5a0d..9ce0d20a6c581204bb4c10e4a11b10e03d3e17a2 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -214,7 +214,7 @@ struct ioa_registers { struct ioc { struct ioa_registers __iomem *ioc_regs; /* I/O MMU base address */ u8 *res_map; /* resource map, bit == pdir entry */ - u64 *pdir_base; /* physical base address */ + __le64 *pdir_base; /* physical base address */ u32 pdir_size; /* bytes, function of IOV Space size */ u32 res_hint; /* next available IOVP - circular search */ @@ -339,7 +339,7 @@ ccio_alloc_range(struct ioc *ioc, struct device *dev, size_t size) BUG_ON(pages_needed == 0); BUG_ON((pages_needed * IOVP_SIZE) > DMA_CHUNK_SIZE); - DBG_RES("%s() size: %d pages_needed %d\n", + DBG_RES("%s() size: %zu pages_needed %d\n", __func__, size, pages_needed); /* @@ -427,7 +427,7 @@ ccio_free_range(struct ioc *ioc, dma_addr_t iova, unsigned long pages_mapped) BUG_ON((pages_mapped * IOVP_SIZE) > DMA_CHUNK_SIZE); BUG_ON(pages_mapped > BITS_PER_LONG); - DBG_RES("%s(): res_idx: %d pages_mapped %d\n", + DBG_RES("%s(): res_idx: %d pages_mapped %lu\n", __func__, res_idx, pages_mapped); #ifdef CCIO_COLLECT_STATS @@ -543,7 +543,7 @@ static u32 hint_lookup[] = { * index are bits 12:19 of the value returned by LCI. */ static void -ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, +ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba, unsigned long hints) { register unsigned long pa; @@ -719,7 +719,7 @@ ccio_map_single(struct device *dev, void *addr, size_t size, unsigned long flags; dma_addr_t iovp; dma_addr_t offset; - u64 *pdir_start; + __le64 *pdir_start; unsigned long hint = hint_lookup[(int)direction]; BUG_ON(!dev); @@ -746,8 +746,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size, pdir_start = &(ioc->pdir_base[idx]); - DBG_RUN("%s() 0x%p -> 0x%lx size: %0x%x\n", - __func__, addr, (long)iovp | offset, size); + DBG_RUN("%s() %px -> %#lx size: %zu\n", + __func__, addr, (long)(iovp | offset), size); /* If not cacheline aligned, force SAFE_DMA on the whole mess */ if((size % L1_CACHE_BYTES) || ((unsigned long)addr % L1_CACHE_BYTES)) @@ -805,7 +805,7 @@ ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size, return; } - DBG_RUN("%s() iovp 0x%lx/%x\n", + DBG_RUN("%s() iovp %#lx/%zx\n", __func__, (long)iova, size); iova ^= offset; /* clear offset bits */ @@ -1283,7 +1283,7 @@ ccio_ioc_init(struct ioc *ioc) iova_space_size>>20, iov_order + PAGE_SHIFT); - ioc->pdir_base = (u64 *)__get_free_pages(GFP_KERNEL, + ioc->pdir_base = (__le64 *)__get_free_pages(GFP_KERNEL, get_order(ioc->pdir_size)); if(NULL == ioc->pdir_base) { panic("%s() could not allocate I/O Page Table\n", __func__); diff --git a/drivers/parisc/iommu-helpers.h b/drivers/parisc/iommu-helpers.h index 0905be256de087d016759f85f4df55046bf882ce..c43f1a212a5c806e66549f7ed5ae31582fa2ea7b 100644 --- a/drivers/parisc/iommu-helpers.h +++ b/drivers/parisc/iommu-helpers.h @@ -14,13 +14,13 @@ static inline unsigned int iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, unsigned long hint, - void (*iommu_io_pdir_entry)(u64 *, space_t, unsigned long, + void (*iommu_io_pdir_entry)(__le64 *, space_t, unsigned long, unsigned long)) { struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ unsigned int n_mappings = 0; unsigned long dma_offset = 0, dma_len = 0; - u64 *pdirp = NULL; + __le64 *pdirp = NULL; /* Horrible hack. For efficiency's sake, dma_sg starts one * entry below the true start (it is immediately incremented @@ -31,8 +31,8 @@ iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, unsigned long vaddr; long size; - DBG_RUN_SG(" %d : %08lx/%05x %p/%05x\n", nents, - (unsigned long)sg_dma_address(startsg), cnt, + DBG_RUN_SG(" %d : %08lx %p/%05x\n", nents, + (unsigned long)sg_dma_address(startsg), sg_virt(startsg), startsg->length ); diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index a7df764f1a7224c1b8148051426035d3f31b73bb..a4011461189b6a225512390d3b99c42cc2635f9c 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -202,9 +202,9 @@ static inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 va static DEFINE_SPINLOCK(iosapic_lock); -static inline void iosapic_eoi(void __iomem *addr, unsigned int data) +static inline void iosapic_eoi(__le32 __iomem *addr, __le32 data) { - __raw_writel(data, addr); + __raw_writel((__force u32)data, addr); } /* diff --git a/drivers/parisc/iosapic_private.h b/drivers/parisc/iosapic_private.h index 73ecc657ad954126455d9be9b62b62a2cdfdd764..bd8ff40162b4b9ed9715a97018c6c1d487908104 100644 --- a/drivers/parisc/iosapic_private.h +++ b/drivers/parisc/iosapic_private.h @@ -118,8 +118,8 @@ struct iosapic_irt { struct vector_info { struct iosapic_info *iosapic; /* I/O SAPIC this vector is on */ struct irt_entry *irte; /* IRT entry */ - u32 __iomem *eoi_addr; /* precalculate EOI reg address */ - u32 eoi_data; /* IA64: ? PA: swapped txn_data */ + __le32 __iomem *eoi_addr; /* precalculate EOI reg address */ + __le32 eoi_data; /* IA64: ? PA: swapped txn_data */ int txn_irq; /* virtual IRQ number for processor */ ulong txn_addr; /* IA64: id_eid PA: partial HPA */ u32 txn_data; /* CPU interrupt bit */ diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index f6b5106753189e91127bdb184c0967783e5eacff..05e7103d1d407a97298bc68196ddde7ee4730283 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -46,8 +46,6 @@ #include #include -#include /* for proc_mckinley_root */ -#include /* for proc_runway_root */ #include /* for PAGE0 */ #include /* for PDC_MODEL_* */ #include /* for is_pdc_pat() */ @@ -122,7 +120,7 @@ MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART"); #endif static struct proc_dir_entry *proc_runway_root __ro_after_init; -struct proc_dir_entry *proc_mckinley_root __ro_after_init; +static struct proc_dir_entry *proc_mckinley_root __ro_after_init; /************************************ ** SBA register read and write support @@ -204,7 +202,7 @@ static void sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) { /* start printing from lowest pde in rval */ - u64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]); + __le64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]); unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]); uint rcnt; @@ -571,7 +569,7 @@ typedef unsigned long space_t; */ static void -sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, +sba_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba, unsigned long hint) { u64 pa; /* physical address */ @@ -615,7 +613,7 @@ static void sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) { u32 iovp = (u32) SBA_IOVP(ioc,iova); - u64 *pdir_ptr = &ioc->pdir_base[PDIR_INDEX(iovp)]; + __le64 *pdir_ptr = &ioc->pdir_base[PDIR_INDEX(iovp)]; #ifdef ASSERT_PDIR_SANITY /* Assert first pdir entry is set. @@ -716,7 +714,7 @@ sba_map_single(struct device *dev, void *addr, size_t size, unsigned long flags; dma_addr_t iovp; dma_addr_t offset; - u64 *pdir_start; + __le64 *pdir_start; int pide; ioc = GET_IOC(dev); @@ -1434,7 +1432,7 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); - DBG_INIT("%s() hpa 0x%lx mem %ldMB IOV %dMB (%d bits)\n", + DBG_INIT("%s() hpa %px mem %ldMB IOV %dMB (%d bits)\n", __func__, ioc->ioc_hpa, (unsigned long) totalram_pages() >> (20 - PAGE_SHIFT), @@ -1471,7 +1469,7 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1); #endif - DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n", + DBG_INIT("%s() IOV base %#lx mask %#0lx\n", __func__, ioc->ibase, ioc->imask); /* @@ -1583,7 +1581,7 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, if (!IS_PLUTO(sba_dev->dev)) { ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL); - DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->", + DBG_INIT("%s() hpa %px ioc_ctl 0x%Lx ->", __func__, sba_dev->sba_hpa, ioc_ctl); ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE); ioc_ctl |= IOC_CTRL_DD | IOC_CTRL_D4 | IOC_CTRL_TC; @@ -1668,14 +1666,14 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, /* flush out the last writes */ READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); - DBG_INIT(" ioc[%d] ROPE_CFG 0x%Lx ROPE_DBG 0x%Lx\n", + DBG_INIT(" ioc[%d] ROPE_CFG %#lx ROPE_DBG %lx\n", i, - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40), - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50) + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40), + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50) ); - DBG_INIT(" STATUS_CONTROL 0x%Lx FLUSH_CTRL 0x%Lx\n", - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108), - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) + DBG_INIT(" STATUS_CONTROL %#lx FLUSH_CTRL %#lx\n", + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108), + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) ); if (IS_PLUTO(sba_dev->dev)) { @@ -1739,7 +1737,7 @@ sba_common_init(struct sba_device *sba_dev) #ifdef ASSERT_PDIR_SANITY /* Mark first bit busy - ie no IOVA 0 */ sba_dev->ioc[i].res_map[0] = 0x80; - sba_dev->ioc[i].pdir_base[0] = 0xeeffc0addbba0080ULL; + sba_dev->ioc[i].pdir_base[0] = (__force __le64) 0xeeffc0addbba0080ULL; #endif /* Third (and last) part of PIRANHA BUG */ @@ -1899,9 +1897,7 @@ static int __init sba_driver_callback(struct parisc_device *dev) int i; char *version; void __iomem *sba_addr = ioremap(dev->hpa.start, SBA_FUNC_SIZE); -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *root; -#endif + struct proc_dir_entry *root __maybe_unused; sba_dump_ranges(sba_addr); @@ -1967,7 +1963,6 @@ static int __init sba_driver_callback(struct parisc_device *dev) hppa_dma_ops = &sba_ops; -#ifdef CONFIG_PROC_FS switch (dev->id.hversion) { case PLUTO_MCKINLEY_PORT: if (!proc_mckinley_root) @@ -1985,7 +1980,6 @@ static int __init sba_driver_callback(struct parisc_device *dev) proc_create_single("sba_iommu", 0, root, sba_proc_info); proc_create_single("sba_iommu-bitmap", 0, root, sba_proc_bitmap_info); -#endif return 0; } diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 49bd09c7dd0a19902998fe414cf5038b4a6bfcaf..e9ae66cc4189b197e36e09be6b2648a30462a162 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -196,7 +196,7 @@ config PCI_HYPERV config PCI_DYNAMIC_OF_NODES bool "Create Device tree nodes for PCI devices" - depends on OF + depends on OF_IRQ select OF_DYNAMIC help This option enables support for generating device tree nodes for some diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index e2f29404c84eca23f59e61f7c63280fdba7da932..64420ecc24d1c38c45fc838e5b635495564172d3 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -43,7 +43,6 @@ #define PARF_PHY_REFCLK 0x4c #define PARF_CONFIG_BITS 0x50 #define PARF_DBI_BASE_ADDR 0x168 -#define PARF_SLV_ADDR_SPACE_SIZE_2_3_3 0x16c /* Register offset specific to IP ver 2.3.3 */ #define PARF_MHI_CLOCK_RESET_CTRL 0x174 #define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 #define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 @@ -797,8 +796,7 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); u32 val; - writel(SLV_ADDR_SPACE_SZ, - pcie->parf + PARF_SLV_ADDR_SPACE_SIZE_2_3_3); + writel(SLV_ADDR_SPACE_SZ, pcie->parf + PARF_SLV_ADDR_SPACE_SIZE); val = readl(pcie->parf + PARF_PHY_CTRL); val &= ~PHY_TEST_PWR_DOWN; diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 2af64bcb7da3a3e0bc28ab0c99ca54d1d4f7b1f5..51e3dd0ea5abe02c4e9ce07d096f5ded30f26883 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -657,30 +657,33 @@ void of_pci_make_dev_node(struct pci_dev *pdev) cset = kmalloc(sizeof(*cset), GFP_KERNEL); if (!cset) - goto failed; + goto out_free_name; of_changeset_init(cset); np = of_changeset_create_node(cset, ppnode, name); if (!np) - goto failed; - np->data = cset; + goto out_destroy_cset; ret = of_pci_add_properties(pdev, cset, np); if (ret) - goto failed; + goto out_free_node; ret = of_changeset_apply(cset); if (ret) - goto failed; + goto out_free_node; + np->data = cset; pdev->dev.of_node = np; kfree(name); return; -failed: - if (np) - of_node_put(np); +out_free_node: + of_node_put(np); +out_destroy_cset: + of_changeset_destroy(cset); + kfree(cset); +out_free_name: kfree(name); } #endif diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index 710ec35ba4a17738906f0e1aad105b702e288e2d..c2c7334152bc0522c9a8ec2a4d44e66ffae344e8 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -186,8 +186,8 @@ static int of_pci_prop_interrupts(struct pci_dev *pdev, static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs, struct device_node *np) { + u32 i, addr_sz[OF_PCI_MAX_INT_PIN] = { 0 }, map_sz = 0; struct of_phandle_args out_irq[OF_PCI_MAX_INT_PIN]; - u32 i, addr_sz[OF_PCI_MAX_INT_PIN], map_sz = 0; __be32 laddr[OF_PCI_ADDRESS_CELLS] = { 0 }; u32 int_map_mask[] = { 0xffff00, 0, 0, 7 }; struct device_node *pnode; @@ -213,33 +213,44 @@ static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs, out_irq[i].args[0] = pin; ret = of_irq_parse_raw(laddr, &out_irq[i]); if (ret) { - pci_err(pdev, "parse irq %d failed, ret %d", pin, ret); + out_irq[i].np = NULL; + pci_dbg(pdev, "parse irq %d failed, ret %d", pin, ret); continue; } - ret = of_property_read_u32(out_irq[i].np, "#address-cells", - &addr_sz[i]); - if (ret) - addr_sz[i] = 0; + of_property_read_u32(out_irq[i].np, "#address-cells", + &addr_sz[i]); } list_for_each_entry(child, &pdev->subordinate->devices, bus_list) { for (pin = 1; pin <= OF_PCI_MAX_INT_PIN; pin++) { i = pci_swizzle_interrupt_pin(child, pin) - 1; + if (!out_irq[i].np) + continue; map_sz += 5 + addr_sz[i] + out_irq[i].args_count; } } + /* + * Parsing interrupt failed for all pins. In this case, it does not + * need to generate interrupt-map property. + */ + if (!map_sz) + return 0; + int_map = kcalloc(map_sz, sizeof(u32), GFP_KERNEL); mapp = int_map; list_for_each_entry(child, &pdev->subordinate->devices, bus_list) { for (pin = 1; pin <= OF_PCI_MAX_INT_PIN; pin++) { + i = pci_swizzle_interrupt_pin(child, pin) - 1; + if (!out_irq[i].np) + continue; + *mapp = (child->bus->number << 16) | (child->devfn << 8); mapp += OF_PCI_ADDRESS_CELLS; *mapp = pin; mapp++; - i = pci_swizzle_interrupt_pin(child, pin) - 1; *mapp = out_irq[i].np->phandle; mapp++; if (addr_sz[i]) { diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index a79c110c7e510e73b598b48e5ac3c0daffdc2341..51ec9e7e784f0e3a82134a05b114abe5be90d07c 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -572,7 +572,19 @@ static void pci_pm_default_resume_early(struct pci_dev *pci_dev) static void pci_pm_bridge_power_up_actions(struct pci_dev *pci_dev) { - pci_bridge_wait_for_secondary_bus(pci_dev, "resume"); + int ret; + + ret = pci_bridge_wait_for_secondary_bus(pci_dev, "resume"); + if (ret) { + /* + * The downstream link failed to come up, so mark the + * devices below as disconnected to make sure we don't + * attempt to resume them. + */ + pci_walk_bus(pci_dev->subordinate, pci_dev_set_disconnected, + NULL); + return; + } /* * When powering on a bridge from D3cold, the whole hierarchy may be diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index e85ff946e8c8cc338442f7dd4b4b8b52c1e44e69..9c8fd69ae5ad8ea2ed8cbe2c20c7f67216b0978f 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -229,6 +229,7 @@ int pcie_aer_is_native(struct pci_dev *dev) return pcie_ports_native || host->native_aer; } +EXPORT_SYMBOL_NS_GPL(pcie_aer_is_native, CXL); static int pci_enable_pcie_error_reporting(struct pci_dev *dev) { diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index 58a2b1a1cae4c03c60416cf72f909c33fecf6d90..1f3803bde7ee16067d4aa801057076268dfe4b51 100644 --- a/drivers/pci/pcie/portdrv.h +++ b/drivers/pci/pcie/portdrv.h @@ -29,10 +29,8 @@ extern bool pcie_ports_dpc_native; #ifdef CONFIG_PCIEAER int pcie_aer_init(void); -int pcie_aer_is_native(struct pci_dev *dev); #else static inline int pcie_aer_init(void) { return 0; } -static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; } #endif #ifdef CONFIG_HOTPLUG_PCI_PCIE diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ab2a4a3a4c06e31b833ff309e3068eaa67a247a4..795534589b98503c75055b2a09df889057532d38 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -997,6 +997,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) res = window->res; if (!res->flags && !res->start && !res->end) { release_resource(res); + resource_list_destroy_entry(window); continue; } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5de09d2eb014ab3969375eb2f2757a4fbfd1633b..eeec1d6f90238ed981d1161cf563862050fbc934 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3726,7 +3726,7 @@ static void quirk_no_bus_reset(struct pci_dev *dev) */ static void quirk_nvidia_no_bus_reset(struct pci_dev *dev) { - if ((dev->device & 0xffc0) == 0x2340 || dev->device == 0x1eb8) + if ((dev->device & 0xffc0) == 0x2340) quirk_no_bus_reset(dev); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c index 913dc04b3a400e3f1f533ba93d8de3fcf038708a..6b50bc551984626aadb25ff039f2c9ef333bac7a 100644 --- a/drivers/perf/arm-cmn.c +++ b/drivers/perf/arm-cmn.c @@ -1972,7 +1972,7 @@ static irqreturn_t arm_cmn_handle_irq(int irq, void *dev_id) u64 delta; int i; - for (i = 0; i < CMN_DTM_NUM_COUNTERS; i++) { + for (i = 0; i < CMN_DT_NUM_COUNTERS; i++) { if (status & (1U << i)) { ret = IRQ_HANDLED; if (WARN_ON(!dtc->counters[i])) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index e5a2ac4155f605900266df778aa010287fd1a207..8fcaa26f0f8a6ffad590fd7c144dd766157b9445 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -749,6 +749,8 @@ static void armv8pmu_start(struct arm_pmu *cpu_pmu) /* Enable all counters */ armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMU_PMCR_E); + + kvm_vcpu_pmu_resync_el0(); } static void armv8pmu_stop(struct arm_pmu *cpu_pmu) diff --git a/drivers/perf/cxl_pmu.c b/drivers/perf/cxl_pmu.c index 0a8f597e695bccf12c5b1de72386c3eafc07131c..365d964b0f6a6d7382455f3b07035fafe1de1fa2 100644 --- a/drivers/perf/cxl_pmu.c +++ b/drivers/perf/cxl_pmu.c @@ -25,7 +25,7 @@ #include "../cxl/pmu.h" #define CXL_PMU_CAP_REG 0x0 -#define CXL_PMU_CAP_NUM_COUNTERS_MSK GENMASK_ULL(4, 0) +#define CXL_PMU_CAP_NUM_COUNTERS_MSK GENMASK_ULL(5, 0) #define CXL_PMU_CAP_COUNTER_WIDTH_MSK GENMASK_ULL(15, 8) #define CXL_PMU_CAP_NUM_EVN_CAP_REG_SUP_MSK GENMASK_ULL(24, 20) #define CXL_PMU_CAP_FILTERS_SUP_MSK GENMASK_ULL(39, 32) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index aac670b90589bc6d07d33e7568aed042f9b3e3eb..d1670bbe6d6bcc7d311519b21efe65c6ddf81fa1 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -93,6 +93,7 @@ source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" source "drivers/phy/socionext/Kconfig" source "drivers/phy/st/Kconfig" +source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index ba7c100b14fc7da4c6367e60f845b6ebb9f31fb9..868a220ed0f6df60ee478df391777a212a210716 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -32,6 +32,7 @@ obj-y += allwinner/ \ samsung/ \ socionext/ \ st/ \ + starfive/ \ sunplus/ \ tegra/ \ ti/ \ diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 56d53f78d002242ed00621dc54293baea45b01d5..ec551464dd4f323964da0bbee5869fdcb2feeecf 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -23,8 +23,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/phy/allwinner/phy-sun50i-usb3.c b/drivers/phy/allwinner/phy-sun50i-usb3.c index 84055b720016ebe0a511817bbd0c28ec8a948484..363f9a0df503ba1d860d11193c1e2d4904c5659b 100644 --- a/drivers/phy/allwinner/phy-sun50i-usb3.c +++ b/drivers/phy/allwinner/phy-sun50i-usb3.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c b/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c index 6e9af79e152cdd70b3f5b7415c920a4501ab93f2..08a86962d94924d12b44af1495f7cf7b671259a9 100644 --- a/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c +++ b/drivers/phy/amlogic/phy-meson-axg-mipi-dphy.c @@ -13,8 +13,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c b/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c index a3e1108b736d6540be073b0d98aa000002a744a7..ae898f93f97b20ffc67b233ab6653cbb1456505a 100644 --- a/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c +++ b/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include diff --git a/drivers/phy/amlogic/phy-meson-axg-pcie.c b/drivers/phy/amlogic/phy-meson-axg-pcie.c index 2299bab38e05d403008f16374d59dce5a963f84f..60be5cdc600b32a093a7286240caa000eaf66ab6 100644 --- a/drivers/phy/amlogic/phy-meson-axg-pcie.c +++ b/drivers/phy/amlogic/phy-meson-axg-pcie.c @@ -4,6 +4,7 @@ * * Copyright (C) 2020 Remi Pommarel */ +#include #include #include #include diff --git a/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c index cabdddbbabfd7dc251a7027b6e5745dea398d273..46e5f7e7eb6c8a3ec19443d7d8e6fa133656cdef 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c +++ b/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c index ec2555bb83d57cae7b3e9c4409f8bc010b26f26d..0e0b5c00b676f476cc38b5bc7f0d3e2f726c84ea 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-usb2.c +++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -319,7 +319,7 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - priv->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev); + priv->soc_id = (uintptr_t)of_device_get_match_data(&pdev->dev); priv->regmap = devm_regmap_init_mmio(dev, base, &phy_meson_g12a_usb2_regmap_conf); diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c index d2a1da8d9e58880186df69eb02bd281f34dd6111..2712c4bd549d87a4607f7f7907ae65d74e2a159d 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c +++ b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb2.c b/drivers/phy/amlogic/phy-meson-gxl-usb2.c index db17c3448bfedbe6426ea2c5f4e66499c9e10d16..14ea89927ab142dbf27670e305b480993e128588 100644 --- a/drivers/phy/amlogic/phy-meson-gxl-usb2.c +++ b/drivers/phy/amlogic/phy-meson-gxl-usb2.c @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/phy/amlogic/phy-meson8-hdmi-tx.c b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c index f9a6572c27d870ae46eeacf813efe2531055515d..2617f7f6c2ec593b368c9351ca44593be7470461 100644 --- a/drivers/phy/amlogic/phy-meson8-hdmi-tx.c +++ b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/amlogic/phy-meson8b-usb2.c b/drivers/phy/amlogic/phy-meson8b-usb2.c index dd96763911b8bec6409b24ce31002fea625a746d..d63147c41b8c9698d728c627cc58d6a910ad87fd 100644 --- a/drivers/phy/amlogic/phy-meson8b-usb2.c +++ b/drivers/phy/amlogic/phy-meson8b-usb2.c @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb3.c b/drivers/phy/broadcom/phy-bcm-ns-usb3.c index bbfad209c890fc255f4f87ff70a932fa4fb7c98b..69584b685edbb7cc1371e6d6f61ce8f313b8c918 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb3.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb3.c @@ -206,7 +206,7 @@ static int bcm_ns_usb3_mdio_probe(struct mdio_device *mdiodev) of_id = of_match_device(bcm_ns_usb3_id_table, dev); if (!of_id) return -EINVAL; - usb3->family = (enum bcm_ns_family)of_id->data; + usb3->family = (uintptr_t)of_id->data; syscon_np = of_parse_phandle(dev->of_node, "usb3-dmp-syscon", 0); err = of_address_to_resource(syscon_np, 0, &res); diff --git a/drivers/phy/broadcom/phy-bcm-sr-usb.c b/drivers/phy/broadcom/phy-bcm-sr-usb.c index 0002da3b5b5d7e5ca92d68b6d031730678193a42..b0bd18a5df8794fcc192a5b927ebfa3065c5fa6e 100644 --- a/drivers/phy/broadcom/phy-bcm-sr-usb.c +++ b/drivers/phy/broadcom/phy-bcm-sr-usb.c @@ -311,7 +311,7 @@ static int bcm_usb_phy_probe(struct platform_device *pdev) of_id = of_match_node(bcm_usb_phy_of_match, dn); if (of_id) - version = (enum bcm_usb_phy_version)of_id->data; + version = (uintptr_t)of_id->data; else return -ENODEV; diff --git a/drivers/phy/broadcom/phy-bcm63xx-usbh.c b/drivers/phy/broadcom/phy-bcm63xx-usbh.c index 6c05ba8b08bec3a1160aa12568673ed2b554d2a1..f8183dea774b697a2cfab9fb6f04926029f0c353 100644 --- a/drivers/phy/broadcom/phy-bcm63xx-usbh.c +++ b/drivers/phy/broadcom/phy-bcm63xx-usbh.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index 769c707d9b7139681fd8439544a2528aa296ad73..ed9e18791ec9498f90b1f832e3d5026f3aac8f82 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -772,7 +772,7 @@ static int brcm_sata_phy_probe(struct platform_device *pdev) of_id = of_match_node(brcm_sata_phy_of_match, dn); if (of_id) - priv->version = (enum brcm_sata_phy_version)of_id->data; + priv->version = (uintptr_t)of_id->data; else priv->version = BRCM_SATA_PHY_STB_28NM; diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index a4cfb777dd836015929456e64090ef4e91b3f642..a16f0b58eb74538e9bd7a31fe0a1723563f3abeb 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/cadence/cdns-dphy-rx.c b/drivers/phy/cadence/cdns-dphy-rx.c index c05b043893a93abda679c173a07e2ed081a346e7..7729cf80a9bd892c3e5db6cdbdc4ece9ada2d99f 100644 --- a/drivers/phy/cadence/cdns-dphy-rx.c +++ b/drivers/phy/cadence/cdns-dphy-rx.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index 6e58012b6488e1996cbbdb582479bb610ea763bc..dddb66de6dba15cf9133c4657dd88fd3819a61fa 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -9,8 +9,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 7df9c79a772aa44f8e46680e56da8accc2584141..d4eb93ce82323888b2442168d1ae14b4c1ffde60 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -30,23 +30,34 @@ #define SIERRA_COMMON_CDB_OFFSET 0x0 #define SIERRA_MACRO_ID_REG 0x0 #define SIERRA_CMN_PLLLC_GEN_PREG 0x42 +#define SIERRA_CMN_PLLLC_FBDIV_INT_MODE0_PREG 0x43 +#define SIERRA_CMN_PLLLC_DCOCAL_CTRL_PREG 0x45 +#define SIERRA_CMN_PLLLC_INIT_PREG 0x46 +#define SIERRA_CMN_PLLLC_ITERTMR_PREG 0x47 #define SIERRA_CMN_PLLLC_MODE_PREG 0x48 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A #define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B +#define SIERRA_CMN_PLLLC_LOCKSEARCH_PREG 0x4C #define SIERRA_CMN_PLLLC_CLK1_PREG 0x4D +#define SIERRA_CMN_PLLLC_CLK0_PREG 0x4E #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50 #define SIERRA_CMN_PLLLC_DSMCORR_PREG 0x51 #define SIERRA_CMN_PLLLC_SS_PREG 0x52 #define SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG 0x53 #define SIERRA_CMN_PLLLC_SSTWOPT_PREG 0x54 +#define SIERRA_CMN_PLLCSM_PLLEN_TMR_PREG 0x5D +#define SIERRA_CMN_PLLCSM_PLLPRE_TMR_PREG 0x5E #define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62 #define SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG 0x63 +#define SIERRA_SDOSCCAL_CLK_CNT_PREG 0x6E #define SIERRA_CMN_REFRCV_PREG 0x98 +#define SIERRA_CMN_RESCAL_CTRLA_PREG 0xA0 #define SIERRA_CMN_REFRCV1_PREG 0xB8 #define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2 #define SIERRA_CMN_PLLLC1_FBDIV_INT_PREG 0xC3 +#define SIERRA_CMN_PLLLC1_DCOCAL_CTRL_PREG 0xC5 #define SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG 0xCA #define SIERRA_CMN_PLLLC1_CLK0_PREG 0xCE #define SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG 0xD0 @@ -86,6 +97,7 @@ #define SIERRA_DFE_BIASTRIM_PREG 0x04C #define SIERRA_DRVCTRL_ATTEN_PREG 0x06A #define SIERRA_DRVCTRL_BOOST_PREG 0x06F +#define SIERRA_LANE_TX_RECEIVER_DETECT_PREG 0x071 #define SIERRA_TX_RCVDET_OVRD_PREG 0x072 #define SIERRA_CLKPATHCTRL_TMR_PREG 0x081 #define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085 @@ -101,6 +113,8 @@ #define SIERRA_CREQ_SPARE_PREG 0x096 #define SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG 0x097 #define SIERRA_CTLELUT_CTRL_PREG 0x098 +#define SIERRA_DEQ_BLK_TAU_CTRL1_PREG 0x0AC +#define SIERRA_DEQ_BLK_TAU_CTRL4_PREG 0x0AF #define SIERRA_DFE_ECMP_RATESEL_PREG 0x0C0 #define SIERRA_DFE_SMP_RATESEL_PREG 0x0C1 #define SIERRA_DEQ_PHALIGN_CTRL 0x0C4 @@ -129,6 +143,9 @@ #define SIERRA_DEQ_GLUT14 0x0F6 #define SIERRA_DEQ_GLUT15 0x0F7 #define SIERRA_DEQ_GLUT16 0x0F8 +#define SIERRA_POSTPRECUR_EN_CEPH_CTRL_PREG 0x0F9 +#define SIERRA_TAU_EN_CEPH2TO0_PREG 0x0FB +#define SIERRA_TAU_EN_CEPH5TO3_PREG 0x0FC #define SIERRA_DEQ_ALUT0 0x108 #define SIERRA_DEQ_ALUT1 0x109 #define SIERRA_DEQ_ALUT2 0x10A @@ -143,6 +160,7 @@ #define SIERRA_DEQ_ALUT11 0x113 #define SIERRA_DEQ_ALUT12 0x114 #define SIERRA_DEQ_ALUT13 0x115 +#define SIERRA_OEPH_EN_CTRL_PREG 0x124 #define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128 #define SIERRA_DEQ_DFETAP0 0x129 #define SIERRA_DEQ_DFETAP1 0x12B @@ -157,6 +175,7 @@ #define SIERRA_DEQ_TAU_CTRL2_PREG 0x151 #define SIERRA_DEQ_TAU_CTRL3_PREG 0x152 #define SIERRA_DEQ_OPENEYE_CTRL_PREG 0x158 +#define SIERRA_DEQ_CONCUR_EPIOFFSET_MODE_PREG 0x159 #define SIERRA_DEQ_PICTRL_PREG 0x161 #define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170 #define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171 @@ -165,6 +184,7 @@ #define SIERRA_CPI_RESBIAS_BIN_PREG 0x17E #define SIERRA_CPI_TRIM_PREG 0x17F #define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183 +#define SIERRA_CPICAL_RES_STARTCODE_MODE01_PREG 0x184 #define SIERRA_EPI_CTRL_PREG 0x187 #define SIERRA_LFPSDET_SUPPORT_PREG 0x188 #define SIERRA_LFPSFILT_NS_PREG 0x18A @@ -176,6 +196,7 @@ #define SIERRA_RXBUFFER_CTLECTRL_PREG 0x19E #define SIERRA_RXBUFFER_RCDFECTRL_PREG 0x19F #define SIERRA_RXBUFFER_DFECTRL_PREG 0x1A0 +#define SIERRA_LN_SPARE_REG_PREG 0x1B0 #define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F #define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 @@ -2402,6 +2423,77 @@ static struct cdns_sierra_vals usb_100_ext_ssc_ln_vals = { .num_regs = ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), }; +/* SGMII PHY common configuration */ +static const struct cdns_reg_pairs sgmii_pma_cmn_vals[] = { + {0x0180, SIERRA_SDOSCCAL_CLK_CNT_PREG}, + {0x6000, SIERRA_CMN_REFRCV_PREG}, + {0x0031, SIERRA_CMN_RESCAL_CTRLA_PREG}, + {0x001C, SIERRA_CMN_PLLLC_FBDIV_INT_MODE0_PREG}, + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_LOCKSEARCH_PREG}, + {0x8103, SIERRA_CMN_PLLLC_CLK0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x0027, SIERRA_CMN_PLLCSM_PLLEN_TMR_PREG}, + {0x0062, SIERRA_CMN_PLLCSM_PLLPRE_TMR_PREG}, + {0x0800, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}, + {0x0000, SIERRA_CMN_PLLLC_INIT_PREG}, + {0x0000, SIERRA_CMN_PLLLC_ITERTMR_PREG}, + {0x0020, SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG}, + {0x0013, SIERRA_CMN_PLLLC_DCOCAL_CTRL_PREG}, + {0x0013, SIERRA_CMN_PLLLC1_DCOCAL_CTRL_PREG}, +}; + +static struct cdns_sierra_vals sgmii_cmn_vals = { + .reg_pairs = sgmii_pma_cmn_vals, + .num_regs = ARRAY_SIZE(sgmii_pma_cmn_vals), +}; + +/* SGMII PHY lane configuration */ +static const struct cdns_reg_pairs sgmii_ln_regs[] = { + {0x691E, SIERRA_DET_STANDEC_D_PREG}, + {0x0FFE, SIERRA_PSC_RX_A0_PREG}, + {0x0104, SIERRA_PLLCTRL_FBDIV_MODE01_PREG}, + {0x0013, SIERRA_PLLCTRL_SUBRATE_PREG}, + {0x0106, SIERRA_PLLCTRL_GEN_D_PREG}, + {0x5234, SIERRA_PLLCTRL_CPGAIN_MODE_PREG}, + {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, + {0x00AB, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x3C0E, SIERRA_CREQ_CCLKDET_MODE01_PREG}, + {0x3220, SIERRA_CREQ_FSMCLK_SEL_PREG}, + {0x0000, SIERRA_CREQ_EQ_CTRL_PREG}, + {0x6320, SIERRA_DEQ_CONCUR_EPIOFFSET_MODE_PREG}, + {0x0000, SIERRA_CPI_OUTBUF_RATESEL_PREG}, + {0x15A2, SIERRA_LN_SPARE_REG_PREG}, + {0x7900, SIERRA_DEQ_BLK_TAU_CTRL1_PREG}, + {0x2202, SIERRA_DEQ_BLK_TAU_CTRL4_PREG}, + {0x2206, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0005, SIERRA_LANE_TX_RECEIVER_DETECT_PREG}, + {0x8001, SIERRA_CREQ_SPARE_PREG}, + {0x0000, SIERRA_DEQ_CONCUR_CTRL1_PREG}, + {0xD004, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x0101, SIERRA_DEQ_GLUT9}, + {0x0101, SIERRA_DEQ_GLUT10}, + {0x0101, SIERRA_DEQ_GLUT11}, + {0x0101, SIERRA_DEQ_GLUT12}, + {0x0000, SIERRA_DEQ_GLUT13}, + {0x0000, SIERRA_DEQ_GLUT16}, + {0x0000, SIERRA_POSTPRECUR_EN_CEPH_CTRL_PREG}, + {0x0000, SIERRA_TAU_EN_CEPH2TO0_PREG}, + {0x0003, SIERRA_TAU_EN_CEPH5TO3_PREG}, + {0x0101, SIERRA_DEQ_ALUT8}, + {0x0101, SIERRA_DEQ_ALUT9}, + {0x0100, SIERRA_DEQ_ALUT10}, + {0x0000, SIERRA_OEPH_EN_CTRL_PREG}, + {0x5425, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x7458, SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG}, + {0x321F, SIERRA_CPICAL_RES_STARTCODE_MODE01_PREG}, +}; + +static struct cdns_sierra_vals sgmii_pma_ln_vals = { + .reg_pairs = sgmii_ln_regs, + .num_regs = ARRAY_SIZE(sgmii_ln_regs), +}; + static const struct cdns_sierra_data cdns_map_sierra = { .id_value = SIERRA_MACRO_ID, .block_offset_shift = 0x2, @@ -2449,6 +2541,9 @@ static const struct cdns_sierra_data cdns_map_sierra = { }, }, [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sgmii_cmn_vals, + }, [TYPE_PCIE] = { [NO_SSC] = &sgmii_100_no_ssc_plllc1_opt3_cmn_vals, [EXTERNAL_SSC] = &sgmii_100_no_ssc_plllc1_opt3_cmn_vals, @@ -2487,6 +2582,9 @@ static const struct cdns_sierra_data cdns_map_sierra = { }, }, [TYPE_SGMII] = { + [TYPE_NONE] = { + [NO_SSC] = &sgmii_pma_ln_vals, + }, [TYPE_PCIE] = { [NO_SSC] = &sgmii_100_no_ssc_plllc1_opt3_ln_vals, [EXTERNAL_SSC] = &sgmii_100_no_ssc_plllc1_opt3_ln_vals, diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 37b6b5c05be859991eef7c74bc0c3b9db31dbb08..a75c96385c57acd9400b67d70f657d4fea1942e7 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #include #include @@ -27,14 +25,11 @@ #define REF_CLK_19_2MHZ 19200000 #define REF_CLK_25MHZ 25000000 #define REF_CLK_100MHZ 100000000 +#define REF_CLK_156_25MHZ 156250000 #define MAX_NUM_LANES 4 #define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */ -#define NUM_SSC_MODE 3 -#define NUM_REF_CLK 3 -#define NUM_PHY_TYPE 6 - #define POLL_TIMEOUT_US 5000 #define PLL_LOCK_TIMEOUT 100000 @@ -106,6 +101,7 @@ #define CMN_PLL0_HIGH_THR_M0 0x0093U #define CMN_PLL0_DSM_DIAG_M0 0x0094U #define CMN_PLL0_DSM_FBH_OVRD_M0 0x0095U +#define CMN_PLL0_DSM_FBL_OVRD_M0 0x0096U #define CMN_PLL0_SS_CTRL1_M0 0x0098U #define CMN_PLL0_SS_CTRL2_M0 0x0099U #define CMN_PLL0_SS_CTRL3_M0 0x009AU @@ -196,6 +192,10 @@ #define RX_PSC_A2 0x0002U #define RX_PSC_A3 0x0003U #define RX_PSC_CAL 0x0006U +#define RX_SDCAL0_INIT_TMR 0x0044U +#define RX_SDCAL0_ITER_TMR 0x0045U +#define RX_SDCAL1_INIT_TMR 0x004CU +#define RX_SDCAL1_ITER_TMR 0x004DU #define RX_CDRLF_CNFG 0x0080U #define RX_CDRLF_CNFG3 0x0082U #define RX_SIGDET_HL_FILT_TMR 0x0090U @@ -294,20 +294,49 @@ enum cdns_torrent_phy_type { TYPE_SGMII, TYPE_QSGMII, TYPE_USB, + TYPE_USXGMII, }; enum cdns_torrent_ref_clk { CLK_19_2_MHZ, CLK_25_MHZ, - CLK_100_MHZ + CLK_100_MHZ, + CLK_156_25_MHZ, + CLK_ANY, }; enum cdns_torrent_ssc_mode { NO_SSC, EXTERNAL_SSC, - INTERNAL_SSC + INTERNAL_SSC, + ANY_SSC, }; +/* Unique key id for vals table entry + * REFCLK0_RATE | REFCLK1_RATE | LINK0_TYPE | LINK1_TYPE | SSC_TYPE + */ +#define REFCLK0_SHIFT 12 +#define REFCLK0_MASK GENMASK(14, 12) +#define REFCLK1_SHIFT 9 +#define REFCLK1_MASK GENMASK(11, 9) +#define LINK0_SHIFT 6 +#define LINK0_MASK GENMASK(8, 6) +#define LINK1_SHIFT 3 +#define LINK1_MASK GENMASK(5, 3) +#define SSC_SHIFT 0 +#define SSC_MASK GENMASK(2, 0) + +#define CDNS_TORRENT_KEY(refclk0, refclk1, link0, link1, ssc) \ + ((((refclk0) << REFCLK0_SHIFT) & REFCLK0_MASK) | \ + (((refclk1) << REFCLK1_SHIFT) & REFCLK1_MASK) | \ + (((link0) << LINK0_SHIFT) & LINK0_MASK) | \ + (((link1) << LINK1_SHIFT) & LINK1_MASK) | \ + (((ssc) << SSC_SHIFT) & SSC_MASK)) + +#define CDNS_TORRENT_KEY_ANYCLK(link0, link1) \ + CDNS_TORRENT_KEY(CLK_ANY, CLK_ANY, \ + (link0), (link1), ANY_SSC) + struct cdns_torrent_inst { struct phy *phy; u32 mlane; @@ -394,21 +423,26 @@ struct cdns_torrent_vals { u32 num_regs; }; +struct cdns_torrent_vals_entry { + u32 key; + struct cdns_torrent_vals *vals; +}; + +struct cdns_torrent_vals_table { + struct cdns_torrent_vals_entry *entries; + u32 num_entries; +}; + struct cdns_torrent_data { u8 block_offset_shift; u8 reg_offset_shift; - struct cdns_torrent_vals *link_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] - [NUM_SSC_MODE]; - struct cdns_torrent_vals *xcvr_diag_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] - [NUM_SSC_MODE]; - struct cdns_torrent_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] - [NUM_SSC_MODE]; - struct cdns_torrent_vals *cmn_vals[NUM_REF_CLK][NUM_PHY_TYPE] - [NUM_PHY_TYPE][NUM_SSC_MODE]; - struct cdns_torrent_vals *tx_ln_vals[NUM_REF_CLK][NUM_PHY_TYPE] - [NUM_PHY_TYPE][NUM_SSC_MODE]; - struct cdns_torrent_vals *rx_ln_vals[NUM_REF_CLK][NUM_PHY_TYPE] - [NUM_PHY_TYPE][NUM_SSC_MODE]; + struct cdns_torrent_vals_table link_cmn_vals_tbl; + struct cdns_torrent_vals_table xcvr_diag_vals_tbl; + struct cdns_torrent_vals_table pcs_cmn_vals_tbl; + struct cdns_torrent_vals_table phy_pma_cmn_vals_tbl; + struct cdns_torrent_vals_table cmn_vals_tbl; + struct cdns_torrent_vals_table tx_ln_vals_tbl; + struct cdns_torrent_vals_table rx_ln_vals_tbl; }; struct cdns_regmap_cdb_context { @@ -417,6 +451,24 @@ struct cdns_regmap_cdb_context { u8 reg_offset_shift; }; +static struct cdns_torrent_vals *cdns_torrent_get_tbl_vals(const struct cdns_torrent_vals_table *tbl, + enum cdns_torrent_ref_clk refclk0, + enum cdns_torrent_ref_clk refclk1, + enum cdns_torrent_phy_type link0, + enum cdns_torrent_phy_type link1, + enum cdns_torrent_ssc_mode ssc) +{ + int i; + u32 key = CDNS_TORRENT_KEY(refclk0, refclk1, link0, link1, ssc); + + for (i = 0; i < tbl->num_entries; i++) { + if (tbl->entries[i].key == key) + return tbl->entries[i].vals; + } + + return NULL; +} + static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val) { struct cdns_regmap_cdb_context *ctx = context; @@ -644,6 +696,8 @@ static const char *cdns_torrent_get_phy_type(enum cdns_torrent_phy_type phy_type return "QSGMII"; case TYPE_USB: return "USB"; + case TYPE_USXGMII: + return "USXGMII"; default: return "None"; } @@ -2244,6 +2298,7 @@ static int cdns_torrent_phy_init(struct phy *phy) struct cdns_torrent_inst *inst = phy_get_drvdata(phy); enum cdns_torrent_phy_type phy_type = inst->phy_type; enum cdns_torrent_ssc_mode ssc = inst->ssc_mode; + struct cdns_torrent_vals *phy_pma_cmn_vals; struct cdns_torrent_vals *pcs_cmn_vals; struct cdns_reg_pairs *reg_pairs; struct regmap *regmap; @@ -2258,13 +2313,16 @@ static int cdns_torrent_phy_init(struct phy *phy) /** * Spread spectrum generation is not required or supported - * for SGMII/QSGMII + * for SGMII/QSGMII/USXGMII */ - if (phy_type == TYPE_SGMII || phy_type == TYPE_QSGMII) + if (phy_type == TYPE_SGMII || phy_type == TYPE_QSGMII || phy_type == TYPE_USXGMII) ssc = NO_SSC; /* PHY configuration specific registers for single link */ - link_cmn_vals = init_data->link_cmn_vals[phy_type][TYPE_NONE][ssc]; + link_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->link_cmn_vals_tbl, + CLK_ANY, CLK_ANY, + phy_type, TYPE_NONE, + ANY_SSC); if (link_cmn_vals) { reg_pairs = link_cmn_vals->reg_pairs; num_regs = link_cmn_vals->num_regs; @@ -2281,7 +2339,10 @@ static int cdns_torrent_phy_init(struct phy *phy) reg_pairs[i].val); } - xcvr_diag_vals = init_data->xcvr_diag_vals[phy_type][TYPE_NONE][ssc]; + xcvr_diag_vals = cdns_torrent_get_tbl_vals(&init_data->xcvr_diag_vals_tbl, + CLK_ANY, CLK_ANY, + phy_type, TYPE_NONE, + ANY_SSC); if (xcvr_diag_vals) { reg_pairs = xcvr_diag_vals->reg_pairs; num_regs = xcvr_diag_vals->num_regs; @@ -2294,7 +2355,10 @@ static int cdns_torrent_phy_init(struct phy *phy) } /* PHY PCS common registers configurations */ - pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc]; + pcs_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->pcs_cmn_vals_tbl, + CLK_ANY, CLK_ANY, + phy_type, TYPE_NONE, + ANY_SSC); if (pcs_cmn_vals) { reg_pairs = pcs_cmn_vals->reg_pairs; num_regs = pcs_cmn_vals->num_regs; @@ -2304,8 +2368,25 @@ static int cdns_torrent_phy_init(struct phy *phy) reg_pairs[i].val); } + /* PHY PMA common registers configurations */ + phy_pma_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->phy_pma_cmn_vals_tbl, + CLK_ANY, CLK_ANY, + phy_type, TYPE_NONE, + ANY_SSC); + if (phy_pma_cmn_vals) { + reg_pairs = phy_pma_cmn_vals->reg_pairs; + num_regs = phy_pma_cmn_vals->num_regs; + regmap = cdns_phy->regmap_phy_pma_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, + reg_pairs[i].val); + } + /* PMA common registers configurations */ - cmn_vals = init_data->cmn_vals[ref_clk][phy_type][TYPE_NONE][ssc]; + cmn_vals = cdns_torrent_get_tbl_vals(&init_data->cmn_vals_tbl, + ref_clk, ref_clk, + phy_type, TYPE_NONE, + ssc); if (cmn_vals) { reg_pairs = cmn_vals->reg_pairs; num_regs = cmn_vals->num_regs; @@ -2316,7 +2397,10 @@ static int cdns_torrent_phy_init(struct phy *phy) } /* PMA TX lane registers configurations */ - tx_ln_vals = init_data->tx_ln_vals[ref_clk][phy_type][TYPE_NONE][ssc]; + tx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->tx_ln_vals_tbl, + ref_clk, ref_clk, + phy_type, TYPE_NONE, + ssc); if (tx_ln_vals) { reg_pairs = tx_ln_vals->reg_pairs; num_regs = tx_ln_vals->num_regs; @@ -2329,7 +2413,10 @@ static int cdns_torrent_phy_init(struct phy *phy) } /* PMA RX lane registers configurations */ - rx_ln_vals = init_data->rx_ln_vals[ref_clk][phy_type][TYPE_NONE][ssc]; + rx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->rx_ln_vals_tbl, + ref_clk, ref_clk, + phy_type, TYPE_NONE, + ssc); if (rx_ln_vals) { reg_pairs = rx_ln_vals->reg_pairs; num_regs = rx_ln_vals->num_regs; @@ -2418,7 +2505,9 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) * being configured, but these can be different for particular * PHY type and are per lane. */ - link_cmn_vals = init_data->link_cmn_vals[phy_t1][phy_t2][ssc]; + link_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->link_cmn_vals_tbl, + CLK_ANY, CLK_ANY, + phy_t1, phy_t2, ANY_SSC); if (link_cmn_vals) { reg_pairs = link_cmn_vals->reg_pairs; num_regs = link_cmn_vals->num_regs; @@ -2436,7 +2525,9 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) reg_pairs[i].val); } - xcvr_diag_vals = init_data->xcvr_diag_vals[phy_t1][phy_t2][ssc]; + xcvr_diag_vals = cdns_torrent_get_tbl_vals(&init_data->xcvr_diag_vals_tbl, + CLK_ANY, CLK_ANY, + phy_t1, phy_t2, ANY_SSC); if (xcvr_diag_vals) { reg_pairs = xcvr_diag_vals->reg_pairs; num_regs = xcvr_diag_vals->num_regs; @@ -2449,7 +2540,9 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) } /* PHY PCS common registers configurations */ - pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc]; + pcs_cmn_vals = cdns_torrent_get_tbl_vals(&init_data->pcs_cmn_vals_tbl, + CLK_ANY, CLK_ANY, + phy_t1, phy_t2, ANY_SSC); if (pcs_cmn_vals) { reg_pairs = pcs_cmn_vals->reg_pairs; num_regs = pcs_cmn_vals->num_regs; @@ -2460,7 +2553,9 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) } /* PMA common registers configurations */ - cmn_vals = init_data->cmn_vals[ref_clk][phy_t1][phy_t2][ssc]; + cmn_vals = cdns_torrent_get_tbl_vals(&init_data->cmn_vals_tbl, + ref_clk, ref_clk, + phy_t1, phy_t2, ssc); if (cmn_vals) { reg_pairs = cmn_vals->reg_pairs; num_regs = cmn_vals->num_regs; @@ -2471,7 +2566,9 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) } /* PMA TX lane registers configurations */ - tx_ln_vals = init_data->tx_ln_vals[ref_clk][phy_t1][phy_t2][ssc]; + tx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->tx_ln_vals_tbl, + ref_clk, ref_clk, + phy_t1, phy_t2, ssc); if (tx_ln_vals) { reg_pairs = tx_ln_vals->reg_pairs; num_regs = tx_ln_vals->num_regs; @@ -2484,7 +2581,9 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) } /* PMA RX lane registers configurations */ - rx_ln_vals = init_data->rx_ln_vals[ref_clk][phy_t1][phy_t2][ssc]; + rx_ln_vals = cdns_torrent_get_tbl_vals(&init_data->rx_ln_vals_tbl, + ref_clk, ref_clk, + phy_t1, phy_t2, ssc); if (rx_ln_vals) { reg_pairs = rx_ln_vals->reg_pairs; num_regs = rx_ln_vals->num_regs; @@ -2617,6 +2716,9 @@ static int cdns_torrent_clk(struct cdns_torrent_phy *cdns_phy) case REF_CLK_100MHZ: cdns_phy->ref_clk_rate = CLK_100_MHZ; break; + case REF_CLK_156_25MHZ: + cdns_phy->ref_clk_rate = CLK_156_25_MHZ; + break; default: dev_err(cdns_phy->dev, "Invalid Ref Clock Rate\n"); clk_disable_unprepare(cdns_phy->clk); @@ -2736,6 +2838,9 @@ static int cdns_torrent_phy_probe(struct platform_device *pdev) case PHY_TYPE_USB3: cdns_phy->phys[node].phy_type = TYPE_USB; break; + case PHY_TYPE_USXGMII: + cdns_phy->phys[node].phy_type = TYPE_USXGMII; + break; default: dev_err(dev, "Unsupported protocol\n"); ret = -EINVAL; @@ -2929,6 +3034,123 @@ static struct cdns_torrent_vals dp_usb_xcvr_diag_ln_vals = { .num_regs = ARRAY_SIZE(dp_usb_xcvr_diag_ln_regs), }; +/* TI USXGMII configuration: Enable cmn_refclk_rcv_out_en */ +static struct cdns_reg_pairs ti_usxgmii_phy_pma_cmn_regs[] = { + {0x0040, PHY_PMA_CMN_CTRL1}, +}; + +static struct cdns_torrent_vals ti_usxgmii_phy_pma_cmn_vals = { + .reg_pairs = ti_usxgmii_phy_pma_cmn_regs, + .num_regs = ARRAY_SIZE(ti_usxgmii_phy_pma_cmn_regs), +}; + +/* Single USXGMII link configuration */ +static struct cdns_reg_pairs sl_usxgmii_link_cmn_regs[] = { + {0x0000, PHY_PLL_CFG}, + {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs sl_usxgmii_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0001, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals sl_usxgmii_link_cmn_vals = { + .reg_pairs = sl_usxgmii_link_cmn_regs, + .num_regs = ARRAY_SIZE(sl_usxgmii_link_cmn_regs), +}; + +static struct cdns_torrent_vals sl_usxgmii_xcvr_diag_ln_vals = { + .reg_pairs = sl_usxgmii_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(sl_usxgmii_xcvr_diag_ln_regs), +}; + +/* Single link USXGMII, 156.25 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs sl_usxgmii_156_25_no_ssc_cmn_regs[] = { + {0x0014, CMN_SSM_BIAS_TMR}, + {0x0028, CMN_PLLSM0_PLLPRE_TMR}, + {0x00A4, CMN_PLLSM0_PLLLOCK_TMR}, + {0x0028, CMN_PLLSM1_PLLPRE_TMR}, + {0x00A4, CMN_PLLSM1_PLLLOCK_TMR}, + {0x0062, CMN_BGCAL_INIT_TMR}, + {0x0062, CMN_BGCAL_ITER_TMR}, + {0x0014, CMN_IBCAL_INIT_TMR}, + {0x0018, CMN_TXPUCAL_INIT_TMR}, + {0x0005, CMN_TXPUCAL_ITER_TMR}, + {0x0018, CMN_TXPDCAL_INIT_TMR}, + {0x0005, CMN_TXPDCAL_ITER_TMR}, + {0x024A, CMN_RXCAL_INIT_TMR}, + {0x0005, CMN_RXCAL_ITER_TMR}, + {0x000B, CMN_SD_CAL_REFTIM_START}, + {0x0132, CMN_SD_CAL_PLLCNT_START}, + {0x0028, CMN_PDIAG_PLL1_CP_PADJ_M0}, + {0x0014, CMN_PLL0_DSM_FBH_OVRD_M0}, + {0x0014, CMN_PLL1_DSM_FBH_OVRD_M0}, + {0x0005, CMN_PLL0_DSM_FBL_OVRD_M0}, + {0x0005, CMN_PLL1_DSM_FBL_OVRD_M0}, + {0x061B, CMN_PLL0_VCOCAL_INIT_TMR}, + {0x061B, CMN_PLL1_VCOCAL_INIT_TMR}, + {0x0019, CMN_PLL0_VCOCAL_ITER_TMR}, + {0x0019, CMN_PLL1_VCOCAL_ITER_TMR}, + {0x1354, CMN_PLL0_VCOCAL_REFTIM_START}, + {0x1354, CMN_PLL1_VCOCAL_REFTIM_START}, + {0x1354, CMN_PLL0_VCOCAL_PLLCNT_START}, + {0x1354, CMN_PLL1_VCOCAL_PLLCNT_START}, + {0x0003, CMN_PLL0_VCOCAL_TCTRL}, + {0x0003, CMN_PLL1_VCOCAL_TCTRL}, + {0x0138, CMN_PLL0_LOCK_REFCNT_START}, + {0x0138, CMN_PLL1_LOCK_REFCNT_START}, + {0x0138, CMN_PLL0_LOCK_PLLCNT_START}, + {0x0138, CMN_PLL1_LOCK_PLLCNT_START} +}; + +static struct cdns_reg_pairs usxgmii_156_25_no_ssc_tx_ln_regs[] = { + {0x07A2, TX_RCVDET_ST_TMR}, + {0x00F3, TX_PSC_A0}, + {0x04A2, TX_PSC_A2}, + {0x04A2, TX_PSC_A3}, + {0x0000, TX_TXCC_CPOST_MULT_00}, + {0x0000, XCVR_DIAG_PSC_OVRD} +}; + +static struct cdns_reg_pairs usxgmii_156_25_no_ssc_rx_ln_regs[] = { + {0x0014, RX_SDCAL0_INIT_TMR}, + {0x0062, RX_SDCAL0_ITER_TMR}, + {0x0014, RX_SDCAL1_INIT_TMR}, + {0x0062, RX_SDCAL1_ITER_TMR}, + {0x091D, RX_PSC_A0}, + {0x0900, RX_PSC_A2}, + {0x0100, RX_PSC_A3}, + {0x0030, RX_REE_SMGM_CTRL1}, + {0x03C7, RX_REE_GCSM1_EQENM_PH1}, + {0x01C7, RX_REE_GCSM1_EQENM_PH2}, + {0x0000, RX_DIAG_DFE_CTRL}, + {0x0019, RX_REE_TAP1_CLIP}, + {0x0019, RX_REE_TAP2TON_CLIP}, + {0x00B9, RX_DIAG_NQST_CTRL}, + {0x0C21, RX_DIAG_DFE_AMP_TUNE_2}, + {0x0002, RX_DIAG_DFE_AMP_TUNE_3}, + {0x0033, RX_DIAG_PI_RATE}, + {0x0001, RX_DIAG_ACYA}, + {0x018C, RX_CDRLF_CNFG} +}; + +static struct cdns_torrent_vals sl_usxgmii_156_25_no_ssc_cmn_vals = { + .reg_pairs = sl_usxgmii_156_25_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(sl_usxgmii_156_25_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals usxgmii_156_25_no_ssc_tx_ln_vals = { + .reg_pairs = usxgmii_156_25_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(usxgmii_156_25_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals usxgmii_156_25_no_ssc_rx_ln_vals = { + .reg_pairs = usxgmii_156_25_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(usxgmii_156_25_no_ssc_rx_ln_regs), +}; + /* PCIe and DP link configuration */ static struct cdns_reg_pairs pcie_dp_link_cmn_regs[] = { {0x0003, PHY_PLL_CFG}, @@ -3934,1093 +4156,401 @@ static struct cdns_torrent_vals pcie_100_no_ssc_rx_ln_vals = { .num_regs = ARRAY_SIZE(pcie_100_ext_no_ssc_rx_ln_regs), }; +static struct cdns_torrent_vals_entry link_cmn_vals_entries[] = { + {CDNS_TORRENT_KEY_ANYCLK(TYPE_DP, TYPE_NONE), &sl_dp_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_DP, TYPE_PCIE), &pcie_dp_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_DP, TYPE_USB), &usb_dp_link_cmn_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_NONE), NULL}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_SGMII), &pcie_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_QSGMII), &pcie_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_USB), &pcie_usb_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_DP), &pcie_dp_link_cmn_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_SGMII, TYPE_NONE), &sl_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_SGMII, TYPE_PCIE), &pcie_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_SGMII, TYPE_USB), &usb_sgmii_link_cmn_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_QSGMII, TYPE_NONE), &sl_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_QSGMII, TYPE_PCIE), &pcie_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_QSGMII, TYPE_USB), &usb_sgmii_link_cmn_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_NONE), &sl_usb_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_PCIE), &pcie_usb_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_SGMII), &usb_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_QSGMII), &usb_sgmii_link_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_DP), &usb_dp_link_cmn_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USXGMII, TYPE_NONE), &sl_usxgmii_link_cmn_vals}, +}; + +static struct cdns_torrent_vals_entry xcvr_diag_vals_entries[] = { + {CDNS_TORRENT_KEY_ANYCLK(TYPE_DP, TYPE_NONE), &sl_dp_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_DP, TYPE_PCIE), &dp_pcie_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_DP, TYPE_USB), &dp_usb_xcvr_diag_ln_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_NONE), NULL}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_SGMII), &pcie_sgmii_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_QSGMII), &pcie_sgmii_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_USB), &pcie_usb_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_PCIE, TYPE_DP), &pcie_dp_xcvr_diag_ln_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_SGMII, TYPE_NONE), &sl_sgmii_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_SGMII, TYPE_PCIE), &sgmii_pcie_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_SGMII, TYPE_USB), &sgmii_usb_xcvr_diag_ln_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_QSGMII, TYPE_NONE), &sl_sgmii_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_QSGMII, TYPE_PCIE), &sgmii_pcie_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_QSGMII, TYPE_USB), &sgmii_usb_xcvr_diag_ln_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_NONE), &sl_usb_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_PCIE), &usb_pcie_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_SGMII), &usb_sgmii_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_QSGMII), &usb_sgmii_xcvr_diag_ln_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_DP), &usb_dp_xcvr_diag_ln_vals}, + + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USXGMII, TYPE_NONE), &sl_usxgmii_xcvr_diag_ln_vals}, +}; + +static struct cdns_torrent_vals_entry pcs_cmn_vals_entries[] = { + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_NONE), &usb_phy_pcs_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_PCIE), &usb_phy_pcs_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_SGMII), &usb_phy_pcs_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_QSGMII), &usb_phy_pcs_cmn_vals}, + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USB, TYPE_DP), &usb_phy_pcs_cmn_vals}, +}; + +static struct cdns_torrent_vals_entry cmn_vals_entries[] = { + {CDNS_TORRENT_KEY(CLK_19_2_MHZ, CLK_19_2_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_19_2_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_25_MHZ, CLK_25_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_25_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_PCIE, NO_SSC), &dp_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_USB, NO_SSC), &sl_dp_100_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, INTERNAL_SSC), &sl_pcie_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, NO_SSC), &pcie_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, EXTERNAL_SSC), &pcie_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, INTERNAL_SSC), &pcie_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, NO_SSC), &pcie_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, EXTERNAL_SSC), &pcie_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, INTERNAL_SSC), &pcie_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, NO_SSC), &pcie_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, EXTERNAL_SSC), &pcie_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, INTERNAL_SSC), &pcie_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_DP, NO_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_NONE, NO_SSC), &sl_sgmii_100_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, NO_SSC), &sgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, EXTERNAL_SSC), &sgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, INTERNAL_SSC), &sgmii_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, NO_SSC), &sgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, EXTERNAL_SSC), &sgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, INTERNAL_SSC), &sgmii_100_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_NONE, NO_SSC), &sl_qsgmii_100_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, NO_SSC), &qsgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, EXTERNAL_SSC), &qsgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, INTERNAL_SSC), &qsgmii_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, NO_SSC), &qsgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, EXTERNAL_SSC), &qsgmii_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, INTERNAL_SSC), &qsgmii_100_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, NO_SSC), &sl_usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, EXTERNAL_SSC), &sl_usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, INTERNAL_SSC), &sl_usb_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, NO_SSC), &usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, EXTERNAL_SSC), &usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, INTERNAL_SSC), &usb_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, NO_SSC), &sl_usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, EXTERNAL_SSC), &sl_usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, INTERNAL_SSC), &sl_usb_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, NO_SSC), &sl_usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, EXTERNAL_SSC), &sl_usb_100_no_ssc_cmn_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, INTERNAL_SSC), &sl_usb_100_int_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_DP, NO_SSC), &usb_100_no_ssc_cmn_vals}, + + {CDNS_TORRENT_KEY(CLK_156_25_MHZ, CLK_156_25_MHZ, TYPE_USXGMII, TYPE_NONE, NO_SSC), &sl_usxgmii_156_25_no_ssc_cmn_vals}, +}; + +static struct cdns_torrent_vals_entry cdns_tx_ln_vals_entries[] = { + {CDNS_TORRENT_KEY(CLK_19_2_MHZ, CLK_19_2_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_19_2_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_25_MHZ, CLK_25_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_25_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_PCIE, NO_SSC), &dp_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_USB, NO_SSC), &dp_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_DP, NO_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_NONE, NO_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, NO_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, EXTERNAL_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, INTERNAL_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, NO_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, EXTERNAL_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, INTERNAL_SSC), &sgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_NONE, NO_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, NO_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, EXTERNAL_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, INTERNAL_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, NO_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, EXTERNAL_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, INTERNAL_SSC), &qsgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_DP, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_156_25_MHZ, CLK_156_25_MHZ, TYPE_USXGMII, TYPE_NONE, NO_SSC), &usxgmii_156_25_no_ssc_tx_ln_vals}, +}; + +static struct cdns_torrent_vals_entry cdns_rx_ln_vals_entries[] = { + {CDNS_TORRENT_KEY(CLK_19_2_MHZ, CLK_19_2_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_19_2_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_25_MHZ, CLK_25_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_25_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_PCIE, NO_SSC), &dp_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_USB, NO_SSC), &dp_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, NO_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, EXTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, INTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, NO_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, EXTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, INTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, NO_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, EXTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, INTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, NO_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, EXTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, INTERNAL_SSC), &pcie_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_DP, NO_SSC), &pcie_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_NONE, NO_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, NO_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, EXTERNAL_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, INTERNAL_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, NO_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, EXTERNAL_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, INTERNAL_SSC), &sgmii_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_NONE, NO_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, NO_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, EXTERNAL_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, INTERNAL_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, NO_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, EXTERNAL_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, INTERNAL_SSC), &qsgmii_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, NO_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, EXTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, INTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, NO_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, EXTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, INTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, NO_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, EXTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, INTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, NO_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, EXTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, INTERNAL_SSC), &usb_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_DP, NO_SSC), &usb_100_no_ssc_rx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_156_25_MHZ, CLK_156_25_MHZ, TYPE_USXGMII, TYPE_NONE, NO_SSC), &usxgmii_156_25_no_ssc_rx_ln_vals}, +}; + static const struct cdns_torrent_data cdns_map_torrent = { .block_offset_shift = 0x2, .reg_offset_shift = 0x2, - .link_cmn_vals = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_dp_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &usb_dp_link_cmn_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_usb_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, - [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &pcie_dp_link_cmn_vals, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_usb_link_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_link_cmn_vals, - [INTERNAL_SSC] = &sl_usb_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_usb_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, - [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_dp_link_cmn_vals, - }, - }, + .link_cmn_vals_tbl = { + .entries = link_cmn_vals_entries, + .num_entries = ARRAY_SIZE(link_cmn_vals_entries), }, - .xcvr_diag_vals = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_pcie_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &dp_usb_xcvr_diag_ln_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &pcie_dp_xcvr_diag_ln_vals, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_pcie_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_dp_xcvr_diag_ln_vals, - }, - }, + .xcvr_diag_vals_tbl = { + .entries = xcvr_diag_vals_entries, + .num_entries = ARRAY_SIZE(xcvr_diag_vals_entries), }, - .pcs_cmn_vals = { - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - }, - }, + .pcs_cmn_vals_tbl = { + .entries = pcs_cmn_vals_entries, + .num_entries = ARRAY_SIZE(pcs_cmn_vals_entries), }, - .cmn_vals = { - [CLK_19_2_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_19_2_no_ssc_cmn_vals, - }, - }, - }, - [CLK_25_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_25_no_ssc_cmn_vals, - }, - }, - }, - [CLK_100_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_100_no_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = NULL, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - }, - }, - }, + .cmn_vals_tbl = { + .entries = cmn_vals_entries, + .num_entries = ARRAY_SIZE(cmn_vals_entries), }, - .tx_ln_vals = { - [CLK_19_2_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_19_2_no_ssc_tx_ln_vals, - }, - }, - }, - [CLK_25_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_25_no_ssc_tx_ln_vals, - }, - }, - }, - [CLK_100_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_SGMII] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_QSGMII] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_USB] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_DP] = { - [NO_SSC] = NULL, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_tx_ln_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_tx_ln_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - }, - }, + .tx_ln_vals_tbl = { + .entries = cdns_tx_ln_vals_entries, + .num_entries = ARRAY_SIZE(cdns_tx_ln_vals_entries), }, - .rx_ln_vals = { - [CLK_19_2_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_19_2_no_ssc_rx_ln_vals, - }, - }, - }, - [CLK_25_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_25_no_ssc_rx_ln_vals, - }, - }, - }, - [CLK_100_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - }, - }, + .rx_ln_vals_tbl = { + .entries = cdns_rx_ln_vals_entries, + .num_entries = ARRAY_SIZE(cdns_rx_ln_vals_entries), }, }; +static struct cdns_torrent_vals_entry j721e_phy_pma_cmn_vals_entries[] = { + {CDNS_TORRENT_KEY_ANYCLK(TYPE_USXGMII, TYPE_NONE), &ti_usxgmii_phy_pma_cmn_vals}, +}; + +static struct cdns_torrent_vals_entry ti_tx_ln_vals_entries[] = { + {CDNS_TORRENT_KEY(CLK_19_2_MHZ, CLK_19_2_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_19_2_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_25_MHZ, CLK_25_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_25_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_NONE, NO_SSC), &sl_dp_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_PCIE, NO_SSC), &dp_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_DP, TYPE_USB, NO_SSC), &dp_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_NONE, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_SGMII, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_QSGMII, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, NO_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, EXTERNAL_SSC), NULL}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_USB, INTERNAL_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_PCIE, TYPE_DP, NO_SSC), NULL}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_NONE, NO_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, NO_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, EXTERNAL_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_PCIE, INTERNAL_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, NO_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, EXTERNAL_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_SGMII, TYPE_USB, INTERNAL_SSC), &ti_sgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_NONE, NO_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, NO_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, EXTERNAL_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_PCIE, INTERNAL_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, NO_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, EXTERNAL_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_QSGMII, TYPE_USB, INTERNAL_SSC), &ti_qsgmii_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_NONE, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_PCIE, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_SGMII, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, EXTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_QSGMII, INTERNAL_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_100_MHZ, CLK_100_MHZ, TYPE_USB, TYPE_DP, NO_SSC), &usb_100_no_ssc_tx_ln_vals}, + + {CDNS_TORRENT_KEY(CLK_156_25_MHZ, CLK_156_25_MHZ, TYPE_USXGMII, TYPE_NONE, NO_SSC), &usxgmii_156_25_no_ssc_tx_ln_vals}, +}; + static const struct cdns_torrent_data ti_j721e_map_torrent = { .block_offset_shift = 0x0, .reg_offset_shift = 0x1, - .link_cmn_vals = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_dp_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &usb_dp_link_cmn_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_usb_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, - [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &pcie_dp_link_cmn_vals, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &pcie_sgmii_link_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_usb_link_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_link_cmn_vals, - [INTERNAL_SSC] = &sl_usb_link_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &pcie_usb_link_cmn_vals, - [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, - [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_sgmii_link_cmn_vals, - [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_dp_link_cmn_vals, - }, - }, + .link_cmn_vals_tbl = { + .entries = link_cmn_vals_entries, + .num_entries = ARRAY_SIZE(link_cmn_vals_entries), + }, + .xcvr_diag_vals_tbl = { + .entries = xcvr_diag_vals_entries, + .num_entries = ARRAY_SIZE(xcvr_diag_vals_entries), }, - .xcvr_diag_vals = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_pcie_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &dp_usb_xcvr_diag_ln_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &pcie_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &pcie_dp_xcvr_diag_ln_vals, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_pcie_xcvr_diag_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sgmii_usb_xcvr_diag_ln_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_usb_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &sl_usb_xcvr_diag_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_pcie_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &usb_pcie_xcvr_diag_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_dp_xcvr_diag_ln_vals, - }, - }, + .pcs_cmn_vals_tbl = { + .entries = pcs_cmn_vals_entries, + .num_entries = ARRAY_SIZE(pcs_cmn_vals_entries), }, - .pcs_cmn_vals = { - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_phy_pcs_cmn_vals, - }, - }, + .phy_pma_cmn_vals_tbl = { + .entries = j721e_phy_pma_cmn_vals_entries, + .num_entries = ARRAY_SIZE(j721e_phy_pma_cmn_vals_entries), }, - .cmn_vals = { - [CLK_19_2_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_19_2_no_ssc_cmn_vals, - }, - }, - }, - [CLK_25_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_25_no_ssc_cmn_vals, - }, - }, - }, - [CLK_100_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_100_no_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = &sl_pcie_100_int_ssc_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = NULL, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_sgmii_100_no_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sgmii_100_int_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_cmn_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_qsgmii_100_no_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &qsgmii_100_int_ssc_cmn_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_cmn_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &usb_100_int_ssc_cmn_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, - [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_100_no_ssc_cmn_vals, - }, - }, - }, + .cmn_vals_tbl = { + .entries = cmn_vals_entries, + .num_entries = ARRAY_SIZE(cmn_vals_entries), }, - .tx_ln_vals = { - [CLK_19_2_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_19_2_no_ssc_tx_ln_vals, - }, - }, - }, - [CLK_25_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_25_no_ssc_tx_ln_vals, - }, - }, - }, - [CLK_100_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_SGMII] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_QSGMII] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_USB] = { - [NO_SSC] = NULL, - [EXTERNAL_SSC] = NULL, - [INTERNAL_SSC] = NULL, - }, - [TYPE_DP] = { - [NO_SSC] = NULL, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &ti_sgmii_100_no_ssc_tx_ln_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &ti_qsgmii_100_no_ssc_tx_ln_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, - }, - }, - }, + .tx_ln_vals_tbl = { + .entries = ti_tx_ln_vals_entries, + .num_entries = ARRAY_SIZE(ti_tx_ln_vals_entries), }, - .rx_ln_vals = { - [CLK_19_2_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_19_2_no_ssc_rx_ln_vals, - }, - }, - }, - [CLK_25_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_25_no_ssc_rx_ln_vals, - }, - }, - }, - [CLK_100_MHZ] = { - [TYPE_DP] = { - [TYPE_NONE] = { - [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_PCIE] = { - [TYPE_NONE] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_SGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &sgmii_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_QSGMII] = { - [TYPE_NONE] = { - [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - }, - [TYPE_USB] = { - [NO_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &qsgmii_100_no_ssc_rx_ln_vals, - }, - }, - [TYPE_USB] = { - [TYPE_NONE] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_PCIE] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_SGMII] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_QSGMII] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - [TYPE_DP] = { - [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, - }, - }, - }, + .rx_ln_vals_tbl = { + .entries = cdns_rx_ln_vals_entries, + .num_entries = ARRAY_SIZE(cdns_rx_ln_vals_entries), }, }; diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c index d4c92498ad1e30485dc6bc682836abdd3794565b..b700f52b7b6799f92c1c66f8c737efaae7a47dfc 100644 --- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index 88826ceb72f8d36ee1bdd07d1aa50f33078a3e34..0b9a59d5b8f023a698d0a0078274d18d17c9a36e 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -394,7 +394,7 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) imx_phy->vbus = devm_regulator_get(dev, "vbus"); if (IS_ERR(imx_phy->vbus)) - return PTR_ERR(imx_phy->vbus); + return dev_err_probe(dev, PTR_ERR(imx_phy->vbus), "failed to get vbus\n"); phy_set_drvdata(imx_phy->phy, imx_phy); diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 569f12af2aafaa77400a9e3201a450fa24e55830..4f036c77284e1d2255bf7796bd45927ecbe63895 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -2,6 +2,7 @@ /* Copyright (c) 2021-2022 NXP. */ #include +#include #include #include #include diff --git a/drivers/phy/hisilicon/phy-hi3660-usb3.c b/drivers/phy/hisilicon/phy-hi3660-usb3.c index 84adce9b42770a7a090aca79327ebb5455e788a7..e2a09d67faed9a61dbd9d1e510aba7cc9716147c 100644 --- a/drivers/phy/hisilicon/phy-hi3660-usb3.c +++ b/drivers/phy/hisilicon/phy-hi3660-usb3.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/hisilicon/phy-hi3670-usb3.c b/drivers/phy/hisilicon/phy-hi3670-usb3.c index b9ffe08abaab4d7b02d12d43ee5813e34cdaed6e..40d3cf128b4403eb256c7a5c862c76bde867e30a 100644 --- a/drivers/phy/hisilicon/phy-hi3670-usb3.c +++ b/drivers/phy/hisilicon/phy-hi3670-usb3.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/hisilicon/phy-hi6220-usb.c b/drivers/phy/hisilicon/phy-hi6220-usb.c index e92ba78da4c83076cd0857d817319bd3fe5bd17e..97bd363dfe8775d1005d9ab9991cec155d82c9f7 100644 --- a/drivers/phy/hisilicon/phy-hi6220-usb.c +++ b/drivers/phy/hisilicon/phy-hi6220-usb.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c index 6ae6d509dfdd82edf3bf3425d685c077ff901a54..c138cd4807d6f94ac31a8fdee24ac42fc9e5a051 100644 --- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c +++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c @@ -9,8 +9,9 @@ #include #include #include -#include +#include #include +#include #include #define INNO_PHY_PORT_NUM 2 diff --git a/drivers/phy/hisilicon/phy-histb-combphy.c b/drivers/phy/hisilicon/phy-histb-combphy.c index f1cb3e4d2add5af662d960dd83f00ca45d061756..c44588fd5a53e44d7391ec24db6d390fed0065ca 100644 --- a/drivers/phy/hisilicon/phy-histb-combphy.c +++ b/drivers/phy/hisilicon/phy-histb-combphy.c @@ -13,8 +13,9 @@ #include #include #include -#include +#include #include +#include #include #include #include diff --git a/drivers/phy/hisilicon/phy-hix5hd2-sata.c b/drivers/phy/hisilicon/phy-hix5hd2-sata.c index b0f99a9ac857769add79ae6897c0653b88835355..1b26ddb4c8a71b2c81982bfd94c02255e6975bd1 100644 --- a/drivers/phy/hisilicon/phy-hix5hd2-sata.c +++ b/drivers/phy/hisilicon/phy-hix5hd2-sata.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/ingenic/phy-ingenic-usb.c b/drivers/phy/ingenic/phy-ingenic-usb.c index 28c28d81648492b9e560c45fda35e38276002b3f..eb2721f72a4c17fd58f8510f9192693b7ea44c2d 100644 --- a/drivers/phy/ingenic/phy-ingenic-usb.c +++ b/drivers/phy/ingenic/phy-ingenic-usb.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c index 29d246ea24b478c8915c6c4543475da05838fe1c..82f1ffc0b0ad97932283fc3b2bed15d2230f793d 100644 --- a/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c +++ b/drivers/phy/lantiq/phy-lantiq-rcu-usb2.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/marvell/phy-armada38x-comphy.c b/drivers/phy/marvell/phy-armada38x-comphy.c index 0fe4089643342b3096be48faaec896767c95a7ca..b7d99861526a559fe2aaaed2f22105667a1f0ab7 100644 --- a/drivers/phy/marvell/phy-armada38x-comphy.c +++ b/drivers/phy/marvell/phy-armada38x-comphy.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-berlin-sata.c b/drivers/phy/marvell/phy-berlin-sata.c index d70ba9bc42d9b6e67c999667e3e198716cb75c86..f972d78372eaf5fb19cdebdb0a177a58ad7fc39e 100644 --- a/drivers/phy/marvell/phy-berlin-sata.c +++ b/drivers/phy/marvell/phy-berlin-sata.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-mmp3-hsic.c b/drivers/phy/marvell/phy-mmp3-hsic.c index f2537fdcc3ab4dfebf62e830e58cc4f0094ea0b8..271f1a2258efb007252afb9e222afe01454b80ba 100644 --- a/drivers/phy/marvell/phy-mmp3-hsic.c +++ b/drivers/phy/marvell/phy-mmp3-hsic.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-mmp3-usb.c b/drivers/phy/marvell/phy-mmp3-usb.c index 04c0bada35194630d2f707dcc41d4ae226ebd4cd..5b71deb0885130e73bf965bf4696364352d4f18e 100644 --- a/drivers/phy/marvell/phy-mmp3-usb.c +++ b/drivers/phy/marvell/phy-mmp3-usb.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c index d641b345afa35cf8392fb335c8214749a4efbf07..24c3371e2bb294402a5be470520f4835bdd49522 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c index 8834436bc9dbccb1cc145e91b226ffa0092c4a16..04f4fb4bed7024c933190bbd9353615d54ac172c 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c index 34672e868a1e143ae316203446bb35c64aa96921..b0dd133665986e377ca164b726257d15ffb0892e 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-comphy.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-comphy.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1011,8 +1012,7 @@ static int mvebu_comphy_probe(struct platform_device *pdev) "marvell,system-controller"); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/phy/marvell/phy-mvebu-cp110-utmi.c b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c index aa27c799461040c1a4869f65c22b2b6cb7d5a341..4922a5f3327d51ff815bc9384e2be2c97ae2e1a5 100644 --- a/drivers/phy/marvell/phy-mvebu-cp110-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-cp110-utmi.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/marvell/phy-mvebu-sata.c b/drivers/phy/marvell/phy-mvebu-sata.c index 51a4646e293357b23d3a2f8071d9627e769a889c..89a5a2b69d80ffd7826ed9442ebe33784e75c2da 100644 --- a/drivers/phy/marvell/phy-mvebu-sata.c +++ b/drivers/phy/marvell/phy-mvebu-sata.c @@ -10,6 +10,7 @@ #include #include #include +#include #include struct priv { diff --git a/drivers/phy/marvell/phy-pxa-28nm-usb2.c b/drivers/phy/marvell/phy-pxa-28nm-usb2.c index 1b2107f80f3a7a3d6da7891fc0dd76869a4cc529..64afb82cf70e5ddf3180d280be633bda8ebb3b93 100644 --- a/drivers/phy/marvell/phy-pxa-28nm-usb2.c +++ b/drivers/phy/marvell/phy-pxa-28nm-usb2.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/marvell/phy-pxa-usb.c b/drivers/phy/marvell/phy-pxa-usb.c index ffe889893ff4a0e5ed9c5290501c74c8690ce935..6c98eb9608e9c1f0c42deef70be0a86b9db834a1 100644 --- a/drivers/phy/marvell/phy-pxa-usb.c +++ b/drivers/phy/marvell/phy-pxa-usb.c @@ -296,7 +296,7 @@ static int pxa_usb_phy_probe(struct platform_device *pdev) of_id = of_match_node(pxa_usb_phy_of_match, dev->of_node); if (of_id) - pxa_usb_phy->version = (enum pxa_usb_phy_version)of_id->data; + pxa_usb_phy->version = (uintptr_t)of_id->data; else pxa_usb_phy->version = PXA_USB_PHY_MMP2; diff --git a/drivers/phy/mediatek/phy-mtk-hdmi.h b/drivers/phy/mediatek/phy-mtk-hdmi.h index fc2ad6a0527f3ff807444073fe09b20b7e02188c..71c02d04348577e78b03aa7fe053e87162b9af89 100644 --- a/drivers/phy/mediatek/phy-mtk-hdmi.h +++ b/drivers/phy/mediatek/phy-mtk-hdmi.h @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c index 526c05a4af5e6a0e9fe3344ab4881e91cf665946..065ea626093aaac08b7dc9d091a37abcab5a0918 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c @@ -36,7 +36,7 @@ static int mtk_mipi_tx_power_on(struct phy *phy) int ret; /* Power up core and enable PLL */ - ret = clk_prepare_enable(mipi_tx->pll); + ret = clk_prepare_enable(mipi_tx->pll_hw.clk); if (ret < 0) return ret; @@ -53,7 +53,7 @@ static int mtk_mipi_tx_power_off(struct phy *phy) mipi_tx->driver_data->mipi_tx_disable_signal(phy); /* Disable PLL and power down core */ - clk_disable_unprepare(mipi_tx->pll); + clk_disable_unprepare(mipi_tx->pll_hw.clk); return 0; } @@ -158,9 +158,9 @@ static int mtk_mipi_tx_probe(struct platform_device *pdev) clk_init.ops = mipi_tx->driver_data->mipi_tx_clk_ops; mipi_tx->pll_hw.init = &clk_init; - mipi_tx->pll = devm_clk_register(dev, &mipi_tx->pll_hw); - if (IS_ERR(mipi_tx->pll)) - return dev_err_probe(dev, PTR_ERR(mipi_tx->pll), "Failed to register PLL\n"); + ret = devm_clk_hw_register(dev, &mipi_tx->pll_hw); + if (ret) + return dev_err_probe(dev, ret, "Failed to register PLL\n"); phy = devm_phy_create(dev, NULL, &mtk_mipi_tx_ops); if (IS_ERR(phy)) @@ -176,29 +176,19 @@ static int mtk_mipi_tx_probe(struct platform_device *pdev) mtk_mipi_tx_get_calibration_datal(mipi_tx); - return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, - mipi_tx->pll); -} - -static void mtk_mipi_tx_remove(struct platform_device *pdev) -{ - of_clk_del_provider(pdev->dev.of_node); + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mipi_tx->pll_hw); } static const struct of_device_id mtk_mipi_tx_match[] = { - { .compatible = "mediatek,mt2701-mipi-tx", - .data = &mt2701_mipitx_data }, - { .compatible = "mediatek,mt8173-mipi-tx", - .data = &mt8173_mipitx_data }, - { .compatible = "mediatek,mt8183-mipi-tx", - .data = &mt8183_mipitx_data }, - { }, + { .compatible = "mediatek,mt2701-mipi-tx", .data = &mt2701_mipitx_data }, + { .compatible = "mediatek,mt8173-mipi-tx", .data = &mt8173_mipitx_data }, + { .compatible = "mediatek,mt8183-mipi-tx", .data = &mt8183_mipitx_data }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_mipi_tx_match); static struct platform_driver mtk_mipi_tx_driver = { .probe = mtk_mipi_tx_probe, - .remove_new = mtk_mipi_tx_remove, .driver = { .name = "mediatek-mipi-tx", .of_match_table = mtk_mipi_tx_match, diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h index 47b60b1a72261752d3b22a1144e374b7c639605d..5d4876f1dc9509e0bd9a0760e7c6fb9e14a86d47 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.h +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -32,7 +31,6 @@ struct mtk_mipi_tx { u32 rt_code[5]; const struct mtk_mipitx_data *driver_data; struct clk_hw pll_hw; - struct clk *pll; }; struct mtk_mipi_tx *mtk_mipi_tx_from_clk_hw(struct clk_hw *hw); diff --git a/drivers/phy/mediatek/phy-mtk-pcie.c b/drivers/phy/mediatek/phy-mtk-pcie.c index 25dbd6e35722239a245ac338d26ad9a5ac95d4d8..a2f69d6c72f0808b3b9cd4440e319a2b5b9d5315 100644 --- a/drivers/phy/mediatek/phy-mtk-pcie.c +++ b/drivers/phy/mediatek/phy-mtk-pcie.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index 0d110e50bbfd0e8c465aa35120b67f16849b1bb7..05eab9014132fc9ff782e855e2c2351e8cedb64d 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -13,8 +13,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/drivers/phy/mediatek/phy-mtk-ufs.c b/drivers/phy/mediatek/phy-mtk-ufs.c index fc19e0fa8ed551bec19efeb64ce65d1a94addabd..0cb5a25b1b7add23b59db8f595ab68e1b4adba4d 100644 --- a/drivers/phy/mediatek/phy-mtk-ufs.c +++ b/drivers/phy/mediatek/phy-mtk-ufs.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 5487b9dd1ead1aca1a650edfc0d0e85c576087dc..840b7f8a31c5fd743af6611673fed4072c99fbd8 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -5,6 +5,7 @@ * Copyright (C) 2021 Texas Instruments Incorporated - https://www.ti.com * */ +#include #include #include #include diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c index d0f4546648f057718455dada8e9d87ac2ae4616a..1f0f908323f0e660a108622cb90ca55c7f595b32 100644 --- a/drivers/phy/phy-xgene.c +++ b/drivers/phy/phy-xgene.c @@ -39,6 +39,7 @@ * Currently, this driver only supports Gen3 SATA mode with external clock. */ #include +#include #include #include #include diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 97ca5952e34e3cb37389b7386fb668c97999798c..d891058b7c39a1683e778f0af804e2b53ef8cfba 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -102,6 +102,16 @@ config PHY_QCOM_QMP_USB Enable this to support the QMP USB PHY transceiver that is used with USB3 controllers on Qualcomm chips. +config PHY_QCOM_QMP_USB_LEGACY + tristate "Qualcomm QMP legacy USB PHY Driver" + select GENERIC_PHY + default n + help + Enable this legacy driver to support the QMP USB+DisplayPort Combo + PHY transceivers working only in USB3 mode on Qualcomm chips. This + driver exists only for compatibility with older device trees, + existing users have been migrated to PHY_QCOM_QMP_COMBO driver. + endif # PHY_QCOM_QMP config PHY_QCOM_QUSB2 @@ -133,6 +143,17 @@ config PHY_QCOM_EUSB2_REPEATER PMICs. The repeater is paired with a Synopsys eUSB2 Phy on Qualcomm SOCs. +config PHY_QCOM_M31_USB + tristate "Qualcomm M31 HS PHY driver support" + depends on USB && (ARCH_QCOM || COMPILE_TEST) + select GENERIC_PHY + help + Enable this to support M31 HS PHY transceivers on Qualcomm chips + with DWC3 USB core. It handles PHY initialization, clock + management required after resetting the hardware and power + management. This driver is required even for peripheral only or + host only mode configurations. + config PHY_QCOM_USB_HS tristate "Qualcomm USB HS PHY module" depends on USB_ULPI_BUS diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index b030858e0f8d6e3a5b14a523c0f5843d60dbc304..ffd609ac62336c1e7fcbb1d287d5a13ea69b399d 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o obj-$(CONFIG_PHY_QCOM_EDP) += phy-qcom-edp.o obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o +obj-$(CONFIG_PHY_QCOM_M31_USB) += phy-qcom-m31.o obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o obj-$(CONFIG_PHY_QCOM_QMP_COMBO) += phy-qcom-qmp-combo.o @@ -11,6 +12,7 @@ obj-$(CONFIG_PHY_QCOM_QMP_PCIE) += phy-qcom-qmp-pcie.o obj-$(CONFIG_PHY_QCOM_QMP_PCIE_8996) += phy-qcom-qmp-pcie-msm8996.o obj-$(CONFIG_PHY_QCOM_QMP_UFS) += phy-qcom-qmp-ufs.o obj-$(CONFIG_PHY_QCOM_QMP_USB) += phy-qcom-qmp-usb.o +obj-$(CONFIG_PHY_QCOM_QMP_USB_LEGACY) += phy-qcom-qmp-usb-legacy.o obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o obj-$(CONFIG_PHY_QCOM_SNPS_EUSB2) += phy-qcom-snps-eusb2.o diff --git a/drivers/phy/qualcomm/phy-ath79-usb.c b/drivers/phy/qualcomm/phy-ath79-usb.c index 09a77e556ecebfa9c7b318e88a5fdaadb6a41747..f8d0199c6e78ffd68ddbcd2939f0c9645e2b7425 100644 --- a/drivers/phy/qualcomm/phy-ath79-usb.c +++ b/drivers/phy/qualcomm/phy-ath79-usb.c @@ -5,6 +5,7 @@ * Copyright (C) 2015-2018 Alban Bedel */ +#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c index 5c4305df7d53320c5ee8a827d6c1160f544ac417..8e5078304646e9fcd4a758aabc712052b61369fe 100644 --- a/drivers/phy/qualcomm/phy-qcom-edp.c +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -13,8 +13,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c index 90f8543ba265b57ea20cb5fba7006e10ec32edaa..52c275fbb2a1c0b1da4ff7b27d07195e881f565a 100644 --- a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c +++ b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c @@ -8,7 +8,6 @@ #include #include #include -#include #include /* eUSB2 status registers */ diff --git a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c index d3e7d5e1d1b6a3786b90b01e959a6a3be299f19a..da6f290af72212318eb1bb4b5445af739d979d63 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c index 7bacc527fbad64657e0ca1457fc35bae347c0471..06392ed7c91b5713ba0cd25518f195b1ebce4821 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-m31.c b/drivers/phy/qualcomm/phy-qcom-m31.c new file mode 100644 index 0000000000000000000000000000000000000000..ed08072ca032cac30976f0ce4d3c34bb5748ca68 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-m31.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014-2023, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB2PHY_PORT_UTMI_CTRL1 0x40 + +#define USB2PHY_PORT_UTMI_CTRL2 0x44 + #define UTMI_ULPI_SEL BIT(7) + #define UTMI_TEST_MUX_SEL BIT(6) + +#define HS_PHY_CTRL_REG 0x10 + #define UTMI_OTG_VBUS_VALID BIT(20) + #define SW_SESSVLD_SEL BIT(28) + +#define USB_PHY_UTMI_CTRL0 0x3c + +#define USB_PHY_UTMI_CTRL5 0x50 + #define POR_EN BIT(1) + +#define USB_PHY_HS_PHY_CTRL_COMMON0 0x54 + #define COMMONONN BIT(7) + #define FSEL BIT(4) + #define RETENABLEN BIT(3) + #define FREQ_24MHZ (BIT(6) | BIT(4)) + +#define USB_PHY_HS_PHY_CTRL2 0x64 + #define USB2_SUSPEND_N_SEL BIT(3) + #define USB2_SUSPEND_N BIT(2) + #define USB2_UTMI_CLK_EN BIT(1) + +#define USB_PHY_CFG0 0x94 + #define UTMI_PHY_OVERRIDE_EN BIT(1) + +#define USB_PHY_REFCLK_CTRL 0xa0 + #define CLKCORE BIT(1) + +#define USB2PHY_PORT_POWERDOWN 0xa4 + #define POWER_UP BIT(0) + #define POWER_DOWN 0 + +#define USB_PHY_FSEL_SEL 0xb8 + #define FREQ_SEL BIT(0) + +#define USB2PHY_USB_PHY_M31_XCFGI_1 0xbc + #define USB2_0_TX_ENABLE BIT(2) + +#define USB2PHY_USB_PHY_M31_XCFGI_4 0xc8 + #define HSTX_SLEW_RATE_565PS GENMASK(1, 0) + #define PLL_CHARGING_PUMP_CURRENT_35UA GENMASK(4, 3) + #define ODT_VALUE_38_02_OHM GENMASK(7, 6) + +#define USB2PHY_USB_PHY_M31_XCFGI_5 0xcc + #define ODT_VALUE_45_02_OHM BIT(2) + #define HSTX_PRE_EMPHASIS_LEVEL_0_55MA BIT(0) + +#define USB2PHY_USB_PHY_M31_XCFGI_11 0xe4 + #define XCFG_COARSE_TUNE_NUM BIT(1) + #define XCFG_FINE_TUNE_NUM BIT(3) + +struct m31_phy_regs { + u32 off; + u32 val; + u32 delay; +}; + +struct m31_priv_data { + bool ulpi_mode; + const struct m31_phy_regs *regs; + unsigned int nregs; +}; + +struct m31_phy_regs m31_ipq5332_regs[] = { + { + USB_PHY_CFG0, + UTMI_PHY_OVERRIDE_EN, + 0 + }, + { + USB_PHY_UTMI_CTRL5, + POR_EN, + 15 + }, + { + USB_PHY_FSEL_SEL, + FREQ_SEL, + 0 + }, + { + USB_PHY_HS_PHY_CTRL_COMMON0, + COMMONONN | FREQ_24MHZ | RETENABLEN, + 0 + }, + { + USB_PHY_UTMI_CTRL5, + POR_EN, + 0 + }, + { + USB_PHY_HS_PHY_CTRL2, + USB2_SUSPEND_N_SEL | USB2_SUSPEND_N | USB2_UTMI_CLK_EN, + 0 + }, + { + USB2PHY_USB_PHY_M31_XCFGI_11, + XCFG_COARSE_TUNE_NUM | XCFG_FINE_TUNE_NUM, + 0 + }, + { + USB2PHY_USB_PHY_M31_XCFGI_4, + HSTX_SLEW_RATE_565PS | PLL_CHARGING_PUMP_CURRENT_35UA | ODT_VALUE_38_02_OHM, + 0 + }, + { + USB2PHY_USB_PHY_M31_XCFGI_1, + USB2_0_TX_ENABLE, + 0 + }, + { + USB2PHY_USB_PHY_M31_XCFGI_5, + ODT_VALUE_45_02_OHM | HSTX_PRE_EMPHASIS_LEVEL_0_55MA, + 4 + }, + { + USB_PHY_UTMI_CTRL5, + 0x0, + 0 + }, + { + USB_PHY_HS_PHY_CTRL2, + USB2_SUSPEND_N | USB2_UTMI_CLK_EN, + 0 + }, +}; + +struct m31usb_phy { + struct phy *phy; + void __iomem *base; + const struct m31_phy_regs *regs; + int nregs; + + struct regulator *vreg; + struct clk *clk; + struct reset_control *reset; + + bool ulpi_mode; +}; + +static int m31usb_phy_init(struct phy *phy) +{ + struct m31usb_phy *qphy = phy_get_drvdata(phy); + const struct m31_phy_regs *regs = qphy->regs; + int i, ret; + + ret = regulator_enable(qphy->vreg); + if (ret) { + dev_err(&phy->dev, "failed to enable regulator, %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(qphy->clk); + if (ret) { + if (qphy->vreg) + regulator_disable(qphy->vreg); + dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret); + return ret; + } + + /* Perform phy reset */ + reset_control_assert(qphy->reset); + udelay(5); + reset_control_deassert(qphy->reset); + + /* configure for ULPI mode if requested */ + if (qphy->ulpi_mode) + writel(0x0, qphy->base + USB2PHY_PORT_UTMI_CTRL2); + + /* Enable the PHY */ + writel(POWER_UP, qphy->base + USB2PHY_PORT_POWERDOWN); + + /* Turn on phy ref clock */ + for (i = 0; i < qphy->nregs; i++) { + writel(regs[i].val, qphy->base + regs[i].off); + if (regs[i].delay) + udelay(regs[i].delay); + } + + return 0; +} + +static int m31usb_phy_shutdown(struct phy *phy) +{ + struct m31usb_phy *qphy = phy_get_drvdata(phy); + + /* Disable the PHY */ + writel_relaxed(POWER_DOWN, qphy->base + USB2PHY_PORT_POWERDOWN); + + clk_disable_unprepare(qphy->clk); + + regulator_disable(qphy->vreg); + + return 0; +} + +static const struct phy_ops m31usb_phy_gen_ops = { + .power_on = m31usb_phy_init, + .power_off = m31usb_phy_shutdown, + .owner = THIS_MODULE, +}; + +static int m31usb_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + const struct m31_priv_data *data; + struct device *dev = &pdev->dev; + struct m31usb_phy *qphy; + + qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL); + if (!qphy) + return -ENOMEM; + + qphy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(qphy->base)) + return PTR_ERR(qphy->base); + + qphy->reset = devm_reset_control_get_exclusive_by_index(dev, 0); + if (IS_ERR(qphy->reset)) + return PTR_ERR(qphy->reset); + + qphy->clk = devm_clk_get(dev, NULL); + if (IS_ERR(qphy->clk)) + return dev_err_probe(dev, PTR_ERR(qphy->clk), + "failed to get clk\n"); + + data = of_device_get_match_data(dev); + qphy->regs = data->regs; + qphy->nregs = data->nregs; + qphy->ulpi_mode = data->ulpi_mode; + + qphy->phy = devm_phy_create(dev, NULL, &m31usb_phy_gen_ops); + if (IS_ERR(qphy->phy)) + return dev_err_probe(dev, PTR_ERR(qphy->phy), + "failed to create phy\n"); + + qphy->vreg = devm_regulator_get(dev, "vdda-phy"); + if (IS_ERR(qphy->vreg)) + return dev_err_probe(dev, PTR_ERR(qphy->phy), + "failed to get vreg\n"); + + phy_set_drvdata(qphy->phy, qphy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (!IS_ERR(phy_provider)) + dev_info(dev, "Registered M31 USB phy\n"); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct m31_priv_data m31_ipq5332_data = { + .ulpi_mode = false, + .regs = m31_ipq5332_regs, + .nregs = ARRAY_SIZE(m31_ipq5332_regs), +}; + +static const struct of_device_id m31usb_phy_id_table[] = { + { .compatible = "qcom,ipq5332-usb-hsphy", .data = &m31_ipq5332_data }, + { }, +}; +MODULE_DEVICE_TABLE(of, m31usb_phy_id_table); + +static struct platform_driver m31usb_phy_driver = { + .probe = m31usb_phy_probe, + .driver = { + .name = "qcom-m31usb-phy", + .of_match_table = m31usb_phy_id_table, + }, +}; + +module_platform_driver(m31usb_phy_driver); + +MODULE_DESCRIPTION("USB2 Qualcomm M31 HSPHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index bebce8c591a301c6cbc9b576baa0efe7b164beab..cbb28afce1354101ab378336042edc9dd040b9c1 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -106,6 +105,20 @@ enum qphy_reg_layout { QPHY_PCS_AUTONOMOUS_MODE_CTRL, QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, QPHY_PCS_POWER_DOWN_CONTROL, + + QPHY_COM_RESETSM_CNTRL, + QPHY_COM_C_READY_STATUS, + QPHY_COM_CMN_STATUS, + QPHY_COM_BIAS_EN_CLKBUFLR_EN, + + QPHY_DP_PHY_STATUS, + + QPHY_TX_TX_POL_INV, + QPHY_TX_TX_DRV_LVL, + QPHY_TX_TX_EMP_POST1_LVL, + QPHY_TX_HIGHZ_DRVR_EN, + QPHY_TX_TRANSCEIVER_BIAS_EN, + /* Keep last to ensure regs_layout arrays are properly initialized */ QPHY_LAYOUT_SIZE }; @@ -117,9 +130,22 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V3_PCS_POWER_DOWN_CONTROL, [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V3_PCS_AUTONOMOUS_MODE_CTRL, [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V3_PCS_LFPS_RXTERM_IRQ_CLEAR, + + [QPHY_COM_RESETSM_CNTRL] = QSERDES_V3_COM_RESETSM_CNTRL, + [QPHY_COM_C_READY_STATUS] = QSERDES_V3_COM_C_READY_STATUS, + [QPHY_COM_CMN_STATUS] = QSERDES_V3_COM_CMN_STATUS, + [QPHY_COM_BIAS_EN_CLKBUFLR_EN] = QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, + + [QPHY_DP_PHY_STATUS] = QSERDES_V3_DP_PHY_STATUS, + + [QPHY_TX_TX_POL_INV] = QSERDES_V3_TX_TX_POL_INV, + [QPHY_TX_TX_DRV_LVL] = QSERDES_V3_TX_TX_DRV_LVL, + [QPHY_TX_TX_EMP_POST1_LVL] = QSERDES_V3_TX_TX_EMP_POST1_LVL, + [QPHY_TX_HIGHZ_DRVR_EN] = QSERDES_V3_TX_HIGHZ_DRVR_EN, + [QPHY_TX_TRANSCEIVER_BIAS_EN] = QSERDES_V3_TX_TRANSCEIVER_BIAS_EN, }; -static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { +static const unsigned int qmp_v45_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_SW_RESET] = QPHY_V4_PCS_SW_RESET, [QPHY_START_CTRL] = QPHY_V4_PCS_START_CONTROL, [QPHY_PCS_STATUS] = QPHY_V4_PCS_PCS_STATUS1, @@ -128,6 +154,67 @@ static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { /* In PCS_USB */ [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL, [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, + + [QPHY_COM_RESETSM_CNTRL] = QSERDES_V4_COM_RESETSM_CNTRL, + [QPHY_COM_C_READY_STATUS] = QSERDES_V4_COM_C_READY_STATUS, + [QPHY_COM_CMN_STATUS] = QSERDES_V4_COM_CMN_STATUS, + [QPHY_COM_BIAS_EN_CLKBUFLR_EN] = QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN, + + [QPHY_DP_PHY_STATUS] = QSERDES_V4_DP_PHY_STATUS, + + [QPHY_TX_TX_POL_INV] = QSERDES_V4_TX_TX_POL_INV, + [QPHY_TX_TX_DRV_LVL] = QSERDES_V4_TX_TX_DRV_LVL, + [QPHY_TX_TX_EMP_POST1_LVL] = QSERDES_V4_TX_TX_EMP_POST1_LVL, + [QPHY_TX_HIGHZ_DRVR_EN] = QSERDES_V4_TX_HIGHZ_DRVR_EN, + [QPHY_TX_TRANSCEIVER_BIAS_EN] = QSERDES_V4_TX_TRANSCEIVER_BIAS_EN, +}; + +static const unsigned int qmp_v5_5nm_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V5_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V5_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V5_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V5_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, + + [QPHY_COM_RESETSM_CNTRL] = QSERDES_V5_COM_RESETSM_CNTRL, + [QPHY_COM_C_READY_STATUS] = QSERDES_V5_COM_C_READY_STATUS, + [QPHY_COM_CMN_STATUS] = QSERDES_V5_COM_CMN_STATUS, + [QPHY_COM_BIAS_EN_CLKBUFLR_EN] = QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, + + [QPHY_DP_PHY_STATUS] = QSERDES_V5_DP_PHY_STATUS, + + [QPHY_TX_TX_POL_INV] = QSERDES_V5_5NM_TX_TX_POL_INV, + [QPHY_TX_TX_DRV_LVL] = QSERDES_V5_5NM_TX_TX_DRV_LVL, + [QPHY_TX_TX_EMP_POST1_LVL] = QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL, + [QPHY_TX_HIGHZ_DRVR_EN] = QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN, + [QPHY_TX_TRANSCEIVER_BIAS_EN] = QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN, +}; + +static const unsigned int qmp_v6_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V5_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V5_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V5_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V5_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, + + [QPHY_COM_RESETSM_CNTRL] = QSERDES_V6_COM_RESETSM_CNTRL, + [QPHY_COM_C_READY_STATUS] = QSERDES_V6_COM_C_READY_STATUS, + [QPHY_COM_CMN_STATUS] = QSERDES_V6_COM_CMN_STATUS, + [QPHY_COM_BIAS_EN_CLKBUFLR_EN] = QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN, + + [QPHY_DP_PHY_STATUS] = QSERDES_V6_DP_PHY_STATUS, + + [QPHY_TX_TX_POL_INV] = QSERDES_V6_TX_TX_POL_INV, + [QPHY_TX_TX_DRV_LVL] = QSERDES_V6_TX_TX_DRV_LVL, + [QPHY_TX_TX_EMP_POST1_LVL] = QSERDES_V6_TX_TX_EMP_POST1_LVL, + [QPHY_TX_HIGHZ_DRVR_EN] = QSERDES_V6_TX_HIGHZ_DRVR_EN, + [QPHY_TX_TRANSCEIVER_BIAS_EN] = QSERDES_V6_TX_TRANSCEIVER_BIAS_EN, }; static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { @@ -1271,9 +1358,6 @@ struct qmp_phy_cfg { int (*calibrate_dp_phy)(struct qmp_combo *qmp); void (*dp_aux_init)(struct qmp_combo *qmp); - /* clock ids to be requested */ - const char * const *clk_list; - int num_clks; /* resets to be requested */ const char * const *reset_list; int num_resets; @@ -1315,6 +1399,7 @@ struct qmp_combo { struct clk *pipe_clk; struct clk_bulk_data *clks; + int num_clks; struct reset_control_bulk_data *resets; struct regulator_bulk_data *vregs; @@ -1350,11 +1435,6 @@ static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp); static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp); static int qmp_v4_calibrate_dp_phy(struct qmp_combo *qmp); -static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp); - -static void qmp_v6_dp_aux_init(struct qmp_combo *qmp); -static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp); - static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) { u32 reg; @@ -1380,19 +1460,10 @@ static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) } /* list of clocks required by phy */ -static const char * const qmp_v3_phy_clk_l[] = { +static const char * const qmp_combo_phy_clk_l[] = { "aux", "cfg_ahb", "ref", "com_aux", }; -static const char * const qmp_v4_phy_clk_l[] = { - "aux", "ref", "com_aux", -}; - -/* the primary usb3 phy on sm8250 doesn't have a ref clock */ -static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { - "aux", "ref_clk_src", "com_aux" -}; - /* list of resets */ static const char * const msm8996_usb3phy_reset_l[] = { "phy", "common", @@ -1433,6 +1504,8 @@ static const struct qmp_combo_offsets qmp_combo_offsets_v5 = { }; static const struct qmp_phy_cfg sc7180_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + .serdes_tbl = qmp_v3_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), .tx_tbl = qmp_v3_usb3_tx_tbl, @@ -1466,8 +1539,6 @@ static const struct qmp_phy_cfg sc7180_usb3dpphy_cfg = { .configure_dp_phy = qmp_v3_configure_dp_phy, .calibrate_dp_phy = qmp_v3_calibrate_dp_phy, - .clk_list = qmp_v3_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), .reset_list = sc7180_usb3phy_reset_l, .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -1478,6 +1549,8 @@ static const struct qmp_phy_cfg sc7180_usb3dpphy_cfg = { }; static const struct qmp_phy_cfg sdm845_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + .serdes_tbl = qmp_v3_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), .tx_tbl = qmp_v3_usb3_tx_tbl, @@ -1511,8 +1584,6 @@ static const struct qmp_phy_cfg sdm845_usb3dpphy_cfg = { .configure_dp_phy = qmp_v3_configure_dp_phy, .calibrate_dp_phy = qmp_v3_calibrate_dp_phy, - .clk_list = qmp_v3_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -1523,6 +1594,8 @@ static const struct qmp_phy_cfg sdm845_usb3dpphy_cfg = { }; static const struct qmp_phy_cfg sc8180x_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + .serdes_tbl = sm8150_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), .tx_tbl = sm8150_usb3_tx_tbl, @@ -1558,13 +1631,11 @@ static const struct qmp_phy_cfg sc8180x_usb3dpphy_cfg = { .configure_dp_phy = qmp_v4_configure_dp_phy, .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, - .clk_list = qmp_v4_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v4_usb3phy_regs_layout, + .regs = qmp_v45_usb3phy_regs_layout, .pcs_usb_offset = 0x300, .has_pwrdn_delay = true, @@ -1603,16 +1674,14 @@ static const struct qmp_phy_cfg sc8280xp_usb43dpphy_cfg = { .dp_aux_init = qmp_v4_dp_aux_init, .configure_dp_tx = qmp_v4_configure_dp_tx, - .configure_dp_phy = qmp_v5_configure_dp_phy, + .configure_dp_phy = qmp_v4_configure_dp_phy, .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, - .clk_list = qmp_v4_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v4_usb3phy_regs_layout, + .regs = qmp_v5_5nm_usb3phy_regs_layout, }; static const struct qmp_phy_cfg sm6350_usb3dpphy_cfg = { @@ -1651,8 +1720,6 @@ static const struct qmp_phy_cfg sm6350_usb3dpphy_cfg = { .configure_dp_phy = qmp_v3_configure_dp_phy, .calibrate_dp_phy = qmp_v3_calibrate_dp_phy, - .clk_list = qmp_v4_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -1661,6 +1728,8 @@ static const struct qmp_phy_cfg sm6350_usb3dpphy_cfg = { }; static const struct qmp_phy_cfg sm8250_usb3dpphy_cfg = { + .offsets = &qmp_combo_offsets_v3, + .serdes_tbl = sm8150_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), .tx_tbl = sm8250_usb3_tx_tbl, @@ -1696,13 +1765,11 @@ static const struct qmp_phy_cfg sm8250_usb3dpphy_cfg = { .configure_dp_phy = qmp_v4_configure_dp_phy, .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, - .clk_list = qmp_v4_sm8250_usbphy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v4_usb3phy_regs_layout, + .regs = qmp_v45_usb3phy_regs_layout, .pcs_usb_offset = 0x300, .has_pwrdn_delay = true, @@ -1746,13 +1813,11 @@ static const struct qmp_phy_cfg sm8350_usb3dpphy_cfg = { .configure_dp_phy = qmp_v4_configure_dp_phy, .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, - .clk_list = qmp_v4_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v4_usb3phy_regs_layout, + .regs = qmp_v45_usb3phy_regs_layout, .has_pwrdn_delay = true, }; @@ -1790,14 +1855,12 @@ static const struct qmp_phy_cfg sm8550_usb3dpphy_cfg = { .swing_hbr3_hbr2 = &qmp_dp_v5_voltage_swing_hbr3_hbr2, .pre_emphasis_hbr3_hbr2 = &qmp_dp_v5_pre_emphasis_hbr3_hbr2, - .dp_aux_init = qmp_v6_dp_aux_init, + .dp_aux_init = qmp_v4_dp_aux_init, .configure_dp_tx = qmp_v4_configure_dp_tx, - .configure_dp_phy = qmp_v6_configure_dp_phy, + .configure_dp_phy = qmp_v4_configure_dp_phy, .calibrate_dp_phy = qmp_v4_calibrate_dp_phy, - .regs = qmp_v4_usb3phy_regs_layout, - .clk_list = qmp_v4_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .regs = qmp_v6_usb3phy_regs_layout, .reset_list = msm8996_usb3phy_reset_l, .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -1865,6 +1928,8 @@ static int qmp_combo_dp_serdes_init(struct qmp_combo *qmp) static void qmp_v3_dp_aux_init(struct qmp_combo *qmp) { + const struct qmp_phy_cfg *cfg = qmp->cfg; + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); @@ -1872,7 +1937,7 @@ static void qmp_v3_dp_aux_init(struct qmp_combo *qmp) /* Turn on BIAS current for PHY/PLL */ writel(QSERDES_V3_COM_BIAS_EN | QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL, - qmp->dp_serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); + qmp->dp_serdes + cfg->regs[QPHY_COM_BIAS_EN_CLKBUFLR_EN]); writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); @@ -1886,7 +1951,7 @@ static void qmp_v3_dp_aux_init(struct qmp_combo *qmp) QSERDES_V3_COM_BIAS_EN_MUX | QSERDES_V3_COM_CLKBUF_R_EN | QSERDES_V3_COM_CLKBUF_L_EN | QSERDES_V3_COM_EN_SYSCLK_TX_SEL | QSERDES_V3_COM_CLKBUF_RX_DRIVE_L, - qmp->dp_serdes + QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN); + qmp->dp_serdes + cfg->regs[QPHY_COM_BIAS_EN_CLKBUFLR_EN]); writel(0x00, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG0); writel(0x13, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG1); @@ -1906,8 +1971,7 @@ static void qmp_v3_dp_aux_init(struct qmp_combo *qmp) qmp->dp_dp_phy + QSERDES_V3_DP_PHY_AUX_INTERRUPT_MASK); } -static int qmp_combo_configure_dp_swing(struct qmp_combo *qmp, - unsigned int drv_lvl_reg, unsigned int emp_post_reg) +static int qmp_combo_configure_dp_swing(struct qmp_combo *qmp) { const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; const struct qmp_phy_cfg *cfg = qmp->cfg; @@ -1936,10 +2000,10 @@ static int qmp_combo_configure_dp_swing(struct qmp_combo *qmp, voltage_swing_cfg |= DP_PHY_TXn_TX_DRV_LVL_MUX_EN; pre_emphasis_cfg |= DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN; - writel(voltage_swing_cfg, qmp->dp_tx + drv_lvl_reg); - writel(pre_emphasis_cfg, qmp->dp_tx + emp_post_reg); - writel(voltage_swing_cfg, qmp->dp_tx2 + drv_lvl_reg); - writel(pre_emphasis_cfg, qmp->dp_tx2 + emp_post_reg); + writel(voltage_swing_cfg, qmp->dp_tx + cfg->regs[QPHY_TX_TX_DRV_LVL]); + writel(pre_emphasis_cfg, qmp->dp_tx + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); + writel(voltage_swing_cfg, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_DRV_LVL]); + writel(pre_emphasis_cfg, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); return 0; } @@ -1949,8 +2013,7 @@ static void qmp_v3_configure_dp_tx(struct qmp_combo *qmp) const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias_en, drvr_en; - if (qmp_combo_configure_dp_swing(qmp, QSERDES_V3_TX_TX_DRV_LVL, - QSERDES_V3_TX_TX_EMP_POST1_LVL) < 0) + if (qmp_combo_configure_dp_swing(qmp) < 0) return; if (dp_opts->lanes == 1) { @@ -1991,17 +2054,12 @@ static bool qmp_combo_configure_dp_mode(struct qmp_combo *qmp) return reverse; } -static int qmp_v3_configure_dp_phy(struct qmp_combo *qmp) +static int qmp_combo_configure_dp_clocks(struct qmp_combo *qmp) { const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; - u32 phy_vco_div, status; + u32 phy_vco_div; unsigned long pixel_freq; - qmp_combo_configure_dp_mode(qmp); - - writel(0x05, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL); - writel(0x05, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL); - switch (dp_opts->link_rate) { case 1620: phy_vco_div = 0x1; @@ -2023,20 +2081,38 @@ static int qmp_v3_configure_dp_phy(struct qmp_combo *qmp) /* Other link rates aren't supported */ return -EINVAL; } - writel(phy_vco_div, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_VCO_DIV); + writel(phy_vco_div, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_VCO_DIV); clk_set_rate(qmp->dp_link_hw.clk, dp_opts->link_rate * 100000); clk_set_rate(qmp->dp_pixel_hw.clk, pixel_freq); + return 0; +} + +static int qmp_v3_configure_dp_phy(struct qmp_combo *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + u32 status; + int ret; + + qmp_combo_configure_dp_mode(qmp); + + writel(0x05, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_TX0_TX1_LANE_CTL); + writel(0x05, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_TX2_TX3_LANE_CTL); + + ret = qmp_combo_configure_dp_clocks(qmp); + if (ret) + return ret; + writel(0x04, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG2); writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x05, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x09, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - writel(0x20, qmp->dp_serdes + QSERDES_V3_COM_RESETSM_CNTRL); + writel(0x20, qmp->dp_serdes + cfg->regs[QPHY_COM_RESETSM_CNTRL]); - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V3_COM_C_READY_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_C_READY_STATUS], status, ((status & BIT(0)) > 0), 500, @@ -2045,7 +2121,7 @@ static int qmp_v3_configure_dp_phy(struct qmp_combo *qmp) writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - if (readl_poll_timeout(qmp->dp_dp_phy + QSERDES_V3_DP_PHY_STATUS, + if (readl_poll_timeout(qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_STATUS], status, ((status & BIT(1)) > 0), 500, @@ -2056,7 +2132,7 @@ static int qmp_v3_configure_dp_phy(struct qmp_combo *qmp) udelay(2000); writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - return readl_poll_timeout(qmp->dp_dp_phy + QSERDES_V3_DP_PHY_STATUS, + return readl_poll_timeout(qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_STATUS], status, ((status & BIT(1)) > 0), 500, @@ -2083,39 +2159,14 @@ static int qmp_v3_calibrate_dp_phy(struct qmp_combo *qmp) static void qmp_v4_dp_aux_init(struct qmp_combo *qmp) { - writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | - DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, - qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); - - /* Turn on BIAS current for PHY/PLL */ - writel(0x17, qmp->dp_serdes + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); - - writel(0x00, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG0); - writel(0x13, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG1); - writel(0xa4, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG2); - writel(0x00, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG3); - writel(0x0a, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG4); - writel(0x26, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG5); - writel(0x0a, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG6); - writel(0x03, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG7); - writel(0xb7, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG8); - writel(0x03, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG9); - qmp->dp_aux_cfg = 0; - - writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | - PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | - PHY_AUX_REQ_ERR_MASK, - qmp->dp_dp_phy + QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK); -} + const struct qmp_phy_cfg *cfg = qmp->cfg; -static void qmp_v6_dp_aux_init(struct qmp_combo *qmp) -{ writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); /* Turn on BIAS current for PHY/PLL */ - writel(0x17, qmp->dp_serdes + QSERDES_V6_COM_PLL_BIAS_EN_CLK_BUFLR_EN); + writel(0x17, qmp->dp_serdes + cfg->regs[QPHY_COM_BIAS_EN_CLKBUFLR_EN]); writel(0x00, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG0); writel(0x13, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG1); @@ -2137,26 +2188,23 @@ static void qmp_v6_dp_aux_init(struct qmp_combo *qmp) static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp) { + const struct qmp_phy_cfg *cfg = qmp->cfg; + /* Program default values before writing proper values */ - writel(0x27, qmp->dp_tx + QSERDES_V4_TX_TX_DRV_LVL); - writel(0x27, qmp->dp_tx2 + QSERDES_V4_TX_TX_DRV_LVL); + writel(0x27, qmp->dp_tx + cfg->regs[QPHY_TX_TX_DRV_LVL]); + writel(0x27, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_DRV_LVL]); - writel(0x20, qmp->dp_tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); - writel(0x20, qmp->dp_tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); + writel(0x20, qmp->dp_tx + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); + writel(0x20, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); - qmp_combo_configure_dp_swing(qmp, QSERDES_V4_TX_TX_DRV_LVL, - QSERDES_V4_TX_TX_EMP_POST1_LVL); + qmp_combo_configure_dp_swing(qmp); } -static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, - unsigned int com_resetm_ctrl_reg, - unsigned int com_c_ready_status_reg, - unsigned int com_cmn_status_reg, - unsigned int dp_phy_status_reg) +static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp) { - const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; - u32 phy_vco_div, status; - unsigned long pixel_freq; + const struct qmp_phy_cfg *cfg = qmp->cfg; + u32 status; + int ret; writel(0x0f, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_CFG_1); @@ -2168,54 +2216,32 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, writel(0x05, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL); writel(0x05, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL); - switch (dp_opts->link_rate) { - case 1620: - phy_vco_div = 0x1; - pixel_freq = 1620000000UL / 2; - break; - case 2700: - phy_vco_div = 0x1; - pixel_freq = 2700000000UL / 2; - break; - case 5400: - phy_vco_div = 0x2; - pixel_freq = 5400000000UL / 4; - break; - case 8100: - phy_vco_div = 0x0; - pixel_freq = 8100000000UL / 6; - break; - default: - /* Other link rates aren't supported */ - return -EINVAL; - } - writel(phy_vco_div, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_VCO_DIV); - - clk_set_rate(qmp->dp_link_hw.clk, dp_opts->link_rate * 100000); - clk_set_rate(qmp->dp_pixel_hw.clk, pixel_freq); + ret = qmp_combo_configure_dp_clocks(qmp); + if (ret) + return ret; writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x05, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); writel(0x09, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - writel(0x20, qmp->dp_serdes + com_resetm_ctrl_reg); + writel(0x20, qmp->dp_serdes + cfg->regs[QPHY_COM_RESETSM_CNTRL]); - if (readl_poll_timeout(qmp->dp_serdes + com_c_ready_status_reg, + if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_C_READY_STATUS], status, ((status & BIT(0)) > 0), 500, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg, + if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_CMN_STATUS], status, ((status & BIT(0)) > 0), 500, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg, + if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_CMN_STATUS], status, ((status & BIT(1)) > 0), 500, @@ -2224,14 +2250,14 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - if (readl_poll_timeout(qmp->dp_dp_phy + dp_phy_status_reg, + if (readl_poll_timeout(qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_STATUS], status, ((status & BIT(0)) > 0), 500, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_dp_phy + dp_phy_status_reg, + if (readl_poll_timeout(qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_STATUS], status, ((status & BIT(1)) > 0), 500, @@ -2243,16 +2269,14 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) { + const struct qmp_phy_cfg *cfg = qmp->cfg; bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; u32 status; int ret; - ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL, - QSERDES_V4_COM_C_READY_STATUS, - QSERDES_V4_COM_CMN_STATUS, - QSERDES_V4_DP_PHY_STATUS); + ret = qmp_v456_configure_dp_phy(qmp); if (ret < 0) return ret; @@ -2278,150 +2302,32 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) drvr1_en = 0x10; } - writel(drvr0_en, qmp->dp_tx + QSERDES_V4_TX_HIGHZ_DRVR_EN); - writel(bias0_en, qmp->dp_tx + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); - writel(drvr1_en, qmp->dp_tx2 + QSERDES_V4_TX_HIGHZ_DRVR_EN); - writel(bias1_en, qmp->dp_tx2 + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); - - writel(0x18, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - udelay(2000); - writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - - if (readl_poll_timeout(qmp->dp_dp_phy + QSERDES_V4_DP_PHY_STATUS, - status, - ((status & BIT(1)) > 0), - 500, - 10000)) - return -ETIMEDOUT; - - writel(0x0a, qmp->dp_tx + QSERDES_V4_TX_TX_POL_INV); - writel(0x0a, qmp->dp_tx2 + QSERDES_V4_TX_TX_POL_INV); - - writel(0x27, qmp->dp_tx + QSERDES_V4_TX_TX_DRV_LVL); - writel(0x27, qmp->dp_tx2 + QSERDES_V4_TX_TX_DRV_LVL); - - writel(0x20, qmp->dp_tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); - writel(0x20, qmp->dp_tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); - - return 0; -} - -static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp) -{ - bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); - const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; - u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - u32 status; - int ret; - - ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL, - QSERDES_V4_COM_C_READY_STATUS, - QSERDES_V4_COM_CMN_STATUS, - QSERDES_V4_DP_PHY_STATUS); - if (ret < 0) - return ret; - - if (dp_opts->lanes == 1) { - bias0_en = reverse ? 0x3e : 0x1a; - drvr0_en = reverse ? 0x13 : 0x10; - bias1_en = reverse ? 0x15 : 0x3e; - drvr1_en = reverse ? 0x10 : 0x13; - } else if (dp_opts->lanes == 2) { - bias0_en = reverse ? 0x3f : 0x15; - drvr0_en = 0x10; - bias1_en = reverse ? 0x15 : 0x3f; - drvr1_en = 0x10; - } else { - bias0_en = 0x3f; - bias1_en = 0x3f; - drvr0_en = 0x10; - drvr1_en = 0x10; - } - - writel(drvr0_en, qmp->dp_tx + QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN); - writel(bias0_en, qmp->dp_tx + QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN); - writel(drvr1_en, qmp->dp_tx2 + QSERDES_V5_5NM_TX_HIGHZ_DRVR_EN); - writel(bias1_en, qmp->dp_tx2 + QSERDES_V5_5NM_TX_TRANSCEIVER_BIAS_EN); + writel(drvr0_en, qmp->dp_tx + cfg->regs[QPHY_TX_HIGHZ_DRVR_EN]); + writel(bias0_en, qmp->dp_tx + cfg->regs[QPHY_TX_TRANSCEIVER_BIAS_EN]); + writel(drvr1_en, qmp->dp_tx2 + cfg->regs[QPHY_TX_HIGHZ_DRVR_EN]); + writel(bias1_en, qmp->dp_tx2 + cfg->regs[QPHY_TX_TRANSCEIVER_BIAS_EN]); writel(0x18, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); udelay(2000); writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - if (readl_poll_timeout(qmp->dp_dp_phy + QSERDES_V4_DP_PHY_STATUS, + if (readl_poll_timeout(qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_STATUS], status, ((status & BIT(1)) > 0), 500, 10000)) return -ETIMEDOUT; - writel(0x0a, qmp->dp_tx + QSERDES_V5_5NM_TX_TX_POL_INV); - writel(0x0a, qmp->dp_tx2 + QSERDES_V5_5NM_TX_TX_POL_INV); + writel(0x0a, qmp->dp_tx + cfg->regs[QPHY_TX_TX_POL_INV]); + writel(0x0a, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_POL_INV]); - writel(0x27, qmp->dp_tx + QSERDES_V5_5NM_TX_TX_DRV_LVL); - writel(0x27, qmp->dp_tx2 + QSERDES_V5_5NM_TX_TX_DRV_LVL); + writel(0x27, qmp->dp_tx + cfg->regs[QPHY_TX_TX_DRV_LVL]); + writel(0x27, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_DRV_LVL]); - writel(0x20, qmp->dp_tx + QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL); - writel(0x20, qmp->dp_tx2 + QSERDES_V5_5NM_TX_TX_EMP_POST1_LVL); + writel(0x20, qmp->dp_tx + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); + writel(0x20, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]); return 0; -} - -static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp) -{ - bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); - const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; - u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - u32 status; - int ret; - - ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V6_COM_RESETSM_CNTRL, - QSERDES_V6_COM_C_READY_STATUS, - QSERDES_V6_COM_CMN_STATUS, - QSERDES_V6_DP_PHY_STATUS); - if (ret < 0) - return ret; - - if (dp_opts->lanes == 1) { - bias0_en = reverse ? 0x3e : 0x1a; - drvr0_en = reverse ? 0x13 : 0x10; - bias1_en = reverse ? 0x15 : 0x3e; - drvr1_en = reverse ? 0x10 : 0x13; - } else if (dp_opts->lanes == 2) { - bias0_en = reverse ? 0x3f : 0x15; - drvr0_en = 0x10; - bias1_en = reverse ? 0x15 : 0x3f; - drvr1_en = 0x10; - } else { - bias0_en = 0x3f; - bias1_en = 0x3f; - drvr0_en = 0x10; - drvr1_en = 0x10; - } - - writel(drvr0_en, qmp->dp_tx + QSERDES_V4_TX_HIGHZ_DRVR_EN); - writel(bias0_en, qmp->dp_tx + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); - writel(drvr1_en, qmp->dp_tx2 + QSERDES_V4_TX_HIGHZ_DRVR_EN); - writel(bias1_en, qmp->dp_tx2 + QSERDES_V4_TX_TRANSCEIVER_BIAS_EN); - - writel(0x18, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - udelay(2000); - writel(0x19, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG); - - if (readl_poll_timeout(qmp->dp_dp_phy + QSERDES_V6_DP_PHY_STATUS, - status, - ((status & BIT(1)) > 0), - 500, - 10000)) - return -ETIMEDOUT; - - writel(0x0a, qmp->dp_tx + QSERDES_V4_TX_TX_POL_INV); - writel(0x0a, qmp->dp_tx2 + QSERDES_V4_TX_TX_POL_INV); - - writel(0x27, qmp->dp_tx + QSERDES_V4_TX_TX_DRV_LVL); - writel(0x27, qmp->dp_tx2 + QSERDES_V4_TX_TX_DRV_LVL); - - writel(0x20, qmp->dp_tx + QSERDES_V4_TX_TX_EMP_POST1_LVL); - writel(0x20, qmp->dp_tx2 + QSERDES_V4_TX_TX_EMP_POST1_LVL); return 0; } @@ -2507,7 +2413,7 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) goto err_disable_regulators; } - ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + ret = clk_bulk_prepare_enable(qmp->num_clks, qmp->clks); if (ret) goto err_assert_reset; @@ -2557,7 +2463,7 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp, bool force) reset_control_bulk_assert(cfg->num_resets, qmp->resets); - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); regulator_bulk_disable(cfg->num_vregs, qmp->vregs); @@ -2836,7 +2742,6 @@ static void qmp_combo_disable_autonomous_mode(struct qmp_combo *qmp) static int __maybe_unused qmp_combo_runtime_suspend(struct device *dev) { struct qmp_combo *qmp = dev_get_drvdata(dev); - const struct qmp_phy_cfg *cfg = qmp->cfg; dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); @@ -2848,7 +2753,7 @@ static int __maybe_unused qmp_combo_runtime_suspend(struct device *dev) qmp_combo_enable_autonomous_mode(qmp); clk_disable_unprepare(qmp->pipe_clk); - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); return 0; } @@ -2856,7 +2761,6 @@ static int __maybe_unused qmp_combo_runtime_suspend(struct device *dev) static int __maybe_unused qmp_combo_runtime_resume(struct device *dev) { struct qmp_combo *qmp = dev_get_drvdata(dev); - const struct qmp_phy_cfg *cfg = qmp->cfg; int ret = 0; dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); @@ -2866,14 +2770,14 @@ static int __maybe_unused qmp_combo_runtime_resume(struct device *dev) return 0; } - ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + ret = clk_bulk_prepare_enable(qmp->num_clks, qmp->clks); if (ret) return ret; ret = clk_prepare_enable(qmp->pipe_clk); if (ret) { dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + clk_bulk_disable_unprepare(qmp->num_clks, qmp->clks); return ret; } @@ -2944,9 +2848,8 @@ static int qmp_combo_reset_init(struct qmp_combo *qmp) static int qmp_combo_clk_init(struct qmp_combo *qmp) { - const struct qmp_phy_cfg *cfg = qmp->cfg; struct device *dev = qmp->dev; - int num = cfg->num_clks; + int num = ARRAY_SIZE(qmp_combo_phy_clk_l); int i; qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); @@ -2954,9 +2857,11 @@ static int qmp_combo_clk_init(struct qmp_combo *qmp) return -ENOMEM; for (i = 0; i < num; i++) - qmp->clks[i].id = cfg->clk_list[i]; + qmp->clks[i].id = qmp_combo_phy_clk_l[i]; + + qmp->num_clks = num; - return devm_clk_bulk_get(dev, num, qmp->clks); + return devm_clk_bulk_get_optional(dev, num, qmp->clks); } static void phy_clk_release_provider(void *res) @@ -3421,6 +3326,12 @@ static int qmp_combo_parse_dt_legacy(struct qmp_combo *qmp, struct device_node * if (ret) return ret; + ret = devm_clk_bulk_get_all(qmp->dev, &qmp->clks); + if (ret < 0) + return ret; + + qmp->num_clks = ret; + return 0; } @@ -3431,6 +3342,7 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp) const struct qmp_combo_offsets *offs = cfg->offsets; struct device *dev = qmp->dev; void __iomem *base; + int ret; if (!offs) return -EINVAL; @@ -3460,6 +3372,10 @@ static int qmp_combo_parse_dt(struct qmp_combo *qmp) } qmp->dp_dp_phy = base + offs->dp_dp_phy; + ret = qmp_combo_clk_init(qmp); + if (ret) + return ret; + qmp->pipe_clk = devm_clk_get(dev, "usb3_pipe"); if (IS_ERR(qmp->pipe_clk)) { return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), @@ -3508,10 +3424,6 @@ static int qmp_combo_probe(struct platform_device *pdev) mutex_init(&qmp->phy_mutex); - ret = qmp_combo_clk_init(qmp); - if (ret) - return ret; - ret = qmp_combo_reset_init(qmp); if (ret) return ret; @@ -3602,6 +3514,10 @@ static const struct of_device_id qmp_combo_of_match_table[] = { .compatible = "qcom,sc7180-qmp-usb3-dp-phy", .data = &sc7180_usb3dpphy_cfg, }, + { + .compatible = "qcom,sc7280-qmp-usb3-dp-phy", + .data = &sm8250_usb3dpphy_cfg, + }, { .compatible = "qcom,sc8180x-qmp-usb3-dp-phy", .data = &sc8180x_usb3dpphy_cfg, @@ -3618,6 +3534,10 @@ static const struct of_device_id qmp_combo_of_match_table[] = { .compatible = "qcom,sm6350-qmp-usb3-dp-phy", .data = &sm6350_usb3dpphy_cfg, }, + { + .compatible = "qcom,sm8150-qmp-usb3-dp-phy", + .data = &sc8180x_usb3dpphy_cfg, + }, { .compatible = "qcom,sm8250-qmp-usb3-dp-phy", .data = &sm8250_usb3dpphy_cfg, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c index 0c603bc06e0998a95a0853072a10fffba87958ff..ab61a9c73b189eabffbbe64fc6889338a00091be 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index df505279edfde397f6c235d7cff71e27c92b4849..a63ca74249745dec3b90fc31cf13e0b86672c265 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1910,6 +1909,244 @@ static const struct qmp_phy_init_tbl sm8550_qmp_gen4x2_pcie_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_PCIE_V6_20_PCS_G4_FOM_EQ_CONFIG5, 0xf2), }; +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORE_CLK_EN, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_DC_LEVEL_CTRL, 0x0f), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x2_pcie_rc_serdes_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0xd0), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x2_pcie_rx_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B0, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1, 0xb0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2, 0x92), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B4, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6, 0x29), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B0, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B1, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B2, 0x92), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B3, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B4, 0x43), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B5, 0xdd), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B6, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B0, 0xf3), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B1, 0xf8), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B2, 0xec), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B3, 0xd6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B4, 0x83), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B5, 0xf5), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B6, 0x5e), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_SO_GAIN_RATE3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_MAN_VAL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x7c), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_IDAC_SAOFFSET, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_DAC_ENABLE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_GM_CAL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2, 0x1f), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_3, 0x0f), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_EQ_CONFIG1, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3, 0x28), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_POWER_STATE_CONFIG2, 0x1d), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x2_pcie_pcs_alt_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_EQ_CONFIG4, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_EQ_CONFIG5, 0x22), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_LANE1_INSIG_SW_CTRL2, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_LANE1_INSIG_MX_CTRL2, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_RX_SIGDET_LVL, 0x66), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_rx_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3, 0x37), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_3, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_DAC_ENABLE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_GM_CAL, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x7c), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_IDAC_SAOFFSET, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_Q_PI_INTRINSIC_BIAS_RATE32, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B0, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1, 0xb0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2, 0x92), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B4, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B0, 0x9a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B1, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B2, 0x92), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B3, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B4, 0x43), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B5, 0xdd), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B6, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B0, 0xf3), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B1, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B2, 0xee), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B3, 0xd2), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B4, 0x83), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B5, 0xf9), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B6, 0x3d), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_SO_GAIN_RATE3, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_CNTRL1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_MAN_VAL, 0x08), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_pcs_alt_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_EQ_CONFIG4, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_EQ_CONFIG5, 0x22), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_RX_SIGDET_LVL, 0x66), +}; + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_serdes_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORE_CLK_EN, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_DC_LEVEL_CTRL, 0x0f), +}; + + +static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x4_pcie_rc_serdes_alt_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0xd0), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34), +}; + struct qmp_pcie_offsets { u16 serdes; u16 pcs; @@ -1957,9 +2194,6 @@ struct qmp_phy_cfg { const struct qmp_phy_init_tbl *serdes_4ln_tbl; int serdes_4ln_num; - /* clock ids to be requested */ - const char * const *clk_list; - int num_clks; /* resets to be requested */ const char * const *reset_list; int num_resets; @@ -2038,20 +2272,8 @@ static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) } /* list of clocks required by phy */ -static const char * const ipq8074_pciephy_clk_l[] = { - "aux", "cfg_ahb", -}; - -static const char * const msm8996_phy_clk_l[] = { - "aux", "cfg_ahb", "ref", -}; - -static const char * const sc8280xp_pciephy_clk_l[] = { - "aux", "cfg_ahb", "ref", "rchng", -}; - -static const char * const sdm845_pciephy_clk_l[] = { - "aux", "cfg_ahb", "ref", "refgen", +static const char * const qmp_pciephy_clk_l[] = { + "aux", "cfg_ahb", "ref", "refgen", "rchng", "phy_aux", }; /* list of regulators */ @@ -2072,6 +2294,56 @@ static const char * const sdm845_pciephy_reset_l[] = { "phy", }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_qhp = { + .serdes = 0, + .pcs = 0x1800, + .tx = 0x0800, + /* no .rx for QHP */ +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v2 = { + .serdes = 0, + .pcs = 0x0800, + .tx = 0x0200, + .rx = 0x0400, +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v3 = { + .serdes = 0, + .pcs = 0x0800, + .pcs_misc = 0x0600, + .tx = 0x0200, + .rx = 0x0400, +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v4x1 = { + .serdes = 0, + .pcs = 0x0800, + .pcs_misc = 0x0c00, + .tx = 0x0200, + .rx = 0x0400, +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v4x2 = { + .serdes = 0, + .pcs = 0x0a00, + .pcs_misc = 0x0e00, + .tx = 0x0200, + .rx = 0x0400, + .tx2 = 0x0600, + .rx2 = 0x0800, +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v4_20 = { + .serdes = 0x1000, + .pcs = 0x1200, + .pcs_misc = 0x1600, + .tx = 0x0000, + .rx = 0x0200, + .tx2 = 0x0800, + .rx2 = 0x0a00, +}; + static const struct qmp_pcie_offsets qmp_pcie_offsets_v5 = { .serdes = 0, .pcs = 0x0200, @@ -2082,6 +2354,26 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v5 = { .rx2 = 0x1800, }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_v5_20 = { + .serdes = 0x1000, + .pcs = 0x1200, + .pcs_misc = 0x1400, + .tx = 0x0000, + .rx = 0x0200, + .tx2 = 0x0800, + .rx2 = 0x0a00, +}; + +static const struct qmp_pcie_offsets qmp_pcie_offsets_v5_30 = { + .serdes = 0x2000, + .pcs = 0x2200, + .pcs_misc = 0x2400, + .tx = 0x0, + .rx = 0x0200, + .tx2 = 0x3800, + .rx2 = 0x3a00, +}; + static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_20 = { .serdes = 0x1000, .pcs = 0x1200, @@ -2096,6 +2388,8 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_20 = { static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v2, + .tbls = { .serdes = ipq8074_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(ipq8074_pcie_serdes_tbl), @@ -2106,8 +2400,6 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { .pcs = ipq8074_pcie_pcs_tbl, .pcs_num = ARRAY_SIZE(ipq8074_pcie_pcs_tbl), }, - .clk_list = ipq8074_pciephy_clk_l, - .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), .reset_list = ipq8074_pciephy_reset_l, .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), .vreg_list = NULL, @@ -2121,6 +2413,8 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = { static const struct qmp_phy_cfg ipq8074_pciephy_gen3_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v4x1, + .tbls = { .serdes = ipq8074_pcie_gen3_serdes_tbl, .serdes_num = ARRAY_SIZE(ipq8074_pcie_gen3_serdes_tbl), @@ -2133,8 +2427,6 @@ static const struct qmp_phy_cfg ipq8074_pciephy_gen3_cfg = { .pcs_misc = ipq8074_pcie_gen3_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(ipq8074_pcie_gen3_pcs_misc_tbl), }, - .clk_list = ipq8074_pciephy_clk_l, - .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), .reset_list = ipq8074_pciephy_reset_l, .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), .vreg_list = NULL, @@ -2150,6 +2442,8 @@ static const struct qmp_phy_cfg ipq8074_pciephy_gen3_cfg = { static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v4x1, + .tbls = { .serdes = ipq6018_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(ipq6018_pcie_serdes_tbl), @@ -2162,8 +2456,6 @@ static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { .pcs_misc = ipq6018_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(ipq6018_pcie_pcs_misc_tbl), }, - .clk_list = ipq8074_pciephy_clk_l, - .num_clks = ARRAY_SIZE(ipq8074_pciephy_clk_l), .reset_list = ipq8074_pciephy_reset_l, .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), .vreg_list = NULL, @@ -2177,6 +2469,8 @@ static const struct qmp_phy_cfg ipq6018_pciephy_cfg = { static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v3, + .tbls = { .serdes = sdm845_qmp_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sdm845_qmp_pcie_serdes_tbl), @@ -2189,8 +2483,6 @@ static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { .pcs_misc = sdm845_qmp_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(sdm845_qmp_pcie_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2204,6 +2496,8 @@ static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = { static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_qhp, + .tbls = { .serdes = sdm845_qhp_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sdm845_qhp_pcie_serdes_tbl), @@ -2212,8 +2506,6 @@ static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { .pcs = sdm845_qhp_pcie_pcs_tbl, .pcs_num = ARRAY_SIZE(sdm845_qhp_pcie_pcs_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2227,6 +2519,8 @@ static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v4x1, + .tbls = { .serdes = sm8250_qmp_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), @@ -2249,8 +2543,6 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = { .pcs_misc = sm8250_qmp_gen3x1_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(sm8250_qmp_gen3x1_pcie_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2264,6 +2556,8 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x1_pciephy_cfg = { static const struct qmp_phy_cfg sm8250_qmp_gen3x2_pciephy_cfg = { .lanes = 2, + .offsets = &qmp_pcie_offsets_v4x2, + .tbls = { .serdes = sm8250_qmp_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), @@ -2286,8 +2580,6 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x2_pciephy_cfg = { .pcs_misc = sm8250_qmp_gen3x2_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(sm8250_qmp_gen3x2_pcie_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2301,6 +2593,8 @@ static const struct qmp_phy_cfg sm8250_qmp_gen3x2_pciephy_cfg = { static const struct qmp_phy_cfg msm8998_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v3, + .tbls = { .serdes = msm8998_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl), @@ -2311,8 +2605,6 @@ static const struct qmp_phy_cfg msm8998_pciephy_cfg = { .pcs = msm8998_pcie_pcs_tbl, .pcs_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl), }, - .clk_list = msm8996_phy_clk_l, - .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), .reset_list = ipq8074_pciephy_reset_l, .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2328,6 +2620,8 @@ static const struct qmp_phy_cfg msm8998_pciephy_cfg = { static const struct qmp_phy_cfg sc8180x_pciephy_cfg = { .lanes = 2, + .offsets = &qmp_pcie_offsets_v4x2, + .tbls = { .serdes = sc8180x_qmp_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sc8180x_qmp_pcie_serdes_tbl), @@ -2340,8 +2634,6 @@ static const struct qmp_phy_cfg sc8180x_pciephy_cfg = { .pcs_misc = sc8180x_qmp_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(sc8180x_qmp_pcie_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2375,8 +2667,6 @@ static const struct qmp_phy_cfg sc8280xp_qmp_gen3x1_pciephy_cfg = { .serdes_num = ARRAY_SIZE(sc8280xp_qmp_gen3x1_pcie_rc_serdes_tbl), }, - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2410,8 +2700,6 @@ static const struct qmp_phy_cfg sc8280xp_qmp_gen3x2_pciephy_cfg = { .serdes_num = ARRAY_SIZE(sc8280xp_qmp_gen3x2_pcie_rc_serdes_tbl), }, - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2448,8 +2736,6 @@ static const struct qmp_phy_cfg sc8280xp_qmp_gen3x4_pciephy_cfg = { .serdes_4ln_tbl = sc8280xp_qmp_gen3x4_pcie_serdes_4ln_tbl, .serdes_4ln_num = ARRAY_SIZE(sc8280xp_qmp_gen3x4_pcie_serdes_4ln_tbl), - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2463,6 +2749,8 @@ static const struct qmp_phy_cfg sc8280xp_qmp_gen3x4_pciephy_cfg = { static const struct qmp_phy_cfg sdx55_qmp_pciephy_cfg = { .lanes = 2, + .offsets = &qmp_pcie_offsets_v4_20, + .tbls = { .serdes = sdx55_qmp_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sdx55_qmp_pcie_serdes_tbl), @@ -2490,8 +2778,6 @@ static const struct qmp_phy_cfg sdx55_qmp_pciephy_cfg = { .pcs_misc_num = ARRAY_SIZE(sdx55_qmp_pcie_ep_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2527,8 +2813,6 @@ static const struct qmp_phy_cfg sm8350_qmp_gen3x1_pciephy_cfg = { .rx_num = ARRAY_SIZE(sm8350_qmp_gen3x1_pcie_rc_rx_tbl), }, - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2564,8 +2848,6 @@ static const struct qmp_phy_cfg sm8350_qmp_gen3x2_pciephy_cfg = { .pcs_num = ARRAY_SIZE(sm8350_qmp_gen3x2_pcie_rc_pcs_tbl), }, - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2593,8 +2875,6 @@ static const struct qmp_phy_cfg sdx65_qmp_pciephy_cfg = { .pcs_misc = sdx65_qmp_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(sdx65_qmp_pcie_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2608,6 +2888,8 @@ static const struct qmp_phy_cfg sdx65_qmp_pciephy_cfg = { static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = { .lanes = 1, + .offsets = &qmp_pcie_offsets_v5, + .tbls = { .serdes = sm8450_qmp_gen3_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sm8450_qmp_gen3_pcie_serdes_tbl), @@ -2628,8 +2910,6 @@ static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = { .rx_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_rc_rx_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2643,6 +2923,8 @@ static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = { static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = { .lanes = 2, + .offsets = &qmp_pcie_offsets_v5_20, + .tbls = { .serdes = sm8450_qmp_gen4x2_pcie_serdes_tbl, .serdes_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_serdes_tbl), @@ -2670,8 +2952,6 @@ static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = { .pcs_misc_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_ep_pcs_misc_tbl), }, - .clk_list = sdm845_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2699,8 +2979,6 @@ static const struct qmp_phy_cfg sm8550_qmp_gen3x2_pciephy_cfg = { .pcs_misc = sm8550_qmp_gen3x2_pcie_pcs_misc_tbl, .pcs_misc_num = ARRAY_SIZE(sm8550_qmp_gen3x2_pcie_pcs_misc_tbl), }, - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = qmp_phy_vreg_l, @@ -2730,8 +3008,6 @@ static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = { .ln_shrd = sm8550_qmp_gen4x2_pcie_ln_shrd_tbl, .ln_shrd_num = ARRAY_SIZE(sm8550_qmp_gen4x2_pcie_ln_shrd_tbl), }, - .clk_list = sc8280xp_pciephy_clk_l, - .num_clks = ARRAY_SIZE(sc8280xp_pciephy_clk_l), .reset_list = sdm845_pciephy_reset_l, .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), .vreg_list = sm8550_qmp_phy_vreg_l, @@ -2743,6 +3019,74 @@ static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = { .has_nocsr_reset = true, }; +static const struct qmp_phy_cfg sa8775p_qmp_gen4x2_pciephy_cfg = { + .lanes = 2, + .offsets = &qmp_pcie_offsets_v5_20, + + .tbls = { + .serdes = sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl, + .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl), + .tx = sa8775p_qmp_gen4_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_tx_tbl), + .rx = sa8775p_qmp_gen4x2_pcie_rx_alt_tbl, + .rx_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_rx_alt_tbl), + .pcs = sa8775p_qmp_gen4x2_pcie_pcs_alt_tbl, + .pcs_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_pcs_alt_tbl), + .pcs_misc = sa8775p_qmp_gen4_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_pcs_misc_tbl), + }, + + .tbls_rc = &(const struct qmp_phy_cfg_tbls) { + .serdes = sa8775p_qmp_gen4x2_pcie_rc_serdes_alt_tbl, + .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x2_pcie_rc_serdes_alt_tbl), + .pcs_misc = sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl), + }, + + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v5_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, +}; + +static const struct qmp_phy_cfg sa8775p_qmp_gen4x4_pciephy_cfg = { + .lanes = 4, + .offsets = &qmp_pcie_offsets_v5_30, + + .tbls = { + .serdes = sa8775p_qmp_gen4x4_pcie_serdes_alt_tbl, + .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x4_pcie_serdes_alt_tbl), + .tx = sa8775p_qmp_gen4_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_tx_tbl), + .rx = sa8775p_qmp_gen4x4_pcie_rx_alt_tbl, + .rx_num = ARRAY_SIZE(sa8775p_qmp_gen4x4_pcie_rx_alt_tbl), + .pcs = sa8775p_qmp_gen4x4_pcie_pcs_alt_tbl, + .pcs_num = ARRAY_SIZE(sa8775p_qmp_gen4x4_pcie_pcs_alt_tbl), + .pcs_misc = sa8775p_qmp_gen4_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_pcs_misc_tbl), + }, + + .tbls_rc = &(const struct qmp_phy_cfg_tbls) { + .serdes = sa8775p_qmp_gen4x4_pcie_rc_serdes_alt_tbl, + .serdes_num = ARRAY_SIZE(sa8775p_qmp_gen4x4_pcie_rc_serdes_alt_tbl), + .pcs_misc = sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sa8775p_qmp_gen4_pcie_rc_pcs_misc_tbl), + }, + + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v5_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, +}; + static void qmp_pcie_configure_lane(void __iomem *base, const struct qmp_phy_init_tbl tbl[], int num, @@ -2855,7 +3199,7 @@ static int qmp_pcie_init(struct phy *phy) goto err_assert_reset; } - ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + ret = clk_bulk_prepare_enable(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks); if (ret) goto err_assert_reset; @@ -2876,7 +3220,7 @@ static int qmp_pcie_exit(struct phy *phy) reset_control_bulk_assert(cfg->num_resets, qmp->resets); - clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + clk_bulk_disable_unprepare(ARRAY_SIZE(qmp_pciephy_clk_l), qmp->clks); regulator_bulk_disable(cfg->num_vregs, qmp->vregs); @@ -3059,9 +3403,8 @@ static int qmp_pcie_reset_init(struct qmp_pcie *qmp) static int qmp_pcie_clk_init(struct qmp_pcie *qmp) { - const struct qmp_phy_cfg *cfg = qmp->cfg; struct device *dev = qmp->dev; - int num = cfg->num_clks; + int num = ARRAY_SIZE(qmp_pciephy_clk_l); int i; qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); @@ -3069,9 +3412,9 @@ static int qmp_pcie_clk_init(struct qmp_pcie *qmp) return -ENOMEM; for (i = 0; i < num; i++) - qmp->clks[i].id = cfg->clk_list[i]; + qmp->clks[i].id = qmp_pciephy_clk_l[i]; - return devm_clk_bulk_get(dev, num, qmp->clks); + return devm_clk_bulk_get_optional(dev, num, qmp->clks); } static void phy_clk_release_provider(void *res) @@ -3377,6 +3720,12 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,msm8998-qmp-pcie-phy", .data = &msm8998_pciephy_cfg, + }, { + .compatible = "qcom,sa8775p-qmp-gen4x2-pcie-phy", + .data = &sa8775p_qmp_gen4x2_pciephy_cfg, + }, { + .compatible = "qcom,sa8775p-qmp-gen4x4-pcie-phy", + .data = &sa8775p_qmp_gen4x4_pciephy_cfg, }, { .compatible = "qcom,sc8180x-qmp-pcie-phy", .data = &sc8180x_pciephy_cfg, @@ -3401,6 +3750,12 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { }, { .compatible = "qcom,sdx65-qmp-gen4x2-pcie-phy", .data = &sdx65_qmp_pciephy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-gen3x1-pcie-phy", + .data = &sm8250_qmp_gen3x1_pciephy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-gen3x2-pcie-phy", + .data = &sm8250_qmp_gen3x2_pciephy_cfg, }, { .compatible = "qcom,sm8250-qmp-gen3x1-pcie-phy", .data = &sm8250_qmp_gen3x1_pciephy_cfg, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h index a3a056741fc77724e33d25a05fade955c94a55c1..cdf8c04ea078a985be82d561ad0918dfdece9987 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-pcie-v5_20.h @@ -7,6 +7,7 @@ #define QCOM_PHY_QMP_PCS_PCIE_V5_20_H_ /* Only for QMP V5_20 PHY - PCIe PCS registers */ +#define QPHY_V5_20_PCS_PCIE_POWER_STATE_CONFIG2 0x00c #define QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x01c #define QPHY_V5_20_PCS_PCIE_OSC_DTCT_MODE2_CONFIG5 0x084 #define QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS 0x090 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h index c7b12c1fb7f5440b306c4d4d50dcfbb354d078a6..cf91154eed139c1764a1c57991f0e58b07faf66d 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v5_20.h @@ -19,6 +19,7 @@ /* Only for QMP V5_20 PHY - RX registers */ #define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2 0x008 #define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3 0x00c +#define QSERDES_V5_20_RX_UCDR_SO_GAIN_RATE3 0x01c #define QSERDES_V5_20_RX_UCDR_PI_CONTROLS 0x020 #define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1 0x02c #define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3 0x030 @@ -80,5 +81,6 @@ #define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3 0x210 #define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3 0x218 #define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3 0x220 +#define QSERDES_V5_20_RX_Q_PI_INTRINSIC_BIAS_RATE32 0x238 #endif diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h index a69233e68f9ab57544b91d04da7ed2e1c5e9b7fe..8883e1de730eff83a8ca3cd7065e78fe93e4add8 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v6.h @@ -7,6 +7,8 @@ #define QCOM_PHY_QMP_QSERDES_TXRX_USB_V6_H_ #define QSERDES_V6_TX_CLKBUF_ENABLE 0x08 +#define QSERDES_V6_TX_TX_EMP_POST1_LVL 0x0c +#define QSERDES_V6_TX_TX_DRV_LVL 0x14 #define QSERDES_V6_TX_RESET_TSYNC_EN 0x1c #define QSERDES_V6_TX_PRE_STALL_LDO_BOOST_EN 0x20 #define QSERDES_V6_TX_TX_BAND 0x24 @@ -15,6 +17,9 @@ #define QSERDES_V6_TX_RES_CODE_LANE_RX 0x38 #define QSERDES_V6_TX_RES_CODE_LANE_OFFSET_TX 0x3c #define QSERDES_V6_TX_RES_CODE_LANE_OFFSET_RX 0x40 +#define QSERDES_V6_TX_TRANSCEIVER_BIAS_EN 0x54 +#define QSERDES_V6_TX_HIGHZ_DRVR_EN 0x58 +#define QSERDES_V6_TX_TX_POL_INV 0x5c #define QSERDES_V6_TX_PARRATE_REC_DETECT_IDLE_EN 0x60 #define QSERDES_V6_TX_BIST_PATTERN7 0x7c #define QSERDES_V6_TX_LANE_MODE_1 0x84 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 8c877b668bb971cc3ed2f8b11b318d75b4f7af7c..3927eba8e46841ac94c8954143f03e29f302fac7 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -833,6 +832,8 @@ static const struct qmp_ufs_offsets qmp_ufs_offsets_v6 = { static const struct qmp_phy_cfg msm8996_ufsphy_cfg = { .lanes = 1, + .offsets = &qmp_ufs_offsets, + .tbls = { .serdes = msm8996_ufsphy_serdes, .serdes_num = ARRAY_SIZE(msm8996_ufsphy_serdes), @@ -924,6 +925,8 @@ static const struct qmp_phy_cfg sc8280xp_ufsphy_cfg = { static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { .lanes = 2, + .offsets = &qmp_ufs_offsets, + .tbls = { .serdes = sdm845_ufsphy_serdes, .serdes_num = ARRAY_SIZE(sdm845_ufsphy_serdes), @@ -1006,6 +1009,8 @@ static const struct qmp_phy_cfg sm7150_ufsphy_cfg = { static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { .lanes = 2, + .offsets = &qmp_ufs_offsets, + .tbls = { .serdes = sm8150_ufsphy_serdes, .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes), @@ -1038,6 +1043,8 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { static const struct qmp_phy_cfg sm8250_ufsphy_cfg = { .lanes = 2, + .offsets = &qmp_ufs_offsets, + .tbls = { .serdes = sm8150_ufsphy_serdes, .serdes_num = ARRAY_SIZE(sm8150_ufsphy_serdes), @@ -1070,6 +1077,8 @@ static const struct qmp_phy_cfg sm8250_ufsphy_cfg = { static const struct qmp_phy_cfg sm8350_ufsphy_cfg = { .lanes = 2, + .offsets = &qmp_ufs_offsets, + .tbls = { .serdes = sm8350_ufsphy_serdes, .serdes_num = ARRAY_SIZE(sm8350_ufsphy_serdes), @@ -1102,6 +1111,8 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = { static const struct qmp_phy_cfg sm8450_ufsphy_cfg = { .lanes = 2, + .offsets = &qmp_ufs_offsets, + .tbls = { .serdes = sm8350_ufsphy_serdes, .serdes_num = ARRAY_SIZE(sm8350_ufsphy_serdes), diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c new file mode 100644 index 0000000000000000000000000000000000000000..cf466f6df94d7a62ab48c90beeaee4295bad677e --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb-legacy.c @@ -0,0 +1,1407 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phy-qcom-qmp.h" +#include "phy-qcom-qmp-pcs-misc-v3.h" +#include "phy-qcom-qmp-pcs-usb-v4.h" +#include "phy-qcom-qmp-pcs-usb-v5.h" + +/* QPHY_SW_RESET bit */ +#define SW_RESET BIT(0) +/* QPHY_POWER_DOWN_CONTROL */ +#define SW_PWRDN BIT(0) +/* QPHY_START_CONTROL bits */ +#define SERDES_START BIT(0) +#define PCS_START BIT(1) +/* QPHY_PCS_STATUS bit */ +#define PHYSTATUS BIT(6) + +/* QPHY_V3_DP_COM_RESET_OVRD_CTRL register bits */ +/* DP PHY soft reset */ +#define SW_DPPHY_RESET BIT(0) +/* mux to select DP PHY reset control, 0:HW control, 1: software reset */ +#define SW_DPPHY_RESET_MUX BIT(1) +/* USB3 PHY soft reset */ +#define SW_USB3PHY_RESET BIT(2) +/* mux to select USB3 PHY reset control, 0:HW control, 1: software reset */ +#define SW_USB3PHY_RESET_MUX BIT(3) + +/* QPHY_V3_DP_COM_PHY_MODE_CTRL register bits */ +#define USB3_MODE BIT(0) /* enables USB3 mode */ +#define DP_MODE BIT(1) /* enables DP mode */ + +/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */ +#define ARCVR_DTCT_EN BIT(0) +#define ALFPS_DTCT_EN BIT(1) +#define ARCVR_DTCT_EVENT_SEL BIT(4) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */ +#define IRQ_CLEAR BIT(0) + +/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ +#define CLAMP_EN BIT(0) /* enables i/o clamp_n */ + +#define PHY_INIT_COMPLETE_TIMEOUT 10000 + +struct qmp_phy_init_tbl { + unsigned int offset; + unsigned int val; + /* + * mask of lanes for which this register is written + * for cases when second lane needs different values + */ + u8 lane_mask; +}; + +#define QMP_PHY_INIT_CFG(o, v) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = 0xff, \ + } + +#define QMP_PHY_INIT_CFG_LANE(o, v, l) \ + { \ + .offset = o, \ + .val = v, \ + .lane_mask = l, \ + } + +/* set of registers with offsets different per-PHY */ +enum qphy_reg_layout { + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_STATUS, + QPHY_PCS_AUTONOMOUS_MODE_CTRL, + QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, + QPHY_PCS_POWER_DOWN_CONTROL, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE +}; + +static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V3_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V3_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V3_PCS_PCS_STATUS, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V3_PCS_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V3_PCS_LFPS_RXTERM_IRQ_CLEAR, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V3_PCS_POWER_DOWN_CONTROL, +}; + +static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V4_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V4_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V4_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V4_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, +}; + +static const unsigned int qmp_v5_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V5_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V5_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V5_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V5_PCS_POWER_DOWN_CONTROL, + + /* In PCS_USB */ + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = QPHY_V5_PCS_USB3_AUTONOMOUS_MODE_CTRL, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), + QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), +}; + +static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = { + /* FLL settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), + + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { + /* Lock Det settings */ + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8150_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), + QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_TX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_RX, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_5, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xbb), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7b), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbb), + QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3d, 1), + QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3c, 2), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdb), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xd2), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x13), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_EN_TIMER, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VTH_CODE, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), +}; + +static const struct qmp_phy_init_tbl sm8350_usb3_pcs_usb_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), +}; + +struct qmp_usb_legacy_offsets { + u16 serdes; + u16 pcs; + u16 pcs_usb; + u16 tx; + u16 rx; +}; + +/* struct qmp_phy_cfg - per-PHY initialization config */ +struct qmp_phy_cfg { + int lanes; + + const struct qmp_usb_legacy_offsets *offsets; + + /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ + const struct qmp_phy_init_tbl *serdes_tbl; + int serdes_tbl_num; + const struct qmp_phy_init_tbl *tx_tbl; + int tx_tbl_num; + const struct qmp_phy_init_tbl *rx_tbl; + int rx_tbl_num; + const struct qmp_phy_init_tbl *pcs_tbl; + int pcs_tbl_num; + const struct qmp_phy_init_tbl *pcs_usb_tbl; + int pcs_usb_tbl_num; + + /* clock ids to be requested */ + const char * const *clk_list; + int num_clks; + /* resets to be requested */ + const char * const *reset_list; + int num_resets; + /* regulators to be requested */ + const char * const *vreg_list; + int num_vregs; + + /* array of registers with different offsets */ + const unsigned int *regs; + + /* Offset from PCS to PCS_USB region */ + unsigned int pcs_usb_offset; +}; + +struct qmp_usb { + struct device *dev; + + const struct qmp_phy_cfg *cfg; + + void __iomem *serdes; + void __iomem *pcs; + void __iomem *pcs_misc; + void __iomem *pcs_usb; + void __iomem *tx; + void __iomem *rx; + void __iomem *tx2; + void __iomem *rx2; + + void __iomem *dp_com; + + struct clk *pipe_clk; + struct clk_bulk_data *clks; + struct reset_control_bulk_data *resets; + struct regulator_bulk_data *vregs; + + enum phy_mode mode; + + struct phy *phy; + + struct clk_fixed_rate pipe_clk_fixed; +}; + +static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +/* list of clocks required by phy */ +static const char * const qmp_v3_phy_clk_l[] = { + "aux", "cfg_ahb", "ref", "com_aux", +}; + +static const char * const qmp_v4_ref_phy_clk_l[] = { + "aux", "ref_clk_src", "ref", "com_aux", +}; + +/* the primary usb3 phy on sm8250 doesn't have a ref clock */ +static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { + "aux", "ref_clk_src", "com_aux" +}; + +/* list of resets */ +static const char * const msm8996_usb3phy_reset_l[] = { + "phy", "common", +}; + +static const char * const sc7180_usb3phy_reset_l[] = { + "phy", +}; + +/* list of regulators */ +static const char * const qmp_phy_vreg_l[] = { + "vdda-phy", "vdda-pll", +}; + +static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, +}; + +static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = qmp_v3_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), + .tx_tbl = qmp_v3_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), + .rx_tbl = qmp_v3_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), + .pcs_tbl = qmp_v3_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), + .clk_list = qmp_v3_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), + .reset_list = sc7180_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, +}; + +static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8150_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl), + .rx_tbl = sm8150_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl), + .pcs_tbl = sm8150_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl), + .pcs_usb_tbl = sm8150_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_ref_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_ref_phy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, +}; + +static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8250_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), + .rx_tbl = sm8250_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), + .pcs_tbl = sm8250_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), + .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v4_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, +}; + +static const struct qmp_phy_cfg sm8350_usb3phy_cfg = { + .lanes = 2, + + .serdes_tbl = sm8150_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), + .tx_tbl = sm8350_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_tx_tbl), + .rx_tbl = sm8350_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_rx_tbl), + .pcs_tbl = sm8350_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_tbl), + .pcs_usb_tbl = sm8350_usb3_pcs_usb_tbl, + .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_usb_tbl), + .clk_list = qmp_v4_sm8250_usbphy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), + .reset_list = msm8996_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v5_usb3phy_regs_layout, + .pcs_usb_offset = 0x300, +}; + +static void qmp_usb_legacy_configure_lane(void __iomem *base, + const struct qmp_phy_init_tbl tbl[], + int num, + u8 lane_mask) +{ + int i; + const struct qmp_phy_init_tbl *t = tbl; + + if (!t) + return; + + for (i = 0; i < num; i++, t++) { + if (!(t->lane_mask & lane_mask)) + continue; + + writel(t->val, base + t->offset); + } +} + +static void qmp_usb_legacy_configure(void __iomem *base, + const struct qmp_phy_init_tbl tbl[], + int num) +{ + qmp_usb_legacy_configure_lane(base, tbl, num, 0xff); +} + +static int qmp_usb_legacy_serdes_init(struct qmp_usb *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *serdes = qmp->serdes; + const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; + int serdes_tbl_num = cfg->serdes_tbl_num; + + qmp_usb_legacy_configure(serdes, serdes_tbl, serdes_tbl_num); + + return 0; +} + +static void qmp_usb_legacy_init_dp_com(struct phy *phy) +{ + struct qmp_usb *qmp = phy_get_drvdata(phy); + void __iomem *dp_com = qmp->dp_com; + + qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, + SW_PWRDN); + /* override hardware control for reset of qmp phy */ + qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + /* Default type-c orientation, i.e CC1 */ + qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02); + + qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, + USB3_MODE | DP_MODE); + + /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ + qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + + qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); + qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); +} + +static int qmp_usb_legacy_init(struct phy *phy) +{ + struct qmp_usb *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *pcs = qmp->pcs; + int ret; + + ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); + if (ret) { + dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret); + return ret; + } + + ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset assert failed\n"); + goto err_disable_regulators; + } + + ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets); + if (ret) { + dev_err(qmp->dev, "reset deassert failed\n"); + goto err_disable_regulators; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + goto err_assert_reset; + + qmp_usb_legacy_init_dp_com(phy); + + qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], SW_PWRDN); + + return 0; + +err_assert_reset: + reset_control_bulk_assert(cfg->num_resets, qmp->resets); +err_disable_regulators: + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return ret; +} + +static int qmp_usb_legacy_exit(struct phy *phy) +{ + struct qmp_usb *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + + reset_control_bulk_assert(cfg->num_resets, qmp->resets); + + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + regulator_bulk_disable(cfg->num_vregs, qmp->vregs); + + return 0; +} + +static int qmp_usb_legacy_power_on(struct phy *phy) +{ + struct qmp_usb *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *tx = qmp->tx; + void __iomem *rx = qmp->rx; + void __iomem *pcs = qmp->pcs; + void __iomem *status; + unsigned int val; + int ret; + + qmp_usb_legacy_serdes_init(qmp); + + ret = clk_prepare_enable(qmp->pipe_clk); + if (ret) { + dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret); + return ret; + } + + /* Tx, Rx, and PCS configurations */ + qmp_usb_legacy_configure_lane(tx, cfg->tx_tbl, cfg->tx_tbl_num, 1); + qmp_usb_legacy_configure_lane(rx, cfg->rx_tbl, cfg->rx_tbl_num, 1); + + if (cfg->lanes >= 2) { + qmp_usb_legacy_configure_lane(qmp->tx2, cfg->tx_tbl, cfg->tx_tbl_num, 2); + qmp_usb_legacy_configure_lane(qmp->rx2, cfg->rx_tbl, cfg->rx_tbl_num, 2); + } + + qmp_usb_legacy_configure(pcs, cfg->pcs_tbl, cfg->pcs_tbl_num); + + usleep_range(10, 20); + + /* Pull PHY out of reset state */ + qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* start SerDes and Phy-Coding-Sublayer */ + qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], SERDES_START | PCS_START); + + status = pcs + cfg->regs[QPHY_PCS_STATUS]; + ret = readl_poll_timeout(status, val, !(val & PHYSTATUS), 200, + PHY_INIT_COMPLETE_TIMEOUT); + if (ret) { + dev_err(qmp->dev, "phy initialization timed-out\n"); + goto err_disable_pipe_clk; + } + + return 0; + +err_disable_pipe_clk: + clk_disable_unprepare(qmp->pipe_clk); + + return ret; +} + +static int qmp_usb_legacy_power_off(struct phy *phy) +{ + struct qmp_usb *qmp = phy_get_drvdata(phy); + const struct qmp_phy_cfg *cfg = qmp->cfg; + + clk_disable_unprepare(qmp->pipe_clk); + + /* PHY reset */ + qphy_setbits(qmp->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET); + + /* stop SerDes and Phy-Coding-Sublayer */ + qphy_clrbits(qmp->pcs, cfg->regs[QPHY_START_CTRL], + SERDES_START | PCS_START); + + /* Put PHY into POWER DOWN state: active low */ + qphy_clrbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], + SW_PWRDN); + + return 0; +} + +static int qmp_usb_legacy_enable(struct phy *phy) +{ + int ret; + + ret = qmp_usb_legacy_init(phy); + if (ret) + return ret; + + ret = qmp_usb_legacy_power_on(phy); + if (ret) + qmp_usb_legacy_exit(phy); + + return ret; +} + +static int qmp_usb_legacy_disable(struct phy *phy) +{ + int ret; + + ret = qmp_usb_legacy_power_off(phy); + if (ret) + return ret; + return qmp_usb_legacy_exit(phy); +} + +static int qmp_usb_legacy_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct qmp_usb *qmp = phy_get_drvdata(phy); + + qmp->mode = mode; + + return 0; +} + +static const struct phy_ops qmp_usb_legacy_phy_ops = { + .init = qmp_usb_legacy_enable, + .exit = qmp_usb_legacy_disable, + .set_mode = qmp_usb_legacy_set_mode, + .owner = THIS_MODULE, +}; + +static void qmp_usb_legacy_enable_autonomous_mode(struct qmp_usb *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *pcs_usb = qmp->pcs_usb ?: qmp->pcs; + void __iomem *pcs_misc = qmp->pcs_misc; + u32 intr_mask; + + if (qmp->mode == PHY_MODE_USB_HOST_SS || + qmp->mode == PHY_MODE_USB_DEVICE_SS) + intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; + else + intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; + + /* Clear any pending interrupts status */ + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL); + + /* Enable required PHY autonomous mode interrupts */ + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask); + + /* Enable i/o clamp_n for autonomous mode */ + if (pcs_misc) + qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); +} + +static void qmp_usb_legacy_disable_autonomous_mode(struct qmp_usb *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *pcs_usb = qmp->pcs_usb ?: qmp->pcs; + void __iomem *pcs_misc = qmp->pcs_misc; + + /* Disable i/o clamp_n on resume for normal mode */ + if (pcs_misc) + qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); + + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN); + + qphy_setbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs_usb, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); +} + +static int __maybe_unused qmp_usb_legacy_runtime_suspend(struct device *dev) +{ + struct qmp_usb *qmp = dev_get_drvdata(dev); + const struct qmp_phy_cfg *cfg = qmp->cfg; + + dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); + + if (!qmp->phy->init_count) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + qmp_usb_legacy_enable_autonomous_mode(qmp); + + clk_disable_unprepare(qmp->pipe_clk); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + return 0; +} + +static int __maybe_unused qmp_usb_legacy_runtime_resume(struct device *dev) +{ + struct qmp_usb *qmp = dev_get_drvdata(dev); + const struct qmp_phy_cfg *cfg = qmp->cfg; + int ret = 0; + + dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); + + if (!qmp->phy->init_count) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) + return ret; + + ret = clk_prepare_enable(qmp->pipe_clk); + if (ret) { + dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + return ret; + } + + qmp_usb_legacy_disable_autonomous_mode(qmp); + + return 0; +} + +static const struct dev_pm_ops qmp_usb_legacy_pm_ops = { + SET_RUNTIME_PM_OPS(qmp_usb_legacy_runtime_suspend, + qmp_usb_legacy_runtime_resume, NULL) +}; + +static int qmp_usb_legacy_vreg_init(struct qmp_usb *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int num = cfg->num_vregs; + int i; + + qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); + if (!qmp->vregs) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->vregs[i].supply = cfg->vreg_list[i]; + + return devm_regulator_bulk_get(dev, num, qmp->vregs); +} + +static int qmp_usb_legacy_reset_init(struct qmp_usb *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int i; + int ret; + + qmp->resets = devm_kcalloc(dev, cfg->num_resets, + sizeof(*qmp->resets), GFP_KERNEL); + if (!qmp->resets) + return -ENOMEM; + + for (i = 0; i < cfg->num_resets; i++) + qmp->resets[i].id = cfg->reset_list[i]; + + ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get resets\n"); + + return 0; +} + +static int qmp_usb_legacy_clk_init(struct qmp_usb *qmp) +{ + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + int num = cfg->num_clks; + int i; + + qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL); + if (!qmp->clks) + return -ENOMEM; + + for (i = 0; i < num; i++) + qmp->clks[i].id = cfg->clk_list[i]; + + return devm_clk_bulk_get(dev, num, qmp->clks); +} + +static void phy_clk_release_provider(void *res) +{ + of_clk_del_provider(res); +} + +/* + * Register a fixed rate pipe clock. + * + * The _pipe_clksrc generated by PHY goes to the GCC that gate + * controls it. The _pipe_clk coming out of the GCC is requested + * by the PHY driver for its operations. + * We register the _pipe_clksrc here. The gcc driver takes care + * of assigning this _pipe_clksrc as parent to _pipe_clk. + * Below picture shows this relationship. + * + * +---------------+ + * | PHY block |<<---------------------------------------+ + * | | | + * | +-------+ | +-----+ | + * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ + * clk | +-------+ | +-----+ + * +---------------+ + */ +static int phy_pipe_clk_register(struct qmp_usb *qmp, struct device_node *np) +{ + struct clk_fixed_rate *fixed = &qmp->pipe_clk_fixed; + struct clk_init_data init = { }; + int ret; + + ret = of_property_read_string(np, "clock-output-names", &init.name); + if (ret) { + dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np); + return ret; + } + + init.ops = &clk_fixed_rate_ops; + + /* controllers using QMP phys use 125MHz pipe clock interface */ + fixed->fixed_rate = 125000000; + fixed->hw.init = &init; + + ret = devm_clk_hw_register(qmp->dev, &fixed->hw); + if (ret) + return ret; + + ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw); + if (ret) + return ret; + + /* + * Roll a devm action because the clock provider is the child node, but + * the child node is not actually a device. + */ + return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); +} + +static void __iomem *qmp_usb_legacy_iomap(struct device *dev, struct device_node *np, + int index, bool exclusive) +{ + struct resource res; + + if (!exclusive) { + if (of_address_to_resource(np, index, &res)) + return IOMEM_ERR_PTR(-EINVAL); + + return devm_ioremap(dev, res.start, resource_size(&res)); + } + + return devm_of_iomap(dev, np, index, NULL); +} + +static int qmp_usb_legacy_parse_dt_legacy(struct qmp_usb *qmp, struct device_node *np) +{ + struct platform_device *pdev = to_platform_device(qmp->dev); + const struct qmp_phy_cfg *cfg = qmp->cfg; + struct device *dev = qmp->dev; + bool exclusive = true; + + qmp->serdes = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(qmp->serdes)) + return PTR_ERR(qmp->serdes); + + qmp->dp_com = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(qmp->dp_com)) + return PTR_ERR(qmp->dp_com); + + /* + * Get memory resources for the PHY: + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * For dual lane PHYs: tx2 -> 3, rx2 -> 4, pcs_misc (optional) -> 5 + * For single lane PHYs: pcs_misc (optional) -> 3. + */ + qmp->tx = devm_of_iomap(dev, np, 0, NULL); + if (IS_ERR(qmp->tx)) + return PTR_ERR(qmp->tx); + + qmp->rx = devm_of_iomap(dev, np, 1, NULL); + if (IS_ERR(qmp->rx)) + return PTR_ERR(qmp->rx); + + qmp->pcs = qmp_usb_legacy_iomap(dev, np, 2, exclusive); + if (IS_ERR(qmp->pcs)) + return PTR_ERR(qmp->pcs); + + if (cfg->pcs_usb_offset) + qmp->pcs_usb = qmp->pcs + cfg->pcs_usb_offset; + + if (cfg->lanes >= 2) { + qmp->tx2 = devm_of_iomap(dev, np, 3, NULL); + if (IS_ERR(qmp->tx2)) + return PTR_ERR(qmp->tx2); + + qmp->rx2 = devm_of_iomap(dev, np, 4, NULL); + if (IS_ERR(qmp->rx2)) + return PTR_ERR(qmp->rx2); + + qmp->pcs_misc = devm_of_iomap(dev, np, 5, NULL); + } else { + qmp->pcs_misc = devm_of_iomap(dev, np, 3, NULL); + } + + if (IS_ERR(qmp->pcs_misc)) { + dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); + qmp->pcs_misc = NULL; + } + + qmp->pipe_clk = devm_get_clk_from_child(dev, np, NULL); + if (IS_ERR(qmp->pipe_clk)) { + return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), + "failed to get pipe clock\n"); + } + + return 0; +} + +static int qmp_usb_legacy_parse_dt(struct qmp_usb *qmp) +{ + struct platform_device *pdev = to_platform_device(qmp->dev); + const struct qmp_phy_cfg *cfg = qmp->cfg; + const struct qmp_usb_legacy_offsets *offs = cfg->offsets; + struct device *dev = qmp->dev; + void __iomem *base; + + if (!offs) + return -EINVAL; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + qmp->serdes = base + offs->serdes; + qmp->pcs = base + offs->pcs; + qmp->pcs_usb = base + offs->pcs_usb; + qmp->tx = base + offs->tx; + qmp->rx = base + offs->rx; + + qmp->pipe_clk = devm_clk_get(dev, "pipe"); + if (IS_ERR(qmp->pipe_clk)) { + return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), + "failed to get pipe clock\n"); + } + + return 0; +} + +static int qmp_usb_legacy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + struct device_node *np; + struct qmp_usb *qmp; + int ret; + + qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); + if (!qmp) + return -ENOMEM; + + qmp->dev = dev; + + qmp->cfg = of_device_get_match_data(dev); + if (!qmp->cfg) + return -EINVAL; + + ret = qmp_usb_legacy_clk_init(qmp); + if (ret) + return ret; + + ret = qmp_usb_legacy_reset_init(qmp); + if (ret) + return ret; + + ret = qmp_usb_legacy_vreg_init(qmp); + if (ret) + return ret; + + /* Check for legacy binding with child node. */ + np = of_get_next_available_child(dev->of_node, NULL); + if (np) { + ret = qmp_usb_legacy_parse_dt_legacy(qmp, np); + } else { + np = of_node_get(dev->of_node); + ret = qmp_usb_legacy_parse_dt(qmp); + } + if (ret) + goto err_node_put; + + pm_runtime_set_active(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + goto err_node_put; + /* + * Prevent runtime pm from being ON by default. Users can enable + * it using power/control in sysfs. + */ + pm_runtime_forbid(dev); + + ret = phy_pipe_clk_register(qmp, np); + if (ret) + goto err_node_put; + + qmp->phy = devm_phy_create(dev, np, &qmp_usb_legacy_phy_ops); + if (IS_ERR(qmp->phy)) { + ret = PTR_ERR(qmp->phy); + dev_err(dev, "failed to create PHY: %d\n", ret); + goto err_node_put; + } + + phy_set_drvdata(qmp->phy, qmp); + + of_node_put(np); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); + +err_node_put: + of_node_put(np); + return ret; +} + +static const struct of_device_id qmp_usb_legacy_of_match_table[] = { + { + .compatible = "qcom,sc7180-qmp-usb3-phy", + .data = &sc7180_usb3phy_cfg, + }, { + .compatible = "qcom,sc8180x-qmp-usb3-phy", + .data = &sm8150_usb3phy_cfg, + }, { + .compatible = "qcom,sdm845-qmp-usb3-phy", + .data = &qmp_v3_usb3phy_cfg, + }, { + .compatible = "qcom,sm8150-qmp-usb3-phy", + .data = &sm8150_usb3phy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-usb3-phy", + .data = &sm8250_usb3phy_cfg, + }, { + .compatible = "qcom,sm8350-qmp-usb3-phy", + .data = &sm8350_usb3phy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-usb3-phy", + .data = &sm8350_usb3phy_cfg, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, qmp_usb_legacy_of_match_table); + +static struct platform_driver qmp_usb_legacy_driver = { + .probe = qmp_usb_legacy_probe, + .driver = { + .name = "qcom-qmp-usb-legacy-phy", + .pm = &qmp_usb_legacy_pm_ops, + .of_match_table = qmp_usb_legacy_of_match_table, + }, +}; + +module_platform_driver(qmp_usb_legacy_driver); + +MODULE_AUTHOR("Vivek Gautam "); +MODULE_DESCRIPTION("Qualcomm QMP legacy USB+DP PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index 466f0a56c82e1da6de8d78771d1c57c66c8e9496..0130bb8e809afcd6cf78fe07ab5fff1e35ded237 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -367,112 +366,6 @@ static const struct qmp_phy_init_tbl msm8996_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG2, 0x08), }; -static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x15), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_CFG, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x85), - QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x07), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x09), - QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x06), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4e), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x18), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x75), -}; - -static const struct qmp_phy_init_tbl qmp_v3_usb3_pcs_tbl[] = { - /* FLL settings */ - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), - - /* Lock Det settings */ - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xba), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V0, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V1, 0x9f), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V2, 0xb7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V3, 0x4e), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_V4, 0x65), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXMGN_LS, 0x6b), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V1, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V1, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V2, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V2, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V3, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V3, 0x1d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V4, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V4, 0x0d), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_LS, 0x15), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_LS, 0x0d), - - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RATE_SLEW_CNTRL, 0x02), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), - QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), -}; - static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07), QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14), @@ -693,117 +586,6 @@ static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), }; -static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e), - QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), -}; - -static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20), -}; - -static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), -}; - -static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = { - /* Lock Det settings */ - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), - - QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), -}; - -static const struct qmp_phy_init_tbl sm8150_usb3_pcs_usb_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), -}; - static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a), QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), @@ -915,78 +697,6 @@ static const struct qmp_phy_init_tbl sm8150_usb3_uniphy_pcs_usb_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), }; -static const struct qmp_phy_init_tbl sm8250_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x60), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x60), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_TX, 0x11), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_OFFSET_RX, 0x02), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), - QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x40, 1), - QMP_PHY_INIT_CFG_LANE(QSERDES_V4_TX_PI_QEC_CTRL, 0x54, 2), -}; - -static const struct qmp_phy_init_tbl sm8250_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x06), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), - QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0xff, 1), - QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_LOW, 0x7f, 2), - QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0x7f, 1), - QMP_PHY_INIT_CFG_LANE(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xff, 2), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x97), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x7b), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb4), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f), - QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10), -}; - -static const struct qmp_phy_init_tbl sm8250_usb3_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xa9), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), -}; - -static const struct qmp_phy_init_tbl sm8250_usb3_pcs_usb_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), -}; - static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12), QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5), @@ -1148,84 +858,6 @@ static const struct qmp_phy_init_tbl sdx65_usb3_uniphy_rx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_ENABLES, 0x00), }; -static const struct qmp_phy_init_tbl sm8350_usb3_tx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_TX, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_RX, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x16), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x35), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_3, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_5, 0x3f), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_RCV_DETECT_LVL_2, 0x12), - QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x21), -}; - -static const struct qmp_phy_init_tbl sm8350_usb3_rx_tbl[] = { - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0x99), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN1, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_GAIN2, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL1, 0x54), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_LOW, 0xc0), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_IDAC_TSETTLE_HIGH, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x47), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_CNTRL, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_SIGDET_DEGLITCH_CNTRL, 0x0e), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0xbb), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0x7b), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbb), - QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3d, 1), - QMP_PHY_INIT_CFG_LANE(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3c, 2), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xdb), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0x64), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0x24), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0xd2), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x13), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa9), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_EN_TIMER, 0x04), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_AUX_DATA_TCOARSE_TFINE, 0xa0), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_DCC_CTRL1, 0x0c), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), - QMP_PHY_INIT_CFG(QSERDES_V5_RX_VTH_CODE, 0x10), -}; - -static const struct qmp_phy_init_tbl sm8350_usb3_pcs_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG3, 0x20), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b), - QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10), -}; - -static const struct qmp_phy_init_tbl sm8350_usb3_pcs_usb_tbl[] = { - QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_L, 0x40), - QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RCVR_DTCT_DLY_U3_H, 0x00), - QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), - QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), -}; - static const struct qmp_phy_init_tbl sm8350_usb3_uniphy_tx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0xa5), QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_2, 0x82), @@ -1556,9 +1188,6 @@ struct qmp_phy_cfg { /* true, if PHY needs delay after POWER_DOWN */ bool has_pwrdn_delay; - /* true, if PHY has a separate DP_COM control block */ - bool has_phy_dp_com_ctrl; - /* Offset from PCS to PCS_USB region */ unsigned int pcs_usb_offset; }; @@ -1577,8 +1206,6 @@ struct qmp_usb { void __iomem *tx2; void __iomem *rx2; - void __iomem *dp_com; - struct clk *pipe_clk; struct clk_bulk_data *clks; struct reset_control_bulk_data *resets; @@ -1632,11 +1259,6 @@ static const char * const qmp_v4_ref_phy_clk_l[] = { "aux", "ref_clk_src", "ref", "com_aux", }; -/* the primary usb3 phy on sm8250 doesn't have a ref clock */ -static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { - "aux", "ref_clk_src", "com_aux" -}; - /* usb3 phy on sdx55 doesn't have com_aux clock */ static const char * const qmp_v4_sdx55_usbphy_clk_l[] = { "aux", "cfg_ahb", "ref" @@ -1651,10 +1273,6 @@ static const char * const msm8996_usb3phy_reset_l[] = { "phy", "common", }; -static const char * const sc7180_usb3phy_reset_l[] = { - "phy", -}; - static const char * const qcm2290_usb3phy_reset_l[] = { "phy_phy", "phy", }; @@ -1752,29 +1370,6 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .regs = qmp_v2_usb3phy_regs_layout, }; -static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { - .lanes = 2, - - .serdes_tbl = qmp_v3_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), - .tx_tbl = qmp_v3_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), - .rx_tbl = qmp_v3_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), - .pcs_tbl = qmp_v3_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), - .clk_list = qmp_v3_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v3_usb3phy_regs_layout, - - .has_pwrdn_delay = true, - .has_phy_dp_com_ctrl = true, -}; - static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = { .lanes = 1, @@ -1797,29 +1392,6 @@ static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = { .regs = qmp_v5_usb3phy_regs_layout, }; -static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { - .lanes = 2, - - .serdes_tbl = qmp_v3_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl), - .tx_tbl = qmp_v3_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_tx_tbl), - .rx_tbl = qmp_v3_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(qmp_v3_usb3_rx_tbl), - .pcs_tbl = qmp_v3_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl), - .clk_list = qmp_v3_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l), - .reset_list = sc7180_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(sc7180_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v3_usb3phy_regs_layout, - - .has_pwrdn_delay = true, - .has_phy_dp_com_ctrl = true, -}; - static const struct qmp_phy_cfg sc8280xp_usb3_uniphy_cfg = { .lanes = 1, @@ -1884,32 +1456,6 @@ static const struct qmp_phy_cfg msm8998_usb3phy_cfg = { .regs = qmp_v3_usb3phy_regs_layout, }; -static const struct qmp_phy_cfg sm8150_usb3phy_cfg = { - .lanes = 2, - - .serdes_tbl = sm8150_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), - .tx_tbl = sm8150_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(sm8150_usb3_tx_tbl), - .rx_tbl = sm8150_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(sm8150_usb3_rx_tbl), - .pcs_tbl = sm8150_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_tbl), - .pcs_usb_tbl = sm8150_usb3_pcs_usb_tbl, - .pcs_usb_tbl_num = ARRAY_SIZE(sm8150_usb3_pcs_usb_tbl), - .clk_list = qmp_v4_ref_phy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_ref_phy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v4_usb3phy_regs_layout, - .pcs_usb_offset = 0x300, - - .has_pwrdn_delay = true, - .has_phy_dp_com_ctrl = true, -}; - static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { .lanes = 1, @@ -1935,32 +1481,6 @@ static const struct qmp_phy_cfg sm8150_usb3_uniphy_cfg = { .has_pwrdn_delay = true, }; -static const struct qmp_phy_cfg sm8250_usb3phy_cfg = { - .lanes = 2, - - .serdes_tbl = sm8150_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), - .tx_tbl = sm8250_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(sm8250_usb3_tx_tbl), - .rx_tbl = sm8250_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(sm8250_usb3_rx_tbl), - .pcs_tbl = sm8250_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_tbl), - .pcs_usb_tbl = sm8250_usb3_pcs_usb_tbl, - .pcs_usb_tbl_num = ARRAY_SIZE(sm8250_usb3_pcs_usb_tbl), - .clk_list = qmp_v4_sm8250_usbphy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v4_usb3phy_regs_layout, - .pcs_usb_offset = 0x300, - - .has_pwrdn_delay = true, - .has_phy_dp_com_ctrl = true, -}; - static const struct qmp_phy_cfg sm8250_usb3_uniphy_cfg = { .lanes = 1, @@ -2036,32 +1556,6 @@ static const struct qmp_phy_cfg sdx65_usb3_uniphy_cfg = { .has_pwrdn_delay = true, }; -static const struct qmp_phy_cfg sm8350_usb3phy_cfg = { - .lanes = 2, - - .serdes_tbl = sm8150_usb3_serdes_tbl, - .serdes_tbl_num = ARRAY_SIZE(sm8150_usb3_serdes_tbl), - .tx_tbl = sm8350_usb3_tx_tbl, - .tx_tbl_num = ARRAY_SIZE(sm8350_usb3_tx_tbl), - .rx_tbl = sm8350_usb3_rx_tbl, - .rx_tbl_num = ARRAY_SIZE(sm8350_usb3_rx_tbl), - .pcs_tbl = sm8350_usb3_pcs_tbl, - .pcs_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_tbl), - .pcs_usb_tbl = sm8350_usb3_pcs_usb_tbl, - .pcs_usb_tbl_num = ARRAY_SIZE(sm8350_usb3_pcs_usb_tbl), - .clk_list = qmp_v4_sm8250_usbphy_clk_l, - .num_clks = ARRAY_SIZE(qmp_v4_sm8250_usbphy_clk_l), - .reset_list = msm8996_usb3phy_reset_l, - .num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l), - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), - .regs = qmp_v5_usb3phy_regs_layout, - .pcs_usb_offset = 0x300, - - .has_pwrdn_delay = true, - .has_phy_dp_com_ctrl = true, -}; - static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { .lanes = 1, @@ -2152,7 +1646,6 @@ static int qmp_usb_init(struct phy *phy) struct qmp_usb *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; void __iomem *pcs = qmp->pcs; - void __iomem *dp_com = qmp->dp_com; int ret; ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); @@ -2177,29 +1670,6 @@ static int qmp_usb_init(struct phy *phy) if (ret) goto err_assert_reset; - if (cfg->has_phy_dp_com_ctrl) { - qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL, - SW_PWRDN); - /* override hardware control for reset of qmp phy */ - qphy_setbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, - SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); - - /* Default type-c orientation, i.e CC1 */ - qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02); - - qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, - USB3_MODE | DP_MODE); - - /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ - qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, - SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); - - qphy_clrbits(dp_com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); - qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); - } - qphy_setbits(pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], SW_PWRDN); return 0; @@ -2582,12 +2052,6 @@ static int qmp_usb_parse_dt_legacy(struct qmp_usb *qmp, struct device_node *np) if (IS_ERR(qmp->serdes)) return PTR_ERR(qmp->serdes); - if (cfg->has_phy_dp_com_ctrl) { - qmp->dp_com = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(qmp->dp_com)) - return PTR_ERR(qmp->dp_com); - } - /* * FIXME: These bindings should be fixed to not rely on overlapping * mappings for PCS. @@ -2779,18 +2243,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { }, { .compatible = "qcom,sa8775p-qmp-usb3-uni-phy", .data = &sa8775p_usb3_uniphy_cfg, - }, { - .compatible = "qcom,sc7180-qmp-usb3-phy", - .data = &sc7180_usb3phy_cfg, - }, { - .compatible = "qcom,sc8180x-qmp-usb3-phy", - .data = &sm8150_usb3phy_cfg, }, { .compatible = "qcom,sc8280xp-qmp-usb3-uni-phy", .data = &sc8280xp_usb3_uniphy_cfg, - }, { - .compatible = "qcom,sdm845-qmp-usb3-phy", - .data = &qmp_v3_usb3phy_cfg, }, { .compatible = "qcom,sdm845-qmp-usb3-uni-phy", .data = &qmp_v3_usb3_uniphy_cfg, @@ -2803,27 +2258,15 @@ static const struct of_device_id qmp_usb_of_match_table[] = { }, { .compatible = "qcom,sm6115-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, - }, { - .compatible = "qcom,sm8150-qmp-usb3-phy", - .data = &sm8150_usb3phy_cfg, }, { .compatible = "qcom,sm8150-qmp-usb3-uni-phy", .data = &sm8150_usb3_uniphy_cfg, - }, { - .compatible = "qcom,sm8250-qmp-usb3-phy", - .data = &sm8250_usb3phy_cfg, }, { .compatible = "qcom,sm8250-qmp-usb3-uni-phy", .data = &sm8250_usb3_uniphy_cfg, - }, { - .compatible = "qcom,sm8350-qmp-usb3-phy", - .data = &sm8350_usb3phy_cfg, }, { .compatible = "qcom,sm8350-qmp-usb3-uni-phy", .data = &sm8350_usb3_uniphy_cfg, - }, { - .compatible = "qcom,sm8450-qmp-usb3-phy", - .data = &sm8350_usb3phy_cfg, }, { }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 7ee4b0e07d11b8e402e4f69f2ab4a32d675ffc71..32d8976847557a5878a227eefb95044205bf515c 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -134,6 +134,8 @@ #define QPHY_V4_PCS_MISC_TYPEC_STATUS 0x10 #define QPHY_V4_PCS_MISC_PLACEHOLDER_STATUS 0x14 +#define QSERDES_V5_DP_PHY_STATUS 0x0dc + /* Only for QMP V6 PHY - DP PHY registers */ #define QSERDES_V6_DP_PHY_AUX_INTERRUPT_STATUS 0x0e0 #define QSERDES_V6_DP_PHY_STATUS 0x0e4 diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index bec6e40d5280f11c2a646102b321cb877910af32..c52655a383cef008552ed4533b9f31d1cbf34a13 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-snps-eusb2.c b/drivers/phy/qualcomm/phy-qcom-snps-eusb2.c index eeaa1eb0e24be3411a688df8728798f397cec5e9..1484691a41d59a7eaf257ef44300827c668bf7e0 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-eusb2.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c index d0319bee01c0fb3be7a26b5627cd8bf29d76424e..eb0b0f61d98e03963bf92e5c822334f52636abe7 100644 --- a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c +++ b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c index 53e46c220a3aa39a945da5679be19225bb40eb79..98a18987f1bec8ddd604541bbe134023383170e5 100644 --- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c +++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/ralink/phy-mt7621-pci.c b/drivers/phy/ralink/phy-mt7621-pci.c index 85888ab2d307adaa4b11d3520d0f6ee263c15084..2f876f158e1df6acd51d20bda470fcc783efcdf8 100644 --- a/drivers/phy/ralink/phy-mt7621-pci.c +++ b/drivers/phy/ralink/phy-mt7621-pci.c @@ -9,8 +9,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/drivers/phy/renesas/phy-rcar-gen2.c b/drivers/phy/renesas/phy-rcar-gen2.c index c375a4676a3dc57f6f1f248faf8a39575e886d33..507435af26567f992540e41dd38068815bd69863 100644 --- a/drivers/phy/renesas/phy-rcar-gen2.c +++ b/drivers/phy/renesas/phy-rcar-gen2.c @@ -16,7 +16,6 @@ #include #include #include -#include #define USBHS_LPSTS 0x02 #define USBHS_UGCTRL 0x80 diff --git a/drivers/phy/renesas/phy-rcar-gen3-pcie.c b/drivers/phy/renesas/phy-rcar-gen3-pcie.c index 9cf786a7daac6cd8628e557a3d21ae95d1dfdbe2..0ce7e9c944447417ccbbfd8c758554f0e7947f2d 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-pcie.c +++ b/drivers/phy/renesas/phy-rcar-gen3-pcie.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index d4e2ee7e4efb12ad6254a85f5de388e59e1eb354..e53eace7c91e372e60d0fcbb6032e2e8fd510595 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -15,8 +15,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/phy/renesas/r8a779f0-ether-serdes.c b/drivers/phy/renesas/r8a779f0-ether-serdes.c index 55b7bdfc10d30d47649293e28aa206aa8c50ba24..683b19bc411a84a3039b7fd18f79a03b7a821042 100644 --- a/drivers/phy/renesas/r8a779f0-ether-serdes.c +++ b/drivers/phy/renesas/r8a779f0-ether-serdes.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -339,22 +340,15 @@ static int r8a779f0_eth_serdes_probe(struct platform_device *pdev) { struct r8a779f0_eth_serdes_drv_data *dd; struct phy_provider *provider; - struct resource *res; int i; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "invalid resource\n"); - return -EINVAL; - } - dd = devm_kzalloc(&pdev->dev, sizeof(*dd), GFP_KERNEL); if (!dd) return -ENOMEM; platform_set_drvdata(pdev, dd); dd->pdev = pdev; - dd->addr = devm_ioremap_resource(&pdev->dev, res); + dd->addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dd->addr)) return PTR_ERR(dd->addr); diff --git a/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c index 639452f47869c1a0559bd50d33b0eba3c9deafd6..e6a768bbb9b316b18d0009f793ca955b1cc5d7ad 100644 --- a/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c +++ b/drivers/phy/rockchip/phy-rockchip-dphy-rx0.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c index 401b0aabb1592cd8a2c86bad2b2c05e8de11287a..6405943a2676786cc5be6b6432dd84e721431372 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -770,6 +770,9 @@ static const struct of_device_id inno_dsidphy_of_match[] = { }, { .compatible = "rockchip,rk3568-dsi-dphy", .data = &max_2_5ghz_video_phy_plat_data, + }, { + .compatible = "rockchip,rv1126-dsi-dphy", + .data = &max_2_5ghz_video_phy_plat_data, }, {} }; diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c index 1e1563f5fffc41f477f82b92e0722ee4de87bfa3..053bd62e31ba779eadef6bb8176135eb7f0c398f 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -245,6 +244,7 @@ struct inno_hdmi_phy { struct clk_hw hw; struct clk *phyclk; unsigned long pixclock; + unsigned long tmdsclock; }; struct pre_pll_config { @@ -291,31 +291,179 @@ struct inno_hdmi_phy_drv_data { }; static const struct pre_pll_config pre_pll_cfg_table[] = { - { 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0}, - { 27000000, 33750000, 1, 90, 1, 3, 3, 10, 3, 3, 4, 0, 0}, - { 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0}, - { 59341000, 59341000, 1, 98, 3, 1, 2, 1, 3, 3, 4, 0, 0xE6AE6B}, - { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0}, - { 59341000, 74176250, 1, 98, 0, 3, 3, 1, 3, 3, 4, 0, 0xE6AE6B}, - { 59400000, 74250000, 1, 99, 1, 2, 2, 1, 3, 3, 4, 0, 0}, - { 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xE6AE6B}, - { 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0}, - { 74176000, 92720000, 4, 494, 1, 2, 2, 1, 3, 3, 4, 0, 0x816817}, - { 74250000, 92812500, 4, 495, 1, 2, 2, 1, 3, 3, 4, 0, 0}, - {148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xE6AE6B}, - {148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0}, - {148352000, 185440000, 4, 494, 0, 2, 2, 1, 3, 2, 2, 0, 0x816817}, - {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0}, - {296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B}, - {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0}, - {296703000, 370878750, 4, 494, 1, 2, 0, 1, 3, 1, 1, 0, 0x816817}, - {297000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 0}, - {593407000, 296703500, 1, 98, 0, 1, 1, 1, 0, 2, 1, 0, 0xE6AE6B}, - {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 1, 0, 0}, - {593407000, 370879375, 4, 494, 1, 2, 0, 1, 3, 1, 1, 1, 0x816817}, - {594000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 0}, - {593407000, 593407000, 1, 98, 0, 2, 0, 1, 0, 1, 1, 0, 0xE6AE6B}, - {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0}, + { 25175000, 25175000, 3, 125, 3, 1, 1, 1, 3, 3, 4, 0, 0xe00000}, + { 25175000, 31468750, 1, 41, 0, 3, 3, 1, 3, 3, 4, 0, 0xf5554f}, + { 27000000, 27000000, 1, 36, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 27000000, 33750000, 1, 45, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 31500000, 31500000, 1, 42, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 31500000, 39375000, 1, 105, 1, 3, 3, 10, 0, 3, 4, 0, 0x0}, + { 33750000, 33750000, 1, 45, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 33750000, 42187500, 1, 169, 2, 3, 3, 15, 0, 3, 4, 0, 0x0}, + { 35500000, 35500000, 1, 71, 2, 2, 2, 6, 0, 3, 4, 0, 0x0}, + { 35500000, 44375000, 1, 74, 3, 1, 1, 25, 0, 1, 1, 0, 0x0}, + { 36000000, 36000000, 1, 36, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + { 36000000, 45000000, 1, 45, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + { 40000000, 40000000, 1, 40, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + { 40000000, 50000000, 1, 50, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + { 49500000, 49500000, 1, 66, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 49500000, 61875000, 1, 165, 1, 3, 3, 10, 0, 3, 4, 0, 0x0}, + { 50000000, 50000000, 1, 50, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + { 50000000, 62500000, 1, 125, 2, 2, 2, 15, 0, 2, 2, 0, 0x0}, + { 54000000, 54000000, 1, 36, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + { 54000000, 67500000, 1, 45, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + { 56250000, 56250000, 1, 75, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 56250000, 70312500, 1, 117, 3, 1, 1, 25, 0, 1, 1, 0, 0x0}, + { 59341000, 59341000, 1, 118, 2, 2, 2, 6, 0, 3, 4, 0, 0xae978d}, + { 59341000, 74176250, 2, 148, 2, 1, 1, 15, 0, 1, 1, 0, 0x5a3d70}, + { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0x0}, + { 59400000, 74250000, 1, 99, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 65000000, 65000000, 1, 65, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + { 65000000, 81250000, 3, 325, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 68250000, 68250000, 1, 91, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 68250000, 85312500, 1, 142, 3, 1, 1, 25, 0, 1, 1, 0, 0x0}, + { 71000000, 71000000, 1, 71, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + { 71000000, 88750000, 3, 355, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 72000000, 72000000, 1, 36, 2, 0, 0, 1, 1, 2, 2, 0, 0x0}, + { 72000000, 90000000, 1, 60, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + { 73250000, 73250000, 3, 293, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 73250000, 91562500, 1, 61, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + { 74176000, 74176000, 1, 37, 2, 0, 0, 1, 1, 2, 2, 0, 0x16872b}, + { 74176000, 92720000, 2, 185, 2, 1, 1, 15, 0, 1, 1, 0, 0x70a3d7}, + { 74250000, 74250000, 1, 99, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 74250000, 92812500, 4, 495, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 75000000, 75000000, 1, 50, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + { 75000000, 93750000, 1, 125, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 78750000, 78750000, 1, 105, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 78750000, 98437500, 1, 164, 3, 1, 1, 25, 0, 1, 1, 0, 0x0}, + { 79500000, 79500000, 1, 53, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + { 79500000, 99375000, 1, 199, 2, 2, 2, 15, 0, 2, 2, 0, 0x0}, + { 83500000, 83500000, 2, 167, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + { 83500000, 104375000, 1, 104, 2, 1, 1, 15, 0, 1, 1, 0, 0x600000}, + { 85500000, 85500000, 1, 57, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + { 85500000, 106875000, 1, 178, 3, 1, 1, 25, 0, 1, 1, 0, 0x0}, + { 85750000, 85750000, 3, 343, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 85750000, 107187500, 1, 143, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + { 88750000, 88750000, 3, 355, 0, 3, 3, 1, 2, 3, 4, 0, 0x0}, + { 88750000, 110937500, 1, 110, 2, 1, 1, 15, 0, 1, 1, 0, 0xf00000}, + { 94500000, 94500000, 1, 63, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + { 94500000, 118125000, 1, 197, 3, 1, 1, 25, 0, 1, 1, 0, 0x0}, + {101000000, 101000000, 1, 101, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + {101000000, 126250000, 1, 42, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {102250000, 102250000, 4, 409, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + {102250000, 127812500, 1, 128, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + {106500000, 106500000, 1, 71, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {106500000, 133125000, 1, 133, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + {108000000, 108000000, 1, 36, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {108000000, 135000000, 1, 45, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {115500000, 115500000, 1, 77, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {115500000, 144375000, 1, 48, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {117500000, 117500000, 2, 235, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + {117500000, 146875000, 1, 49, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {119000000, 119000000, 1, 119, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + {119000000, 148750000, 3, 148, 0, 1, 1, 1, 3, 1, 1, 0, 0xc00000}, + {121750000, 121750000, 4, 487, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + {121750000, 152187500, 1, 203, 0, 3, 3, 1, 3, 3, 4, 0, 0x0}, + {122500000, 122500000, 2, 245, 2, 1, 1, 1, 1, 3, 4, 0, 0x0}, + {122500000, 153125000, 1, 51, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {135000000, 135000000, 1, 45, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {135000000, 168750000, 1, 169, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + {136750000, 136750000, 1, 68, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000}, + {136750000, 170937500, 1, 113, 0, 2, 2, 1, 3, 2, 2, 0, 0xf5554f}, + {140250000, 140250000, 2, 187, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {140250000, 175312500, 1, 117, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {146250000, 146250000, 2, 195, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {146250000, 182812500, 1, 61, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {148250000, 148250000, 3, 222, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000}, + {148250000, 185312500, 1, 123, 0, 2, 2, 1, 3, 2, 2, 0, 0x8aaab0}, + {148352000, 148352000, 2, 148, 2, 0, 0, 1, 1, 2, 2, 0, 0x5a1cac}, + {148352000, 185440000, 3, 185, 0, 1, 1, 1, 3, 1, 1, 0, 0x70a3d7}, + {148500000, 148500000, 1, 99, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {154000000, 154000000, 1, 77, 2, 0, 0, 1, 1, 2, 2, 0, 0x0}, + {154000000, 192500000, 1, 64, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {156000000, 156000000, 1, 52, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {156000000, 195000000, 1, 65, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {156750000, 156750000, 2, 209, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {156750000, 195937500, 1, 196, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + {157000000, 157000000, 2, 157, 2, 0, 0, 1, 1, 2, 2, 0, 0x0}, + {157000000, 196250000, 1, 131, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {157500000, 157500000, 1, 105, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {157500000, 196875000, 1, 197, 2, 1, 1, 15, 0, 1, 1, 0, 0x0}, + {162000000, 162000000, 1, 54, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {162000000, 202500000, 2, 135, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {175500000, 175500000, 1, 117, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {175500000, 219375000, 1, 73, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {179500000, 179500000, 3, 359, 0, 2, 2, 1, 0, 3, 4, 0, 0x0}, + {179500000, 224375000, 1, 75, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {182750000, 182750000, 1, 91, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000}, + {182750000, 228437500, 1, 152, 0, 2, 2, 1, 3, 2, 2, 0, 0x4aaab0}, + {182750000, 228437500, 1, 152, 0, 2, 2, 1, 3, 2, 2, 0, 0x4aaab0}, + {187000000, 187000000, 2, 187, 2, 0, 0, 1, 1, 2, 2, 0, 0x0}, + {187000000, 233750000, 1, 39, 0, 0, 0, 1, 3, 0, 0, 1, 0x0}, + {187250000, 187250000, 3, 280, 2, 0, 0, 1, 1, 2, 2, 0, 0xe00000}, + {187250000, 234062500, 1, 156, 0, 2, 2, 1, 3, 2, 2, 0, 0xaaab0}, + {189000000, 189000000, 1, 63, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {189000000, 236250000, 1, 79, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {193250000, 193250000, 3, 289, 2, 0, 0, 1, 1, 2, 2, 0, 0xe00000}, + {193250000, 241562500, 1, 161, 0, 2, 2, 1, 3, 2, 2, 0, 0xaaab0}, + {202500000, 202500000, 2, 135, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {202500000, 253125000, 1, 169, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {204750000, 204750000, 4, 273, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {204750000, 255937500, 1, 171, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {208000000, 208000000, 1, 104, 2, 0, 0, 1, 1, 2, 2, 0, 0x0}, + {208000000, 260000000, 1, 173, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {214750000, 214750000, 1, 107, 2, 0, 0, 1, 1, 2, 2, 0, 0x600000}, + {214750000, 268437500, 1, 178, 0, 2, 2, 1, 3, 2, 2, 0, 0xf5554f}, + {218250000, 218250000, 4, 291, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {218250000, 272812500, 1, 91, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {229500000, 229500000, 2, 153, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {229500000, 286875000, 1, 191, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {234000000, 234000000, 1, 39, 0, 0, 0, 1, 0, 1, 1, 0, 0x0}, + {234000000, 292500000, 1, 195, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {241500000, 241500000, 2, 161, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {241500000, 301875000, 1, 201, 0, 2, 2, 1, 3, 2, 2, 0, 0x0}, + {245250000, 245250000, 4, 327, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {245250000, 306562500, 1, 51, 0, 0, 0, 1, 3, 0, 0, 1, 0x0}, + {245500000, 245500000, 4, 491, 2, 0, 0, 1, 1, 2, 2, 0, 0x0}, + {245500000, 306875000, 1, 51, 0, 0, 0, 1, 3, 0, 0, 1, 0x0}, + {261000000, 261000000, 1, 87, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {261000000, 326250000, 1, 109, 0, 1, 1, 1, 3, 1, 1, 0, 0x0}, + {268250000, 268250000, 9, 402, 0, 0, 0, 1, 0, 1, 1, 0, 0x600000}, + {268250000, 335312500, 1, 111, 0, 1, 1, 1, 3, 1, 1, 0, 0xc5554f}, + {268500000, 268500000, 2, 179, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {268500000, 335625000, 1, 56, 0, 0, 0, 1, 3, 0, 0, 1, 0x0}, + {281250000, 281250000, 4, 375, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {281250000, 351562500, 1, 117, 0, 3, 1, 1, 3, 1, 1, 0, 0x0}, + {288000000, 288000000, 1, 48, 0, 0, 0, 1, 0, 1, 1, 0, 0x0}, + {288000000, 360000000, 1, 60, 0, 2, 0, 1, 3, 0, 0, 1, 0x0}, + {296703000, 296703000, 1, 49, 0, 0, 0, 1, 0, 1, 1, 0, 0x7353f7}, + {296703000, 370878750, 1, 123, 0, 3, 1, 1, 3, 1, 1, 0, 0xa051eb}, + {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {297000000, 371250000, 4, 495, 0, 3, 1, 1, 3, 1, 1, 0, 0x0}, + {312250000, 312250000, 9, 468, 0, 0, 0, 1, 0, 1, 1, 0, 0x600000}, + {312250000, 390312500, 1, 130, 0, 3, 1, 1, 3, 1, 1, 0, 0x1aaab0}, + {317000000, 317000000, 3, 317, 0, 1, 1, 1, 0, 2, 2, 0, 0x0}, + {317000000, 396250000, 1, 66, 0, 2, 0, 1, 3, 0, 0, 1, 0x0}, + {319750000, 319750000, 3, 159, 0, 0, 0, 1, 0, 1, 1, 0, 0xe00000}, + {319750000, 399687500, 3, 199, 0, 2, 0, 1, 3, 0, 0, 1, 0xd80000}, + {333250000, 333250000, 9, 499, 0, 0, 0, 1, 0, 1, 1, 0, 0xe00000}, + {333250000, 416562500, 1, 138, 0, 3, 1, 1, 3, 1, 1, 0, 0xdaaab0}, + {348500000, 348500000, 9, 522, 0, 2, 0, 1, 0, 1, 1, 0, 0xc00000}, + {348500000, 435625000, 1, 145, 0, 3, 1, 1, 3, 1, 1, 0, 0x35554f}, + {356500000, 356500000, 9, 534, 0, 2, 0, 1, 0, 1, 1, 0, 0xc00000}, + {356500000, 445625000, 1, 148, 0, 3, 1, 1, 3, 1, 1, 0, 0x8aaab0}, + {380500000, 380500000, 9, 570, 0, 2, 0, 1, 0, 1, 1, 0, 0xc00000}, + {380500000, 475625000, 1, 158, 0, 3, 1, 1, 3, 1, 1, 0, 0x8aaab0}, + {443250000, 443250000, 1, 73, 0, 2, 0, 1, 0, 1, 1, 0, 0xe00000}, + {443250000, 554062500, 1, 92, 0, 2, 0, 1, 3, 0, 0, 1, 0x580000}, + {505250000, 505250000, 9, 757, 0, 2, 0, 1, 0, 1, 1, 0, 0xe00000}, + {552750000, 552750000, 3, 276, 0, 2, 0, 1, 0, 1, 1, 0, 0x600000}, + {593407000, 296703500, 3, 296, 0, 1, 1, 1, 0, 1, 1, 0, 0xb41893}, + {593407000, 370879375, 4, 494, 0, 3, 1, 1, 3, 0, 0, 1, 0x817e4a}, + {593407000, 593407000, 3, 296, 0, 2, 0, 1, 0, 1, 1, 0, 0xb41893}, + {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 1, 1, 0, 0x0}, + {594000000, 371250000, 4, 495, 0, 3, 1, 1, 3, 0, 0, 1, 0x0}, + {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0x0}, { /* sentinel */ } }; @@ -485,6 +633,8 @@ static int inno_hdmi_phy_power_on(struct phy *phy) dev_dbg(inno->dev, "Inno HDMI PHY Power On\n"); + inno->plat_data->clk_ops->set_rate(&inno->hw, inno->pixclock, 24000000); + ret = clk_prepare_enable(inno->phyclk); if (ret) return ret; @@ -509,6 +659,8 @@ static int inno_hdmi_phy_power_off(struct phy *phy) clk_disable_unprepare(inno->phyclk); + inno->tmdsclock = 0; + dev_dbg(inno->dev, "Inno HDMI PHY Power Off\n"); return 0; @@ -628,6 +780,9 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw, dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n", __func__, rate, tmdsclock); + if (inno->pixclock == rate && inno->tmdsclock == tmdsclock) + return 0; + cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate); if (IS_ERR(cfg)) return PTR_ERR(cfg); @@ -670,6 +825,7 @@ static int inno_hdmi_phy_rk3228_clk_set_rate(struct clk_hw *hw, } inno->pixclock = rate; + inno->tmdsclock = tmdsclock; return 0; } @@ -714,7 +870,7 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw, { struct inno_hdmi_phy *inno = to_inno_hdmi_phy(hw); unsigned long frac; - u8 nd, no_a, no_b, no_c, no_d; + u8 nd, no_a, no_b, no_d; u64 vco; u16 nf; @@ -737,18 +893,17 @@ unsigned long inno_hdmi_phy_rk3328_clk_recalc_rate(struct clk_hw *hw, no_b = inno_read(inno, 0xa5) & RK3328_PRE_PLL_PCLK_DIV_B_MASK; no_b >>= RK3328_PRE_PLL_PCLK_DIV_B_SHIFT; no_b += 2; - no_c = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_C_MASK; - no_c >>= RK3328_PRE_PLL_PCLK_DIV_C_SHIFT; - no_c = 1 << no_c; no_d = inno_read(inno, 0xa6) & RK3328_PRE_PLL_PCLK_DIV_D_MASK; do_div(vco, (nd * (no_a == 1 ? no_b : no_a) * no_d * 2)); } - inno->pixclock = vco; - dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock); + inno->pixclock = DIV_ROUND_CLOSEST((unsigned long)vco, 1000) * 1000; - return vco; + dev_dbg(inno->dev, "%s rate %lu vco %llu\n", + __func__, inno->pixclock, vco); + + return inno->pixclock; } static long inno_hdmi_phy_rk3328_clk_round_rate(struct clk_hw *hw, @@ -782,6 +937,9 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw, dev_dbg(inno->dev, "%s rate %lu tmdsclk %lu\n", __func__, rate, tmdsclock); + if (inno->pixclock == rate && inno->tmdsclock == tmdsclock) + return 0; + cfg = inno_hdmi_phy_get_pre_pll_cfg(inno, rate); if (IS_ERR(cfg)) return PTR_ERR(cfg); @@ -790,8 +948,8 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw, RK3328_PRE_PLL_POWER_DOWN); /* Configure pre-pll */ - inno_update_bits(inno, 0xa0, RK3228_PCLK_VCO_DIV_5_MASK, - RK3228_PCLK_VCO_DIV_5(cfg->vco_div_5_en)); + inno_update_bits(inno, 0xa0, RK3328_PCLK_VCO_DIV_5_MASK, + RK3328_PCLK_VCO_DIV_5(cfg->vco_div_5_en)); inno_write(inno, 0xa1, RK3328_PRE_PLL_PRE_DIV(cfg->prediv)); val = RK3328_SPREAD_SPECTRUM_MOD_DISABLE; @@ -821,6 +979,7 @@ static int inno_hdmi_phy_rk3328_clk_set_rate(struct clk_hw *hw, } inno->pixclock = rate; + inno->tmdsclock = tmdsclock; return 0; } @@ -1021,9 +1180,10 @@ inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno, inno_write(inno, 0xac, RK3328_POST_PLL_FB_DIV_7_0(cfg->fbdiv)); if (cfg->postdiv == 1) { - inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS); inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) | RK3328_POST_PLL_PRE_DIV(cfg->prediv)); + inno_write(inno, 0xaa, RK3328_POST_PLL_REFCLK_SEL_TMDS | + RK3328_POST_PLL_POWER_DOWN); } else { v = (cfg->postdiv / 2) - 1; v &= RK3328_POST_PLL_POST_DIV_MASK; @@ -1031,7 +1191,8 @@ inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno, inno_write(inno, 0xab, RK3328_POST_PLL_FB_DIV_8(cfg->fbdiv) | RK3328_POST_PLL_PRE_DIV(cfg->prediv)); inno_write(inno, 0xaa, RK3328_POST_PLL_POST_DIV_ENABLE | - RK3328_POST_PLL_REFCLK_SEL_TMDS); + RK3328_POST_PLL_REFCLK_SEL_TMDS | + RK3328_POST_PLL_POWER_DOWN); } for (v = 0; v < 14; v++) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index a0bc10aa79618350caf846a2bbce0cb1ea5d0039..b982c3f0d4b56bf58e3d762b077efa8e491c06d4 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #define SCHEDULE_DELAY (60 * HZ) #define OTG_SCHEDULE_DELAY (2 * HZ) +struct rockchip_usb2phy; + enum rockchip_usb2phy_port_id { USB2PHY_PORT_OTG, USB2PHY_PORT_HOST, @@ -116,6 +119,12 @@ struct rockchip_chg_det_reg { * @bvalid_det_en: vbus valid rise detection enable register. * @bvalid_det_st: vbus valid rise detection status register. * @bvalid_det_clr: vbus valid rise detection clear register. + * @disfall_en: host disconnect fall edge detection enable. + * @disfall_st: host disconnect fall edge detection state. + * @disfall_clr: host disconnect fall edge detection clear. + * @disrise_en: host disconnect rise edge detection enable. + * @disrise_st: host disconnect rise edge detection state. + * @disrise_clr: host disconnect rise edge detection clear. * @id_det_en: id detection enable register. * @id_det_st: id detection state register. * @id_det_clr: id detection clear register. @@ -133,6 +142,12 @@ struct rockchip_usb2phy_port_cfg { struct usb2phy_reg bvalid_det_en; struct usb2phy_reg bvalid_det_st; struct usb2phy_reg bvalid_det_clr; + struct usb2phy_reg disfall_en; + struct usb2phy_reg disfall_st; + struct usb2phy_reg disfall_clr; + struct usb2phy_reg disrise_en; + struct usb2phy_reg disrise_st; + struct usb2phy_reg disrise_clr; struct usb2phy_reg id_det_en; struct usb2phy_reg id_det_st; struct usb2phy_reg id_det_clr; @@ -150,6 +165,7 @@ struct rockchip_usb2phy_port_cfg { * struct rockchip_usb2phy_cfg - usb-phy configuration. * @reg: the address offset of grf for usb-phy config. * @num_ports: specify how many ports that the phy has. + * @phy_tuning: phy default parameters tuning. * @clkout_ctl: keep on/turn off output clk of phy. * @port_cfgs: usb-phy port configurations. * @chg_det: charger detection registers. @@ -157,6 +173,7 @@ struct rockchip_usb2phy_port_cfg { struct rockchip_usb2phy_cfg { unsigned int reg; unsigned int num_ports; + int (*phy_tuning)(struct rockchip_usb2phy *rphy); struct usb2phy_reg clkout_ctl; const struct rockchip_usb2phy_port_cfg port_cfgs[USB2PHY_NUM_PORTS]; const struct rockchip_chg_det_reg chg_det; @@ -168,6 +185,7 @@ struct rockchip_usb2phy_cfg { * @port_id: flag for otg port or host port. * @suspended: phy suspended flag. * @vbus_attached: otg device vbus status. + * @host_disconnect: usb host disconnect status. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. * @id_irq: IRQ number assigned for ID pin detection. * @ls_irq: IRQ number assigned for linestate detection. @@ -187,6 +205,7 @@ struct rockchip_usb2phy_port { unsigned int port_id; bool suspended; bool vbus_attached; + bool host_disconnect; int bvalid_irq; int id_irq; int ls_irq; @@ -209,6 +228,7 @@ struct rockchip_usb2phy_port { * @clk: clock struct of phy input clk. * @clk480m: clock struct of phy output clk. * @clk480m_hw: clock struct of phy output clk management. + * @phy_reset: phy reset control. * @chg_state: states involved in USB charger detection. * @chg_type: USB charger types. * @dcd_retries: The retry count used to track Data contact @@ -225,6 +245,7 @@ struct rockchip_usb2phy { struct clk *clk; struct clk *clk480m; struct clk_hw clk480m_hw; + struct reset_control *phy_reset; enum usb_chg_state chg_state; enum power_supply_type chg_type; u8 dcd_retries; @@ -266,6 +287,25 @@ static inline bool property_enabled(struct regmap *base, return tmp != reg->disable; } +static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy) +{ + int ret; + + ret = reset_control_assert(rphy->phy_reset); + if (ret) + return ret; + + udelay(10); + + ret = reset_control_deassert(rphy->phy_reset); + if (ret) + return ret; + + usleep_range(100, 200); + + return 0; +} + static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw) { struct rockchip_usb2phy *rphy = @@ -405,6 +445,27 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy) return 0; } +static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + bool en) +{ + int ret; + + ret = property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true); + if (ret) + return ret; + + ret = property_enable(rphy->grf, &rport->port_cfg->disfall_en, en); + if (ret) + return ret; + + ret = property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true); + if (ret) + return ret; + + return property_enable(rphy->grf, &rport->port_cfg->disrise_en, en); +} + static int rockchip_usb2phy_init(struct phy *phy) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); @@ -449,6 +510,15 @@ static int rockchip_usb2phy_init(struct phy *phy) dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode); } } else if (rport->port_id == USB2PHY_PORT_HOST) { + if (rport->port_cfg->disfall_en.offset) { + rport->host_disconnect = true; + ret = rockchip_usb2phy_enable_host_disc_irq(rphy, rport, true); + if (ret) { + dev_err(rphy->dev, "failed to enable disconnect irq\n"); + goto out; + } + } + /* clear linestate and enable linestate detect irq */ ret = property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true); @@ -490,6 +560,18 @@ static int rockchip_usb2phy_power_on(struct phy *phy) return ret; } + /* + * For rk3588, it needs to reset phy when exit from + * suspend mode with common_on_n 1'b1(aka REFCLK_LOGIC, + * Bias, and PLL blocks are powered down) for lower + * power consumption. If you don't want to reset phy, + * please keep the common_on_n 1'b0 to set these blocks + * remain powered. + */ + ret = rockchip_usb2phy_reset(rphy); + if (ret) + return ret; + /* waiting for the utmi_clk to become stable */ usleep_range(1500, 2000); @@ -810,9 +892,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work) struct rockchip_usb2phy_port *rport = container_of(work, struct rockchip_usb2phy_port, sm_work.work); struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); - unsigned int sh = rport->port_cfg->utmi_hstdet.bitend - - rport->port_cfg->utmi_hstdet.bitstart + 1; - unsigned int ul, uhd, state; + unsigned int sh, ul, uhd, state; unsigned int ul_mask, uhd_mask; int ret; @@ -822,18 +902,26 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work) if (ret < 0) goto next_schedule; - ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd); - if (ret < 0) - goto next_schedule; - - uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, - rport->port_cfg->utmi_hstdet.bitstart); ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend, rport->port_cfg->utmi_ls.bitstart); - /* stitch on utmi_ls and utmi_hstdet as phy state */ - state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | - (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); + if (rport->port_cfg->utmi_hstdet.offset) { + ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd); + if (ret < 0) + goto next_schedule; + + uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend, + rport->port_cfg->utmi_hstdet.bitstart); + + sh = rport->port_cfg->utmi_hstdet.bitend - + rport->port_cfg->utmi_hstdet.bitstart + 1; + /* stitch on utmi_ls and utmi_hstdet as phy state */ + state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) | + (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh); + } else { + state = ((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << 1 | + rport->host_disconnect; + } switch (state) { case PHY_STATE_HS_ONLINE: @@ -966,6 +1054,31 @@ static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) return ret; } +static irqreturn_t rockchip_usb2phy_host_disc_irq(int irq, void *data) +{ + struct rockchip_usb2phy_port *rport = data; + struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + + if (!property_enabled(rphy->grf, &rport->port_cfg->disfall_st) && + !property_enabled(rphy->grf, &rport->port_cfg->disrise_st)) + return IRQ_NONE; + + mutex_lock(&rport->mutex); + + /* clear disconnect fall or rise detect irq pending status */ + if (property_enabled(rphy->grf, &rport->port_cfg->disfall_st)) { + property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true); + rport->host_disconnect = false; + } else if (property_enabled(rphy->grf, &rport->port_cfg->disrise_st)) { + property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true); + rport->host_disconnect = true; + } + + mutex_unlock(&rport->mutex); + + return IRQ_HANDLED; +} + static irqreturn_t rockchip_usb2phy_irq(int irq, void *data) { struct rockchip_usb2phy *rphy = data; @@ -978,6 +1091,10 @@ static irqreturn_t rockchip_usb2phy_irq(int irq, void *data) if (!rport->phy) continue; + if (rport->port_id == USB2PHY_PORT_HOST && + rport->port_cfg->disfall_en.offset) + ret |= rockchip_usb2phy_host_disc_irq(irq, rport); + switch (rport->port_id) { case USB2PHY_PORT_OTG: if (rport->mode != USB_DR_MODE_HOST && @@ -1188,7 +1305,6 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) struct phy_provider *provider; struct rockchip_usb2phy *rphy; const struct rockchip_usb2phy_cfg *phy_cfgs; - const struct of_device_id *match; unsigned int reg; int index, ret; @@ -1196,12 +1312,6 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) if (!rphy) return -ENOMEM; - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { - dev_err(dev, "phy configs are not assigned!\n"); - return -EINVAL; - } - if (!dev->parent || !dev->parent->of_node) { rphy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf"); if (IS_ERR(rphy->grf)) { @@ -1233,7 +1343,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) } /* support address_cells=2 */ - if (reg == 0) { + if (of_property_count_u32_elems(np, "reg") > 2 && reg == 0) { if (of_property_read_u32_index(np, "reg", 1, ®)) { dev_err(dev, "the reg property is not assigned in %pOFn node\n", np); @@ -1242,45 +1352,55 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) } rphy->dev = dev; - phy_cfgs = match->data; + phy_cfgs = device_get_match_data(dev); rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; rphy->irq = platform_get_irq_optional(pdev, 0); platform_set_drvdata(pdev, rphy); + if (!phy_cfgs) + return dev_err_probe(dev, -EINVAL, "phy configs are not assigned!\n"); + ret = rockchip_usb2phy_extcon_register(rphy); if (ret) return ret; /* find out a proper config which can be matched with dt. */ index = 0; - while (phy_cfgs[index].reg) { + do { if (phy_cfgs[index].reg == reg) { rphy->phy_cfg = &phy_cfgs[index]; break; } ++index; - } + } while (phy_cfgs[index].reg); if (!rphy->phy_cfg) { - dev_err(dev, "no phy-config can be matched with %pOFn node\n", - np); + dev_err(dev, "could not find phy config for reg=0x%08x\n", reg); return -EINVAL; } - rphy->clk = of_clk_get_by_name(np, "phyclk"); - if (!IS_ERR(rphy->clk)) { - clk_prepare_enable(rphy->clk); - } else { - dev_info(&pdev->dev, "no phyclk specified\n"); - rphy->clk = NULL; + rphy->phy_reset = devm_reset_control_get_optional(dev, "phy"); + if (IS_ERR(rphy->phy_reset)) + return PTR_ERR(rphy->phy_reset); + + rphy->clk = devm_clk_get_optional_enabled(dev, "phyclk"); + if (IS_ERR(rphy->clk)) { + return dev_err_probe(&pdev->dev, PTR_ERR(rphy->clk), + "failed to get phyclk\n"); } ret = rockchip_usb2phy_clk480m_register(rphy); if (ret) { dev_err(dev, "failed to register 480m output clock\n"); - goto disable_clks; + return ret; + } + + if (rphy->phy_cfg->phy_tuning) { + ret = rphy->phy_cfg->phy_tuning(rphy); + if (ret) + return ret; } index = 0; @@ -1343,11 +1463,55 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) put_child: of_node_put(child_np); -disable_clks: - if (rphy->clk) { - clk_disable_unprepare(rphy->clk); - clk_put(rphy->clk); + return ret; +} + +static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy) +{ + int ret; + bool usb3otg = false; + /* + * utmi_termselect = 1'b1 (en FS terminations) + * utmi_xcvrselect = 2'b01 (FS transceiver) + */ + int suspend_cfg = 0x14; + + if (rphy->phy_cfg->reg == 0x0000 || rphy->phy_cfg->reg == 0x4000) { + /* USB2 config for USB3_0 and USB3_1 */ + suspend_cfg |= 0x01; /* utmi_opmode = 2'b01 (no-driving) */ + usb3otg = true; + } else if (rphy->phy_cfg->reg == 0x8000 || rphy->phy_cfg->reg == 0xc000) { + /* USB2 config for USB2_0 and USB2_1 */ + suspend_cfg |= 0x00; /* utmi_opmode = 2'b00 (normal) */ + } else { + return -EINVAL; } + + /* Deassert SIDDQ to power on analog block */ + ret = regmap_write(rphy->grf, 0x0008, GENMASK(29, 29) | 0x0000); + if (ret) + return ret; + + /* Do reset after exit IDDQ mode */ + ret = rockchip_usb2phy_reset(rphy); + if (ret) + return ret; + + /* suspend configuration */ + ret |= regmap_write(rphy->grf, 0x000c, GENMASK(20, 16) | suspend_cfg); + + /* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */ + ret |= regmap_write(rphy->grf, 0x0004, GENMASK(27, 24) | 0x0900); + + /* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */ + ret |= regmap_write(rphy->grf, 0x0008, GENMASK(20, 19) | 0x0010); + + if (!usb3otg) + return ret; + + /* Pullup iddig pin for USB3_0 OTG mode */ + ret |= regmap_write(rphy->grf, 0x0010, GENMASK(17, 16) | 0x0003); + return ret; } @@ -1664,6 +1828,126 @@ static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = { + { + .reg = 0x0000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x000c, 11, 11, 0, 1 }, + .bvalid_det_en = { 0x0080, 1, 1, 0, 1 }, + .bvalid_det_st = { 0x0084, 1, 1, 0, 1 }, + .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_avalid = { 0x00c0, 7, 7, 0, 1 }, + .utmi_bvalid = { 0x00c0, 6, 6, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + .chg_det = { + .cp_det = { 0x00c0, 0, 0, 0, 1 }, + .dcp_det = { 0x00c0, 0, 0, 0, 1 }, + .dp_det = { 0x00c0, 1, 1, 1, 0 }, + .idm_sink_en = { 0x0008, 5, 5, 1, 0 }, + .idp_sink_en = { 0x0008, 5, 5, 0, 1 }, + .idp_src_en = { 0x0008, 14, 14, 0, 1 }, + .rdm_pdwn_en = { 0x0008, 14, 14, 0, 1 }, + .vdm_src_en = { 0x0008, 7, 6, 0, 3 }, + .vdp_src_en = { 0x0008, 7, 6, 0, 3 }, + }, + }, + { + .reg = 0x4000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x000c, 11, 11, 0, 1 }, + .bvalid_det_en = { 0x0080, 1, 1, 0, 1 }, + .bvalid_det_st = { 0x0084, 1, 1, 0, 1 }, + .bvalid_det_clr = { 0x0088, 1, 1, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_avalid = { 0x00c0, 7, 7, 0, 1 }, + .utmi_bvalid = { 0x00c0, 6, 6, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + .chg_det = { + .cp_det = { 0x00c0, 0, 0, 0, 1 }, + .dcp_det = { 0x00c0, 0, 0, 0, 1 }, + .dp_det = { 0x00c0, 1, 1, 1, 0 }, + .idm_sink_en = { 0x0008, 5, 5, 1, 0 }, + .idp_sink_en = { 0x0008, 5, 5, 0, 1 }, + .idp_src_en = { 0x0008, 14, 14, 0, 1 }, + .rdm_pdwn_en = { 0x0008, 14, 14, 0, 1 }, + .vdm_src_en = { 0x0008, 7, 6, 0, 3 }, + .vdp_src_en = { 0x0008, 7, 6, 0, 3 }, + }, + }, + { + .reg = 0x8000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0008, 2, 2, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + }, + { + .reg = 0xc000, + .num_ports = 1, + .phy_tuning = rk3588_usb2phy_tuning, + .clkout_ctl = { 0x0000, 0, 0, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0008, 2, 2, 0, 1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .disfall_en = { 0x0080, 6, 6, 0, 1 }, + .disfall_st = { 0x0084, 6, 6, 0, 1 }, + .disfall_clr = { 0x0088, 6, 6, 0, 1 }, + .disrise_en = { 0x0080, 5, 5, 0, 1 }, + .disrise_st = { 0x0084, 5, 5, 0, 1 }, + .disrise_clr = { 0x0088, 5, 5, 0, 1 }, + .utmi_ls = { 0x00c0, 10, 9, 0, 1 }, + } + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { { .reg = 0x100, @@ -1714,6 +1998,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, + { .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs }, { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, {} }; diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index 7b8b001e4f9e3759385b00b7f1eaa70e261a6f62..5de5e2e97ffa0f17d1997d49661f3699ef5fe3c1 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -8,8 +8,9 @@ #include #include #include -#include +#include #include +#include #include #include #include diff --git a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c index 1d355b32ba5599394c090c8939592f49c0408a82..121e5961ce11472ff1576dc83686072eb2f115e0 100644 --- a/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c +++ b/drivers/phy/rockchip/phy-rockchip-snps-pcie3.c @@ -12,9 +12,10 @@ #include #include #include -#include +#include #include #include +#include #include #include diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 8b1667be4915d472c46812908eb5cee3b9c68365..4efcb78b0ab18e656330ab6383601252a1035377 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -1116,8 +1116,7 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tcphy->base = devm_ioremap_resource(dev, res); + tcphy->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(tcphy->base)) return PTR_ERR(tcphy->base); diff --git a/drivers/phy/samsung/phy-exynos-dp-video.c b/drivers/phy/samsung/phy-exynos-dp-video.c index 6069fedbd8f3bb0c1b477c787f60d4ddd0685acc..a636dee075858c70b577cf993fed8a6bee81326b 100644 --- a/drivers/phy/samsung/phy-exynos-dp-video.c +++ b/drivers/phy/samsung/phy-exynos-dp-video.c @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/phy/samsung/phy-exynos-mipi-video.c b/drivers/phy/samsung/phy-exynos-mipi-video.c index a7f67857e5b2538efe39ad7c8837a3ca1ec840c7..592d8067e848e280bb6185542c26123059bdbd17 100644 --- a/drivers/phy/samsung/phy-exynos-mipi-video.c +++ b/drivers/phy/samsung/phy-exynos-mipi-video.c @@ -11,9 +11,8 @@ #include #include #include -#include -#include #include +#include #include #include #include diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index ee0848fe8432473a63ff195e1825361bfb0f5cb9..3f310b28bfff799cbe9d687bc6c1af3cc3941159 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include #include @@ -32,6 +30,7 @@ #define EXYNOS5_FSEL_19MHZ2 0x3 #define EXYNOS5_FSEL_20MHZ 0x4 #define EXYNOS5_FSEL_24MHZ 0x5 +#define EXYNOS5_FSEL_26MHZ 0x82 #define EXYNOS5_FSEL_50MHZ 0x7 /* Exynos5: USB 3.0 DRD PHY registers */ @@ -146,6 +145,34 @@ #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4) #define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4) +/* Exynos850: USB DRD PHY registers */ +#define EXYNOS850_DRD_LINKCTRL 0x04 +#define LINKCTRL_BUS_FILTER_BYPASS(_x) ((_x) << 4) +#define LINKCTRL_FORCE_QACT BIT(8) + +#define EXYNOS850_DRD_CLKRST 0x20 +#define CLKRST_LINK_SW_RST BIT(0) +#define CLKRST_PORT_RST BIT(1) +#define CLKRST_PHY_SW_RST BIT(3) + +#define EXYNOS850_DRD_UTMI 0x50 +#define UTMI_FORCE_SLEEP BIT(0) +#define UTMI_FORCE_SUSPEND BIT(1) +#define UTMI_DM_PULLDOWN BIT(2) +#define UTMI_DP_PULLDOWN BIT(3) +#define UTMI_FORCE_BVALID BIT(4) +#define UTMI_FORCE_VBUSVALID BIT(5) + +#define EXYNOS850_DRD_HSP 0x54 +#define HSP_COMMONONN BIT(8) +#define HSP_EN_UTMISUSPEND BIT(9) +#define HSP_VBUSVLDEXT BIT(12) +#define HSP_VBUSVLDEXTSEL BIT(13) +#define HSP_FSV_OUT_EN BIT(24) + +#define EXYNOS850_DRD_HSP_TEST 0x5c +#define HSP_TEST_SIDDQ BIT(24) + #define KHZ 1000 #define MHZ (KHZ * KHZ) @@ -167,6 +194,7 @@ struct exynos5_usbdrd_phy_config { struct exynos5_usbdrd_phy_drvdata { const struct exynos5_usbdrd_phy_config *phy_cfg; + const struct phy_ops *phy_ops; u32 pmu_offset_usbdrd0_phy; u32 pmu_offset_usbdrd1_phy; bool has_common_clk_gate; @@ -245,6 +273,9 @@ static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) case 24 * MHZ: *reg = EXYNOS5_FSEL_24MHZ; break; + case 26 * MHZ: + *reg = EXYNOS5_FSEL_26MHZ; + break; case 50 * MHZ: *reg = EXYNOS5_FSEL_50MHZ; break; @@ -713,6 +744,129 @@ static const struct phy_ops exynos5_usbdrd_phy_ops = { .owner = THIS_MODULE, }; +static void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) +{ + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + + /* + * Disable HWACG (hardware auto clock gating control). This will force + * QACTIVE signal in Q-Channel interface to HIGH level, to make sure + * the PHY clock is not gated by the hardware. + */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= LINKCTRL_FORCE_QACT; + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + /* Start PHY Reset (POR=high) */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg |= CLKRST_PHY_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + + /* Enable UTMI+ */ + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN | + UTMI_DM_PULLDOWN); + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + /* Set PHY clock and control HS PHY */ + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN; + writel(reg, regs_base + EXYNOS850_DRD_HSP); + + /* Set VBUS Valid and D+ pull-up control by VBUS pad usage */ + reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); + reg |= LINKCTRL_BUS_FILTER_BYPASS(0xf); + writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); + + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID; + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL; + writel(reg, regs_base + EXYNOS850_DRD_HSP); + + /* Power up PHY analog blocks */ + reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); + reg &= ~HSP_TEST_SIDDQ; + writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); + + /* Finish PHY reset (POR=low) */ + udelay(10); /* required before doing POR=low */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST); + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + udelay(75); /* required after POR=low for guaranteed PHY clock */ + + /* Disable single ended signal out */ + reg = readl(regs_base + EXYNOS850_DRD_HSP); + reg &= ~HSP_FSV_OUT_EN; + writel(reg, regs_base + EXYNOS850_DRD_HSP); +} + +static int exynos850_usbdrd_phy_init(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + int ret; + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + /* UTMI or PIPE3 specific init */ + inst->phy_cfg->phy_init(phy_drd); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static int exynos850_usbdrd_phy_exit(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + void __iomem *regs_base = phy_drd->reg_phy; + u32 reg; + int ret; + + ret = clk_prepare_enable(phy_drd->clk); + if (ret) + return ret; + + /* Set PHY clock and control HS PHY */ + reg = readl(regs_base + EXYNOS850_DRD_UTMI); + reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN); + reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP; + writel(reg, regs_base + EXYNOS850_DRD_UTMI); + + /* Power down PHY analog blocks */ + reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); + reg |= HSP_TEST_SIDDQ; + writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); + + /* Link reset */ + reg = readl(regs_base + EXYNOS850_DRD_CLKRST); + reg |= CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + udelay(10); /* required before doing POR=low */ + reg &= ~CLKRST_LINK_SW_RST; + writel(reg, regs_base + EXYNOS850_DRD_CLKRST); + + clk_disable_unprepare(phy_drd->clk); + + return 0; +} + +static const struct phy_ops exynos850_usbdrd_phy_ops = { + .init = exynos850_usbdrd_phy_init, + .exit = exynos850_usbdrd_phy_exit, + .power_on = exynos5_usbdrd_phy_power_on, + .power_off = exynos5_usbdrd_phy_power_off, + .owner = THIS_MODULE, +}; + static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) { unsigned long ref_rate; @@ -779,8 +933,17 @@ static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { }, }; +static const struct exynos5_usbdrd_phy_config phy_cfg_exynos850[] = { + { + .id = EXYNOS5_DRDPHY_UTMI, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos850_usbdrd_utmi_init, + }, +}; + static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, + .phy_ops = &exynos5_usbdrd_phy_ops, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, .has_common_clk_gate = true, @@ -788,12 +951,14 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, + .phy_ops = &exynos5_usbdrd_phy_ops, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .has_common_clk_gate = true, }; static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, + .phy_ops = &exynos5_usbdrd_phy_ops, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, .has_common_clk_gate = false, @@ -801,10 +966,18 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { .phy_cfg = phy_cfg_exynos5, + .phy_ops = &exynos5_usbdrd_phy_ops, .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, .has_common_clk_gate = false, }; +static const struct exynos5_usbdrd_phy_drvdata exynos850_usbdrd_phy = { + .phy_cfg = phy_cfg_exynos850, + .phy_ops = &exynos850_usbdrd_phy_ops, + .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, + .has_common_clk_gate = true, +}; + static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { { .compatible = "samsung,exynos5250-usbdrd-phy", @@ -818,6 +991,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { }, { .compatible = "samsung,exynos7-usbdrd-phy", .data = &exynos7_usbdrd_phy + }, { + .compatible = "samsung,exynos850-usbdrd-phy", + .data = &exynos850_usbdrd_phy }, { }, }; @@ -908,8 +1084,8 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev) dev_vdbg(dev, "Creating usbdrd_phy phy\n"); for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { - struct phy *phy = devm_phy_create(dev, NULL, - &exynos5_usbdrd_phy_ops); + struct phy *phy = devm_phy_create(dev, NULL, drv_data->phy_ops); + if (IS_ERR(phy)) { dev_err(dev, "Failed to create usbdrd_phy phy\n"); return PTR_ERR(phy); diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c index ec2befabeea6b5214226775303fc3149e3d12808..68a174eca0ba8a01ae22199c7939b7fa26f53794 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.c +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c index ebca296ef12383b2d47a53aa8fbfe9238fee4f83..c19173492b794fde169178c306f866fd251f6bc6 100644 --- a/drivers/phy/socionext/phy-uniphier-pcie.c +++ b/drivers/phy/socionext/phy-uniphier-pcie.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/st/phy-spear1310-miphy.c b/drivers/phy/st/phy-spear1310-miphy.c index 292413db7da477cdc0008c3a0b9ec7aef0d169d8..35a9831b51610cad2b0b3fe22b0c2d8aba58bf63 100644 --- a/drivers/phy/st/phy-spear1310-miphy.c +++ b/drivers/phy/st/phy-spear1310-miphy.c @@ -13,8 +13,9 @@ #include #include #include -#include +#include #include +#include #include /* SPEAr1310 Registers */ diff --git a/drivers/phy/st/phy-spear1340-miphy.c b/drivers/phy/st/phy-spear1340-miphy.c index c1d9ffa5a311e59b2da6a2e4661f89e52e2308b3..34a1cf21015f59d89de002ae154e01173b25f6dc 100644 --- a/drivers/phy/st/phy-spear1340-miphy.c +++ b/drivers/phy/st/phy-spear1340-miphy.c @@ -13,8 +13,9 @@ #include #include #include -#include +#include #include +#include #include /* SPEAr1340 Registers */ diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 0a8552628cbd29a038768379444828af8e54341e..d5e7e44000b56ec0fe16703a52e47b05926ac866 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -12,8 +12,9 @@ #include #include #include -#include +#include #include +#include #include #include diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..9508e21430110ad96d3e7158230a010112f7d64b --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for StarFive platforms +# + +if ARCH_STARFIVE || COMPILE_TEST + +config PHY_STARFIVE_JH7110_DPHY_RX + tristate "StarFive JH7110 D-PHY RX support" + depends on HAS_IOMEM + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Choose this option if you have a StarFive D-PHY in your + system. If M is selected, the module will be called + phy-jh7110-dphy-rx.ko. + +config PHY_STARFIVE_JH7110_PCIE + tristate "Starfive JH7110 PCIE 2.0/USB 3.0 PHY support" + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the StarFive PCIe 2.0 PHY, + or used as USB 3.0 PHY. + If M is selected, the module will be called + phy-jh7110-pcie.ko. + +config PHY_STARFIVE_JH7110_USB + tristate "Starfive JH7110 USB 2.0 PHY support" + depends on USB_SUPPORT + select GENERIC_PHY + help + Enable this to support the StarFive USB 2.0 PHY, + used with the Cadence USB controller. + If M is selected, the module will be called + phy-jh7110-usb.ko. + +endif # ARCH_STARFIVE || COMPILE_TEST diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b391018b7c47130da243d358810b8eae0e6410a5 --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_STARFIVE_JH7110_DPHY_RX) += phy-jh7110-dphy-rx.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o diff --git a/drivers/phy/starfive/phy-jh7110-dphy-rx.c b/drivers/phy/starfive/phy-jh7110-dphy-rx.c new file mode 100644 index 0000000000000000000000000000000000000000..037a9e0263cdad3a32b6a85be880c7f6fe43ad10 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-dphy-rx.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 DPHY RX driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Jack Zhu + * Author: Changhuang Liang + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STF_DPHY_APBCFGSAIF_SYSCFG(x) (x) + +#define STF_DPHY_ENABLE_CLK BIT(6) +#define STF_DPHY_ENABLE_CLK1 BIT(7) +#define STF_DPHY_ENABLE_LAN0 BIT(8) +#define STF_DPHY_ENABLE_LAN1 BIT(9) +#define STF_DPHY_ENABLE_LAN2 BIT(10) +#define STF_DPHY_ENABLE_LAN3 BIT(11) +#define STF_DPHY_LANE_SWAP_CLK GENMASK(22, 20) +#define STF_DPHY_LANE_SWAP_CLK1 GENMASK(25, 23) +#define STF_DPHY_LANE_SWAP_LAN0 GENMASK(28, 26) +#define STF_DPHY_LANE_SWAP_LAN1 GENMASK(31, 29) + +#define STF_DPHY_LANE_SWAP_LAN2 GENMASK(2, 0) +#define STF_DPHY_LANE_SWAP_LAN3 GENMASK(5, 3) +#define STF_DPHY_PLL_CLK_SEL GENMASK(21, 12) +#define STF_DPHY_PRECOUNTER_IN_CLK GENMASK(29, 22) + +#define STF_DPHY_PRECOUNTER_IN_CLK1 GENMASK(7, 0) +#define STF_DPHY_PRECOUNTER_IN_LAN0 GENMASK(15, 8) +#define STF_DPHY_PRECOUNTER_IN_LAN1 GENMASK(23, 16) +#define STF_DPHY_PRECOUNTER_IN_LAN2 GENMASK(31, 24) + +#define STF_DPHY_PRECOUNTER_IN_LAN3 GENMASK(7, 0) +#define STF_DPHY_RX_1C2C_SEL BIT(8) + +#define STF_MAP_LANES_NUM 6 + +struct regval { + u32 addr; + u32 val; +}; + +struct stf_dphy_info { + /** + * @maps: + * + * Physical lanes and logic lanes mapping table. + * + * The default order is: + * [clk lane0, data lane 0, data lane 1, data lane 2, date lane 3, clk lane 1] + */ + u8 maps[STF_MAP_LANES_NUM]; +}; + +struct stf_dphy { + struct device *dev; + void __iomem *regs; + struct clk *cfg_clk; + struct clk *ref_clk; + struct clk *tx_clk; + struct reset_control *rstc; + struct regulator *mipi_0p9; + struct phy *phy; + const struct stf_dphy_info *info; +}; + +static int stf_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + const struct stf_dphy_info *info = dphy->info; + + writel(FIELD_PREP(STF_DPHY_ENABLE_CLK, 1) | + FIELD_PREP(STF_DPHY_ENABLE_CLK1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN0, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN2, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN3, 1) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK, info->maps[0]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK1, info->maps[5]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN0, info->maps[1]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN1, info->maps[2]), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(188)); + + writel(FIELD_PREP(STF_DPHY_LANE_SWAP_LAN2, info->maps[3]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN3, info->maps[4]) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK, 8), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(192)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK1, 8) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN0, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN1, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN2, 7), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(196)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN3, 7), + dphy->regs + STF_DPHY_APBCFGSAIF_SYSCFG(200)); + + return 0; +} + +static int stf_dphy_power_on(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + int ret; + + ret = pm_runtime_resume_and_get(dphy->dev); + if (ret < 0) + return ret; + + ret = regulator_enable(dphy->mipi_0p9); + if (ret) { + pm_runtime_put(dphy->dev); + return ret; + } + + clk_set_rate(dphy->cfg_clk, 99000000); + clk_set_rate(dphy->ref_clk, 49500000); + clk_set_rate(dphy->tx_clk, 19800000); + reset_control_deassert(dphy->rstc); + + return 0; +} + +static int stf_dphy_power_off(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + reset_control_assert(dphy->rstc); + + regulator_disable(dphy->mipi_0p9); + + pm_runtime_put_sync(dphy->dev); + + return 0; +} + +static const struct phy_ops stf_dphy_ops = { + .configure = stf_dphy_configure, + .power_on = stf_dphy_power_on, + .power_off = stf_dphy_power_off, +}; + +static int stf_dphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct stf_dphy *dphy; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + + dphy->info = of_device_get_match_data(&pdev->dev); + + dev_set_drvdata(&pdev->dev, dphy); + dphy->dev = &pdev->dev; + + dphy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->cfg_clk = devm_clk_get(&pdev->dev, "cfg"); + if (IS_ERR(dphy->cfg_clk)) + return PTR_ERR(dphy->cfg_clk); + + dphy->ref_clk = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(dphy->ref_clk)) + return PTR_ERR(dphy->ref_clk); + + dphy->tx_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(dphy->tx_clk)) + return PTR_ERR(dphy->tx_clk); + + dphy->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dphy->rstc)) + return PTR_ERR(dphy->rstc); + + dphy->mipi_0p9 = devm_regulator_get(&pdev->dev, "mipi_0p9"); + if (IS_ERR(dphy->mipi_0p9)) + return PTR_ERR(dphy->mipi_0p9); + + dphy->phy = devm_phy_create(&pdev->dev, NULL, &stf_dphy_ops); + if (IS_ERR(dphy->phy)) { + dev_err(&pdev->dev, "Failed to create PHY\n"); + return PTR_ERR(dphy->phy); + } + + pm_runtime_enable(&pdev->dev); + + phy_set_drvdata(dphy->phy, dphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct stf_dphy_info starfive_dphy_info = { + .maps = {4, 0, 1, 2, 3, 5}, +}; + +static const struct of_device_id stf_dphy_dt_ids[] = { + { + .compatible = "starfive,jh7110-dphy-rx", + .data = &starfive_dphy_info, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, stf_dphy_dt_ids); + +static struct platform_driver stf_dphy_driver = { + .probe = stf_dphy_probe, + .driver = { + .name = "starfive-dphy-rx", + .of_match_table = stf_dphy_dt_ids, + }, +}; +module_platform_driver(stf_dphy_driver); + +MODULE_AUTHOR("Jack Zhu "); +MODULE_AUTHOR("Changhuang Liang "); +MODULE_DESCRIPTION("StarFive JH7110 DPHY RX driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 0000000000000000000000000000000000000000..734c8e007727734e7d6db938fc8217808cab6e0b --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIE_KVCO_LEVEL_OFF 0x28 +#define PCIE_USB3_PHY_PLL_CTL_OFF 0x7c +#define PCIE_KVCO_TUNE_SIGNAL_OFF 0x80 +#define PCIE_USB3_PHY_ENABLE BIT(4) +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +#define USB_PDRSTN_SPLIT BIT(17) + +#define PCIE_PHY_MODE BIT(20) +#define PCIE_PHY_MODE_MASK GENMASK(21, 20) +#define PCIE_USB3_BUS_WIDTH_MASK GENMASK(3, 2) +#define PCIE_USB3_BUS_WIDTH BIT(3) +#define PCIE_USB3_RATE_MASK GENMASK(6, 5) +#define PCIE_USB3_RX_STANDBY_MASK BIT(7) +#define PCIE_USB3_PHY_ENABLE BIT(4) + +struct jh7110_pcie_phy { + struct phy *phy; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + void __iomem *regs; + u32 sys_phy_connect; + u32 stg_pcie_mode; + u32 stg_pcie_usb; + enum phy_mode mode; +}; + +static int phy_usb3_mode_set(struct jh7110_pcie_phy *data) +{ + if (!data->stg_syscon || !data->sys_syscon) { + dev_err(&data->phy->dev, "doesn't support usb3 mode\n"); + return -EINVAL; + } + + regmap_update_bits(data->stg_syscon, data->stg_pcie_mode, + PCIE_PHY_MODE_MASK, PCIE_PHY_MODE); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_BUS_WIDTH_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_PHY_ENABLE, PCIE_USB3_PHY_ENABLE); + + /* Connect usb 3.0 phy mode */ + regmap_update_bits(data->sys_syscon, data->sys_phy_connect, + USB_PDRSTN_SPLIT, 0); + + /* Configuare spread-spectrum mode: down-spread-spectrum */ + writel(PCIE_USB3_PHY_ENABLE, data->regs + PCIE_USB3_PHY_PLL_CTL_OFF); + + return 0; +} + +static void phy_pcie_mode_set(struct jh7110_pcie_phy *data) +{ + u32 val; + + /* default is PCIe mode */ + if (!data->stg_syscon || !data->sys_syscon) + return; + + regmap_update_bits(data->stg_syscon, data->stg_pcie_mode, + PCIE_PHY_MODE_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_BUS_WIDTH_MASK, + PCIE_USB3_BUS_WIDTH); + regmap_update_bits(data->stg_syscon, data->stg_pcie_usb, + PCIE_USB3_PHY_ENABLE, 0); + + regmap_update_bits(data->sys_syscon, data->sys_phy_connect, + USB_PDRSTN_SPLIT, 0); + + val = readl(data->regs + PCIE_USB3_PHY_PLL_CTL_OFF); + val &= ~PCIE_USB3_PHY_ENABLE; + writel(val, data->regs + PCIE_USB3_PHY_PLL_CTL_OFF); +} + +static void phy_kvco_gain_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_pcie_phy *phy = phy_get_drvdata(_phy); + int ret; + + if (mode == phy->mode) + return 0; + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + ret = phy_usb3_mode_set(phy); + if (ret) + return ret; + break; + case PHY_MODE_PCIE: + phy_pcie_mode_set(phy); + break; + default: + return -EINVAL; + } + + dev_dbg(&_phy->dev, "Changing phy mode to %d\n", mode); + phy->mode = mode; + + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .set_mode = jh7110_pcie_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_pcie_phy_probe(struct platform_device *pdev) +{ + struct jh7110_pcie_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + u32 args[2]; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_pcie_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->phy), + "Failed to map phy base\n"); + + phy->sys_syscon = + syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,sys-syscon", + 1, args); + + if (!IS_ERR_OR_NULL(phy->sys_syscon)) + phy->sys_phy_connect = args[0]; + else + phy->sys_syscon = NULL; + + phy->stg_syscon = + syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", + 2, args); + + if (!IS_ERR_OR_NULL(phy->stg_syscon)) { + phy->stg_pcie_mode = args[0]; + phy->stg_pcie_usb = args[1]; + } else { + phy->stg_syscon = NULL; + } + + phy_kvco_gain_set(phy); + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id jh7110_pcie_phy_of_match[] = { + { .compatible = "starfive,jh7110-pcie-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_pcie_phy_of_match); + +static struct platform_driver jh7110_pcie_phy_driver = { + .probe = jh7110_pcie_phy_probe, + .driver = { + .of_match_table = jh7110_pcie_phy_of_match, + .name = "jh7110-pcie-phy", + } +}; +module_platform_driver(jh7110_pcie_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/starfive/phy-jh7110-usb.c b/drivers/phy/starfive/phy-jh7110-usb.c new file mode 100644 index 0000000000000000000000000000000000000000..633912f8a05d04e913eecf4ad5e4608dbace6f32 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_125M_CLK_RATE 125000000 +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) + +struct jh7110_usb2_phy { + struct phy *phy; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125m; + enum phy_mode mode; +}; + +static void usb2_set_ls_keepalive(struct jh7110_usb2_phy *phy, bool set) +{ + unsigned int val; + + /* Host mode enable the LS speed keep-alive signal */ + val = readl(phy->regs + USB_LS_KEEPALIVE_OFF); + if (set) + val |= USB_LS_KEEPALIVE_ENABLE; + else + val &= ~USB_LS_KEEPALIVE_ENABLE; + + writel(val, phy->regs + USB_LS_KEEPALIVE_OFF); +} + +static int usb2_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + break; + default: + return -EINVAL; + } + + if (mode != phy->mode) { + dev_dbg(&_phy->dev, "Changing phy to %d\n", mode); + phy->mode = mode; + usb2_set_ls_keepalive(phy, (mode != PHY_MODE_USB_DEVICE)); + } + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + int ret; + + ret = clk_set_rate(phy->usb_125m_clk, USB_125M_CLK_RATE); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->app_125m); + if (ret) + return ret; + + return 0; +} + +static int jh7110_usb2_phy_exit(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + + clk_disable_unprepare(phy->app_125m); + + return 0; +} + +static const struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = usb2_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_usb_phy_probe(struct platform_device *pdev) +{ + struct jh7110_usb2_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_125m_clk), + "Failed to get 125m clock\n"); + + phy->app_125m = devm_clk_get(dev, "app_125m"); + if (IS_ERR(phy->app_125m)) + return dev_err_probe(dev, PTR_ERR(phy->app_125m), + "Failed to get app 125m clock\n"); + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_usb2_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->phy), + "Failed to create phy\n"); + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id jh7110_usb_phy_of_match[] = { + { .compatible = "starfive,jh7110-usb-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_usb_phy_of_match); + +static struct platform_driver jh7110_usb_phy_driver = { + .probe = jh7110_usb_phy_probe, + .driver = { + .of_match_table = jh7110_usb_phy_of_match, + .name = "jh7110-usb-phy", + } +}; +module_platform_driver(jh7110_usb_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 USB 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/sunplus/phy-sunplus-usb2.c b/drivers/phy/sunplus/phy-sunplus-usb2.c index 56de41091d639721fd797491c04684339d4eb64d..0efe74ac9c6afc254dd48d9be37e3fc611d8e20f 100644 --- a/drivers/phy/sunplus/phy-sunplus-usb2.c +++ b/drivers/phy/sunplus/phy-sunplus-usb2.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/tegra/phy-tegra194-p2u.c b/drivers/phy/tegra/phy-tegra194-p2u.c index 633e6b7472759a6b819320dfa9d29adce2171ecc..f49b417c9eb605b42dce5910fb29d5331bdfb4ce 100644 --- a/drivers/phy/tegra/phy-tegra194-p2u.c +++ b/drivers/phy/tegra/phy-tegra194-p2u.c @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include #define P2U_CONTROL_CMN 0x74 #define P2U_CONTROL_CMN_ENABLE_L2_EXIT_RATE_CHANGE BIT(13) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index a296b87dced18f626a417b2c2584504e28f11a88..142ebe0247cc00f82080303269835ad9565e584f 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -8,7 +8,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 6286cf25a426496b7ec215b300a43c75bbf168ac..555b323f45da1ee6dc69ef606860786ac5cc16ae 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -465,9 +465,12 @@ static int phy_gmii_sel_probe(struct platform_device *pdev) priv->regmap = syscon_node_to_regmap(node->parent); if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); - dev_err(dev, "Failed to get syscon %d\n", ret); - return ret; + priv->regmap = device_node_to_regmap(node); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(dev, "Failed to get syscon %d\n", ret); + return ret; + } } ret = phy_gmii_sel_init_ports(priv); diff --git a/drivers/phy/ti/phy-tusb1210.c b/drivers/phy/ti/phy-tusb1210.c index 669c13d6e402fa422d5ee67c5770835a0dd953ee..b4881cb344759de87a9ae6fbbb3a10c96fe55833 100644 --- a/drivers/phy/ti/phy-tusb1210.c +++ b/drivers/phy/ti/phy-tusb1210.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define TUSB1211_POWER_CONTROL 0x3d diff --git a/drivers/phy/ti/phy-twl4030-usb.c b/drivers/phy/ti/phy-twl4030-usb.c index da50732625d17719b917f3e3763c5c9d3d6308c3..6b265992d988faff2abbb564b454761ab1d3c313 100644 --- a/drivers/phy/ti/phy-twl4030-usb.c +++ b/drivers/phy/ti/phy-twl4030-usb.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 8833680923a140c0caa01591734fc164845608de..2559c6594cea2bcdde2a06af52bcc40eebf246d0 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -571,6 +572,10 @@ static int xpsgtr_phy_init(struct phy *phy) mutex_lock(>r_dev->gtr_mutex); + /* Configure and enable the clock when peripheral phy_init call */ + if (clk_prepare_enable(gtr_dev->clk[gtr_phy->lane])) + goto out; + /* Skip initialization if not required. */ if (!xpsgtr_phy_init_required(gtr_phy)) goto out; @@ -615,9 +620,13 @@ static int xpsgtr_phy_init(struct phy *phy) static int xpsgtr_phy_exit(struct phy *phy) { struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); + struct xpsgtr_dev *gtr_dev = gtr_phy->dev; gtr_phy->skip_phy_init = false; + /* Ensure that disable clock only, which configure for lane */ + clk_disable_unprepare(gtr_dev->clk[gtr_phy->lane]); + return 0; } @@ -820,34 +829,23 @@ static struct phy *xpsgtr_xlate(struct device *dev, * Power Management */ -static int __maybe_unused xpsgtr_suspend(struct device *dev) +static int xpsgtr_runtime_suspend(struct device *dev) { struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); - unsigned int i; /* Save the snapshot ICM_CFG registers. */ gtr_dev->saved_icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); gtr_dev->saved_icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); - for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) - clk_disable_unprepare(gtr_dev->clk[i]); - return 0; } -static int __maybe_unused xpsgtr_resume(struct device *dev) +static int xpsgtr_runtime_resume(struct device *dev) { struct xpsgtr_dev *gtr_dev = dev_get_drvdata(dev); unsigned int icm_cfg0, icm_cfg1; unsigned int i; bool skip_phy_init; - int err; - - for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) { - err = clk_prepare_enable(gtr_dev->clk[i]); - if (err) - goto err_clk_put; - } icm_cfg0 = xpsgtr_read(gtr_dev, ICM_CFG0); icm_cfg1 = xpsgtr_read(gtr_dev, ICM_CFG1); @@ -868,18 +866,10 @@ static int __maybe_unused xpsgtr_resume(struct device *dev) gtr_dev->phys[i].skip_phy_init = skip_phy_init; return 0; - -err_clk_put: - while (i--) - clk_disable_unprepare(gtr_dev->clk[i]); - - return err; } -static const struct dev_pm_ops xpsgtr_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xpsgtr_suspend, xpsgtr_resume) -}; - +static DEFINE_RUNTIME_DEV_PM_OPS(xpsgtr_pm_ops, xpsgtr_runtime_suspend, + xpsgtr_runtime_resume, NULL); /* * Probe & Platform Driver */ @@ -887,7 +877,6 @@ static const struct dev_pm_ops xpsgtr_pm_ops = { static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) { unsigned int refclk; - int ret; for (refclk = 0; refclk < ARRAY_SIZE(gtr_dev->refclk_sscs); ++refclk) { unsigned long rate; @@ -898,19 +887,14 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) snprintf(name, sizeof(name), "ref%u", refclk); clk = devm_clk_get_optional(gtr_dev->dev, name); if (IS_ERR(clk)) { - ret = dev_err_probe(gtr_dev->dev, PTR_ERR(clk), - "Failed to get reference clock %u\n", - refclk); - goto err_clk_put; + return dev_err_probe(gtr_dev->dev, PTR_ERR(clk), + "Failed to get ref clock %u\n", + refclk); } if (!clk) continue; - ret = clk_prepare_enable(clk); - if (ret) - goto err_clk_put; - gtr_dev->clk[refclk] = clk; /* @@ -920,7 +904,10 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) rate = clk_get_rate(clk); for (i = 0 ; i < ARRAY_SIZE(ssc_lookup); i++) { - if (rate == ssc_lookup[i].refclk_rate) { + /* Allow an error of 100 ppm */ + unsigned long error = ssc_lookup[i].refclk_rate / 10000; + + if (abs(rate - ssc_lookup[i].refclk_rate) < error) { gtr_dev->refclk_sscs[refclk] = &ssc_lookup[i]; break; } @@ -930,18 +917,11 @@ static int xpsgtr_get_ref_clocks(struct xpsgtr_dev *gtr_dev) dev_err(gtr_dev->dev, "Invalid rate %lu for reference clock %u\n", rate, refclk); - ret = -EINVAL; - goto err_clk_put; + return -EINVAL; } } return 0; - -err_clk_put: - while (refclk--) - clk_disable_unprepare(gtr_dev->clk[refclk]); - - return ret; } static int xpsgtr_probe(struct platform_device *pdev) @@ -950,7 +930,6 @@ static int xpsgtr_probe(struct platform_device *pdev) struct xpsgtr_dev *gtr_dev; struct phy_provider *provider; unsigned int port; - unsigned int i; int ret; gtr_dev = devm_kzalloc(&pdev->dev, sizeof(*gtr_dev), GFP_KERNEL); @@ -990,8 +969,7 @@ static int xpsgtr_probe(struct platform_device *pdev) phy = devm_phy_create(&pdev->dev, np, &xpsgtr_phyops); if (IS_ERR(phy)) { dev_err(&pdev->dev, "failed to create PHY\n"); - ret = PTR_ERR(phy); - goto err_clk_put; + return PTR_ERR(phy); } gtr_phy->phy = phy; @@ -1002,16 +980,30 @@ static int xpsgtr_probe(struct platform_device *pdev) provider = devm_of_phy_provider_register(&pdev->dev, xpsgtr_xlate); if (IS_ERR(provider)) { dev_err(&pdev->dev, "registering provider failed\n"); - ret = PTR_ERR(provider); - goto err_clk_put; + return PTR_ERR(provider); } + + pm_runtime_set_active(gtr_dev->dev); + pm_runtime_enable(gtr_dev->dev); + + ret = pm_runtime_resume_and_get(gtr_dev->dev); + if (ret < 0) { + pm_runtime_disable(gtr_dev->dev); + return ret; + } + return 0; +} -err_clk_put: - for (i = 0; i < ARRAY_SIZE(gtr_dev->clk); i++) - clk_disable_unprepare(gtr_dev->clk[i]); +static int xpsgtr_remove(struct platform_device *pdev) +{ + struct xpsgtr_dev *gtr_dev = platform_get_drvdata(pdev); - return ret; + pm_runtime_disable(gtr_dev->dev); + pm_runtime_put_noidle(gtr_dev->dev); + pm_runtime_set_suspended(gtr_dev->dev); + + return 0; } static const struct of_device_id xpsgtr_of_match[] = { @@ -1023,10 +1015,11 @@ MODULE_DEVICE_TABLE(of, xpsgtr_of_match); static struct platform_driver xpsgtr_driver = { .probe = xpsgtr_probe, + .remove = xpsgtr_remove, .driver = { .name = "xilinx-psgtr", .of_match_table = xpsgtr_of_match, - .pm = &xpsgtr_pm_ops, + .pm = pm_ptr(&xpsgtr_pm_ops), }, }; diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index 382793e73a60a8b3f4ade16b00768fc368eb50e2..f7dfa0e785fd6e0cc603b2a9a12b848e967a0c1d 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -60,6 +60,7 @@ config MLXBF_BOOTCTL tristate "Mellanox BlueField Firmware Boot Control driver" depends on ARM64 depends on ACPI + depends on NET help The Mellanox BlueField firmware implements functionality to request swapping the primary and alternate eMMC boot partition, @@ -80,8 +81,8 @@ config MLXBF_PMC config NVSW_SN2201 tristate "Nvidia SN2201 platform driver support" - depends on HWMON - depends on I2C + depends on HWMON && I2C + depends on ACPI || COMPILE_TEST select REGMAP_I2C help This driver provides support for the Nvidia SN2201 platform. diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index be967d797c28e04c6ce726b434e0edb322737f88..2d4bbe99959ef45e614707ada7875e3eec8e6c5e 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -191,6 +191,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_smgen_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_1[] = { + { 0x0, "DISABLE" }, { 0xa0, "TPIO_DATA_BEAT" }, { 0xa1, "TDMA_DATA_BEAT" }, { 0xa2, "MAP_DATA_BEAT" }, @@ -214,6 +215,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_1[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_2[] = { + { 0x0, "DISABLE" }, { 0xa0, "TPIO_DATA_BEAT" }, { 0xa1, "TDMA_DATA_BEAT" }, { 0xa2, "MAP_DATA_BEAT" }, @@ -246,6 +248,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_2[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_ecc_events[] = { + { 0x0, "DISABLE" }, { 0x100, "ECC_SINGLE_ERROR_CNT" }, { 0x104, "ECC_DOUBLE_ERROR_CNT" }, { 0x114, "SERR_INJ" }, @@ -258,6 +261,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_ecc_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { + { 0x0, "DISABLE" }, { 0xc0, "RXREQ_MSS" }, { 0xc1, "RXDAT_MSS" }, { 0xc2, "TXRSP_MSS" }, @@ -265,6 +269,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_hnf_events[] = { + { 0x0, "DISABLE" }, { 0x45, "HNF_REQUESTS" }, { 0x46, "HNF_REJECTS" }, { 0x47, "ALL_BUSY" }, @@ -323,6 +328,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_hnf_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_hnfnet_events[] = { + { 0x0, "DISABLE" }, { 0x12, "CDN_REQ" }, { 0x13, "DDN_REQ" }, { 0x14, "NDN_REQ" }, @@ -892,7 +898,7 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, uint64_t *result) { uint32_t perfcfg_offset, perfval_offset; - uint64_t perfmon_cfg, perfevt, perfctl; + uint64_t perfmon_cfg, perfevt; if (cnt_num >= pmc->block[blk_num].counters) return -EINVAL; @@ -904,25 +910,6 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, perfval_offset = perfcfg_offset + pmc->block[blk_num].counters * MLXBF_PMC_REG_SIZE; - /* Set counter in "read" mode */ - perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, - MLXBF_PMC_PERFCTL); - perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); - perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 0); - - if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + perfcfg_offset, - MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) - return -EFAULT; - - /* Check if the counter is enabled */ - - if (mlxbf_pmc_read(pmc->block[blk_num].mmio_base + perfval_offset, - MLXBF_PMC_READ_REG_64, &perfctl)) - return -EFAULT; - - if (!FIELD_GET(MLXBF_PMC_PERFCTL_EN0, perfctl)) - return -EINVAL; - /* Set counter in "read" mode */ perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, MLXBF_PMC_PERFEVT); @@ -1008,7 +995,7 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev, } else return -EINVAL; - return sprintf(buf, "0x%llx\n", value); + return sysfs_emit(buf, "0x%llx\n", value); } /* Store function for "counter" sysfs files */ @@ -1078,13 +1065,13 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev, err = mlxbf_pmc_read_event(blk_num, cnt_num, is_l3, &evt_num); if (err) - return sprintf(buf, "No event being monitored\n"); + return sysfs_emit(buf, "No event being monitored\n"); evt_name = mlxbf_pmc_get_event_name(pmc->block_name[blk_num], evt_num); if (!evt_name) return -EINVAL; - return sprintf(buf, "0x%llx: %s\n", evt_num, evt_name); + return sysfs_emit(buf, "0x%llx: %s\n", evt_num, evt_name); } /* Store function for "event" sysfs files */ @@ -1139,9 +1126,9 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev, return -EINVAL; for (i = 0, buf[0] = '\0'; i < size; ++i) { - len += sprintf(e_info, "0x%x: %s\n", events[i].evt_num, - events[i].evt_name); - if (len > PAGE_SIZE) + len += snprintf(e_info, sizeof(e_info), "0x%x: %s\n", + events[i].evt_num, events[i].evt_name); + if (len >= PAGE_SIZE) break; strcat(buf, e_info); ret = len; @@ -1168,7 +1155,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev, value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg); - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } /* Store function for "enable" sysfs files - only for l3cache */ diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index b600b77d91ef2c3834d6e837b1f64f78f962ba97..fd38d8c8371e29217b98a01e9d4bdcac6616f233 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -53,12 +53,13 @@ struct mlxbf_tmfifo; /** - * mlxbf_tmfifo_vring - Structure of the TmFifo virtual ring + * struct mlxbf_tmfifo_vring - Structure of the TmFifo virtual ring * @va: virtual address of the ring * @dma: dma address of the ring * @vq: pointer to the virtio virtqueue * @desc: current descriptor of the pending packet * @desc_head: head descriptor of the pending packet + * @drop_desc: dummy desc for packet dropping * @cur_len: processed length of the current descriptor * @rem_len: remaining length of the pending packet * @pkt_len: total length of the pending packet @@ -75,6 +76,7 @@ struct mlxbf_tmfifo_vring { struct virtqueue *vq; struct vring_desc *desc; struct vring_desc *desc_head; + struct vring_desc drop_desc; int cur_len; int rem_len; u32 pkt_len; @@ -86,6 +88,14 @@ struct mlxbf_tmfifo_vring { struct mlxbf_tmfifo *fifo; }; +/* Check whether vring is in drop mode. */ +#define IS_VRING_DROP(_r) ({ \ + typeof(_r) (r) = (_r); \ + (r->desc_head == &r->drop_desc ? true : false); }) + +/* A stub length to drop maximum length packet. */ +#define VRING_DROP_DESC_MAX_LEN GENMASK(15, 0) + /* Interrupt types. */ enum { MLXBF_TM_RX_LWM_IRQ, @@ -103,12 +113,13 @@ enum { }; /** - * mlxbf_tmfifo_vdev - Structure of the TmFifo virtual device + * struct mlxbf_tmfifo_vdev - Structure of the TmFifo virtual device * @vdev: virtio device, in which the vdev.id.device field has the * VIRTIO_ID_xxx id to distinguish the virtual device. * @status: status of the device * @features: supported features of the device * @vrings: array of tmfifo vrings of this device + * @config: non-anonymous union for cons and net * @config.cons: virtual console config - * select if vdev.id.device is VIRTIO_ID_CONSOLE * @config.net: virtual network config - @@ -128,7 +139,7 @@ struct mlxbf_tmfifo_vdev { }; /** - * mlxbf_tmfifo_irq_info - Structure of the interrupt information + * struct mlxbf_tmfifo_irq_info - Structure of the interrupt information * @fifo: pointer to the tmfifo structure * @irq: interrupt number * @index: index into the interrupt array @@ -140,7 +151,7 @@ struct mlxbf_tmfifo_irq_info { }; /** - * mlxbf_tmfifo_io - Structure of the TmFifo IO resource (for both rx & tx) + * struct mlxbf_tmfifo_io - Structure of the TmFifo IO resource (for both rx & tx) * @ctl: control register offset (TMFIFO_RX_CTL / TMFIFO_TX_CTL) * @sts: status register offset (TMFIFO_RX_STS / TMFIFO_TX_STS) * @data: data register offset (TMFIFO_RX_DATA / TMFIFO_TX_DATA) @@ -152,7 +163,7 @@ struct mlxbf_tmfifo_io { }; /** - * mlxbf_tmfifo - Structure of the TmFifo + * struct mlxbf_tmfifo - Structure of the TmFifo * @vdev: array of the virtual devices running over the TmFifo * @lock: lock to protect the TmFifo access * @res0: mapped resource block 0 @@ -188,7 +199,7 @@ struct mlxbf_tmfifo { }; /** - * mlxbf_tmfifo_msg_hdr - Structure of the TmFifo message header + * struct mlxbf_tmfifo_msg_hdr - Structure of the TmFifo message header * @type: message type * @len: payload length in network byte order. Messages sent into the FIFO * will be read by the other side as data stream in the same byte order. @@ -198,6 +209,7 @@ struct mlxbf_tmfifo { struct mlxbf_tmfifo_msg_hdr { u8 type; __be16 len; + /* private: */ u8 unused[5]; } __packed __aligned(sizeof(u64)); @@ -214,7 +226,7 @@ static u8 mlxbf_tmfifo_net_default_mac[ETH_ALEN] = { static efi_char16_t mlxbf_tmfifo_efi_name[] = L"RshimMacAddr"; /* Maximum L2 header length. */ -#define MLXBF_TMFIFO_NET_L2_OVERHEAD 36 +#define MLXBF_TMFIFO_NET_L2_OVERHEAD (ETH_HLEN + VLAN_HLEN) /* Supported virtio-net features. */ #define MLXBF_TMFIFO_NET_FEATURES \ @@ -262,6 +274,7 @@ static int mlxbf_tmfifo_alloc_vrings(struct mlxbf_tmfifo *fifo, vring->align = SMP_CACHE_BYTES; vring->index = i; vring->vdev_id = tm_vdev->vdev.id.device; + vring->drop_desc.len = VRING_DROP_DESC_MAX_LEN; dev = &tm_vdev->vdev.dev; size = vring_size(vring->num, vring->align); @@ -367,7 +380,7 @@ static u32 mlxbf_tmfifo_get_pkt_len(struct mlxbf_tmfifo_vring *vring, return len; } -static void mlxbf_tmfifo_release_pending_pkt(struct mlxbf_tmfifo_vring *vring) +static void mlxbf_tmfifo_release_pkt(struct mlxbf_tmfifo_vring *vring) { struct vring_desc *desc_head; u32 len = 0; @@ -596,19 +609,25 @@ static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, if (vring->cur_len + sizeof(u64) <= len) { /* The whole word. */ - if (is_rx) - memcpy(addr + vring->cur_len, &data, sizeof(u64)); - else - memcpy(&data, addr + vring->cur_len, sizeof(u64)); + if (!IS_VRING_DROP(vring)) { + if (is_rx) + memcpy(addr + vring->cur_len, &data, + sizeof(u64)); + else + memcpy(&data, addr + vring->cur_len, + sizeof(u64)); + } vring->cur_len += sizeof(u64); } else { /* Leftover bytes. */ - if (is_rx) - memcpy(addr + vring->cur_len, &data, - len - vring->cur_len); - else - memcpy(&data, addr + vring->cur_len, - len - vring->cur_len); + if (!IS_VRING_DROP(vring)) { + if (is_rx) + memcpy(addr + vring->cur_len, &data, + len - vring->cur_len); + else + memcpy(&data, addr + vring->cur_len, + len - vring->cur_len); + } vring->cur_len = len; } @@ -625,13 +644,14 @@ static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, * flag is set. */ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, - struct vring_desc *desc, + struct vring_desc **desc, bool is_rx, bool *vring_change) { struct mlxbf_tmfifo *fifo = vring->fifo; struct virtio_net_config *config; struct mlxbf_tmfifo_msg_hdr hdr; int vdev_id, hdr_len; + bool drop_rx = false; /* Read/Write packet header. */ if (is_rx) { @@ -651,8 +671,8 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, if (ntohs(hdr.len) > __virtio16_to_cpu(virtio_legacy_is_little_endian(), config->mtu) + - MLXBF_TMFIFO_NET_L2_OVERHEAD) - return; + MLXBF_TMFIFO_NET_L2_OVERHEAD) + drop_rx = true; } else { vdev_id = VIRTIO_ID_CONSOLE; hdr_len = 0; @@ -667,16 +687,25 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, if (!tm_dev2) return; - vring->desc = desc; + vring->desc = *desc; vring = &tm_dev2->vrings[MLXBF_TMFIFO_VRING_RX]; *vring_change = true; } + + if (drop_rx && !IS_VRING_DROP(vring)) { + if (vring->desc_head) + mlxbf_tmfifo_release_pkt(vring); + *desc = &vring->drop_desc; + vring->desc_head = *desc; + vring->desc = *desc; + } + vring->pkt_len = ntohs(hdr.len) + hdr_len; } else { /* Network virtio has an extra header. */ hdr_len = (vring->vdev_id == VIRTIO_ID_NET) ? sizeof(struct virtio_net_hdr) : 0; - vring->pkt_len = mlxbf_tmfifo_get_pkt_len(vring, desc); + vring->pkt_len = mlxbf_tmfifo_get_pkt_len(vring, *desc); hdr.type = (vring->vdev_id == VIRTIO_ID_NET) ? VIRTIO_ID_NET : VIRTIO_ID_CONSOLE; hdr.len = htons(vring->pkt_len - hdr_len); @@ -709,15 +738,23 @@ static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring, /* Get the descriptor of the next packet. */ if (!vring->desc) { desc = mlxbf_tmfifo_get_next_pkt(vring, is_rx); - if (!desc) - return false; + if (!desc) { + /* Drop next Rx packet to avoid stuck. */ + if (is_rx) { + desc = &vring->drop_desc; + vring->desc_head = desc; + vring->desc = desc; + } else { + return false; + } + } } else { desc = vring->desc; } /* Beginning of a packet. Start to Rx/Tx packet header. */ if (vring->pkt_len == 0) { - mlxbf_tmfifo_rxtx_header(vring, desc, is_rx, &vring_change); + mlxbf_tmfifo_rxtx_header(vring, &desc, is_rx, &vring_change); (*avail)--; /* Return if new packet is for another ring. */ @@ -743,17 +780,24 @@ static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring, vring->rem_len -= len; /* Get the next desc on the chain. */ - if (vring->rem_len > 0 && + if (!IS_VRING_DROP(vring) && vring->rem_len > 0 && (virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT)) { idx = virtio16_to_cpu(vdev, desc->next); desc = &vr->desc[idx]; goto mlxbf_tmfifo_desc_done; } - /* Done and release the pending packet. */ - mlxbf_tmfifo_release_pending_pkt(vring); + /* Done and release the packet. */ desc = NULL; fifo->vring[is_rx] = NULL; + if (!IS_VRING_DROP(vring)) { + mlxbf_tmfifo_release_pkt(vring); + } else { + vring->pkt_len = 0; + vring->desc_head = NULL; + vring->desc = NULL; + return false; + } /* * Make sure the load/store are in order before @@ -933,7 +977,7 @@ static void mlxbf_tmfifo_virtio_del_vqs(struct virtio_device *vdev) /* Release the pending packet. */ if (vring->desc) - mlxbf_tmfifo_release_pending_pkt(vring); + mlxbf_tmfifo_release_pkt(vring); vq = vring->vq; if (vq) { vring->vq = NULL; diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index fdf7da06af3067495fe7132e725a52542010ff8d..d85d895fee89431a42d94bfdde087f8e64183547 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -478,6 +478,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_tablet_mode, }, + { + .callback = dmi_matched, + .ident = "ASUS ROG FLOW X16", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "GV601V"), + }, + .driver_data = &quirk_asus_tablet_mode, + }, { .callback = dmi_matched, .ident = "ASUS VivoBook E410MA", diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c index 8c4f9e12f0181d950dd6b38b0069298f5f96bd4d..5798b49ddaba901db74a072b123b7cfbb1e2ef0b 100644 --- a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c +++ b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c @@ -659,7 +659,7 @@ static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type, const char *guid, int min_elements, int instance_id) { - struct kobject *attr_name_kobj; + struct kobject *attr_name_kobj, *duplicate; union acpi_object *elements; struct kset *temp_kset; @@ -704,8 +704,11 @@ static int hp_init_bios_package_attribute(enum hp_wmi_data_type attr_type, } /* All duplicate attributes found are ignored */ - if (kset_find_obj(temp_kset, str_value)) { + duplicate = kset_find_obj(temp_kset, str_value); + if (duplicate) { pr_debug("Duplicate attribute name found - %s\n", str_value); + /* kset_find_obj() returns a reference */ + kobject_put(duplicate); goto pack_attr_exit; } @@ -768,7 +771,7 @@ static int hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type, const char *guid, int min_elements, int instance_id) { - struct kobject *attr_name_kobj; + struct kobject *attr_name_kobj, *duplicate; struct kset *temp_kset; char str[MAX_BUFF_SIZE]; @@ -794,8 +797,11 @@ static int hp_init_bios_buffer_attribute(enum hp_wmi_data_type attr_type, temp_kset = bioscfg_drv.main_dir_kset; /* All duplicate attributes found are ignored */ - if (kset_find_obj(temp_kset, str)) { + duplicate = kset_find_obj(temp_kset, str); + if (duplicate) { pr_debug("Duplicate attribute name found - %s\n", str); + /* kset_find_obj() returns a reference */ + kobject_put(duplicate); goto buff_attr_exit; } diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index e76e5458db35095a55204848f3c7bdc8907fe09c..8ebb7be52ee7258981597e10e9f76355eaca002e 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -1548,7 +1548,13 @@ static const struct dev_pm_ops hp_wmi_pm_ops = { .restore = hp_wmi_resume_handler, }; -static struct platform_driver hp_wmi_driver = { +/* + * hp_wmi_bios_remove() lives in .exit.text. For drivers registered via + * module_platform_driver_probe() this is ok because they cannot get unbound at + * runtime. So mark the driver struct with __refdata to prevent modpost + * triggering a section mismatch warning. + */ +static struct platform_driver hp_wmi_driver __refdata = { .driver = { .name = "hp-wmi", .pm = &hp_wmi_pm_ops, diff --git a/drivers/platform/x86/intel/ifs/runtest.c b/drivers/platform/x86/intel/ifs/runtest.c index 1061eb7ec39985e7d43ece8680eeeaa9096e7a00..43c864add778f82259a5969550f569cc393e276a 100644 --- a/drivers/platform/x86/intel/ifs/runtest.c +++ b/drivers/platform/x86/intel/ifs/runtest.c @@ -331,14 +331,15 @@ int do_core_test(int cpu, struct device *dev) switch (test->test_num) { case IFS_TYPE_SAF: if (!ifsd->loaded) - return -EPERM; - ifs_test_core(cpu, dev); + ret = -EPERM; + else + ifs_test_core(cpu, dev); break; case IFS_TYPE_ARRAY_BIST: ifs_array_test_core(cpu, dev); break; default: - return -EINVAL; + ret = -EINVAL; } out: cpus_read_unlock(); diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 6851d10d6582525509c2a3f05163abfbeb7a3802..a68df41334035185d5301006e4f70f1f3f29af49 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -231,19 +232,15 @@ static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset) /* Wait till scu status is busy */ static inline int busy_loop(struct intel_scu_ipc_dev *scu) { - unsigned long end = jiffies + IPC_TIMEOUT; - - do { - u32 status; - - status = ipc_read_status(scu); - if (!(status & IPC_STATUS_BUSY)) - return (status & IPC_STATUS_ERR) ? -EIO : 0; + u8 status; + int err; - usleep_range(50, 100); - } while (time_before(jiffies, end)); + err = readx_poll_timeout(ipc_read_status, scu, status, !(status & IPC_STATUS_BUSY), + 100, jiffies_to_usecs(IPC_TIMEOUT)); + if (err) + return err; - return -ETIMEDOUT; + return (status & IPC_STATUS_ERR) ? -EIO : 0; } /* Wait till ipc ioc interrupt is received or timeout in 10 HZ */ @@ -251,10 +248,12 @@ static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu) { int status; - if (!wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT)) - return -ETIMEDOUT; + wait_for_completion_timeout(&scu->cmd_complete, IPC_TIMEOUT); status = ipc_read_status(scu); + if (status & IPC_STATUS_BUSY) + return -ETIMEDOUT; + if (status & IPC_STATUS_ERR) return -EIO; @@ -266,6 +265,24 @@ static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu) return scu->irq > 0 ? ipc_wait_for_interrupt(scu) : busy_loop(scu); } +static struct intel_scu_ipc_dev *intel_scu_ipc_get(struct intel_scu_ipc_dev *scu) +{ + u8 status; + + if (!scu) + scu = ipcdev; + if (!scu) + return ERR_PTR(-ENODEV); + + status = ipc_read_status(scu); + if (status & IPC_STATUS_BUSY) { + dev_dbg(&scu->dev, "device is busy\n"); + return ERR_PTR(-EBUSY); + } + + return scu; +} + /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, u32 count, u32 op, u32 id) @@ -279,11 +296,10 @@ static int pwr_reg_rdwr(struct intel_scu_ipc_dev *scu, u16 *addr, u8 *data, memset(cbuf, 0, sizeof(cbuf)); mutex_lock(&ipclock); - if (!scu) - scu = ipcdev; - if (!scu) { + scu = intel_scu_ipc_get(scu); + if (IS_ERR(scu)) { mutex_unlock(&ipclock); - return -ENODEV; + return PTR_ERR(scu); } for (nc = 0; nc < count; nc++, offset += 2) { @@ -438,13 +454,12 @@ int intel_scu_ipc_dev_simple_command(struct intel_scu_ipc_dev *scu, int cmd, int err; mutex_lock(&ipclock); - if (!scu) - scu = ipcdev; - if (!scu) { + scu = intel_scu_ipc_get(scu); + if (IS_ERR(scu)) { mutex_unlock(&ipclock); - return -ENODEV; + return PTR_ERR(scu); } - scu = ipcdev; + cmdval = sub << 12 | cmd; ipc_command(scu, cmdval); err = intel_scu_ipc_check_status(scu); @@ -484,11 +499,10 @@ int intel_scu_ipc_dev_command_with_size(struct intel_scu_ipc_dev *scu, int cmd, return -EINVAL; mutex_lock(&ipclock); - if (!scu) - scu = ipcdev; - if (!scu) { + scu = intel_scu_ipc_get(scu); + if (IS_ERR(scu)) { mutex_unlock(&ipclock); - return -ENODEV; + return PTR_ERR(scu); } memcpy(inbuf, in, inlen); diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c index 79346881cadb1291571d4c94cb2f56da93ec4175..aee869769843f5614ed373dd68df9764d816f5d4 100644 --- a/drivers/platform/x86/think-lmi.c +++ b/drivers/platform/x86/think-lmi.c @@ -1248,6 +1248,24 @@ static void tlmi_release_attr(void) kset_unregister(tlmi_priv.authentication_kset); } +static int tlmi_validate_setting_name(struct kset *attribute_kset, char *name) +{ + struct kobject *duplicate; + + if (!strcmp(name, "Reserved")) + return -EINVAL; + + duplicate = kset_find_obj(attribute_kset, name); + if (duplicate) { + pr_debug("Duplicate attribute name found - %s\n", name); + /* kset_find_obj() returns a reference */ + kobject_put(duplicate); + return -EBUSY; + } + + return 0; +} + static int tlmi_sysfs_init(void) { int i, ret; @@ -1276,10 +1294,8 @@ static int tlmi_sysfs_init(void) continue; /* check for duplicate or reserved values */ - if (kset_find_obj(tlmi_priv.attribute_kset, tlmi_priv.setting[i]->display_name) || - !strcmp(tlmi_priv.setting[i]->display_name, "Reserved")) { - pr_debug("duplicate or reserved attribute name found - %s\n", - tlmi_priv.setting[i]->display_name); + if (tlmi_validate_setting_name(tlmi_priv.attribute_kset, + tlmi_priv.setting[i]->display_name) < 0) { kfree(tlmi_priv.setting[i]->possible_values); kfree(tlmi_priv.setting[i]); tlmi_priv.setting[i] = NULL; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index d70c89d3253440b725bf32791a8f0d1a49f408d6..41584427dc323b7aafb7f214d9a35abc9b272d8d 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -4116,9 +4116,11 @@ static void hotkey_resume(void) { tpacpi_disable_brightness_delay(); + mutex_lock(&hotkey_mutex); if (hotkey_status_set(true) < 0 || hotkey_mask_set(hotkey_acpi_mask) < 0) pr_err("error while attempting to reset the event firmware interface\n"); + mutex_unlock(&hotkey_mutex); tpacpi_send_radiosw_update(); tpacpi_input_send_tabletsw(); diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index f9301a9382e7412b3baab302207a30444bc082fb..0c6733772698408ef1a23b977d1a0698a19347d5 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -42,6 +42,21 @@ static const struct ts_dmi_data archos_101_cesium_educ_data = { .properties = archos_101_cesium_educ_props, }; +static const struct property_entry bush_bush_windows_tablet_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1850), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1280), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-bush-bush-windows-tablet.fw"), + { } +}; + +static const struct ts_dmi_data bush_bush_windows_tablet_data = { + .acpi_name = "MSSL1680:00", + .properties = bush_bush_windows_tablet_props, +}; + static const struct property_entry chuwi_hi8_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), @@ -756,6 +771,21 @@ static const struct ts_dmi_data pipo_w11_data = { .properties = pipo_w11_props, }; +static const struct property_entry positivo_c4128b_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 4), + PROPERTY_ENTRY_U32("touchscreen-min-y", 13), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1915), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1269), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-positivo-c4128b.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + { } +}; + +static const struct ts_dmi_data positivo_c4128b_data = { + .acpi_name = "MSSL1680:00", + .properties = positivo_c4128b_props, +}; + static const struct property_entry pov_mobii_wintab_p800w_v20_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 32), PROPERTY_ENTRY_U32("touchscreen-min-y", 16), @@ -1070,6 +1100,13 @@ const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "ARCHOS 101 Cesium Educ"), }, }, + { + /* Bush Windows tablet */ + .driver_data = (void *)&bush_bush_windows_tablet_data, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Bush Windows tablet"), + }, + }, { /* Chuwi Hi8 */ .driver_data = (void *)&chuwi_hi8_data, @@ -1480,6 +1517,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = { DMI_MATCH(DMI_BIOS_VERSION, "MOMO.G.WI71C.MABMRBA02"), }, }, + { + /* Positivo C4128B */ + .driver_data = (void *)&positivo_c4128b_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Positivo Tecnologia SA"), + DMI_MATCH(DMI_PRODUCT_NAME, "C4128B-1"), + }, + }, { /* Point of View mobii wintab p800w (v2.0) */ .driver_data = (void *)&pov_mobii_wintab_p800w_v20_data, diff --git a/drivers/genpd/Makefile b/drivers/pmdomain/Makefile similarity index 100% rename from drivers/genpd/Makefile rename to drivers/pmdomain/Makefile diff --git a/drivers/genpd/actions/Makefile b/drivers/pmdomain/actions/Makefile similarity index 100% rename from drivers/genpd/actions/Makefile rename to drivers/pmdomain/actions/Makefile diff --git a/drivers/genpd/actions/owl-sps-helper.c b/drivers/pmdomain/actions/owl-sps-helper.c similarity index 100% rename from drivers/genpd/actions/owl-sps-helper.c rename to drivers/pmdomain/actions/owl-sps-helper.c diff --git a/drivers/genpd/actions/owl-sps.c b/drivers/pmdomain/actions/owl-sps.c similarity index 100% rename from drivers/genpd/actions/owl-sps.c rename to drivers/pmdomain/actions/owl-sps.c diff --git a/drivers/genpd/amlogic/Makefile b/drivers/pmdomain/amlogic/Makefile similarity index 100% rename from drivers/genpd/amlogic/Makefile rename to drivers/pmdomain/amlogic/Makefile diff --git a/drivers/genpd/amlogic/meson-ee-pwrc.c b/drivers/pmdomain/amlogic/meson-ee-pwrc.c similarity index 100% rename from drivers/genpd/amlogic/meson-ee-pwrc.c rename to drivers/pmdomain/amlogic/meson-ee-pwrc.c diff --git a/drivers/genpd/amlogic/meson-gx-pwrc-vpu.c b/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c similarity index 100% rename from drivers/genpd/amlogic/meson-gx-pwrc-vpu.c rename to drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c diff --git a/drivers/genpd/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c similarity index 100% rename from drivers/genpd/amlogic/meson-secure-pwrc.c rename to drivers/pmdomain/amlogic/meson-secure-pwrc.c diff --git a/drivers/genpd/apple/Makefile b/drivers/pmdomain/apple/Makefile similarity index 100% rename from drivers/genpd/apple/Makefile rename to drivers/pmdomain/apple/Makefile diff --git a/drivers/genpd/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c similarity index 100% rename from drivers/genpd/apple/pmgr-pwrstate.c rename to drivers/pmdomain/apple/pmgr-pwrstate.c diff --git a/drivers/genpd/bcm/Makefile b/drivers/pmdomain/bcm/Makefile similarity index 100% rename from drivers/genpd/bcm/Makefile rename to drivers/pmdomain/bcm/Makefile diff --git a/drivers/genpd/bcm/bcm-pmb.c b/drivers/pmdomain/bcm/bcm-pmb.c similarity index 100% rename from drivers/genpd/bcm/bcm-pmb.c rename to drivers/pmdomain/bcm/bcm-pmb.c diff --git a/drivers/genpd/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c similarity index 100% rename from drivers/genpd/bcm/bcm2835-power.c rename to drivers/pmdomain/bcm/bcm2835-power.c diff --git a/drivers/genpd/bcm/bcm63xx-power.c b/drivers/pmdomain/bcm/bcm63xx-power.c similarity index 100% rename from drivers/genpd/bcm/bcm63xx-power.c rename to drivers/pmdomain/bcm/bcm63xx-power.c diff --git a/drivers/genpd/bcm/raspberrypi-power.c b/drivers/pmdomain/bcm/raspberrypi-power.c similarity index 100% rename from drivers/genpd/bcm/raspberrypi-power.c rename to drivers/pmdomain/bcm/raspberrypi-power.c diff --git a/drivers/genpd/imx/Makefile b/drivers/pmdomain/imx/Makefile similarity index 100% rename from drivers/genpd/imx/Makefile rename to drivers/pmdomain/imx/Makefile diff --git a/drivers/genpd/imx/gpc.c b/drivers/pmdomain/imx/gpc.c similarity index 100% rename from drivers/genpd/imx/gpc.c rename to drivers/pmdomain/imx/gpc.c diff --git a/drivers/genpd/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c similarity index 100% rename from drivers/genpd/imx/gpcv2.c rename to drivers/pmdomain/imx/gpcv2.c diff --git a/drivers/genpd/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c similarity index 100% rename from drivers/genpd/imx/imx8m-blk-ctrl.c rename to drivers/pmdomain/imx/imx8m-blk-ctrl.c diff --git a/drivers/genpd/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c similarity index 100% rename from drivers/genpd/imx/imx8mp-blk-ctrl.c rename to drivers/pmdomain/imx/imx8mp-blk-ctrl.c diff --git a/drivers/genpd/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c similarity index 100% rename from drivers/genpd/imx/imx93-blk-ctrl.c rename to drivers/pmdomain/imx/imx93-blk-ctrl.c diff --git a/drivers/genpd/imx/imx93-pd.c b/drivers/pmdomain/imx/imx93-pd.c similarity index 100% rename from drivers/genpd/imx/imx93-pd.c rename to drivers/pmdomain/imx/imx93-pd.c diff --git a/drivers/genpd/imx/scu-pd.c b/drivers/pmdomain/imx/scu-pd.c similarity index 100% rename from drivers/genpd/imx/scu-pd.c rename to drivers/pmdomain/imx/scu-pd.c diff --git a/drivers/genpd/mediatek/Makefile b/drivers/pmdomain/mediatek/Makefile similarity index 100% rename from drivers/genpd/mediatek/Makefile rename to drivers/pmdomain/mediatek/Makefile diff --git a/drivers/genpd/mediatek/mt6795-pm-domains.h b/drivers/pmdomain/mediatek/mt6795-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt6795-pm-domains.h rename to drivers/pmdomain/mediatek/mt6795-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8167-pm-domains.h b/drivers/pmdomain/mediatek/mt8167-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8167-pm-domains.h rename to drivers/pmdomain/mediatek/mt8167-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8173-pm-domains.h b/drivers/pmdomain/mediatek/mt8173-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8173-pm-domains.h rename to drivers/pmdomain/mediatek/mt8173-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8183-pm-domains.h b/drivers/pmdomain/mediatek/mt8183-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8183-pm-domains.h rename to drivers/pmdomain/mediatek/mt8183-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8186-pm-domains.h b/drivers/pmdomain/mediatek/mt8186-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8186-pm-domains.h rename to drivers/pmdomain/mediatek/mt8186-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8188-pm-domains.h b/drivers/pmdomain/mediatek/mt8188-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8188-pm-domains.h rename to drivers/pmdomain/mediatek/mt8188-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8192-pm-domains.h b/drivers/pmdomain/mediatek/mt8192-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8192-pm-domains.h rename to drivers/pmdomain/mediatek/mt8192-pm-domains.h diff --git a/drivers/genpd/mediatek/mt8195-pm-domains.h b/drivers/pmdomain/mediatek/mt8195-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mt8195-pm-domains.h rename to drivers/pmdomain/mediatek/mt8195-pm-domains.h diff --git a/drivers/genpd/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c similarity index 100% rename from drivers/genpd/mediatek/mtk-pm-domains.c rename to drivers/pmdomain/mediatek/mtk-pm-domains.c diff --git a/drivers/genpd/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h similarity index 100% rename from drivers/genpd/mediatek/mtk-pm-domains.h rename to drivers/pmdomain/mediatek/mtk-pm-domains.h diff --git a/drivers/genpd/mediatek/mtk-scpsys.c b/drivers/pmdomain/mediatek/mtk-scpsys.c similarity index 100% rename from drivers/genpd/mediatek/mtk-scpsys.c rename to drivers/pmdomain/mediatek/mtk-scpsys.c diff --git a/drivers/genpd/qcom/Makefile b/drivers/pmdomain/qcom/Makefile similarity index 100% rename from drivers/genpd/qcom/Makefile rename to drivers/pmdomain/qcom/Makefile diff --git a/drivers/genpd/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c similarity index 100% rename from drivers/genpd/qcom/cpr.c rename to drivers/pmdomain/qcom/cpr.c diff --git a/drivers/genpd/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c similarity index 100% rename from drivers/genpd/qcom/rpmhpd.c rename to drivers/pmdomain/qcom/rpmhpd.c diff --git a/drivers/genpd/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c similarity index 100% rename from drivers/genpd/qcom/rpmpd.c rename to drivers/pmdomain/qcom/rpmpd.c diff --git a/drivers/genpd/renesas/Makefile b/drivers/pmdomain/renesas/Makefile similarity index 100% rename from drivers/genpd/renesas/Makefile rename to drivers/pmdomain/renesas/Makefile diff --git a/drivers/genpd/renesas/r8a7742-sysc.c b/drivers/pmdomain/renesas/r8a7742-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7742-sysc.c rename to drivers/pmdomain/renesas/r8a7742-sysc.c diff --git a/drivers/genpd/renesas/r8a7743-sysc.c b/drivers/pmdomain/renesas/r8a7743-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7743-sysc.c rename to drivers/pmdomain/renesas/r8a7743-sysc.c diff --git a/drivers/genpd/renesas/r8a7745-sysc.c b/drivers/pmdomain/renesas/r8a7745-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7745-sysc.c rename to drivers/pmdomain/renesas/r8a7745-sysc.c diff --git a/drivers/genpd/renesas/r8a77470-sysc.c b/drivers/pmdomain/renesas/r8a77470-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a77470-sysc.c rename to drivers/pmdomain/renesas/r8a77470-sysc.c diff --git a/drivers/genpd/renesas/r8a774a1-sysc.c b/drivers/pmdomain/renesas/r8a774a1-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a774a1-sysc.c rename to drivers/pmdomain/renesas/r8a774a1-sysc.c diff --git a/drivers/genpd/renesas/r8a774b1-sysc.c b/drivers/pmdomain/renesas/r8a774b1-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a774b1-sysc.c rename to drivers/pmdomain/renesas/r8a774b1-sysc.c diff --git a/drivers/genpd/renesas/r8a774c0-sysc.c b/drivers/pmdomain/renesas/r8a774c0-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a774c0-sysc.c rename to drivers/pmdomain/renesas/r8a774c0-sysc.c diff --git a/drivers/genpd/renesas/r8a774e1-sysc.c b/drivers/pmdomain/renesas/r8a774e1-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a774e1-sysc.c rename to drivers/pmdomain/renesas/r8a774e1-sysc.c diff --git a/drivers/genpd/renesas/r8a7779-sysc.c b/drivers/pmdomain/renesas/r8a7779-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7779-sysc.c rename to drivers/pmdomain/renesas/r8a7779-sysc.c diff --git a/drivers/genpd/renesas/r8a7790-sysc.c b/drivers/pmdomain/renesas/r8a7790-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7790-sysc.c rename to drivers/pmdomain/renesas/r8a7790-sysc.c diff --git a/drivers/genpd/renesas/r8a7791-sysc.c b/drivers/pmdomain/renesas/r8a7791-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7791-sysc.c rename to drivers/pmdomain/renesas/r8a7791-sysc.c diff --git a/drivers/genpd/renesas/r8a7792-sysc.c b/drivers/pmdomain/renesas/r8a7792-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7792-sysc.c rename to drivers/pmdomain/renesas/r8a7792-sysc.c diff --git a/drivers/genpd/renesas/r8a7794-sysc.c b/drivers/pmdomain/renesas/r8a7794-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7794-sysc.c rename to drivers/pmdomain/renesas/r8a7794-sysc.c diff --git a/drivers/genpd/renesas/r8a7795-sysc.c b/drivers/pmdomain/renesas/r8a7795-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7795-sysc.c rename to drivers/pmdomain/renesas/r8a7795-sysc.c diff --git a/drivers/genpd/renesas/r8a7796-sysc.c b/drivers/pmdomain/renesas/r8a7796-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a7796-sysc.c rename to drivers/pmdomain/renesas/r8a7796-sysc.c diff --git a/drivers/genpd/renesas/r8a77965-sysc.c b/drivers/pmdomain/renesas/r8a77965-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a77965-sysc.c rename to drivers/pmdomain/renesas/r8a77965-sysc.c diff --git a/drivers/genpd/renesas/r8a77970-sysc.c b/drivers/pmdomain/renesas/r8a77970-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a77970-sysc.c rename to drivers/pmdomain/renesas/r8a77970-sysc.c diff --git a/drivers/genpd/renesas/r8a77980-sysc.c b/drivers/pmdomain/renesas/r8a77980-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a77980-sysc.c rename to drivers/pmdomain/renesas/r8a77980-sysc.c diff --git a/drivers/genpd/renesas/r8a77990-sysc.c b/drivers/pmdomain/renesas/r8a77990-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a77990-sysc.c rename to drivers/pmdomain/renesas/r8a77990-sysc.c diff --git a/drivers/genpd/renesas/r8a77995-sysc.c b/drivers/pmdomain/renesas/r8a77995-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a77995-sysc.c rename to drivers/pmdomain/renesas/r8a77995-sysc.c diff --git a/drivers/genpd/renesas/r8a779a0-sysc.c b/drivers/pmdomain/renesas/r8a779a0-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a779a0-sysc.c rename to drivers/pmdomain/renesas/r8a779a0-sysc.c diff --git a/drivers/genpd/renesas/r8a779f0-sysc.c b/drivers/pmdomain/renesas/r8a779f0-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a779f0-sysc.c rename to drivers/pmdomain/renesas/r8a779f0-sysc.c diff --git a/drivers/genpd/renesas/r8a779g0-sysc.c b/drivers/pmdomain/renesas/r8a779g0-sysc.c similarity index 100% rename from drivers/genpd/renesas/r8a779g0-sysc.c rename to drivers/pmdomain/renesas/r8a779g0-sysc.c diff --git a/drivers/genpd/renesas/rcar-gen4-sysc.c b/drivers/pmdomain/renesas/rcar-gen4-sysc.c similarity index 100% rename from drivers/genpd/renesas/rcar-gen4-sysc.c rename to drivers/pmdomain/renesas/rcar-gen4-sysc.c diff --git a/drivers/genpd/renesas/rcar-gen4-sysc.h b/drivers/pmdomain/renesas/rcar-gen4-sysc.h similarity index 100% rename from drivers/genpd/renesas/rcar-gen4-sysc.h rename to drivers/pmdomain/renesas/rcar-gen4-sysc.h diff --git a/drivers/genpd/renesas/rcar-sysc.c b/drivers/pmdomain/renesas/rcar-sysc.c similarity index 100% rename from drivers/genpd/renesas/rcar-sysc.c rename to drivers/pmdomain/renesas/rcar-sysc.c diff --git a/drivers/genpd/renesas/rcar-sysc.h b/drivers/pmdomain/renesas/rcar-sysc.h similarity index 100% rename from drivers/genpd/renesas/rcar-sysc.h rename to drivers/pmdomain/renesas/rcar-sysc.h diff --git a/drivers/genpd/renesas/rmobile-sysc.c b/drivers/pmdomain/renesas/rmobile-sysc.c similarity index 100% rename from drivers/genpd/renesas/rmobile-sysc.c rename to drivers/pmdomain/renesas/rmobile-sysc.c diff --git a/drivers/genpd/rockchip/Makefile b/drivers/pmdomain/rockchip/Makefile similarity index 100% rename from drivers/genpd/rockchip/Makefile rename to drivers/pmdomain/rockchip/Makefile diff --git a/drivers/genpd/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c similarity index 100% rename from drivers/genpd/rockchip/pm-domains.c rename to drivers/pmdomain/rockchip/pm-domains.c diff --git a/drivers/genpd/samsung/Makefile b/drivers/pmdomain/samsung/Makefile similarity index 100% rename from drivers/genpd/samsung/Makefile rename to drivers/pmdomain/samsung/Makefile diff --git a/drivers/genpd/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c similarity index 100% rename from drivers/genpd/samsung/exynos-pm-domains.c rename to drivers/pmdomain/samsung/exynos-pm-domains.c diff --git a/drivers/genpd/st/Makefile b/drivers/pmdomain/st/Makefile similarity index 100% rename from drivers/genpd/st/Makefile rename to drivers/pmdomain/st/Makefile diff --git a/drivers/genpd/st/ste-ux500-pm-domain.c b/drivers/pmdomain/st/ste-ux500-pm-domain.c similarity index 100% rename from drivers/genpd/st/ste-ux500-pm-domain.c rename to drivers/pmdomain/st/ste-ux500-pm-domain.c diff --git a/drivers/genpd/starfive/Makefile b/drivers/pmdomain/starfive/Makefile similarity index 100% rename from drivers/genpd/starfive/Makefile rename to drivers/pmdomain/starfive/Makefile diff --git a/drivers/genpd/starfive/jh71xx-pmu.c b/drivers/pmdomain/starfive/jh71xx-pmu.c similarity index 100% rename from drivers/genpd/starfive/jh71xx-pmu.c rename to drivers/pmdomain/starfive/jh71xx-pmu.c diff --git a/drivers/genpd/sunxi/Makefile b/drivers/pmdomain/sunxi/Makefile similarity index 100% rename from drivers/genpd/sunxi/Makefile rename to drivers/pmdomain/sunxi/Makefile diff --git a/drivers/genpd/sunxi/sun20i-ppu.c b/drivers/pmdomain/sunxi/sun20i-ppu.c similarity index 100% rename from drivers/genpd/sunxi/sun20i-ppu.c rename to drivers/pmdomain/sunxi/sun20i-ppu.c diff --git a/drivers/genpd/tegra/Makefile b/drivers/pmdomain/tegra/Makefile similarity index 100% rename from drivers/genpd/tegra/Makefile rename to drivers/pmdomain/tegra/Makefile diff --git a/drivers/genpd/tegra/powergate-bpmp.c b/drivers/pmdomain/tegra/powergate-bpmp.c similarity index 100% rename from drivers/genpd/tegra/powergate-bpmp.c rename to drivers/pmdomain/tegra/powergate-bpmp.c diff --git a/drivers/genpd/ti/Makefile b/drivers/pmdomain/ti/Makefile similarity index 100% rename from drivers/genpd/ti/Makefile rename to drivers/pmdomain/ti/Makefile diff --git a/drivers/genpd/ti/omap_prm.c b/drivers/pmdomain/ti/omap_prm.c similarity index 100% rename from drivers/genpd/ti/omap_prm.c rename to drivers/pmdomain/ti/omap_prm.c diff --git a/drivers/genpd/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c similarity index 100% rename from drivers/genpd/ti/ti_sci_pm_domains.c rename to drivers/pmdomain/ti/ti_sci_pm_domains.c diff --git a/drivers/genpd/xilinx/Makefile b/drivers/pmdomain/xilinx/Makefile similarity index 100% rename from drivers/genpd/xilinx/Makefile rename to drivers/pmdomain/xilinx/Makefile diff --git a/drivers/genpd/xilinx/zynqmp-pm-domains.c b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c similarity index 100% rename from drivers/genpd/xilinx/zynqmp-pm-domains.c rename to drivers/pmdomain/xilinx/zynqmp-pm-domains.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 59e1ebb7842e5e4ee6daa01cfd9cc99f845658d5..411e00b255d69751ac33e1f71cfbdde9482da54f 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -300,7 +300,7 @@ config NVMEM_REBOOT_MODE config POWER_MLXBF tristate "Mellanox BlueField power handling driver" - depends on (GPIO_MLXBF2 && ACPI) + depends on (GPIO_MLXBF2 || GPIO_MLXBF3) && ACPI help This driver supports reset or low power mode handling for Mellanox BlueField. diff --git a/drivers/power/reset/pwr-mlxbf.c b/drivers/power/reset/pwr-mlxbf.c index 12dedf841a44016d6b7113ea99e59f7922501ae0..de35d24bb7ef3edcf22afbdf597ad3436cba0ae5 100644 --- a/drivers/power/reset/pwr-mlxbf.c +++ b/drivers/power/reset/pwr-mlxbf.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause /* * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index 447ffdacddf9d0e77d96a3d07fb20a1ea61c475c..17064d7b19f61b1b778d34befbfd0d55ce571388 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -121,7 +121,7 @@ static int vexpress_reset_probe(struct platform_device *pdev) return PTR_ERR(regmap); dev_set_drvdata(&pdev->dev, regmap); - switch ((enum vexpress_reset_func)match->data) { + switch ((uintptr_t)match->data) { case FUNC_SHUTDOWN: vexpress_power_off_device = &pdev->dev; pm_power_off = vexpress_power_off; diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 663a1c4238066eddacbab1a1f2ade63cc1e79807..a61bb1283e1978c717493b2197868d5ab78cd35b 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -769,6 +769,7 @@ config BATTERY_RT5033 config CHARGER_RT5033 tristate "RT5033 battery charger support" depends on MFD_RT5033 + depends on EXTCON || !EXTCON help This adds support for battery charger in Richtek RT5033 PMIC. The device supports pre-charge mode, fast charge mode and diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index 6f83e99d2eb72e34b5666a76ecff1cf7ffd367ab..ce36d6ca3422674e72c82ae9c11a9df0de3150ed 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -115,7 +115,6 @@ struct ab8500_btemp { static enum power_supply_property ab8500_btemp_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_TEMP, }; @@ -532,12 +531,6 @@ static int ab8500_btemp_get_property(struct power_supply *psy, else val->intval = 1; break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - if (di->bm->bi) - val->intval = di->bm->bi->technology; - else - val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; - break; case POWER_SUPPLY_PROP_TEMP: val->intval = ab8500_btemp_get_temp(di); break; @@ -662,7 +655,7 @@ static char *supply_interface[] = { static const struct power_supply_desc ab8500_btemp_desc = { .name = "ab8500_btemp", - .type = POWER_SUPPLY_TYPE_BATTERY, + .type = POWER_SUPPLY_TYPE_UNKNOWN, .properties = ab8500_btemp_props, .num_properties = ARRAY_SIZE(ab8500_btemp_props), .get_property = ab8500_btemp_get_property, diff --git a/drivers/power/supply/ab8500_chargalg.c b/drivers/power/supply/ab8500_chargalg.c index ea4ad61d4c7e2ed66f386a29e367cefe5705a81a..2205ea0834a614c1aff346defec934aa2174a6cb 100644 --- a/drivers/power/supply/ab8500_chargalg.c +++ b/drivers/power/supply/ab8500_chargalg.c @@ -1720,7 +1720,7 @@ static char *supply_interface[] = { static const struct power_supply_desc ab8500_chargalg_desc = { .name = "ab8500_chargalg", - .type = POWER_SUPPLY_TYPE_BATTERY, + .type = POWER_SUPPLY_TYPE_UNKNOWN, .properties = ab8500_chargalg_props, .num_properties = ARRAY_SIZE(ab8500_chargalg_props), .get_property = ab8500_chargalg_get_property, diff --git a/drivers/power/supply/mt6370-charger.c b/drivers/power/supply/mt6370-charger.c index f27dae5043f5b41704b26a635db355eab5305ec7..a9641bd3d8cf8a413f9679dfb2ca9ee41ae45e75 100644 --- a/drivers/power/supply/mt6370-charger.c +++ b/drivers/power/supply/mt6370-charger.c @@ -324,7 +324,7 @@ static int mt6370_chg_toggle_cfo(struct mt6370_priv *priv) if (fl_strobe) { dev_err(priv->dev, "Flash led is still in strobe mode\n"); - return ret; + return -EINVAL; } /* cfo off */ diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 4aa466c945e2574d16811216fdd17aecfd14511e..0b69fb7bafd85da9b51a9d40db7a24553447bf5b 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -1309,8 +1309,8 @@ static int psy_register_thermal(struct power_supply *psy) struct thermal_zone_params tzp = { .no_hwmon = IS_ENABLED(CONFIG_POWER_SUPPLY_HWMON) }; - psy->tzd = thermal_zone_device_register(psy->desc->name, - 0, 0, psy, &psy_tzd_ops, &tzp, 0, 0); + psy->tzd = thermal_tripless_zone_device_register(psy->desc->name, + psy, &psy_tzd_ops, &tzp); if (IS_ERR(psy->tzd)) return PTR_ERR(psy->tzd); ret = thermal_zone_device_enable(psy->tzd); diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index 06e5b6b0e255ce8bf159c2f5f0e012916fe12551..d483a81560ab0bd91c041a7a8cd71f0db315144a 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -482,6 +482,13 @@ int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env) if (ret) return ret; + /* + * Kernel generates KOBJ_REMOVE uevent in device removal path, after + * resources have been freed. Exit early to avoid use-after-free. + */ + if (psy->removing) + return 0; + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); if (!prop_buf) return -ENOMEM; diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c index 8328bcea1a29906e38e7d214d79b1ed3cae5972f..f64daf5a41d9383e8b98a71c1e423542b3644b30 100644 --- a/drivers/power/supply/rk817_charger.c +++ b/drivers/power/supply/rk817_charger.c @@ -1045,6 +1045,13 @@ static void rk817_charging_monitor(struct work_struct *work) queue_delayed_work(system_wq, &charger->work, msecs_to_jiffies(8000)); } +static void rk817_cleanup_node(void *data) +{ + struct device_node *node = data; + + of_node_put(node); +} + static int rk817_charger_probe(struct platform_device *pdev) { struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent); @@ -1061,11 +1068,13 @@ static int rk817_charger_probe(struct platform_device *pdev) if (!node) return -ENODEV; + ret = devm_add_action_or_reset(&pdev->dev, rk817_cleanup_node, node); + if (ret) + return ret; + charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); - if (!charger) { - of_node_put(node); + if (!charger) return -ENOMEM; - } charger->rk808 = rk808; @@ -1211,3 +1220,4 @@ MODULE_DESCRIPTION("Battery power supply driver for RK817 PMIC"); MODULE_AUTHOR("Maya Matuszczyk "); MODULE_AUTHOR("Chris Morgan "); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rk817-charger"); diff --git a/drivers/power/supply/rt9467-charger.c b/drivers/power/supply/rt9467-charger.c index 683adb18253dd92fc05f62c6e7622cbd6f3412ec..fdfdc83ab045887299c8f0d154e23b9dc734cb9b 100644 --- a/drivers/power/supply/rt9467-charger.c +++ b/drivers/power/supply/rt9467-charger.c @@ -598,8 +598,8 @@ static int rt9467_run_aicl(struct rt9467_chg_data *data) reinit_completion(&data->aicl_done); ret = wait_for_completion_timeout(&data->aicl_done, msecs_to_jiffies(3500)); - if (ret) - return ret; + if (ret == 0) + return -ETIMEDOUT; ret = rt9467_get_value_from_ranges(data, F_IAICR, RT9467_RANGE_IAICR, &aicr_get); if (ret) { diff --git a/drivers/power/supply/ucs1002_power.c b/drivers/power/supply/ucs1002_power.c index 954feba6600b8aa7751386ee4ee7b515192312a9..7970843a4f480d70999cf54449360aedf85f9598 100644 --- a/drivers/power/supply/ucs1002_power.c +++ b/drivers/power/supply/ucs1002_power.c @@ -384,7 +384,8 @@ static int ucs1002_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_USB_TYPE: return ucs1002_get_usb_type(info, val); case POWER_SUPPLY_PROP_HEALTH: - return val->intval = info->health; + val->intval = info->health; + return 0; case POWER_SUPPLY_PROP_PRESENT: val->intval = info->present; return 0; diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index 5c2e6d5eea2a22f4d6b2b15e4f57d75bbd875038..40a2cc649c79b4cb6961cc2382b98b0b942efffb 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -658,8 +658,6 @@ static struct rapl_primitive_info rpi_msr[NR_RAPL_PRIMITIVES] = { RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), [PL2_CLAMP] = PRIMITIVE_INFO_INIT(PL2_CLAMP, POWER_LIMIT2_CLAMP, 48, RAPL_DOMAIN_REG_LIMIT, ARBITRARY_UNIT, 0), - [PL4_ENABLE] = PRIMITIVE_INFO_INIT(PL4_ENABLE, POWER_LIMIT4_MASK, 0, - RAPL_DOMAIN_REG_PL4, ARBITRARY_UNIT, 0), [TIME_WINDOW1] = PRIMITIVE_INFO_INIT(TIME_WINDOW1, TIME_WINDOW1_MASK, 17, RAPL_DOMAIN_REG_LIMIT, TIME_UNIT, 0), [TIME_WINDOW2] = PRIMITIVE_INFO_INIT(TIME_WINDOW2, TIME_WINDOW2_MASK, 49, @@ -1458,7 +1456,7 @@ static void rapl_detect_powerlimit(struct rapl_domain *rd) } } - if (rapl_read_pl_data(rd, i, PL_ENABLE, false, &val64)) + if (rapl_read_pl_data(rd, i, PL_LIMIT, false, &val64)) rd->rpl[i].name = NULL; } } diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 20a974ced8d6c30d09f6770409c9d49d0f6daa94..a7a6947ab4bc59d58d7df7320321e9318ce96b06 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -3998,7 +3998,6 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev) return 0; out: - ptp_ocp_dev_release(&bp->dev); put_device(&bp->dev); return err; } diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 6210babb0741a8cac70b7e06e975e72aeea0fe2a..8ebcddf91f7b78582ab3b182879477a7be4f5d38 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -505,7 +505,7 @@ config PWM_ROCKCHIP config PWM_RZ_MTU3 tristate "Renesas RZ/G2L MTU3a PWM Timer support" - depends on RZ_MTU3 || COMPILE_TEST + depends on RZ_MTU3 depends on HAS_IOMEM help This driver exposes the MTU3a PWM Timer controller found in Renesas diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 3dacceaef4a9bed757d697d862d2de7808894d69..dc66e3405bf50bf10f128d01bacb899023cb7dfe 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -8,8 +8,8 @@ #include #include +#include #include -#include #include #include #include @@ -127,28 +127,28 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) } struct pwm_device * -of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) +of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) { struct pwm_device *pwm; - if (pc->of_pwm_n_cells < 2) + if (chip->of_pwm_n_cells < 2) return ERR_PTR(-EINVAL); /* flags in the third cell are optional */ if (args->args_count < 2) return ERR_PTR(-EINVAL); - if (args->args[0] >= pc->npwm) + if (args->args[0] >= chip->npwm) return ERR_PTR(-EINVAL); - pwm = pwm_request_from_chip(pc, args->args[0], NULL); + pwm = pwm_request_from_chip(chip, args->args[0], NULL); if (IS_ERR(pwm)) return pwm; pwm->args.period = args->args[1]; pwm->args.polarity = PWM_POLARITY_NORMAL; - if (pc->of_pwm_n_cells >= 3) { + if (chip->of_pwm_n_cells >= 3) { if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; } @@ -158,18 +158,18 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); struct pwm_device * -of_pwm_single_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) +of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) { struct pwm_device *pwm; - if (pc->of_pwm_n_cells < 1) + if (chip->of_pwm_n_cells < 1) return ERR_PTR(-EINVAL); /* validate that one cell is specified, optionally with flags */ if (args->args_count != 1 && args->args_count != 2) return ERR_PTR(-EINVAL); - pwm = pwm_request_from_chip(pc, 0, NULL); + pwm = pwm_request_from_chip(chip, 0, NULL); if (IS_ERR(pwm)) return pwm; @@ -312,22 +312,19 @@ EXPORT_SYMBOL_GPL(pwmchip_add); * pwmchip_remove() - remove a PWM chip * @chip: the PWM chip to remove * - * Removes a PWM chip. This function may return busy if the PWM chip provides - * a PWM device that is still requested. - * - * Returns: 0 on success or a negative error code on failure. + * Removes a PWM chip. */ void pwmchip_remove(struct pwm_chip *chip) { pwmchip_sysfs_unexport(chip); + if (IS_ENABLED(CONFIG_OF)) + of_pwmchip_remove(chip); + mutex_lock(&pwm_lock); list_del_init(&chip->list); - if (IS_ENABLED(CONFIG_OF)) - of_pwmchip_remove(chip); - free_pwms(chip); mutex_unlock(&pwm_lock); @@ -692,7 +689,7 @@ static struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, struct pwm_device *pwm = NULL; struct of_phandle_args args; struct device_link *dl; - struct pwm_chip *pc; + struct pwm_chip *chip; int index = 0; int err; @@ -709,16 +706,16 @@ static struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, return ERR_PTR(err); } - pc = fwnode_to_pwmchip(of_fwnode_handle(args.np)); - if (IS_ERR(pc)) { - if (PTR_ERR(pc) != -EPROBE_DEFER) + chip = fwnode_to_pwmchip(of_fwnode_handle(args.np)); + if (IS_ERR(chip)) { + if (PTR_ERR(chip) != -EPROBE_DEFER) pr_err("%s(): PWM chip not found\n", __func__); - pwm = ERR_CAST(pc); + pwm = ERR_CAST(chip); goto put; } - pwm = pc->of_xlate(pc, &args); + pwm = chip->of_xlate(chip, &args); if (IS_ERR(pwm)) goto put; diff --git a/drivers/pwm/pwm-apple.c b/drivers/pwm/pwm-apple.c index a38a62edd71322a5b23e2abeb002aa3faa36f03f..8e7d67fb5fbefd84e32c7437335784e5b6238e0b 100644 --- a/drivers/pwm/pwm-apple.c +++ b/drivers/pwm/pwm-apple.c @@ -12,6 +12,7 @@ * - When APPLE_PWM_CTRL is set to 0, the output is constant low */ +#include #include #include #include diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 96a709a9d49a812feb2a24515a78052fe5b65b91..e271d920151e471c8d6622841c7b432557da8c46 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -38,11 +39,11 @@ static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip) return container_of(chip, struct atmel_hlcdc_pwm, chip); } -static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, +static int atmel_hlcdc_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { - struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c); - struct atmel_hlcdc *hlcdc = chip->hlcdc; + struct atmel_hlcdc_pwm *atmel = to_atmel_hlcdc_pwm(chip); + struct atmel_hlcdc *hlcdc = atmel->hlcdc; unsigned int status; int ret; @@ -54,7 +55,7 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, u32 pwmcfg; int pres; - if (!chip->errata || !chip->errata->slow_clk_erratum) { + if (!atmel->errata || !atmel->errata->slow_clk_erratum) { clk_freq = clk_get_rate(new_clk); if (!clk_freq) return -EINVAL; @@ -64,7 +65,7 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, } /* Errata: cannot use slow clk on some IP revisions */ - if ((chip->errata && chip->errata->slow_clk_erratum) || + if ((atmel->errata && atmel->errata->slow_clk_erratum) || clk_period_ns > state->period) { new_clk = hlcdc->sys_clk; clk_freq = clk_get_rate(new_clk); @@ -77,8 +78,8 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) { /* Errata: cannot divide by 1 on some IP revisions */ - if (!pres && chip->errata && - chip->errata->div1_clk_erratum) + if (!pres && atmel->errata && + atmel->errata->div1_clk_erratum) continue; if ((clk_period_ns << pres) >= state->period) @@ -90,7 +91,7 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, pwmcfg = ATMEL_HLCDC_PWMPS(pres); - if (new_clk != chip->cur_clk) { + if (new_clk != atmel->cur_clk) { u32 gencfg = 0; int ret; @@ -98,8 +99,8 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, if (ret) return ret; - clk_disable_unprepare(chip->cur_clk); - chip->cur_clk = new_clk; + clk_disable_unprepare(atmel->cur_clk); + atmel->cur_clk = new_clk; if (new_clk == hlcdc->sys_clk) gencfg = ATMEL_HLCDC_CLKPWMSEL; @@ -160,8 +161,8 @@ static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm, if (ret) return ret; - clk_disable_unprepare(chip->cur_clk); - chip->cur_clk = NULL; + clk_disable_unprepare(atmel->cur_clk); + atmel->cur_clk = NULL; } return 0; @@ -183,31 +184,32 @@ static const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = { #ifdef CONFIG_PM_SLEEP static int atmel_hlcdc_pwm_suspend(struct device *dev) { - struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev); + struct atmel_hlcdc_pwm *atmel = dev_get_drvdata(dev); /* Keep the periph clock enabled if the PWM is still running. */ - if (pwm_is_enabled(&chip->chip.pwms[0])) - clk_disable_unprepare(chip->hlcdc->periph_clk); + if (pwm_is_enabled(&atmel->chip.pwms[0])) + clk_disable_unprepare(atmel->hlcdc->periph_clk); return 0; } static int atmel_hlcdc_pwm_resume(struct device *dev) { - struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev); + struct atmel_hlcdc_pwm *atmel = dev_get_drvdata(dev); struct pwm_state state; int ret; - pwm_get_state(&chip->chip.pwms[0], &state); + pwm_get_state(&atmel->chip.pwms[0], &state); /* Re-enable the periph clock it was stopped during suspend. */ if (!state.enabled) { - ret = clk_prepare_enable(chip->hlcdc->periph_clk); + ret = clk_prepare_enable(atmel->hlcdc->periph_clk); if (ret) return ret; } - return atmel_hlcdc_pwm_apply(&chip->chip, &chip->chip.pwms[0], &state); + return atmel_hlcdc_pwm_apply(&atmel->chip, &atmel->chip.pwms[0], + &state); } #endif @@ -244,14 +246,14 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) { const struct of_device_id *match; struct device *dev = &pdev->dev; - struct atmel_hlcdc_pwm *chip; + struct atmel_hlcdc_pwm *atmel; struct atmel_hlcdc *hlcdc; int ret; hlcdc = dev_get_drvdata(dev->parent); - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) + atmel = devm_kzalloc(dev, sizeof(*atmel), GFP_KERNEL); + if (!atmel) return -ENOMEM; ret = clk_prepare_enable(hlcdc->periph_clk); @@ -260,31 +262,31 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node); if (match) - chip->errata = match->data; + atmel->errata = match->data; - chip->hlcdc = hlcdc; - chip->chip.ops = &atmel_hlcdc_pwm_ops; - chip->chip.dev = dev; - chip->chip.npwm = 1; + atmel->hlcdc = hlcdc; + atmel->chip.ops = &atmel_hlcdc_pwm_ops; + atmel->chip.dev = dev; + atmel->chip.npwm = 1; - ret = pwmchip_add(&chip->chip); + ret = pwmchip_add(&atmel->chip); if (ret) { clk_disable_unprepare(hlcdc->periph_clk); return ret; } - platform_set_drvdata(pdev, chip); + platform_set_drvdata(pdev, atmel); return 0; } static void atmel_hlcdc_pwm_remove(struct platform_device *pdev) { - struct atmel_hlcdc_pwm *chip = platform_get_drvdata(pdev); + struct atmel_hlcdc_pwm *atmel = platform_get_drvdata(pdev); - pwmchip_remove(&chip->chip); + pwmchip_remove(&atmel->chip); - clk_disable_unprepare(chip->hlcdc->periph_clk); + clk_disable_unprepare(atmel->hlcdc->periph_clk); } static const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = { diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 4a116dc44f6e7f0e1e652b6e38b752a048b438a1..c00dd37c5fbd86b9dd060d5c16757348e145a281 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -34,7 +33,6 @@ ATMEL_TC_BEEVT | ATMEL_TC_BSWTRG) struct atmel_tcb_pwm_device { - enum pwm_polarity polarity; /* PWM polarity */ unsigned div; /* PWM clock divider */ unsigned duty; /* PWM duty expressed in clk cycles */ unsigned period; /* PWM period expressed in clk cycles */ @@ -57,7 +55,7 @@ struct atmel_tcb_pwm_chip { struct clk *clk; struct clk *gclk; struct clk *slow_clk; - struct atmel_tcb_pwm_device *pwms[NPWM]; + struct atmel_tcb_pwm_device pwms[NPWM]; struct atmel_tcb_channel bkup; }; @@ -68,37 +66,18 @@ static inline struct atmel_tcb_pwm_chip *to_tcb_chip(struct pwm_chip *chip) return container_of(chip, struct atmel_tcb_pwm_chip, chip); } -static int atmel_tcb_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; - - tcbpwm->polarity = polarity; - - return 0; -} - static int atmel_tcb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; unsigned cmr; int ret; - tcbpwm = devm_kzalloc(chip->dev, sizeof(*tcbpwm), GFP_KERNEL); - if (!tcbpwm) - return -ENOMEM; - ret = clk_prepare_enable(tcbpwmc->clk); - if (ret) { - devm_kfree(chip->dev, tcbpwm); + if (ret) return ret; - } - tcbpwm->polarity = PWM_POLARITY_NORMAL; tcbpwm->duty = 0; tcbpwm->period = 0; tcbpwm->div = 0; @@ -131,27 +110,22 @@ static int atmel_tcb_pwm_request(struct pwm_chip *chip, regmap_write(tcbpwmc->regmap, ATMEL_TC_REG(tcbpwmc->channel, CMR), cmr); spin_unlock(&tcbpwmc->lock); - tcbpwmc->pwms[pwm->hwpwm] = tcbpwm; - return 0; } static void atmel_tcb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; clk_disable_unprepare(tcbpwmc->clk); - tcbpwmc->pwms[pwm->hwpwm] = NULL; - devm_kfree(chip->dev, tcbpwm); } -static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; unsigned cmr; - enum pwm_polarity polarity = tcbpwm->polarity; /* * If duty is 0 the timer will be stopped and we have to @@ -203,12 +177,12 @@ static void atmel_tcb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) spin_unlock(&tcbpwmc->lock); } -static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int atmel_tcb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; u32 cmr; - enum pwm_polarity polarity = tcbpwm->polarity; /* * If duty is 0 the timer will be stopped and we have to @@ -291,7 +265,7 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct atmel_tcb_pwm_chip *tcbpwmc = to_tcb_chip(chip); - struct atmel_tcb_pwm_device *tcbpwm = tcbpwmc->pwms[pwm->hwpwm]; + struct atmel_tcb_pwm_device *tcbpwm = &tcbpwmc->pwms[pwm->hwpwm]; struct atmel_tcb_pwm_device *atcbpwm = NULL; int i = 0; int slowclk = 0; @@ -338,9 +312,9 @@ static int atmel_tcb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, period = div_u64(period_ns, min); if (pwm->hwpwm == 0) - atcbpwm = tcbpwmc->pwms[1]; + atcbpwm = &tcbpwmc->pwms[1]; else - atcbpwm = tcbpwmc->pwms[0]; + atcbpwm = &tcbpwmc->pwms[0]; /* * PWM devices provided by the TCB driver are grouped by 2. @@ -371,11 +345,8 @@ static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, int duty_cycle, period; int ret; - /* This function only sets a flag in driver data */ - atmel_tcb_pwm_set_polarity(chip, pwm, state->polarity); - if (!state->enabled) { - atmel_tcb_pwm_disable(chip, pwm); + atmel_tcb_pwm_disable(chip, pwm, state->polarity); return 0; } @@ -386,7 +357,7 @@ static int atmel_tcb_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (ret) return ret; - return atmel_tcb_pwm_enable(chip, pwm); + return atmel_tcb_pwm_enable(chip, pwm, state->polarity); } static const struct pwm_ops atmel_tcb_pwm_ops = { @@ -422,13 +393,14 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) struct atmel_tcb_pwm_chip *tcbpwm; const struct atmel_tcb_config *config; struct device_node *np = pdev->dev.of_node; - struct regmap *regmap; - struct clk *clk, *gclk = NULL; - struct clk *slow_clk; char clk_name[] = "t0_clk"; int err; int channel; + tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); + if (tcbpwm == NULL) + return -ENOMEM; + err = of_property_read_u32(np, "reg", &channel); if (err < 0) { dev_err(&pdev->dev, @@ -437,49 +409,43 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) return err; } - regmap = syscon_node_to_regmap(np->parent); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + tcbpwm->regmap = syscon_node_to_regmap(np->parent); + if (IS_ERR(tcbpwm->regmap)) + return PTR_ERR(tcbpwm->regmap); - slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); - if (IS_ERR(slow_clk)) - return PTR_ERR(slow_clk); + tcbpwm->slow_clk = of_clk_get_by_name(np->parent, "slow_clk"); + if (IS_ERR(tcbpwm->slow_clk)) + return PTR_ERR(tcbpwm->slow_clk); clk_name[1] += channel; - clk = of_clk_get_by_name(np->parent, clk_name); - if (IS_ERR(clk)) - clk = of_clk_get_by_name(np->parent, "t0_clk"); - if (IS_ERR(clk)) - return PTR_ERR(clk); + tcbpwm->clk = of_clk_get_by_name(np->parent, clk_name); + if (IS_ERR(tcbpwm->clk)) + tcbpwm->clk = of_clk_get_by_name(np->parent, "t0_clk"); + if (IS_ERR(tcbpwm->clk)) { + err = PTR_ERR(tcbpwm->clk); + goto err_slow_clk; + } match = of_match_node(atmel_tcb_of_match, np->parent); config = match->data; if (config->has_gclk) { - gclk = of_clk_get_by_name(np->parent, "gclk"); - if (IS_ERR(gclk)) - return PTR_ERR(gclk); - } - - tcbpwm = devm_kzalloc(&pdev->dev, sizeof(*tcbpwm), GFP_KERNEL); - if (tcbpwm == NULL) { - err = -ENOMEM; - goto err_slow_clk; + tcbpwm->gclk = of_clk_get_by_name(np->parent, "gclk"); + if (IS_ERR(tcbpwm->gclk)) { + err = PTR_ERR(tcbpwm->gclk); + goto err_clk; + } } tcbpwm->chip.dev = &pdev->dev; tcbpwm->chip.ops = &atmel_tcb_pwm_ops; tcbpwm->chip.npwm = NPWM; tcbpwm->channel = channel; - tcbpwm->regmap = regmap; - tcbpwm->clk = clk; - tcbpwm->gclk = gclk; - tcbpwm->slow_clk = slow_clk; tcbpwm->width = config->counter_width; - err = clk_prepare_enable(slow_clk); + err = clk_prepare_enable(tcbpwm->slow_clk); if (err) - goto err_slow_clk; + goto err_gclk; spin_lock_init(&tcbpwm->lock); @@ -494,8 +460,14 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) err_disable_clk: clk_disable_unprepare(tcbpwm->slow_clk); +err_gclk: + clk_put(tcbpwm->gclk); + +err_clk: + clk_put(tcbpwm->clk); + err_slow_clk: - clk_put(slow_clk); + clk_put(tcbpwm->slow_clk); return err; } @@ -507,8 +479,9 @@ static void atmel_tcb_pwm_remove(struct platform_device *pdev) pwmchip_remove(&tcbpwm->chip); clk_disable_unprepare(tcbpwm->slow_clk); - clk_put(tcbpwm->slow_clk); + clk_put(tcbpwm->gclk); clk_put(tcbpwm->clk); + clk_put(tcbpwm->slow_clk); } static const struct of_device_id atmel_tcb_pwm_dt_ids[] = { diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 5f7d286871cf0ec619f6a04806435f7589d5c05a..1f73325d1bea0a3fe2669934af3930a2cce2f233 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -36,7 +35,7 @@ #define PWM_SR 0x0C #define PWM_ISR 0x1C /* Bit field in SR */ -#define PWM_SR_ALL_CH_ON 0x0F +#define PWM_SR_ALL_CH_MASK 0x0F /* The following register is PWM channel related registers */ #define PWM_CH_REG_OFFSET 0x200 @@ -464,6 +463,42 @@ static const struct of_device_id atmel_pwm_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); +static int atmel_pwm_enable_clk_if_on(struct atmel_pwm_chip *atmel_pwm, bool on) +{ + unsigned int i, cnt = 0; + unsigned long sr; + int ret = 0; + + sr = atmel_pwm_readl(atmel_pwm, PWM_SR) & PWM_SR_ALL_CH_MASK; + if (!sr) + return 0; + + cnt = bitmap_weight(&sr, atmel_pwm->chip.npwm); + + if (!on) + goto disable_clk; + + for (i = 0; i < cnt; i++) { + ret = clk_enable(atmel_pwm->clk); + if (ret) { + dev_err(atmel_pwm->chip.dev, + "failed to enable clock for pwm %pe\n", + ERR_PTR(ret)); + + cnt = i; + goto disable_clk; + } + } + + return 0; + +disable_clk: + while (cnt--) + clk_disable(atmel_pwm->clk); + + return ret; +} + static int atmel_pwm_probe(struct platform_device *pdev) { struct atmel_pwm_chip *atmel_pwm; @@ -482,51 +517,39 @@ static int atmel_pwm_probe(struct platform_device *pdev) if (IS_ERR(atmel_pwm->base)) return PTR_ERR(atmel_pwm->base); - atmel_pwm->clk = devm_clk_get(&pdev->dev, NULL); + atmel_pwm->clk = devm_clk_get_prepared(&pdev->dev, NULL); if (IS_ERR(atmel_pwm->clk)) - return PTR_ERR(atmel_pwm->clk); - - ret = clk_prepare(atmel_pwm->clk); - if (ret) { - dev_err(&pdev->dev, "failed to prepare PWM clock\n"); - return ret; - } + return dev_err_probe(&pdev->dev, PTR_ERR(atmel_pwm->clk), + "failed to get prepared PWM clock\n"); atmel_pwm->chip.dev = &pdev->dev; atmel_pwm->chip.ops = &atmel_pwm_ops; atmel_pwm->chip.npwm = 4; - ret = pwmchip_add(&atmel_pwm->chip); + ret = atmel_pwm_enable_clk_if_on(atmel_pwm, true); + if (ret < 0) + return ret; + + ret = devm_pwmchip_add(&pdev->dev, &atmel_pwm->chip); if (ret < 0) { - dev_err(&pdev->dev, "failed to add PWM chip %d\n", ret); - goto unprepare_clk; + dev_err_probe(&pdev->dev, ret, "failed to add PWM chip\n"); + goto disable_clk; } - platform_set_drvdata(pdev, atmel_pwm); + return 0; - return ret; +disable_clk: + atmel_pwm_enable_clk_if_on(atmel_pwm, false); -unprepare_clk: - clk_unprepare(atmel_pwm->clk); return ret; } -static void atmel_pwm_remove(struct platform_device *pdev) -{ - struct atmel_pwm_chip *atmel_pwm = platform_get_drvdata(pdev); - - pwmchip_remove(&atmel_pwm->chip); - - clk_unprepare(atmel_pwm->clk); -} - static struct platform_driver atmel_pwm_driver = { .driver = { .name = "atmel-pwm", .of_match_table = of_match_ptr(atmel_pwm_dt_ids), }, .probe = atmel_pwm_probe, - .remove_new = atmel_pwm_remove, }; module_platform_driver(atmel_pwm_driver); diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 4fa6e249e4cf38b85903cdfa2ee7b8af45d53d7f..e5b00cc9f7a762d4b4fe1b2c753c62aec394f72a 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -61,9 +61,9 @@ struct kona_pwmc { struct clk *clk; }; -static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *_chip) +static inline struct kona_pwmc *to_kona_pwmc(struct pwm_chip *chip) { - return container_of(_chip, struct kona_pwmc, chip); + return container_of(chip, struct kona_pwmc, chip); } /* diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index 0c5992a046b20544ec50618ef5ab2844943832eb..0971c666afd134f28e343b7218e4dbb2a947d88f 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 4703b4a0b6e4ad8a7f355e03e8c7e784d6b6ca64..b9f063dc6b5fd9c70631cfd473f80db748924d46 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -34,9 +34,9 @@ struct crystalcove_pwm { struct regmap *regmap; }; -static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *pc) +static inline struct crystalcove_pwm *to_crc_pwm(struct pwm_chip *chip) { - return container_of(pc, struct crystalcove_pwm, chip); + return container_of(chip, struct crystalcove_pwm, chip); } static int crc_pwm_calc_clk_div(int period_ns) diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c index 74e863aa1d8d4602b413fa04688a615d5f58c15c..baaac0c33aa0687a1a44fa7763b468417670a4e2 100644 --- a/drivers/pwm/pwm-cros-ec.c +++ b/drivers/pwm/pwm-cros-ec.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -37,9 +38,9 @@ struct cros_ec_pwm { u16 duty_cycle; }; -static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *c) +static inline struct cros_ec_pwm_device *pwm_to_cros_ec_pwm(struct pwm_chip *chip) { - return container_of(c, struct cros_ec_pwm_device, chip); + return container_of(chip, struct cros_ec_pwm_device, chip); } static int cros_ec_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -218,14 +219,14 @@ static int cros_ec_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, } static struct pwm_device * -cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) +cros_ec_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) { struct pwm_device *pwm; - if (args->args[0] >= pc->npwm) + if (args->args[0] >= chip->npwm) return ERR_PTR(-EINVAL); - pwm = pwm_request_from_chip(pc, args->args[0], NULL); + pwm = pwm_request_from_chip(chip, args->args[0], NULL); if (IS_ERR(pwm)) return pwm; diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 5caadbd6194e0aee534ac88d2557201e3c72a08a..b7c6045c5d0898de1424a7c022f389467524fcec 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -11,8 +11,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index b95df1a9612789250e7f90a15725db4a126e69a4..f7ba6fe9a349a3ebdb09f16bc50004624ab75f7d 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index 1f2eb1c8ff6c95c04c59a5e54596131a4ee0bff2..0651983bed190c411df3bdf1b7d84b1c6fbb68bc 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index 3b7067f6cd0dc8d664077d23dd7f0345137e8b15..ef1293f2a897eeeb5daefb06afc0789898b32be4 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/pwm/pwm-lp3943.c b/drivers/pwm/pwm-lp3943.c index 35675e4058c6bb71601d0a1b34ca06d36326d9f9..4b133a17f4be3bea2b070237abaed93d25080ef4 100644 --- a/drivers/pwm/pwm-lp3943.c +++ b/drivers/pwm/pwm-lp3943.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -24,9 +25,9 @@ struct lp3943_pwm { struct lp3943_platform_data *pdata; }; -static inline struct lp3943_pwm *to_lp3943_pwm(struct pwm_chip *_chip) +static inline struct lp3943_pwm *to_lp3943_pwm(struct pwm_chip *chip) { - return container_of(_chip, struct lp3943_pwm, chip); + return container_of(chip, struct lp3943_pwm, chip); } static struct lp3943_pwm_map * diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index b9bf5b366f4b1e95b375174497f7a948640c448b..7a19a840bca5fca00d90af908b872ef8e3fdc0de 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -366,30 +367,21 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) if (IS_ERR(lpc18xx_pwm->base)) return PTR_ERR(lpc18xx_pwm->base); - lpc18xx_pwm->pwm_clk = devm_clk_get(&pdev->dev, "pwm"); + lpc18xx_pwm->pwm_clk = devm_clk_get_enabled(&pdev->dev, "pwm"); if (IS_ERR(lpc18xx_pwm->pwm_clk)) return dev_err_probe(&pdev->dev, PTR_ERR(lpc18xx_pwm->pwm_clk), "failed to get pwm clock\n"); - ret = clk_prepare_enable(lpc18xx_pwm->pwm_clk); - if (ret < 0) - return dev_err_probe(&pdev->dev, ret, - "could not prepare or enable pwm clock\n"); - lpc18xx_pwm->clk_rate = clk_get_rate(lpc18xx_pwm->pwm_clk); - if (!lpc18xx_pwm->clk_rate) { - ret = dev_err_probe(&pdev->dev, - -EINVAL, "pwm clock has no frequency\n"); - goto disable_pwmclk; - } + if (!lpc18xx_pwm->clk_rate) + return dev_err_probe(&pdev->dev, + -EINVAL, "pwm clock has no frequency\n"); /* * If clkrate is too fast, the calculations in .apply() might overflow. */ - if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC) { - ret = dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n"); - goto disable_pwmclk; - } + if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC) + return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n"); mutex_init(&lpc18xx_pwm->res_lock); mutex_init(&lpc18xx_pwm->period_lock); @@ -435,18 +427,12 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val); ret = pwmchip_add(&lpc18xx_pwm->chip); - if (ret < 0) { - dev_err_probe(&pdev->dev, ret, "pwmchip_add failed\n"); - goto disable_pwmclk; - } + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "pwmchip_add failed\n"); platform_set_drvdata(pdev, lpc18xx_pwm); return 0; - -disable_pwmclk: - clk_disable_unprepare(lpc18xx_pwm->pwm_clk); - return ret; } static void lpc18xx_pwm_remove(struct platform_device *pdev) @@ -459,8 +445,6 @@ static void lpc18xx_pwm_remove(struct platform_device *pdev) val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val | LPC18XX_PWM_CTRL_HALT); - - clk_disable_unprepare(lpc18xx_pwm->pwm_clk); } static struct platform_driver lpc18xx_pwm_driver = { diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 86a0ea0f6955c47e906c54e947455ca282c6fb7f..806f0bb3ad6d8d1ba6b1c8094b15bcb979111899 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c @@ -51,10 +51,10 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (duty_cycles > 255) duty_cycles = 255; - val = readl(lpc32xx->base + (pwm->hwpwm << 2)); + val = readl(lpc32xx->base); val &= ~0xFFFF; val |= (period_cycles << 8) | duty_cycles; - writel(val, lpc32xx->base + (pwm->hwpwm << 2)); + writel(val, lpc32xx->base); return 0; } @@ -69,9 +69,9 @@ static int lpc32xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) if (ret) return ret; - val = readl(lpc32xx->base + (pwm->hwpwm << 2)); + val = readl(lpc32xx->base); val |= PWM_ENABLE; - writel(val, lpc32xx->base + (pwm->hwpwm << 2)); + writel(val, lpc32xx->base); return 0; } @@ -81,9 +81,9 @@ static void lpc32xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) struct lpc32xx_pwm_chip *lpc32xx = to_lpc32xx_pwm_chip(chip); u32 val; - val = readl(lpc32xx->base + (pwm->hwpwm << 2)); + val = readl(lpc32xx->base); val &= ~PWM_ENABLE; - writel(val, lpc32xx->base + (pwm->hwpwm << 2)); + writel(val, lpc32xx->base); clk_disable_unprepare(lpc32xx->clk); } @@ -141,9 +141,9 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) lpc32xx->chip.npwm = 1; /* If PWM is disabled, configure the output to the default value */ - val = readl(lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2)); + val = readl(lpc32xx->base); val &= ~PWM_PIN_LEVEL; - writel(val, lpc32xx->base + (lpc32xx->chip.pwms[0].hwpwm << 2)); + writel(val, lpc32xx->base); ret = devm_pwmchip_add(&pdev->dev, &lpc32xx->chip); if (ret < 0) { diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 7a51d210a8778150852aa40cc89e086f59ce1018..6adb0ed019066a0f29bb70ff704958aeb020caac 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 22f54db3ae8ea8d36144b837af5254b354bb51b2..25519cddc2a9dd5461671b3de449a0709a4e1bc7 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pwm/pwm-microchip-core.c b/drivers/pwm/pwm-microchip-core.c index 8750b57684a90fb7c131e296042477807788462a..e7525c98105ebc30db26648be60df27bd0b029f1 100644 --- a/drivers/pwm/pwm-microchip-core.c +++ b/drivers/pwm/pwm-microchip-core.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 2401b67332417e3148338ee9531a3f0e4bc4cab1..a83bd6e18b07ff8b2e618f03bae754386b0f2d63 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pwm/pwm-ntxec.c b/drivers/pwm/pwm-ntxec.c index ab63b081df531d66e7b43baf08e850b197276b18..7514ea384ec56585d33c93783fea68bc153528e0 100644 --- a/drivers/pwm/pwm-ntxec.c +++ b/drivers/pwm/pwm-ntxec.c @@ -24,7 +24,6 @@ #include struct ntxec_pwm { - struct device *dev; struct ntxec *ec; struct pwm_chip chip; }; @@ -141,14 +140,13 @@ static int ntxec_pwm_probe(struct platform_device *pdev) struct ntxec_pwm *priv; struct pwm_chip *chip; - pdev->dev.of_node = pdev->dev.parent->of_node; + device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent); priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->ec = ec; - priv->dev = &pdev->dev; chip = &priv->chip; chip->dev = &pdev->dev; diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 762429d5647fd5633119528885e281e41e0175ea..1e475ed10180ea302b0c60d66949bb93ab13323d 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -15,6 +15,7 @@ * input clock (PWMCR_SD is set) and the output is driven to inactive. */ +#include #include #include #include @@ -156,13 +157,6 @@ MODULE_DEVICE_TABLE(of, pwm_of_match); #define pwm_of_match NULL #endif -static const struct platform_device_id *pxa_pwm_get_id_dt(struct device *dev) -{ - const struct of_device_id *id = of_match_device(pwm_of_match, dev); - - return id ? id->data : NULL; -} - static int pwm_probe(struct platform_device *pdev) { const struct platform_device_id *id = platform_get_device_id(pdev); @@ -170,7 +164,7 @@ static int pwm_probe(struct platform_device *pdev) int ret = 0; if (IS_ENABLED(CONFIG_OF) && id == NULL) - id = pxa_pwm_get_id_dt(&pdev->dev); + id = of_device_get_match_data(&pdev->dev); if (id == NULL) return -EINVAL; diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index c1a1f2d864b5ecb107cbac91fc74cd69bba831ef..03ee18fb82d5b40dc5699da1c5410dac830d5ac1 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -52,9 +52,9 @@ struct rockchip_pwm_data { u32 enable_conf; }; -static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) +static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip) { - return container_of(c, struct rockchip_pwm_chip, chip); + return container_of(chip, struct rockchip_pwm_chip, chip); } static int rockchip_pwm_get_state(struct pwm_chip *chip, diff --git a/drivers/pwm/pwm-rz-mtu3.c b/drivers/pwm/pwm-rz-mtu3.c index bed8bd671e374f25f118d597ee4fd262989e433c..a56cecb0e46e3e207811d95104e3a3c74cad375e 100644 --- a/drivers/pwm/pwm-rz-mtu3.c +++ b/drivers/pwm/pwm-rz-mtu3.c @@ -40,7 +40,7 @@ * struct rz_mtu3_channel_io_map - MTU3 pwm channel map * * @base_pwm_number: First PWM of a channel - * @num: number of IOs on the HW channel. + * @num_channel_ios: number of IOs on the HW channel. */ struct rz_mtu3_channel_io_map { u8 base_pwm_number; diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index ae49d67ab2b1a62a6d84d0f031f715146c64d067..eabddb7c782083cefbdffeedcc0f54184ccc814a 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -13,6 +13,7 @@ */ #include #include +#include #include #include #include @@ -51,9 +52,9 @@ struct pwm_sifive_ddata { }; static inline -struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *c) +struct pwm_sifive_ddata *pwm_sifive_chip_to_ddata(struct pwm_chip *chip) { - return container_of(c, struct pwm_sifive_ddata, chip); + return container_of(chip, struct pwm_sifive_ddata, chip); } static int pwm_sifive_request(struct pwm_chip *chip, struct pwm_device *pwm) diff --git a/drivers/pwm/pwm-sl28cpld.c b/drivers/pwm/pwm-sl28cpld.c index e64900ad4ba1e9c6c9bcaecd0465fff36fdb99d1..9e42e3a74ad6ec6e73de13a939fb4f517396fd15 100644 --- a/drivers/pwm/pwm-sl28cpld.c +++ b/drivers/pwm/pwm-sl28cpld.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -80,12 +81,15 @@ regmap_write((priv)->regmap, (priv)->offset + (reg), (val)) struct sl28cpld_pwm { - struct pwm_chip pwm_chip; + struct pwm_chip chip; struct regmap *regmap; u32 offset; }; -#define sl28cpld_pwm_from_chip(_chip) \ - container_of(_chip, struct sl28cpld_pwm, pwm_chip) + +static inline struct sl28cpld_pwm *sl28cpld_pwm_from_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct sl28cpld_pwm, chip); +} static int sl28cpld_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, @@ -228,12 +232,12 @@ static int sl28cpld_pwm_probe(struct platform_device *pdev) } /* Initialize the pwm_chip structure */ - chip = &priv->pwm_chip; + chip = &priv->chip; chip->dev = &pdev->dev; chip->ops = &sl28cpld_pwm_ops; chip->npwm = 1; - ret = devm_pwmchip_add(&pdev->dev, &priv->pwm_chip); + ret = devm_pwmchip_add(&pdev->dev, chip); if (ret) { dev_err(&pdev->dev, "failed to add PWM chip (%pe)", ERR_PTR(ret)); diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c index d43a6fa3f4e0446ab5d9ddc5e07809e87a9a8a15..1499c8c1fe3753a89a655d1638f5513641c3e193 100644 --- a/drivers/pwm/pwm-sprd.c +++ b/drivers/pwm/pwm-sprd.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index 62e397aeb9aa3ff59c19ea1d0a6d89e8f7fbbcce..3d6be7749e23142a7bdd9cda6c4640f3f6ff0246 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -637,7 +637,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) priv->chip.ops = &stm32pwm_ops; priv->chip.npwm = stm32_pwm_detect_channels(priv); - ret = pwmchip_add(&priv->chip); + ret = devm_pwmchip_add(dev, &priv->chip); if (ret < 0) return ret; @@ -646,17 +646,6 @@ static int stm32_pwm_probe(struct platform_device *pdev) return 0; } -static void stm32_pwm_remove(struct platform_device *pdev) -{ - struct stm32_pwm *priv = platform_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < priv->chip.npwm; i++) - pwm_disable(&priv->chip.pwms[i]); - - pwmchip_remove(&priv->chip); -} - static int __maybe_unused stm32_pwm_suspend(struct device *dev) { struct stm32_pwm *priv = dev_get_drvdata(dev); @@ -701,7 +690,6 @@ MODULE_DEVICE_TABLE(of, stm32_pwm_of_match); static struct platform_driver stm32_pwm_driver = { .probe = stm32_pwm_probe, - .remove_new = stm32_pwm_remove, .driver = { .name = "stm32-pwm", .of_match_table = stm32_pwm_of_match, diff --git a/drivers/pwm/pwm-stmpe.c b/drivers/pwm/pwm-stmpe.c index 5d4a4762ce0cdb1475bc345b19151d89e5a98756..e205405c4828b81561199b2d9704efa75e19f022 100644 --- a/drivers/pwm/pwm-stmpe.c +++ b/drivers/pwm/pwm-stmpe.c @@ -61,8 +61,8 @@ static int stmpe_24xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void stmpe_24xx_pwm_disable(struct pwm_chip *chip, - struct pwm_device *pwm) +static int stmpe_24xx_pwm_disable(struct pwm_chip *chip, + struct pwm_device *pwm) { struct stmpe_pwm *stmpe_pwm = to_stmpe_pwm(chip); u8 value; @@ -72,17 +72,16 @@ static void stmpe_24xx_pwm_disable(struct pwm_chip *chip, if (ret < 0) { dev_err(chip->dev, "error reading PWM#%u control\n", pwm->hwpwm); - return; + return ret; } value = ret & ~BIT(pwm->hwpwm); ret = stmpe_reg_write(stmpe_pwm->stmpe, STMPE24XX_PWMCS, value); - if (ret) { + if (ret) dev_err(chip->dev, "error writing PWM#%u control\n", pwm->hwpwm); - return; - } + return ret; } /* STMPE 24xx PWM instructions */ @@ -111,7 +110,9 @@ static int stmpe_24xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Make sure we are disabled */ if (pwm_is_enabled(pwm)) { - stmpe_24xx_pwm_disable(chip, pwm); + ret = stmpe_24xx_pwm_disable(chip, pwm); + if (ret) + return ret; } else { /* Connect the PWM to the pin */ pin = pwm->hwpwm; @@ -269,7 +270,7 @@ static int stmpe_24xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (!state->enabled) { if (pwm->state.enabled) - stmpe_24xx_pwm_disable(chip, pwm); + return stmpe_24xx_pwm_disable(chip, pwm); return 0; } diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index a8790a8fc53e1255561d2811cacde786e9d1f66d..c84fcf1a13dc2c4d2aad25a6edcca4958d4f9f66 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pwm/pwm-sunplus.c b/drivers/pwm/pwm-sunplus.c index d6ebe9f03b3548f247010cce68b04defb6358d54..7705c7b86c3a3066544bcabf8787133d8a8d6231 100644 --- a/drivers/pwm/pwm-sunplus.c +++ b/drivers/pwm/pwm-sunplus.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index 5810abf66e2a48a9758d998c50e40574f47b7a10..a169a34e07781683bf0d68de9ffa475ca12cb43d 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 1094499563077a098f2c491fbe2266dd32a3f0b0..8c94b266c1b2a0ee4b44a820e800ca5fa496a6ff 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include /* ECAP registers and bits definitions */ #define CAP1 0x08 diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index bb3959ace6b48b0bd67ba33d50240be5cbff0874..ecbfd7e954ecb8a1feb10c3c2cadb156e256ac4a 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include /* EHRPWM registers and bits definitions */ diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index e3fb79b3e2a7a023e9ed108cff036946138a9d09..7f7591a2384c558e81d81b609cb665891a656779 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index d2c48fd987067ed2449c0b936f14a7c17b5f16c9..6d46db51daaccdf0cb124d34f172dfe9ff7be9d6 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -6,6 +6,7 @@ * Copyright (C) 2010 Alexey Charkov */ +#include #include #include #include @@ -18,10 +19,6 @@ #include -#include -#include -#include - /* * SoC architecture allocates register space for 4 PWMs but only * 2 are currently implemented. diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d8e1caaf207e108f124988b7a9f1ee3780a12e8c..3137e40fcd3e0586a81b1626304ab955a87695d4 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -5542,6 +5542,8 @@ regulator_register(struct device *dev, goto rinse; } device_initialize(&rdev->dev); + dev_set_drvdata(&rdev->dev, rdev); + rdev->dev.class = ®ulator_class; spin_lock_init(&rdev->err_lock); /* @@ -5603,11 +5605,9 @@ regulator_register(struct device *dev, rdev->supply_name = regulator_desc->supply_name; /* register with sysfs */ - rdev->dev.class = ®ulator_class; rdev->dev.parent = config->dev; dev_set_name(&rdev->dev, "regulator.%lu", (unsigned long) atomic_inc_return(®ulator_no)); - dev_set_drvdata(&rdev->dev, rdev); /* set regulator constraints */ if (init_data) @@ -5724,15 +5724,11 @@ regulator_register(struct device *dev, mutex_lock(®ulator_list_mutex); regulator_ena_gpio_free(rdev); mutex_unlock(®ulator_list_mutex); - put_device(&rdev->dev); - rdev = NULL; clean: if (dangling_of_gpiod) gpiod_put(config->ena_gpiod); - if (rdev && rdev->dev.of_node) - of_node_put(rdev->dev.of_node); - kfree(rdev); kfree(config); + put_device(&rdev->dev); rinse: if (dangling_cfg_gpiod) gpiod_put(cfg->ena_gpiod); diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 5ad5f3b3a6b5d8fc91f1ea2e689a65fc087740be..d4926833655327d6a3494a5b6865087df80ad36d 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -197,7 +197,7 @@ int regulator_set_voltage_sel_pickable_regmap(struct regulator_dev *rdev, sel += rdev->desc->linear_ranges[i].min_sel; range = rdev->desc->linear_range_selectors_bitfield[i]; - range <<= ffs(rdev->desc->vsel_mask) - 1; + range <<= ffs(rdev->desc->vsel_range_mask) - 1; if (rdev->desc->vsel_reg == rdev->desc->vsel_range_reg) { ret = regmap_update_bits(rdev->regmap, diff --git a/drivers/regulator/mt6358-regulator.c b/drivers/regulator/mt6358-regulator.c index b9cda2210c3308655a27914429c7ff52965cb03a..65fbd95f1dbb0c6935f66e32fa61d0664edb562d 100644 --- a/drivers/regulator/mt6358-regulator.c +++ b/drivers/regulator/mt6358-regulator.c @@ -43,7 +43,7 @@ struct mt6358_regulator_info { .desc = { \ .name = #vreg, \ .of_match = of_match_ptr(match), \ - .ops = &mt6358_volt_range_ops, \ + .ops = &mt6358_buck_ops, \ .type = REGULATOR_VOLTAGE, \ .id = MT6358_ID_##vreg, \ .owner = THIS_MODULE, \ @@ -139,7 +139,7 @@ struct mt6358_regulator_info { .desc = { \ .name = #vreg, \ .of_match = of_match_ptr(match), \ - .ops = &mt6358_volt_range_ops, \ + .ops = &mt6358_buck_ops, \ .type = REGULATOR_VOLTAGE, \ .id = MT6366_ID_##vreg, \ .owner = THIS_MODULE, \ @@ -450,7 +450,7 @@ static unsigned int mt6358_regulator_get_mode(struct regulator_dev *rdev) } } -static const struct regulator_ops mt6358_volt_range_ops = { +static const struct regulator_ops mt6358_buck_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -464,6 +464,18 @@ static const struct regulator_ops mt6358_volt_range_ops = { .get_mode = mt6358_regulator_get_mode, }; +static const struct regulator_ops mt6358_volt_range_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = mt6358_get_buck_voltage_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6358_get_status, +}; + static const struct regulator_ops mt6358_volt_table_ops = { .list_voltage = regulator_list_voltage_table, .map_voltage = regulator_map_voltage_iterate, diff --git a/drivers/regulator/tps6287x-regulator.c b/drivers/regulator/tps6287x-regulator.c index d022184a8e7dac6529d251046f7aab7aa683c6e6..9b7c3d77789e3dca8e42f24e9ab9b8ed615fb628 100644 --- a/drivers/regulator/tps6287x-regulator.c +++ b/drivers/regulator/tps6287x-regulator.c @@ -119,7 +119,7 @@ static struct regulator_desc tps6287x_reg = { .ramp_mask = TPS6287X_CTRL1_VRAMP, .ramp_delay_table = tps6287x_ramp_table, .n_ramp_values = ARRAY_SIZE(tps6287x_ramp_table), - .n_voltages = 256, + .n_voltages = 256 * ARRAY_SIZE(tps6287x_voltage_ranges), .linear_ranges = tps6287x_voltage_ranges, .n_linear_ranges = ARRAY_SIZE(tps6287x_voltage_ranges), .linear_range_selectors_bitfield = tps6287x_voltage_range_sel, diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 25ef102c82707c4becd4e99adcc09852273fc16c..b7f0c87797577eee4d34a9cbb16ccae039559169 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -384,21 +384,19 @@ static int tps6594_request_reg_irqs(struct platform_device *pdev, if (irq < 0) return -EINVAL; - irq_data[*irq_idx + j].dev = tps->dev; - irq_data[*irq_idx + j].type = irq_type; - irq_data[*irq_idx + j].rdev = rdev; + irq_data[*irq_idx].dev = tps->dev; + irq_data[*irq_idx].type = irq_type; + irq_data[*irq_idx].rdev = rdev; error = devm_request_threaded_irq(tps->dev, irq, NULL, - tps6594_regulator_irq_handler, - IRQF_ONESHOT, - irq_type->irq_name, - &irq_data[*irq_idx]); - (*irq_idx)++; + tps6594_regulator_irq_handler, IRQF_ONESHOT, + irq_type->irq_name, &irq_data[*irq_idx]); if (error) { dev_err(tps->dev, "tps6594 failed to request %s IRQ %d: %d\n", irq_type->irq_name, irq, error); return error; } + (*irq_idx)++; } return 0; } @@ -420,8 +418,8 @@ static int tps6594_regulator_probe(struct platform_device *pdev) int error, i, irq, multi, delta; int irq_idx = 0; int buck_idx = 0; - int ext_reg_irq_nb = 2; - + size_t ext_reg_irq_nb = 2; + size_t reg_irq_nb; enum { MULTI_BUCK12, MULTI_BUCK123, @@ -484,15 +482,16 @@ static int tps6594_regulator_probe(struct platform_device *pdev) } } - if (tps->chip_id == LP8764) + if (tps->chip_id == LP8764) { /* There is only 4 buck on LP8764 */ buck_configured[4] = 1; + reg_irq_nb = size_mul(REGS_INT_NB, (BUCK_NB - 1)); + } else { + reg_irq_nb = size_mul(REGS_INT_NB, (size_add(BUCK_NB, LDO_NB))); + } - irq_data = devm_kmalloc_array(tps->dev, - REGS_INT_NB * sizeof(struct tps6594_regulator_irq_data), - ARRAY_SIZE(tps6594_bucks_irq_types) + - ARRAY_SIZE(tps6594_ldos_irq_types), - GFP_KERNEL); + irq_data = devm_kmalloc_array(tps->dev, reg_irq_nb, + sizeof(struct tps6594_regulator_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index d95fa558618998b433b8c7467b6a1143cfa5131b..8fcda9b7454597dec300ae6addb6bcbc656efe7c 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include #include diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index f9874fc5a80ff56cd448156d20e69b2ccc74a84d..8bb293b9f327cc65952efd1f9bc1c674c2ac2b0f 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -13,9 +13,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -40,6 +40,12 @@ #define IMX7D_M4_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST | \ IMX7D_SW_M4C_NON_SCLR_RST) +#define IMX8M_M7_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST) +#define IMX8M_M7_POLL IMX7D_ENABLE_M4 + +#define IMX8M_GPR22 0x58 +#define IMX8M_GPR22_CM7_CPUWAIT BIT(0) + /* Address: 0x020D8000 */ #define IMX6SX_SRC_SCR 0x00 #define IMX6SX_ENABLE_M4 BIT(22) @@ -91,6 +97,7 @@ static int imx_rproc_detach_pd(struct rproc *rproc); struct imx_rproc { struct device *dev; struct regmap *regmap; + struct regmap *gpr; struct rproc *rproc; const struct imx_rproc_dcfg *dcfg; struct imx_rproc_mem mem[IMX_RPROC_MEM_MAX]; @@ -285,6 +292,18 @@ static const struct imx_rproc_att imx_rproc_att_imx6sx[] = { { 0x80000000, 0x80000000, 0x60000000, 0 }, }; +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = { + .src_reg = IMX7D_SRC_SCR, + .src_mask = IMX7D_M4_RST_MASK, + .src_start = IMX7D_M4_START, + .src_stop = IMX8M_M7_STOP, + .gpr_reg = IMX8M_GPR22, + .gpr_wait = IMX8M_GPR22_CM7_CPUWAIT, + .att = imx_rproc_att_imx8mn, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), + .method = IMX_RPROC_MMIO, +}; + static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = { .att = imx_rproc_att_imx8mn, .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), @@ -365,8 +384,14 @@ static int imx_rproc_start(struct rproc *rproc) switch (dcfg->method) { case IMX_RPROC_MMIO: - ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, - dcfg->src_start); + if (priv->gpr) { + ret = regmap_clear_bits(priv->gpr, dcfg->gpr_reg, + dcfg->gpr_wait); + } else { + ret = regmap_update_bits(priv->regmap, dcfg->src_reg, + dcfg->src_mask, + dcfg->src_start); + } break; case IMX_RPROC_SMC: arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_START, 0, 0, 0, 0, 0, 0, &res); @@ -395,6 +420,16 @@ static int imx_rproc_stop(struct rproc *rproc) switch (dcfg->method) { case IMX_RPROC_MMIO: + if (priv->gpr) { + ret = regmap_set_bits(priv->gpr, dcfg->gpr_reg, + dcfg->gpr_wait); + if (ret) { + dev_err(priv->dev, + "Failed to quiescence M4 platform!\n"); + return ret; + } + } + ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_stop); break; @@ -725,13 +760,22 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, return 0; } +static int imx_rproc_notified_idr_cb(int id, void *ptr, void *data) +{ + struct rproc *rproc = data; + + rproc_vq_interrupt(rproc, id); + + return 0; +} + static void imx_rproc_vq_work(struct work_struct *work) { struct imx_rproc *priv = container_of(work, struct imx_rproc, rproc_work); + struct rproc *rproc = priv->rproc; - rproc_vq_interrupt(priv->rproc, 0); - rproc_vq_interrupt(priv->rproc, 1); + idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); } static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) @@ -983,6 +1027,10 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv) break; } + priv->gpr = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,iomuxc-gpr"); + if (IS_ERR(priv->gpr)) + priv->gpr = NULL; + regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); if (IS_ERR(regmap)) { dev_err(dev, "failed to find syscon\n"); @@ -992,6 +1040,19 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv) priv->regmap = regmap; regmap_attach_dev(dev, regmap, &config); + if (priv->gpr) { + ret = regmap_read(priv->gpr, dcfg->gpr_reg, &val); + if (val & dcfg->gpr_wait) { + /* + * After cold boot, the CM indicates its in wait + * state, but not fully powered off. Power it off + * fully so firmware can be loaded into it. + */ + imx_rproc_stop(priv->rproc); + return 0; + } + } + ret = regmap_read(regmap, dcfg->src_reg, &val); if (ret) { dev_err(dev, "Failed to read src\n"); @@ -1133,6 +1194,8 @@ static const struct of_device_id imx_rproc_of_match[] = { { .compatible = "fsl,imx8mm-cm4", .data = &imx_rproc_cfg_imx8mq }, { .compatible = "fsl,imx8mn-cm7", .data = &imx_rproc_cfg_imx8mn }, { .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mn }, + { .compatible = "fsl,imx8mn-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio }, + { .compatible = "fsl,imx8mp-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio }, { .compatible = "fsl,imx8qxp-cm4", .data = &imx_rproc_cfg_imx8qxp }, { .compatible = "fsl,imx8qm-cm4", .data = &imx_rproc_cfg_imx8qm }, { .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp }, diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h index 1c7e2127c758451f9a8baab6a51070b6605812b3..79a1b8956d1421b5346ba2ca7d46b25591239524 100644 --- a/drivers/remoteproc/imx_rproc.h +++ b/drivers/remoteproc/imx_rproc.h @@ -31,6 +31,8 @@ struct imx_rproc_dcfg { u32 src_mask; u32 src_start; u32 src_stop; + u32 gpr_reg; + u32 gpr_wait; const struct imx_rproc_att *att; size_t att_size; enum imx_rproc_method method; diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 82ed90f03d91bf93f1fc45a6b49a3fddd60c64b0..8f50ab80e56f423106de8f32efbf8e59bba89e17 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -19,7 +19,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index 2874c8d324f79cb238eb23d9d1216fb9f9c4b7ec..327f0c7ee3d6ba59e20e4e4a2cc1ac2ce1a5b27a 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -16,8 +16,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -109,6 +110,7 @@ struct pru_private_data { * @dbg_single_step: debug state variable to set PRU into single step mode * @dbg_continuous: debug state variable to restore PRU execution mode * @evt_count: number of mapped events + * @gpmux_save: saved value for gpmux config */ struct pru_rproc { int id; @@ -127,6 +129,7 @@ struct pru_rproc { u32 dbg_single_step; u32 dbg_continuous; u8 evt_count; + u8 gpmux_save; }; static inline u32 pru_control_read_reg(struct pru_rproc *pru, unsigned int reg) @@ -228,6 +231,7 @@ struct rproc *pru_rproc_get(struct device_node *np, int index, struct device *dev; const char *fw_name; int ret; + u32 mux; rproc = __pru_rproc_get(np, index); if (IS_ERR(rproc)) @@ -252,6 +256,23 @@ struct rproc *pru_rproc_get(struct device_node *np, int index, if (pru_id) *pru_id = pru->id; + ret = pruss_cfg_get_gpmux(pru->pruss, pru->id, &pru->gpmux_save); + if (ret) { + dev_err(dev, "failed to get cfg gpmux: %d\n", ret); + goto err; + } + + /* An error here is acceptable for backward compatibility */ + ret = of_property_read_u32_index(np, "ti,pruss-gp-mux-sel", index, + &mux); + if (!ret) { + ret = pruss_cfg_set_gpmux(pru->pruss, pru->id, mux); + if (ret) { + dev_err(dev, "failed to set cfg gpmux: %d\n", ret); + goto err; + } + } + ret = of_property_read_string_index(np, "firmware-name", index, &fw_name); if (!ret) { @@ -290,6 +311,8 @@ void pru_rproc_put(struct rproc *rproc) pru = rproc->priv; + pruss_cfg_set_gpmux(pru->pruss, pru->id, pru->gpmux_save); + pru_rproc_set_firmware(rproc, NULL); mutex_lock(&pru->lock); diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index a0d4238492e90c20bcb57f22f8666c7db1916951..03e5f5d533eb37421f7508731053a1667fc5eea7 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -29,9 +29,9 @@ #define MAX_NUM_OF_SS 10 #define MAX_REGION_NAME_LENGTH 16 #define SBL_MINIDUMP_SMEM_ID 602 -#define MD_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0) -#define MD_SS_ENCR_DONE ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0) -#define MD_SS_ENABLED ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0) +#define MINIDUMP_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0) +#define MINIDUMP_SS_ENCR_DONE ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0) +#define MINIDUMP_SS_ENABLED ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0) /** * struct minidump_region - Minidump region @@ -125,7 +125,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy for (i = 0; i < seg_cnt; i++) { memcpy_fromio(®ion, ptr + i, sizeof(region)); - if (le32_to_cpu(region.valid) == MD_REGION_VALID) { + if (le32_to_cpu(region.valid) == MINIDUMP_REGION_VALID) { name = kstrndup(region.name, MAX_REGION_NAME_LENGTH - 1, GFP_KERNEL); if (!name) { iounmap(ptr); @@ -168,12 +168,21 @@ void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, */ if (subsystem->regions_baseptr == 0 || le32_to_cpu(subsystem->status) != 1 || - le32_to_cpu(subsystem->enabled) != MD_SS_ENABLED || - le32_to_cpu(subsystem->encryption_status) != MD_SS_ENCR_DONE) { + le32_to_cpu(subsystem->enabled) != MINIDUMP_SS_ENABLED) { + return rproc_coredump(rproc); + } + + if (le32_to_cpu(subsystem->encryption_status) != MINIDUMP_SS_ENCR_DONE) { dev_err(&rproc->dev, "Minidump not ready, skipping\n"); return; } + /** + * Clear out the dump segments populated by parse_fw before + * re-populating them with minidump segments. + */ + rproc_coredump_cleanup(rproc); + ret = qcom_add_minidump_segments(rproc, subsystem, rproc_dumpfn_t); if (ret) { dev_err(&rproc->dev, "Failed with error: %d while adding minidump entries\n", ret); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 6777a3bd62264c2204bfb508385007cb10fbce9e..6c67514cc49319f6d4b1205afc115c564307581e 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -14,8 +14,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -637,28 +637,26 @@ static int adsp_init_mmio(struct qcom_adsp *adsp, static int adsp_alloc_memory_region(struct qcom_adsp *adsp) { + struct reserved_mem *rmem = NULL; struct device_node *node; - struct resource r; - int ret; node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0); - if (!node) { - dev_err(adsp->dev, "no memory-region specified\n"); + if (node) + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + + if (!rmem) { + dev_err(adsp->dev, "unable to resolve memory-region\n"); return -EINVAL; } - ret = of_address_to_resource(node, 0, &r); - of_node_put(node); - if (ret) - return ret; - - adsp->mem_phys = adsp->mem_reloc = r.start; - adsp->mem_size = resource_size(&r); + adsp->mem_phys = adsp->mem_reloc = rmem->base; + adsp->mem_size = rmem->size; adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size); if (!adsp->mem_region) { dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n", - &r.start, adsp->mem_size); + &rmem->base, adsp->mem_size); return -EBUSY; } diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 70bffc9f33f6cc833f4add2ac3b5f2da629ee176..22fe7b5f5236da08fb9cb2ba6991c4a07f4f904a 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -15,9 +15,9 @@ #include #include #include -#include -#include +#include #include +#include #include #include #include @@ -71,6 +71,7 @@ #define QDSP6SS_MEM_PWR_CTL 0x0B0 #define QDSP6V6SS_MEM_PWR_CTL 0x034 #define QDSP6SS_STRAP_ACC 0x110 +#define QDSP6V62SS_BHS_STATUS 0x0C4 /* AXI Halt Register Offsets */ #define AXI_HALTREQ_REG 0x0 @@ -123,6 +124,7 @@ #define QDSP6v56_CLAMP_QMC_MEM BIT(22) #define QDSP6SS_XO_CBCR 0x0038 #define QDSP6SS_ACC_OVERRIDE_VAL 0x20 +#define QDSP6v55_BHS_EN_REST_ACK BIT(0) /* QDSP6v65 parameters */ #define QDSP6SS_CORE_CBCR 0x20 @@ -130,6 +132,7 @@ #define QDSP6SS_BOOT_CORE_START 0x400 #define QDSP6SS_BOOT_CMD 0x404 #define BOOT_FSM_TIMEOUT 10000 +#define BHS_CHECK_MAX_LOOPS 200 struct reg_info { struct regulator *reg; @@ -250,6 +253,7 @@ enum { MSS_MSM8998, MSS_SC7180, MSS_SC7280, + MSS_SDM660, MSS_SDM845, }; @@ -700,7 +704,8 @@ static int q6v5proc_reset(struct q6v5 *qproc) } else if (qproc->version == MSS_MSM8909 || qproc->version == MSS_MSM8953 || qproc->version == MSS_MSM8996 || - qproc->version == MSS_MSM8998) { + qproc->version == MSS_MSM8998 || + qproc->version == MSS_SDM660) { if (qproc->version != MSS_MSM8909 && qproc->version != MSS_MSM8953) @@ -734,6 +739,16 @@ static int q6v5proc_reset(struct q6v5 *qproc) val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); udelay(1); + if (qproc->version == MSS_SDM660) { + ret = readl_relaxed_poll_timeout(qproc->reg_base + QDSP6V62SS_BHS_STATUS, + i, (i & QDSP6v55_BHS_EN_REST_ACK), + 1, BHS_CHECK_MAX_LOOPS); + if (ret == -ETIMEDOUT) { + dev_err(qproc->dev, "BHS_EN_REST_ACK not set!\n"); + return -ETIMEDOUT; + } + } + /* Put LDO in bypass mode */ val |= QDSP6v56_LDO_BYP; writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); @@ -756,7 +771,7 @@ static int q6v5proc_reset(struct q6v5 *qproc) mem_pwr_ctl = QDSP6SS_MEM_PWR_CTL; i = 19; } else { - /* MSS_MSM8998 */ + /* MSS_MSM8998, MSS_SDM660 */ mem_pwr_ctl = QDSP6V6SS_MEM_PWR_CTL; i = 28; } @@ -1875,8 +1890,6 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) struct device_node *child; struct reserved_mem *rmem; struct device_node *node; - struct resource r; - int ret; /* * In the absence of mba/mpss sub-child, extract the mba and mpss @@ -1891,15 +1904,20 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) of_node_put(child); } - ret = of_address_to_resource(node, 0, &r); + if (!node) { + dev_err(qproc->dev, "no mba memory-region specified\n"); + return -EINVAL; + } + + rmem = of_reserved_mem_lookup(node); of_node_put(node); - if (ret) { + if (!rmem) { dev_err(qproc->dev, "unable to resolve mba region\n"); - return ret; + return -EINVAL; } - qproc->mba_phys = r.start; - qproc->mba_size = resource_size(&r); + qproc->mba_phys = rmem->base; + qproc->mba_size = rmem->size; if (!child) { node = of_parse_phandle(qproc->dev->of_node, @@ -1910,15 +1928,20 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) of_node_put(child); } - ret = of_address_to_resource(node, 0, &r); + if (!node) { + dev_err(qproc->dev, "no mpss memory-region specified\n"); + return -EINVAL; + } + + rmem = of_reserved_mem_lookup(node); of_node_put(node); - if (ret) { + if (!rmem) { dev_err(qproc->dev, "unable to resolve mpss region\n"); - return ret; + return -EINVAL; } - qproc->mpss_phys = qproc->mpss_reloc = r.start; - qproc->mpss_size = resource_size(&r); + qproc->mpss_phys = qproc->mpss_reloc = rmem->base; + qproc->mpss_size = rmem->size; if (!child) { node = of_parse_phandle(qproc->dev->of_node, "memory-region", 2); @@ -2191,6 +2214,37 @@ static const struct rproc_hexagon_res sc7280_mss = { .version = MSS_SC7280, }; +static const struct rproc_hexagon_res sdm660_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_clk_names = (char*[]){ + "xo", + "qdss", + "mem", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "gpll0_mss", + "mnoc_axi", + "snoc_axi", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + NULL + }, + .need_mem_protection = true, + .has_alt_reset = false, + .has_mba_logs = false, + .has_spare_reg = false, + .has_qaccept_regs = false, + .has_ext_cntl_regs = false, + .has_vq6 = false, + .version = MSS_SDM660, +}; + static const struct rproc_hexagon_res sdm845_mss = { .hexagon_mba_image = "mba.mbn", .proxy_clk_names = (char*[]){ @@ -2473,6 +2527,7 @@ static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss}, { .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss}, { .compatible = "qcom,sc7280-mss-pil", .data = &sc7280_mss}, + { .compatible = "qcom,sdm660-mss-pil", .data = &sdm660_mss}, { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss}, { }, }; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 3153d82037e717a713dab0d7dd21fa967d583fb5..b5447dd2dd35e461620726a88d6ee4e9bc42502f 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -13,8 +13,9 @@ #include #include #include +#include #include -#include +#include #include #include #include @@ -425,6 +426,7 @@ static const struct rproc_ops adsp_minidump_ops = { .start = adsp_start, .stop = adsp_stop, .da_to_va = adsp_da_to_va, + .parse_fw = qcom_register_dump_segments, .load = adsp_load, .panic = adsp_panic, .coredump = adsp_minidump, @@ -533,9 +535,8 @@ static void adsp_pds_detach(struct qcom_adsp *adsp, struct device **pds, static int adsp_alloc_memory_region(struct qcom_adsp *adsp) { + struct reserved_mem *rmem; struct device_node *node; - struct resource r; - int ret; node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0); if (!node) { @@ -543,17 +544,19 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) return -EINVAL; } - ret = of_address_to_resource(node, 0, &r); + rmem = of_reserved_mem_lookup(node); of_node_put(node); - if (ret) - return ret; + if (!rmem) { + dev_err(adsp->dev, "unable to resolve memory-region\n"); + return -EINVAL; + } - adsp->mem_phys = adsp->mem_reloc = r.start; - adsp->mem_size = resource_size(&r); + adsp->mem_phys = adsp->mem_reloc = rmem->base; + adsp->mem_size = rmem->size; adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size); if (!adsp->mem_region) { dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n", - &r.start, adsp->mem_size); + &rmem->base, adsp->mem_size); return -EBUSY; } @@ -566,16 +569,19 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) return -EINVAL; } - ret = of_address_to_resource(node, 0, &r); - if (ret) - return ret; + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(adsp->dev, "unable to resolve dtb memory-region\n"); + return -EINVAL; + } - adsp->dtb_mem_phys = adsp->dtb_mem_reloc = r.start; - adsp->dtb_mem_size = resource_size(&r); + adsp->dtb_mem_phys = adsp->dtb_mem_reloc = rmem->base; + adsp->dtb_mem_size = rmem->size; adsp->dtb_mem_region = devm_ioremap_wc(adsp->dev, adsp->dtb_mem_phys, adsp->dtb_mem_size); if (!adsp->dtb_mem_region) { dev_err(adsp->dev, "unable to map dtb memory region: %pa+%zx\n", - &r.start, adsp->dtb_mem_size); + &rmem->base, adsp->dtb_mem_size); return -EBUSY; } @@ -584,29 +590,28 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) static int adsp_assign_memory_region(struct qcom_adsp *adsp) { + struct reserved_mem *rmem = NULL; struct qcom_scm_vmperm perm; struct device_node *node; - struct resource r; int ret; if (!adsp->region_assign_idx) return 0; node = of_parse_phandle(adsp->dev->of_node, "memory-region", adsp->region_assign_idx); - if (!node) { - dev_err(adsp->dev, "missing shareable memory-region\n"); + if (node) + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + dev_err(adsp->dev, "unable to resolve shareable memory-region\n"); return -EINVAL; } - ret = of_address_to_resource(node, 0, &r); - if (ret) - return ret; - perm.vmid = QCOM_SCM_VMID_MSS_MSA; perm.perm = QCOM_SCM_PERM_RW; - adsp->region_assign_phys = r.start; - adsp->region_assign_size = resource_size(&r); + adsp->region_assign_phys = rmem->base; + adsp->region_assign_size = rmem->size; adsp->region_assign_perms = BIT(QCOM_SCM_VMID_HLOS); ret = qcom_scm_assign_mem(adsp->region_assign_phys, @@ -1012,7 +1017,7 @@ static const struct adsp_data sc8180x_mpss_resource = { .ssctl_id = 0x12, }; -static const struct adsp_data slpi_resource_init = { +static const struct adsp_data msm8996_slpi_resource_init = { .crash_reason_smem = 424, .firmware_name = "slpi.mdt", .pas_id = 12, @@ -1026,7 +1031,7 @@ static const struct adsp_data slpi_resource_init = { .ssctl_id = 0x16, }; -static const struct adsp_data sm8150_slpi_resource = { +static const struct adsp_data sdm845_slpi_resource_init = { .crash_reason_smem = 424, .firmware_name = "slpi.mdt", .pas_id = 12, @@ -1042,38 +1047,6 @@ static const struct adsp_data sm8150_slpi_resource = { .ssctl_id = 0x16, }; -static const struct adsp_data sm8250_slpi_resource = { - .crash_reason_smem = 424, - .firmware_name = "slpi.mdt", - .pas_id = 12, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "lcx", - "lmx", - NULL - }, - .load_state = "slpi", - .ssr_name = "dsps", - .sysmon_name = "slpi", - .ssctl_id = 0x16, -}; - -static const struct adsp_data sm8350_slpi_resource = { - .crash_reason_smem = 424, - .firmware_name = "slpi.mdt", - .pas_id = 12, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "lcx", - "lmx", - NULL - }, - .load_state = "slpi", - .ssr_name = "dsps", - .sysmon_name = "slpi", - .ssctl_id = 0x16, -}; - static const struct adsp_data wcss_resource_init = { .crash_reason_smem = 421, .firmware_name = "wcnss.mdt", @@ -1182,9 +1155,9 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,msm8953-adsp-pil", .data = &msm8996_adsp_resource}, { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init}, { .compatible = "qcom,msm8996-adsp-pil", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init}, + { .compatible = "qcom,msm8996-slpi-pil", .data = &msm8996_slpi_resource_init}, { .compatible = "qcom,msm8998-adsp-pas", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8998-slpi-pas", .data = &slpi_resource_init}, + { .compatible = "qcom,msm8998-slpi-pas", .data = &msm8996_slpi_resource_init}, { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init }, { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init }, { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, @@ -1199,6 +1172,7 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sdm660-adsp-pas", .data = &adsp_resource_init}, { .compatible = "qcom,sdm845-adsp-pas", .data = &sdm845_adsp_resource_init}, { .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init}, + { .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init}, { .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource}, { .compatible = "qcom,sm6115-adsp-pas", .data = &adsp_resource_init}, { .compatible = "qcom,sm6115-cdsp-pas", .data = &cdsp_resource_init}, @@ -1209,17 +1183,17 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sm8150-adsp-pas", .data = &sm8150_adsp_resource}, { .compatible = "qcom,sm8150-cdsp-pas", .data = &sm8150_cdsp_resource}, { .compatible = "qcom,sm8150-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sm8150-slpi-pas", .data = &sm8150_slpi_resource}, + { .compatible = "qcom,sm8150-slpi-pas", .data = &sdm845_slpi_resource_init}, { .compatible = "qcom,sm8250-adsp-pas", .data = &sm8250_adsp_resource}, { .compatible = "qcom,sm8250-cdsp-pas", .data = &sm8250_cdsp_resource}, - { .compatible = "qcom,sm8250-slpi-pas", .data = &sm8250_slpi_resource}, + { .compatible = "qcom,sm8250-slpi-pas", .data = &sdm845_slpi_resource_init}, { .compatible = "qcom,sm8350-adsp-pas", .data = &sm8350_adsp_resource}, { .compatible = "qcom,sm8350-cdsp-pas", .data = &sm8350_cdsp_resource}, - { .compatible = "qcom,sm8350-slpi-pas", .data = &sm8350_slpi_resource}, + { .compatible = "qcom,sm8350-slpi-pas", .data = &sdm845_slpi_resource_init}, { .compatible = "qcom,sm8350-mpss-pas", .data = &mpss_resource_init}, { .compatible = "qcom,sm8450-adsp-pas", .data = &sm8350_adsp_resource}, { .compatible = "qcom,sm8450-cdsp-pas", .data = &sm8350_cdsp_resource}, - { .compatible = "qcom,sm8450-slpi-pas", .data = &sm8350_slpi_resource}, + { .compatible = "qcom,sm8450-slpi-pas", .data = &sdm845_slpi_resource_init}, { .compatible = "qcom,sm8450-mpss-pas", .data = &sm8450_mpss_resource}, { .compatible = "qcom,sm8550-adsp-pas", .data = &sm8550_adsp_resource}, { .compatible = "qcom,sm8550-cdsp-pas", .data = &sm8550_cdsp_resource}, diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index b437044aa12674b6169eed462b23c44d706877eb..cff1fa07d1def7fb0ad462d162e68248888cdfde 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -837,8 +837,7 @@ static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss, return -ENOMEM; if (wcss->version == WCSS_IPQ8074) { - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb"); - wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res); + wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb"); if (IS_ERR(wcss->rmb_base)) return PTR_ERR(wcss->rmb_base); } diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 746f56b4bafbe8f4a379da50d46883cfdd16082e..c24e4a88287381b3438b56851665c173b8d3e2b6 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 1ed0647bc962d90f7ccdbfcb37aad4c66e026f7d..90de22c81da9761ec560f198c13d9d1b8d639ffe 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -14,8 +14,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -506,27 +506,25 @@ static int wcnss_request_irq(struct qcom_wcnss *wcnss, static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss) { + struct reserved_mem *rmem = NULL; struct device_node *node; - struct resource r; - int ret; node = of_parse_phandle(wcnss->dev->of_node, "memory-region", 0); - if (!node) { - dev_err(wcnss->dev, "no memory-region specified\n"); + if (node) + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + + if (!rmem) { + dev_err(wcnss->dev, "unable to resolve memory-region\n"); return -EINVAL; } - ret = of_address_to_resource(node, 0, &r); - of_node_put(node); - if (ret) - return ret; - - wcnss->mem_phys = wcnss->mem_reloc = r.start; - wcnss->mem_size = resource_size(&r); + wcnss->mem_phys = wcnss->mem_reloc = rmem->base; + wcnss->mem_size = rmem->size; wcnss->mem_region = devm_ioremap_wc(wcnss->dev, wcnss->mem_phys, wcnss->mem_size); if (!wcnss->mem_region) { dev_err(wcnss->dev, "unable to map memory region: %pa+%zx\n", - &r.start, wcnss->mem_size); + &rmem->base, wcnss->mem_size); return -EBUSY; } @@ -538,7 +536,6 @@ static int wcnss_probe(struct platform_device *pdev) const char *fw_name = WCNSS_FIRMWARE_NAME; const struct wcnss_data *data; struct qcom_wcnss *wcnss; - struct resource *res; struct rproc *rproc; void __iomem *mmio; int ret; @@ -576,8 +573,7 @@ static int wcnss_probe(struct platform_device *pdev) mutex_init(&wcnss->iris_lock); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmu"); - mmio = devm_ioremap_resource(&pdev->dev, res); + mmio = devm_platform_ioremap_resource_byname(pdev, "pmu"); if (IS_ERR(mmio)) { ret = PTR_ERR(mmio); goto free_rproc; diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c index 09720ddddc8570468ba6c3b230ad5554b03c7436..dd36fd077911afec942f553a55128258c10ba946 100644 --- a/drivers/remoteproc/qcom_wcnss_iris.c +++ b/drivers/remoteproc/qcom_wcnss_iris.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c index 90e8769d5624869dd53ef9171e9d80cd3873b879..cc17e8421f65bf7e02c68ce7c47c3b4fce732b26 100644 --- a/drivers/remoteproc/rcar_rproc.c +++ b/drivers/remoteproc/rcar_rproc.c @@ -5,8 +5,9 @@ #include #include -#include +#include #include +#include #include #include #include diff --git a/drivers/remoteproc/remoteproc_coredump.c b/drivers/remoteproc/remoteproc_coredump.c index bc0e1603a7a3f232143fbb1f3a042582dda42649..6ede8c0c93ad5a6e1c572aeb49c97174df2815c4 100644 --- a/drivers/remoteproc/remoteproc_coredump.c +++ b/drivers/remoteproc/remoteproc_coredump.c @@ -32,6 +32,7 @@ void rproc_coredump_cleanup(struct rproc *rproc) kfree(entry); } } +EXPORT_SYMBOL_GPL(rproc_coredump_cleanup); /** * rproc_coredump_add_segment() - add segment of device memory to coredump @@ -327,6 +328,7 @@ void rproc_coredump(struct rproc *rproc) */ wait_for_completion(&dump_state.dump_done); } +EXPORT_SYMBOL_GPL(rproc_coredump); /** * rproc_coredump_using_sections() - perform coredump using section headers diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index d4dbb8d1d80cc9b2735f315b67723f1c13b6e8ee..f62a82d71dfaac3c221ad166a4e6f130207dfda7 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -76,10 +76,6 @@ extern struct class rproc_class; int rproc_init_sysfs(void); void rproc_exit_sysfs(void); -/* from remoteproc_coredump.c */ -void rproc_coredump_cleanup(struct rproc *rproc); -void rproc_coredump(struct rproc *rproc); - #ifdef CONFIG_REMOTEPROC_CDEV void rproc_init_cdev(void); void rproc_exit_cdev(void); diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 4ed9467897e5bedccc2cd93e708283530806a6c1..d17719384c16532f54b28a71d4e66c76ea8f2ef9 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index cf073bac79f735b443226fa23e9044522198e1ee..9d9b13530f78aa1193c68550cbecd9d7237e33d6 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -12,9 +12,9 @@ #include #include #include -#include -#include +#include #include +#include #include #include #include @@ -921,7 +921,7 @@ static void stm32_rproc_remove(struct platform_device *pdev) rproc_free(rproc); } -static int __maybe_unused stm32_rproc_suspend(struct device *dev) +static int stm32_rproc_suspend(struct device *dev) { struct rproc *rproc = dev_get_drvdata(dev); struct stm32_rproc *ddata = rproc->priv; @@ -932,7 +932,7 @@ static int __maybe_unused stm32_rproc_suspend(struct device *dev) return 0; } -static int __maybe_unused stm32_rproc_resume(struct device *dev) +static int stm32_rproc_resume(struct device *dev) { struct rproc *rproc = dev_get_drvdata(dev); struct stm32_rproc *ddata = rproc->priv; @@ -943,16 +943,16 @@ static int __maybe_unused stm32_rproc_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(stm32_rproc_pm_ops, - stm32_rproc_suspend, stm32_rproc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(stm32_rproc_pm_ops, + stm32_rproc_suspend, stm32_rproc_resume); static struct platform_driver stm32_rproc_driver = { .probe = stm32_rproc_probe, .remove_new = stm32_rproc_remove, .driver = { .name = "stm32-rproc", - .pm = &stm32_rproc_pm_ops, - .of_match_table = of_match_ptr(stm32_rproc_match), + .pm = pm_ptr(&stm32_rproc_pm_ops), + .of_match_table = stm32_rproc_match, }, }; module_platform_driver(stm32_rproc_driver); diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index ec626a37fef6014f403ecac7946f1eab583c246a..ef8415a7cd542d065c13bc704ddafe7d03b2bd71 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 23fe44d4d7a58edec5d1a892c21614614688b52c..ad3415a3851b264ecd044fc3cab06387b5eb4319 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -12,9 +12,10 @@ #include #include #include +#include #include -#include #include +#include #include #include #include diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c index 120dc7d2dac19e5abed0ba3cba22b8d0f4b81eb4..36a55f7ffa64d535cd832af8ce4ea28da505f368 100644 --- a/drivers/remoteproc/wkup_m3_rproc.c +++ b/drivers/remoteproc/wkup_m3_rproc.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 1beb40a1d3df2844dac36eb96e7b2ce52d4aafa9..82d460ff477718e5a847401186e8b7ca6e4fbe7a 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -200,9 +200,15 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops; #define GLINK_CMD_TX_DATA_CONT 12 #define GLINK_CMD_READ_NOTIF 13 #define GLINK_CMD_RX_DONE_W_REUSE 14 +#define GLINK_CMD_SIGNALS 15 #define GLINK_FEATURE_INTENTLESS BIT(1) +#define NATIVE_DTR_SIG NATIVE_DSR_SIG +#define NATIVE_DSR_SIG BIT(31) +#define NATIVE_RTS_SIG NATIVE_CTS_SIG +#define NATIVE_CTS_SIG BIT(30) + static void qcom_glink_rx_done_work(struct work_struct *work); static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, @@ -221,6 +227,10 @@ static struct glink_channel *qcom_glink_alloc_channel(struct qcom_glink *glink, channel->glink = glink; channel->name = kstrdup(name, GFP_KERNEL); + if (!channel->name) { + kfree(channel); + return ERR_PTR(-ENOMEM); + } init_completion(&channel->open_req); init_completion(&channel->open_ack); @@ -1025,6 +1035,52 @@ static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid) return 0; } +/** + * qcom_glink_set_flow_control() - convert a signal cmd to wire format and transmit + * @ept: Rpmsg endpoint for channel. + * @pause: Pause transmission + * @dst: destination address of the endpoint + * + * Return: 0 on success or standard Linux error code. + */ +static int qcom_glink_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst) +{ + struct glink_channel *channel = to_glink_channel(ept); + struct qcom_glink *glink = channel->glink; + struct glink_msg msg; + u32 sigs = 0; + + if (pause) + sigs |= NATIVE_DTR_SIG | NATIVE_RTS_SIG; + + msg.cmd = cpu_to_le16(GLINK_CMD_SIGNALS); + msg.param1 = cpu_to_le16(channel->lcid); + msg.param2 = cpu_to_le32(sigs); + + return qcom_glink_tx(glink, &msg, sizeof(msg), NULL, 0, true); +} + +static void qcom_glink_handle_signals(struct qcom_glink *glink, + unsigned int rcid, unsigned int sigs) +{ + struct glink_channel *channel; + unsigned long flags; + bool enable; + + spin_lock_irqsave(&glink->idr_lock, flags); + channel = idr_find(&glink->rcids, rcid); + spin_unlock_irqrestore(&glink->idr_lock, flags); + if (!channel) { + dev_err(glink->dev, "signal for non-existing channel\n"); + return; + } + + enable = sigs & NATIVE_DSR_SIG || sigs & NATIVE_CTS_SIG; + + if (channel->ept.flow_cb) + channel->ept.flow_cb(channel->ept.rpdev, channel->ept.priv, enable); +} + void qcom_glink_native_rx(struct qcom_glink *glink) { struct glink_msg msg; @@ -1086,6 +1142,10 @@ void qcom_glink_native_rx(struct qcom_glink *glink) qcom_glink_handle_intent_req_ack(glink, param1, param2); qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); break; + case GLINK_CMD_SIGNALS: + qcom_glink_handle_signals(glink, param1, param2); + qcom_glink_rx_advance(glink, ALIGN(sizeof(msg), 8)); + break; default: dev_err(glink->dev, "unhandled rx cmd: %d\n", cmd); ret = -EINVAL; @@ -1446,6 +1506,7 @@ static const struct rpmsg_endpoint_ops glink_endpoint_ops = { .sendto = qcom_glink_sendto, .trysend = qcom_glink_trysend, .trysendto = qcom_glink_trysendto, + .set_flow_control = qcom_glink_set_flow_control, }; static void qcom_glink_rpdev_release(struct device *dev) diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index a271fceb16f421ecda950384fc40c67538471192..09833ad05da75cb6ca91f53e4e425cac7fad6fc9 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -52,6 +52,8 @@ static DEFINE_IDA(rpmsg_minor_ida); * @readq: wait object for incoming queue * @default_ept: set to channel default endpoint if the default endpoint should be re-used * on device open to prevent endpoint address update. + * remote_flow_restricted: to indicate if the remote has requested for flow to be limited + * remote_flow_updated: to indicate if the flow control has been requested */ struct rpmsg_eptdev { struct device dev; @@ -68,6 +70,8 @@ struct rpmsg_eptdev { struct sk_buff_head queue; wait_queue_head_t readq; + bool remote_flow_restricted; + bool remote_flow_updated; }; int rpmsg_chrdev_eptdev_destroy(struct device *dev, void *data) @@ -116,6 +120,18 @@ static int rpmsg_ept_cb(struct rpmsg_device *rpdev, void *buf, int len, return 0; } +static int rpmsg_ept_flow_cb(struct rpmsg_device *rpdev, void *priv, bool enable) +{ + struct rpmsg_eptdev *eptdev = priv; + + eptdev->remote_flow_restricted = enable; + eptdev->remote_flow_updated = true; + + wake_up_interruptible(&eptdev->readq); + + return 0; +} + static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); @@ -152,6 +168,7 @@ static int rpmsg_eptdev_open(struct inode *inode, struct file *filp) return -EINVAL; } + ept->flow_cb = rpmsg_ept_flow_cb; eptdev->ept = ept; filp->private_data = eptdev; mutex_unlock(&eptdev->ept_lock); @@ -172,6 +189,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) eptdev->ept = NULL; } mutex_unlock(&eptdev->ept_lock); + eptdev->remote_flow_updated = false; /* Discard all SKBs */ skb_queue_purge(&eptdev->queue); @@ -285,6 +303,9 @@ static __poll_t rpmsg_eptdev_poll(struct file *filp, poll_table *wait) if (!skb_queue_empty(&eptdev->queue)) mask |= EPOLLIN | EPOLLRDNORM; + if (eptdev->remote_flow_updated) + mask |= EPOLLPRI; + mutex_lock(&eptdev->ept_lock); mask |= rpmsg_poll(eptdev->ept, filp, wait); mutex_unlock(&eptdev->ept_lock); @@ -297,14 +318,35 @@ static long rpmsg_eptdev_ioctl(struct file *fp, unsigned int cmd, { struct rpmsg_eptdev *eptdev = fp->private_data; - if (cmd != RPMSG_DESTROY_EPT_IOCTL) - return -EINVAL; + bool set; + int ret; - /* Don't allow to destroy a default endpoint. */ - if (eptdev->default_ept) - return -EINVAL; + switch (cmd) { + case RPMSG_GET_OUTGOING_FLOWCONTROL: + eptdev->remote_flow_updated = false; + ret = put_user(eptdev->remote_flow_restricted, (int __user *)arg); + break; + case RPMSG_SET_INCOMING_FLOWCONTROL: + if (arg > 1) { + ret = -EINVAL; + break; + } + set = !!arg; + ret = rpmsg_set_flow_control(eptdev->ept, set, eptdev->chinfo.dst); + break; + case RPMSG_DESTROY_EPT_IOCTL: + /* Don't allow to destroy a default endpoint. */ + if (eptdev->default_ept) { + ret = -EINVAL; + break; + } + ret = rpmsg_chrdev_eptdev_destroy(&eptdev->dev, NULL); + break; + default: + ret = -EINVAL; + } - return rpmsg_chrdev_eptdev_destroy(&eptdev->dev, NULL); + return ret; } static const struct file_operations rpmsg_eptdev_fops = { diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c index 5039df757127a9f5f0b0ef3e8cd2e5d13a852892..32b550c91d9f1c7f1918774181654bcf3404407a 100644 --- a/drivers/rpmsg/rpmsg_core.c +++ b/drivers/rpmsg/rpmsg_core.c @@ -330,6 +330,25 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, } EXPORT_SYMBOL(rpmsg_trysend_offchannel); +/** + * rpmsg_set_flow_control() - request remote to pause/resume transmission + * @ept: the rpmsg endpoint + * @pause: pause transmission + * @dst: destination address of the endpoint + * + * Return: 0 on success and an appropriate error value on failure. + */ +int rpmsg_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst) +{ + if (WARN_ON(!ept)) + return -EINVAL; + if (!ept->ops->set_flow_control) + return -EOPNOTSUPP; + + return ept->ops->set_flow_control(ept, pause, dst); +} +EXPORT_SYMBOL_GPL(rpmsg_set_flow_control); + /** * rpmsg_get_mtu() - get maximum transmission buffer size for sending message. * @ept: the rpmsg endpoint @@ -539,6 +558,8 @@ static int rpmsg_dev_probe(struct device *dev) rpdev->ept = ept; rpdev->src = ept->addr; + + ept->flow_cb = rpdrv->flowcontrol; } err = rpdrv->probe(rpdev); diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h index 39b646d0d40d7c40637016332f8e67775a2ed993..b950d6f790a34011e34d35ea87b92da1b8022832 100644 --- a/drivers/rpmsg/rpmsg_internal.h +++ b/drivers/rpmsg/rpmsg_internal.h @@ -55,6 +55,7 @@ struct rpmsg_device_ops { * @trysendto: see @rpmsg_trysendto(), optional * @trysend_offchannel: see @rpmsg_trysend_offchannel(), optional * @poll: see @rpmsg_poll(), optional + * @set_flow_control: see @rpmsg_set_flow_control(), optional * @get_mtu: see @rpmsg_get_mtu(), optional * * Indirection table for the operations that a rpmsg backend should implement. @@ -75,6 +76,7 @@ struct rpmsg_endpoint_ops { void *data, int len); __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, poll_table *wait); + int (*set_flow_control)(struct rpmsg_endpoint *ept, bool pause, u32 dst); ssize_t (*get_mtu)(struct rpmsg_endpoint *ept); }; diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 05f4b2d66290dc17be553bfb647568e87bbaa0a3..d7502433c78aa318a1a8baf1b835d1d4e1e639f3 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -904,9 +904,9 @@ config RTC_DRV_PCF2127 select REGMAP_SPI if SPI_MASTER select WATCHDOG_CORE if WATCHDOG help - If you say yes here you get support for the NXP PCF2127/29 RTC + If you say yes here you get support for the NXP PCF2127/29/31 RTC chips with integrated quartz crystal for industrial applications. - Both chips also have watchdog timer and tamper switch detection + These chips also have watchdog timer and tamper switch detection features. PCF2127 has an additional feature of 512 bytes battery backed @@ -1196,6 +1196,7 @@ config RTC_DRV_MSM6242 config RTC_DRV_BQ4802 tristate "TI BQ4802" depends on HAS_IOMEM && HAS_IOPORT + depends on SPARC || COMPILE_TEST help If you say Y here you will get support for the TI BQ4802 RTC chip. diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 499d89150afc9a54299a4a8dab7d8aabe60c7847..1b63111cdda2e91b880c5270dbde42c79f1a3b92 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -376,7 +376,7 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) err = rtc_valid_tm(&alarm->time); done: - if (err) + if (err && alarm->enabled) dev_warn(&rtc->dev, "invalid alarm value: %ptR\n", &alarm->time); diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index e08d3181bd2a66211d3de01d64983000da3f540b..fde2b8054c2ea8a49440ce8489777244fa020706 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index b4139c200676613bd7dbc9a7feb44fea8a4409a8..569c1054d6b0bcd3be4263a35804981b94b79cf3 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -474,7 +473,6 @@ static const struct armada38x_rtc_data armada8k_data = { .alarm = ALARM2, }; -#ifdef CONFIG_OF static const struct of_device_id armada38x_rtc_of_match_table[] = { { .compatible = "marvell,armada-380-rtc", @@ -487,7 +485,6 @@ static const struct of_device_id armada38x_rtc_of_match_table[] = { {} }; MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); -#endif static __init int armada38x_rtc_probe(struct platform_device *pdev) { @@ -577,7 +574,7 @@ static struct platform_driver armada38x_rtc_driver = { .driver = { .name = "armada38x-rtc", .pm = &armada38x_rtc_pm_ops, - .of_match_table = of_match_ptr(armada38x_rtc_of_match_table), + .of_match_table = armada38x_rtc_of_match_table, }, }; diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c index a93352ed3aec99f94612248a2b4a4d2a145a3615..880b015eebaf796f68ed82c29998f345e1fd418b 100644 --- a/drivers/rtc/rtc-aspeed.c +++ b/drivers/rtc/rtc-aspeed.c @@ -118,7 +118,7 @@ MODULE_DEVICE_TABLE(of, aspeed_rtc_match); static struct platform_driver aspeed_rtc_driver = { .driver = { .name = "aspeed-rtc", - .of_match_table = of_match_ptr(aspeed_rtc_match), + .of_match_table = aspeed_rtc_match, }, }; diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index e9d17232d0a8163e37e3ee5b5d51fd1a71fcc55a..add4f71d7b3b98db7fa0e5e993add4ba0448ecdf 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -642,7 +641,7 @@ static struct platform_driver at91_rtc_driver = { .driver = { .name = "at91_rtc", .pm = &at91_rtc_pm_ops, - .of_match_table = of_match_ptr(at91_rtc_dt_ids), + .of_match_table = at91_rtc_dt_ids, }, }; diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 610f27dfc462dad34ee65e6e81c2e750af8bc2d8..f93bee96e3623330b179e459b2c41e9084709f31 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -534,7 +534,7 @@ static struct platform_driver at91_rtc_driver = { .driver = { .name = "rtc-at91sam9", .pm = &at91_rtc_pm_ops, - .of_match_table = of_match_ptr(at91_rtc_dt_ids), + .of_match_table = at91_rtc_dt_ids, }, }; diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index c9416fe8542d7ea2800250c5d17d30f86e210bd4..228fb2d11c7091e00d9bb26b2254db2ed923c097 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -913,6 +913,10 @@ static inline void cmos_check_acpi_rtc_status(struct device *dev, #define INITSECTION __init #endif +#define SECS_PER_DAY (24 * 60 * 60) +#define SECS_PER_MONTH (28 * SECS_PER_DAY) +#define SECS_PER_YEAR (365 * SECS_PER_DAY) + static int INITSECTION cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) { @@ -1019,6 +1023,13 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) goto cleanup0; } + if (cmos_rtc.mon_alrm) + cmos_rtc.rtc->alarm_offset_max = SECS_PER_YEAR - 1; + else if (cmos_rtc.day_alrm) + cmos_rtc.rtc->alarm_offset_max = SECS_PER_MONTH - 1; + else + cmos_rtc.rtc->alarm_offset_max = SECS_PER_DAY - 1; + rename_region(ports, dev_name(&cmos_rtc.rtc->dev)); if (!mc146818_does_rtc_work()) { diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c index 998ab8606f0b61ed84fd6a50ec36a219f5627109..0cd397c04ff0e03e815ecc82c9af59484d51c8a7 100644 --- a/drivers/rtc/rtc-cros-ec.c +++ b/drivers/rtc/rtc-cros-ec.c @@ -182,21 +182,15 @@ static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset); if (ret < 0) { - if (ret == -EINVAL && alarm_offset >= SECS_PER_DAY) { - /* - * RTC chips on some older Chromebooks can only handle - * alarms up to 24h in the future. Try to set an alarm - * below that limit to avoid suspend failures. - */ - ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, - SECS_PER_DAY - 1); - } - - if (ret < 0) { - dev_err(dev, "error setting alarm in %u seconds: %d\n", - alarm_offset, ret); - return ret; - } + dev_err(dev, "error setting alarm in %u seconds: %d\n", + alarm_offset, ret); + /* + * The EC code returns -EINVAL if the alarm time is too + * far in the future. Convert it to the expected error code. + */ + if (ret == -EINVAL) + ret = -ERANGE; + return ret; } return 0; @@ -355,6 +349,20 @@ static int cros_ec_rtc_probe(struct platform_device *pdev) cros_ec_rtc->rtc->ops = &cros_ec_rtc_ops; cros_ec_rtc->rtc->range_max = U32_MAX; + /* + * The RTC on some older Chromebooks can only handle alarms less than + * 24 hours in the future. The only way to find out is to try to set an + * alarm further in the future. If that fails, assume that the RTC + * connected to the EC can only handle less than 24 hours of alarm + * window. + */ + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, SECS_PER_DAY * 2); + if (ret == -EINVAL) + cros_ec_rtc->rtc->alarm_offset_max = SECS_PER_DAY - 1; + + (void)cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + EC_RTC_ALARM_CLEAR); + ret = devm_rtc_register_device(cros_ec_rtc->rtc); if (ret) return ret; diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index ee2efb4961744b77942e98f984ea08403dba5c33..2f5d60622564a0d83ec3be91bf89710f34e2b1b7 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -496,6 +497,12 @@ static int da9063_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n", irq_alarm, ret); + ret = dev_pm_set_wake_irq(&pdev->dev, irq_alarm); + if (ret) + dev_warn(&pdev->dev, + "Failed to set IRQ %d as a wake IRQ: %d\n", + irq_alarm, ret); + device_init_wakeup(&pdev->dev, true); return devm_rtc_register_device(rtc->rtc_dev); diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index ed9360486953e77ad668ec44756dc4ea49677b48..d4de401548b424673dd3fa6b4b83d90fc4d6f63c 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -336,8 +336,8 @@ static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm) /* make sure alarm fires within the next 24 hours */ if (later <= now) return -EINVAL; - if ((later - now) > 24 * 60 * 60) - return -EDOM; + if ((later - now) > ds1305->rtc->alarm_offset_max) + return -ERANGE; /* disable alarm if needed */ if (ds1305->ctrl[0] & DS1305_AEI0) { @@ -691,6 +691,7 @@ static int ds1305_probe(struct spi_device *spi) ds1305->rtc->ops = &ds1305_ops; ds1305->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; ds1305->rtc->range_max = RTC_TIMESTAMP_END_2099; + ds1305->rtc->alarm_offset_max = 24 * 60 * 60; ds1305_nvmem_cfg.priv = ds1305; status = devm_rtc_register_device(ds1305->rtc); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index cb5acecc11aa4e0700a7e0b537493cac0357c061..506b7d1c23970169d891bd8c4ac4cd8cde8dc3a3 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1744,7 +1744,7 @@ static int ds1307_probe(struct i2c_client *client) match = device_get_match_data(&client->dev); if (match) { - ds1307->type = (enum ds_type)match; + ds1307->type = (uintptr_t)match; chip = &chips[ds1307->type]; } else if (id) { chip = &chips[id->driver_data]; diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index a5026b0514e78d27739ef664b1f250c7e921c839..6ae8b9a294feef573183b201b37fd785b6f4d875 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c index 0480f592307e9a56f00959511a27f12ac79a6ce5..3231fd9f61da49bdaf00e46486fc34836094ee78 100644 --- a/drivers/rtc/rtc-ds2404.c +++ b/drivers/rtc/rtc-ds2404.c @@ -7,9 +7,8 @@ #include #include #include -#include #include -#include +#include #include #include @@ -27,164 +26,140 @@ #define DS2404_CLK 1 #define DS2404_DQ 2 -struct ds2404_gpio { - const char *name; - unsigned int gpio; -}; - struct ds2404 { - struct ds2404_gpio *gpio; + struct device *dev; + struct gpio_desc *rst_gpiod; + struct gpio_desc *clk_gpiod; + struct gpio_desc *dq_gpiod; struct rtc_device *rtc; }; -static struct ds2404_gpio ds2404_gpio[] = { - { "RTC RST", 0 }, - { "RTC CLK", 0 }, - { "RTC DQ", 0 }, -}; - -static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev, - struct ds2404_platform_data *pdata) +static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev) { - int i, err; - - ds2404_gpio[DS2404_RST].gpio = pdata->gpio_rst; - ds2404_gpio[DS2404_CLK].gpio = pdata->gpio_clk; - ds2404_gpio[DS2404_DQ].gpio = pdata->gpio_dq; - - for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) { - err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name); - if (err) { - dev_err(&pdev->dev, "error mapping gpio %s: %d\n", - ds2404_gpio[i].name, err); - goto err_request; - } - if (i != DS2404_DQ) - gpio_direction_output(ds2404_gpio[i].gpio, 1); - } + struct device *dev = &pdev->dev; - chip->gpio = ds2404_gpio; - return 0; + /* This will de-assert RESET, declare this GPIO as GPIOD_ACTIVE_LOW */ + chip->rst_gpiod = devm_gpiod_get(dev, "rst", GPIOD_OUT_LOW); + if (IS_ERR(chip->rst_gpiod)) + return PTR_ERR(chip->rst_gpiod); -err_request: - while (--i >= 0) - gpio_free(ds2404_gpio[i].gpio); - return err; -} + chip->clk_gpiod = devm_gpiod_get(dev, "clk", GPIOD_OUT_HIGH); + if (IS_ERR(chip->clk_gpiod)) + return PTR_ERR(chip->clk_gpiod); -static void ds2404_gpio_unmap(void *data) -{ - int i; + chip->dq_gpiod = devm_gpiod_get(dev, "dq", GPIOD_ASIS); + if (IS_ERR(chip->dq_gpiod)) + return PTR_ERR(chip->dq_gpiod); - for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) - gpio_free(ds2404_gpio[i].gpio); + return 0; } -static void ds2404_reset(struct device *dev) +static void ds2404_reset(struct ds2404 *chip) { - gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0); + gpiod_set_value(chip->rst_gpiod, 1); udelay(1000); - gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 1); - gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); - gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 0); + gpiod_set_value(chip->rst_gpiod, 0); + gpiod_set_value(chip->clk_gpiod, 0); + gpiod_direction_output(chip->dq_gpiod, 0); udelay(10); } -static void ds2404_write_byte(struct device *dev, u8 byte) +static void ds2404_write_byte(struct ds2404 *chip, u8 byte) { int i; - gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 1); + gpiod_direction_output(chip->dq_gpiod, 1); for (i = 0; i < 8; i++) { - gpio_set_value(ds2404_gpio[DS2404_DQ].gpio, byte & (1 << i)); + gpiod_set_value(chip->dq_gpiod, byte & (1 << i)); udelay(10); - gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1); + gpiod_set_value(chip->clk_gpiod, 1); udelay(10); - gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); + gpiod_set_value(chip->clk_gpiod, 0); udelay(10); } } -static u8 ds2404_read_byte(struct device *dev) +static u8 ds2404_read_byte(struct ds2404 *chip) { int i; u8 ret = 0; - gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio); + gpiod_direction_input(chip->dq_gpiod); for (i = 0; i < 8; i++) { - gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0); + gpiod_set_value(chip->clk_gpiod, 0); udelay(10); - if (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio)) + if (gpiod_get_value(chip->dq_gpiod)) ret |= 1 << i; - gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1); + gpiod_set_value(chip->clk_gpiod, 1); udelay(10); } return ret; } -static void ds2404_read_memory(struct device *dev, u16 offset, +static void ds2404_read_memory(struct ds2404 *chip, u16 offset, int length, u8 *out) { - ds2404_reset(dev); - ds2404_write_byte(dev, DS2404_READ_MEMORY_CMD); - ds2404_write_byte(dev, offset & 0xff); - ds2404_write_byte(dev, (offset >> 8) & 0xff); + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_READ_MEMORY_CMD); + ds2404_write_byte(chip, offset & 0xff); + ds2404_write_byte(chip, (offset >> 8) & 0xff); while (length--) - *out++ = ds2404_read_byte(dev); + *out++ = ds2404_read_byte(chip); } -static void ds2404_write_memory(struct device *dev, u16 offset, +static void ds2404_write_memory(struct ds2404 *chip, u16 offset, int length, u8 *out) { int i; u8 ta01, ta02, es; - ds2404_reset(dev); - ds2404_write_byte(dev, DS2404_WRITE_SCRATCHPAD_CMD); - ds2404_write_byte(dev, offset & 0xff); - ds2404_write_byte(dev, (offset >> 8) & 0xff); + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_WRITE_SCRATCHPAD_CMD); + ds2404_write_byte(chip, offset & 0xff); + ds2404_write_byte(chip, (offset >> 8) & 0xff); for (i = 0; i < length; i++) - ds2404_write_byte(dev, out[i]); + ds2404_write_byte(chip, out[i]); - ds2404_reset(dev); - ds2404_write_byte(dev, DS2404_READ_SCRATCHPAD_CMD); + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_READ_SCRATCHPAD_CMD); - ta01 = ds2404_read_byte(dev); - ta02 = ds2404_read_byte(dev); - es = ds2404_read_byte(dev); + ta01 = ds2404_read_byte(chip); + ta02 = ds2404_read_byte(chip); + es = ds2404_read_byte(chip); for (i = 0; i < length; i++) { - if (out[i] != ds2404_read_byte(dev)) { - dev_err(dev, "read invalid data\n"); + if (out[i] != ds2404_read_byte(chip)) { + dev_err(chip->dev, "read invalid data\n"); return; } } - ds2404_reset(dev); - ds2404_write_byte(dev, DS2404_COPY_SCRATCHPAD_CMD); - ds2404_write_byte(dev, ta01); - ds2404_write_byte(dev, ta02); - ds2404_write_byte(dev, es); + ds2404_reset(chip); + ds2404_write_byte(chip, DS2404_COPY_SCRATCHPAD_CMD); + ds2404_write_byte(chip, ta01); + ds2404_write_byte(chip, ta02); + ds2404_write_byte(chip, es); - gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio); - while (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio)) + while (gpiod_get_value(chip->dq_gpiod)) ; } -static void ds2404_enable_osc(struct device *dev) +static void ds2404_enable_osc(struct ds2404 *chip) { u8 in[1] = { 0x10 }; /* enable oscillator */ - ds2404_write_memory(dev, 0x201, 1, in); + + ds2404_write_memory(chip, 0x201, 1, in); } static int ds2404_read_time(struct device *dev, struct rtc_time *dt) { + struct ds2404 *chip = dev_get_drvdata(dev); unsigned long time = 0; __le32 hw_time = 0; - ds2404_read_memory(dev, 0x203, 4, (u8 *)&hw_time); + ds2404_read_memory(chip, 0x203, 4, (u8 *)&hw_time); time = le32_to_cpu(hw_time); rtc_time64_to_tm(time, dt); @@ -193,8 +168,9 @@ static int ds2404_read_time(struct device *dev, struct rtc_time *dt) static int ds2404_set_time(struct device *dev, struct rtc_time *dt) { + struct ds2404 *chip = dev_get_drvdata(dev); u32 time = cpu_to_le32(rtc_tm_to_time64(dt)); - ds2404_write_memory(dev, 0x203, 4, (u8 *)&time); + ds2404_write_memory(chip, 0x203, 4, (u8 *)&time); return 0; } @@ -205,7 +181,6 @@ static const struct rtc_class_ops ds2404_rtc_ops = { static int rtc_probe(struct platform_device *pdev) { - struct ds2404_platform_data *pdata = dev_get_platdata(&pdev->dev); struct ds2404 *chip; int retval = -EBUSY; @@ -213,22 +188,16 @@ static int rtc_probe(struct platform_device *pdev) if (!chip) return -ENOMEM; + chip->dev = &pdev->dev; + chip->rtc = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(chip->rtc)) return PTR_ERR(chip->rtc); - retval = ds2404_gpio_map(chip, pdev, pdata); + retval = ds2404_gpio_map(chip, pdev); if (retval) return retval; - retval = devm_add_action_or_reset(&pdev->dev, ds2404_gpio_unmap, chip); - if (retval) - return retval; - - dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n", - chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio, - chip->gpio[DS2404_DQ].gpio); - platform_set_drvdata(pdev, chip); chip->rtc->ops = &ds2404_rtc_ops; @@ -238,7 +207,7 @@ static int rtc_probe(struct platform_device *pdev) if (retval) return retval; - ds2404_enable_osc(&pdev->dev); + ds2404_enable_osc(chip); return 0; } diff --git a/drivers/rtc/rtc-fsl-ftm-alarm.c b/drivers/rtc/rtc-fsl-ftm-alarm.c index 3d7c4077fe1c69267aa46bc1037a6f7c8bd881d0..a72c4ad0cec6e5f0c37c9e9ffcaa0c466a44ad30 100644 --- a/drivers/rtc/rtc-fsl-ftm-alarm.c +++ b/drivers/rtc/rtc-fsl-ftm-alarm.c @@ -11,11 +11,8 @@ #include #include #include -#include -#include #include -#include -#include +#include #include #include #include diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index a613257d1574d387e80208e092a65e764a3e84c1..4eef7afcc8bcce5f207e30eeb4b0b0dc9dd956a8 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -9,6 +9,8 @@ */ #include +#include +#include #include #include #include @@ -31,6 +33,8 @@ #define ISL12022_REG_SR 0x07 #define ISL12022_REG_INT 0x08 +#define ISL12022_REG_PWR_VBAT 0x0a + #define ISL12022_REG_BETA 0x0d #define ISL12022_REG_TEMP_L 0x28 @@ -41,6 +45,12 @@ #define ISL12022_SR_LBAT75 (1 << 1) #define ISL12022_INT_WRTC (1 << 6) +#define ISL12022_INT_FO_MASK GENMASK(3, 0) +#define ISL12022_INT_FO_OFF 0x0 +#define ISL12022_INT_FO_32K 0x1 + +#define ISL12022_REG_VB85_MASK GENMASK(5, 3) +#define ISL12022_REG_VB75_MASK GENMASK(2, 0) #define ISL12022_BETA_TSE (1 << 7) @@ -141,12 +151,6 @@ static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm) if (ret) return ret; - if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) { - dev_warn(dev, - "voltage dropped below %u%%, date and time is not reliable.\n", - buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75); - } - dev_dbg(dev, "raw data is sec=%02x, min=%02x, hr=%02x, mday=%02x, mon=%02x, year=%02x, wday=%02x, sr=%02x, int=%02x", buf[ISL12022_REG_SC], @@ -204,7 +208,34 @@ static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) return regmap_bulk_write(regmap, ISL12022_REG_SC, buf, sizeof(buf)); } +static int isl12022_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 user, val; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(regmap, ISL12022_REG_SR, &val); + if (ret) + return ret; + + user = 0; + if (val & ISL12022_SR_LBAT85) + user |= RTC_VL_BACKUP_LOW; + + if (val & ISL12022_SR_LBAT75) + user |= RTC_VL_BACKUP_EMPTY; + + return put_user(user, (u32 __user *)arg); + + default: + return -ENOIOCTLCMD; + } +} + static const struct rtc_class_ops isl12022_rtc_ops = { + .ioctl = isl12022_rtc_ioctl, .read_time = isl12022_rtc_read_time, .set_time = isl12022_rtc_set_time, }; @@ -215,10 +246,88 @@ static const struct regmap_config regmap_config = { .use_single_write = true, }; +static int isl12022_register_clock(struct device *dev) +{ + struct regmap *regmap = dev_get_drvdata(dev); + struct clk_hw *hw; + int ret; + + if (!device_property_present(dev, "#clock-cells")) { + /* + * Disabling the F_OUT pin reduces the power + * consumption in battery mode by ~25%. + */ + regmap_update_bits(regmap, ISL12022_REG_INT, ISL12022_INT_FO_MASK, + ISL12022_INT_FO_OFF); + + return 0; + } + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + /* + * For now, only support a fixed clock of 32768Hz (the reset default). + */ + ret = regmap_update_bits(regmap, ISL12022_REG_INT, + ISL12022_INT_FO_MASK, ISL12022_INT_FO_32K); + if (ret) + return ret; + + hw = devm_clk_hw_register_fixed_rate(dev, "isl12022", NULL, 0, 32768); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); +} + +static const u32 trip_levels[2][7] = { + { 2125000, 2295000, 2550000, 2805000, 3060000, 4250000, 4675000 }, + { 1875000, 2025000, 2250000, 2475000, 2700000, 3750000, 4125000 }, +}; + +static void isl12022_set_trip_levels(struct device *dev) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 levels[2] = {0, 0}; + int ret, i, j, x[2]; + u8 val, mask; + + device_property_read_u32_array(dev, "isil,battery-trip-levels-microvolt", + levels, 2); + + for (i = 0; i < 2; i++) { + for (j = 0; j < ARRAY_SIZE(trip_levels[i]) - 1; j++) { + if (levels[i] <= trip_levels[i][j]) + break; + } + x[i] = j; + } + + val = FIELD_PREP(ISL12022_REG_VB85_MASK, x[0]) | + FIELD_PREP(ISL12022_REG_VB75_MASK, x[1]); + mask = ISL12022_REG_VB85_MASK | ISL12022_REG_VB75_MASK; + + ret = regmap_update_bits(regmap, ISL12022_REG_PWR_VBAT, mask, val); + if (ret) + dev_warn(dev, "unable to set battery alarm levels: %d\n", ret); + + /* + * Force a write of the TSE bit in the BETA register, in order + * to trigger an update of the LBAT75 and LBAT85 bits in the + * status register. In battery backup mode, those bits have + * another meaning, so without this, they may contain stale + * values for up to a minute after power-on. + */ + regmap_write_bits(regmap, ISL12022_REG_BETA, + ISL12022_BETA_TSE, ISL12022_BETA_TSE); +} + static int isl12022_probe(struct i2c_client *client) { struct rtc_device *rtc; struct regmap *regmap; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -231,6 +340,11 @@ static int isl12022_probe(struct i2c_client *client) dev_set_drvdata(&client->dev, regmap); + ret = isl12022_register_clock(&client->dev); + if (ret) + return ret; + + isl12022_set_trip_levels(&client->dev); isl12022_hwmon_register(&client->dev); rtc = devm_rtc_allocate_device(&client->dev); diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c index 5abff5d348acdb6333e3bf1454da6292e2b0466b..2aabb9151d4c6c018d7101ec206d03897cff01d5 100644 --- a/drivers/rtc/rtc-isl12026.c +++ b/drivers/rtc/rtc-isl12026.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -429,7 +428,7 @@ static void isl12026_force_power_modes(struct i2c_client *client) } } -static int isl12026_probe_new(struct i2c_client *client) +static int isl12026_probe(struct i2c_client *client) { struct isl12026 *priv; int ret; @@ -490,7 +489,7 @@ static struct i2c_driver isl12026_driver = { .name = "rtc-isl12026", .of_match_table = isl12026_dt_match, }, - .probe = isl12026_probe_new, + .probe = isl12026_probe, .remove = isl12026_remove, }; diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index b0712b4e364891656598d0d764c92398057f346d..e50c23ee1646a5d90af3aa9940dd1be0ace7c55a 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -188,7 +188,7 @@ isl1208_i2c_validate_client(struct i2c_client *client) static int isl1208_set_xtoscb(struct i2c_client *client, int sr, int xtosb_val) { /* Do nothing if bit is already set to desired value */ - if ((sr & ISL1208_REG_SR_XTOSCB) == xtosb_val) + if (!!(sr & ISL1208_REG_SR_XTOSCB) == xtosb_val) return 0; if (xtosb_val) @@ -862,17 +862,9 @@ isl1208_probe(struct i2c_client *client) i2c_set_clientdata(client, isl1208); /* Determine which chip we have */ - if (client->dev.of_node) { - isl1208->config = of_device_get_match_data(&client->dev); - if (!isl1208->config) - return -ENODEV; - } else { - const struct i2c_device_id *id = i2c_match_id(isl1208_id, client); - - if (!id) - return -ENODEV; - isl1208->config = (struct isl1208_config *)id->driver_data; - } + isl1208->config = i2c_get_match_data(client); + if (!isl1208->config) + return -ENODEV; rc = isl1208_clk_present(client, "xin"); if (rc < 0) @@ -952,7 +944,6 @@ isl1208_probe(struct i2c_client *client) rc = isl1208_setup_irq(client, client->irq); if (rc) return rc; - } else { clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features); } diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 36453b008139b3d8897c8df47c087d794268f650..bafa7d1b9b883a99e51582bf8ec4a797595ce3b9 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include @@ -349,7 +349,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev) if (!rtc) return -ENOMEM; - rtc->type = (enum jz4740_rtc_type)device_get_match_data(dev); + rtc->type = (uintptr_t)device_get_match_data(dev); irq = platform_get_irq(pdev, 0); if (irq < 0) diff --git a/drivers/rtc/rtc-lpc24xx.c b/drivers/rtc/rtc-lpc24xx.c index a4612e543f35348e3c1c2c98bb411c9e2dfc3eb2..df17c48ff086ee7228ce157f8bf54a356e8ad006 100644 --- a/drivers/rtc/rtc-lpc24xx.c +++ b/drivers/rtc/rtc-lpc24xx.c @@ -9,9 +9,8 @@ #include #include #include +#include #include -#include -#include #include #include diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 3cc5151e098644aaf4abdfa22c66f2eda14a8c53..866489ad56d67db1b5a69c4b9e7bc294b864c7f4 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index 481c9525b1ddbfdf709165d87e911f9cfa34b295..dd4a62e2d39c73b1c8c3f50a59fd4fffc0b45105 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -269,9 +270,16 @@ static int m48t86_rtc_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id m48t86_rtc_of_ids[] = { + { .compatible = "st,m48t86" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, m48t86_rtc_of_ids); + static struct platform_driver m48t86_rtc_platform_driver = { .driver = { .name = "rtc-m48t86", + .of_match_table = m48t86_rtc_of_ids, }, .probe = m48t86_rtc_probe, }; diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c index 07df43e4c4d06cf089834209a9ea602a1f64e2df..28858fcaea8f6b4fd48f7e794ff8fbcbfb4cdbb0 100644 --- a/drivers/rtc/rtc-mpc5121.c +++ b/drivers/rtc/rtc-mpc5121.c @@ -11,10 +11,8 @@ #include #include #include -#include -#include #include -#include +#include #include #include diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c index 1d297af80f878818f5ee6ca00a6aab7f501b9742..1617063669cc58a4314957a98a9832856229cadc 100644 --- a/drivers/rtc/rtc-mt6397.c +++ b/drivers/rtc/rtc-mt6397.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c index 81857a457c326ad5364e6f56de576a77417d1296..094c649fc137f20929b356bf2209c57fdd842c99 100644 --- a/drivers/rtc/rtc-mt7622.c +++ b/drivers/rtc/rtc-mt7622.c @@ -7,9 +7,9 @@ #include #include +#include +#include #include -#include -#include #include #include diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 762cf03345f1450164b633a59cdefeb29c1420c2..dbb935dbbd8ab79a43d72043806d2821ca141918 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -11,7 +11,6 @@ #include #include #include -#include #define RTC_INPUT_CLK_32768HZ (0x00 << 5) #define RTC_INPUT_CLK_32000HZ (0x01 << 5) diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c index a4e3f924837e0cf904a8c098ccdff895ae1e3fed..ed4e606be8e58d2779bd6aff148b388435770830 100644 --- a/drivers/rtc/rtc-nct3018y.c +++ b/drivers/rtc/rtc-nct3018y.c @@ -538,7 +538,7 @@ MODULE_DEVICE_TABLE(of, nct3018y_of_match); static struct i2c_driver nct3018y_driver = { .driver = { .name = "rtc-nct3018y", - .of_match_table = of_match_ptr(nct3018y_of_match), + .of_match_table = nct3018y_of_match, }, .probe = nct3018y_probe, .id_table = nct3018y_id, diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 8ae4d7824ec9f7b77fd2d8511743ca93625b857a..5b10ab06cd2e2fa7df95808768fc4606b674b08c 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -747,12 +747,12 @@ static int omap_rtc_probe(struct platform_device *pdev) } rtc->irq_timer = platform_get_irq(pdev, 0); - if (rtc->irq_timer <= 0) - return -ENOENT; + if (rtc->irq_timer < 0) + return rtc->irq_timer; rtc->irq_alarm = platform_get_irq(pdev, 1); - if (rtc->irq_alarm <= 0) - return -ENOENT; + if (rtc->irq_alarm < 0) + return rtc->irq_alarm; rtc->clk = devm_clk_get(&pdev->dev, "ext-clk"); if (!IS_ERR(rtc->clk)) diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index ee03b04b74baa7c2740e15f672eb90bbb0a8602e..9c04c4e1a49c370ee50178b5bf7e853c62c75321 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * An I2C and SPI driver for the NXP PCF2127/29 RTC + * An I2C and SPI driver for the NXP PCF2127/29/31 RTC * Copyright 2013 Til-Technologies * * Author: Renaud Cerrato @@ -8,9 +8,13 @@ * Watchdog and tamper functions * Author: Bruno Thomsen * + * PCF2131 support + * Author: Hugo Villeneuve + * * based on the other drivers in this same directory. * - * Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf + * Datasheets: https://www.nxp.com/docs/en/data-sheet/PCF2127.pdf + * https://www.nxp.com/docs/en/data-sheet/PCF2131DS.pdf */ #include @@ -21,6 +25,7 @@ #include #include #include +#include #include #include @@ -28,6 +33,7 @@ #define PCF2127_REG_CTRL1 0x00 #define PCF2127_BIT_CTRL1_POR_OVRD BIT(3) #define PCF2127_BIT_CTRL1_TSF1 BIT(4) +#define PCF2127_BIT_CTRL1_STOP BIT(5) /* Control register 2 */ #define PCF2127_REG_CTRL2 0x01 #define PCF2127_BIT_CTRL2_AIE BIT(1) @@ -43,20 +49,10 @@ #define PCF2127_BIT_CTRL3_BF BIT(3) #define PCF2127_BIT_CTRL3_BTSE BIT(4) /* Time and date registers */ -#define PCF2127_REG_SC 0x03 +#define PCF2127_REG_TIME_BASE 0x03 #define PCF2127_BIT_SC_OSF BIT(7) -#define PCF2127_REG_MN 0x04 -#define PCF2127_REG_HR 0x05 -#define PCF2127_REG_DM 0x06 -#define PCF2127_REG_DW 0x07 -#define PCF2127_REG_MO 0x08 -#define PCF2127_REG_YR 0x09 /* Alarm registers */ -#define PCF2127_REG_ALARM_SC 0x0A -#define PCF2127_REG_ALARM_MN 0x0B -#define PCF2127_REG_ALARM_HR 0x0C -#define PCF2127_REG_ALARM_DM 0x0D -#define PCF2127_REG_ALARM_DW 0x0E +#define PCF2127_REG_ALARM_BASE 0x0A #define PCF2127_BIT_ALARM_AE BIT(7) /* CLKOUT control register */ #define PCF2127_REG_CLKOUT 0x0f @@ -68,21 +64,15 @@ #define PCF2127_BIT_WD_CTL_CD0 BIT(6) #define PCF2127_BIT_WD_CTL_CD1 BIT(7) #define PCF2127_REG_WD_VAL 0x11 -/* Tamper timestamp registers */ -#define PCF2127_REG_TS_CTRL 0x12 +/* Tamper timestamp1 registers */ +#define PCF2127_REG_TS1_BASE 0x12 #define PCF2127_BIT_TS_CTRL_TSOFF BIT(6) #define PCF2127_BIT_TS_CTRL_TSM BIT(7) -#define PCF2127_REG_TS_SC 0x13 -#define PCF2127_REG_TS_MN 0x14 -#define PCF2127_REG_TS_HR 0x15 -#define PCF2127_REG_TS_DM 0x16 -#define PCF2127_REG_TS_MO 0x17 -#define PCF2127_REG_TS_YR 0x18 /* * RAM registers * PCF2127 has 512 bytes general-purpose static RAM (SRAM) that is * battery backed and can survive a power outage. - * PCF2129 doesn't have this feature. + * PCF2129/31 doesn't have this feature. */ #define PCF2127_REG_RAM_ADDR_MSB 0x1A #define PCF2127_REG_RAM_WRT_CMD 0x1C @@ -90,9 +80,14 @@ /* Watchdog timer value constants */ #define PCF2127_WD_VAL_STOP 0 -#define PCF2127_WD_VAL_MIN 2 -#define PCF2127_WD_VAL_MAX 255 -#define PCF2127_WD_VAL_DEFAULT 60 +/* PCF2127/29 watchdog timer value constants */ +#define PCF2127_WD_CLOCK_HZ_X1000 1000 /* 1Hz */ +#define PCF2127_WD_MIN_HW_HEARTBEAT_MS 500 +/* PCF2131 watchdog timer value constants */ +#define PCF2131_WD_CLOCK_HZ_X1000 250 /* 1/4Hz */ +#define PCF2131_WD_MIN_HW_HEARTBEAT_MS 4000 + +#define PCF2127_WD_DEFAULT_TIMEOUT_S 60 /* Mask for currently enabled interrupts */ #define PCF2127_CTRL1_IRQ_MASK (PCF2127_BIT_CTRL1_TSF1) @@ -101,13 +96,117 @@ PCF2127_BIT_CTRL2_WDTF | \ PCF2127_BIT_CTRL2_TSF2) +#define PCF2127_MAX_TS_SUPPORTED 4 + +/* Control register 4 */ +#define PCF2131_REG_CTRL4 0x03 +#define PCF2131_BIT_CTRL4_TSF4 BIT(4) +#define PCF2131_BIT_CTRL4_TSF3 BIT(5) +#define PCF2131_BIT_CTRL4_TSF2 BIT(6) +#define PCF2131_BIT_CTRL4_TSF1 BIT(7) +/* Control register 5 */ +#define PCF2131_REG_CTRL5 0x04 +#define PCF2131_BIT_CTRL5_TSIE4 BIT(4) +#define PCF2131_BIT_CTRL5_TSIE3 BIT(5) +#define PCF2131_BIT_CTRL5_TSIE2 BIT(6) +#define PCF2131_BIT_CTRL5_TSIE1 BIT(7) +/* Software reset register */ +#define PCF2131_REG_SR_RESET 0x05 +#define PCF2131_SR_RESET_READ_PATTERN (BIT(2) | BIT(5)) +#define PCF2131_SR_RESET_CPR_CMD (PCF2131_SR_RESET_READ_PATTERN | BIT(7)) +/* Time and date registers */ +#define PCF2131_REG_TIME_BASE 0x07 +/* Alarm registers */ +#define PCF2131_REG_ALARM_BASE 0x0E +/* CLKOUT control register */ +#define PCF2131_REG_CLKOUT 0x13 +/* Watchdog registers */ +#define PCF2131_REG_WD_CTL 0x35 +#define PCF2131_REG_WD_VAL 0x36 +/* Tamper timestamp1 registers */ +#define PCF2131_REG_TS1_BASE 0x14 +/* Tamper timestamp2 registers */ +#define PCF2131_REG_TS2_BASE 0x1B +/* Tamper timestamp3 registers */ +#define PCF2131_REG_TS3_BASE 0x22 +/* Tamper timestamp4 registers */ +#define PCF2131_REG_TS4_BASE 0x29 +/* Interrupt mask registers */ +#define PCF2131_REG_INT_A_MASK1 0x31 +#define PCF2131_REG_INT_A_MASK2 0x32 +#define PCF2131_REG_INT_B_MASK1 0x33 +#define PCF2131_REG_INT_B_MASK2 0x34 +#define PCF2131_BIT_INT_BLIE BIT(0) +#define PCF2131_BIT_INT_BIE BIT(1) +#define PCF2131_BIT_INT_AIE BIT(2) +#define PCF2131_BIT_INT_WD_CD BIT(3) +#define PCF2131_BIT_INT_SI BIT(4) +#define PCF2131_BIT_INT_MI BIT(5) +#define PCF2131_CTRL2_IRQ_MASK ( \ + PCF2127_BIT_CTRL2_AF | \ + PCF2127_BIT_CTRL2_WDTF) +#define PCF2131_CTRL4_IRQ_MASK ( \ + PCF2131_BIT_CTRL4_TSF4 | \ + PCF2131_BIT_CTRL4_TSF3 | \ + PCF2131_BIT_CTRL4_TSF2 | \ + PCF2131_BIT_CTRL4_TSF1) + +enum pcf21xx_type { + PCF2127, + PCF2129, + PCF2131, + PCF21XX_LAST_ID +}; + +struct pcf21xx_ts_config { + u8 reg_base; /* Base register to read timestamp values. */ + + /* + * If the TS input pin is driven to GND, an interrupt can be generated + * (supported by all variants). + */ + u8 gnd_detect_reg; /* Interrupt control register address. */ + u8 gnd_detect_bit; /* Interrupt bit. */ + + /* + * If the TS input pin is driven to an intermediate level between GND + * and supply, an interrupt can be generated (optional feature depending + * on variant). + */ + u8 inter_detect_reg; /* Interrupt control register address. */ + u8 inter_detect_bit; /* Interrupt bit. */ + + u8 ie_reg; /* Interrupt enable control register. */ + u8 ie_bit; /* Interrupt enable bit. */ +}; + +struct pcf21xx_config { + int type; /* IC variant */ + int max_register; + unsigned int has_nvmem:1; + unsigned int has_bit_wd_ctl_cd0:1; + unsigned int wd_val_reg_readable:1; /* If watchdog value register can be read. */ + unsigned int has_int_a_b:1; /* PCF2131 supports two interrupt outputs. */ + u8 reg_time_base; /* Time/date base register. */ + u8 regs_alarm_base; /* Alarm function base registers. */ + u8 reg_wd_ctl; /* Watchdog control register. */ + u8 reg_wd_val; /* Watchdog value register. */ + u8 reg_clkout; /* Clkout register. */ + int wdd_clock_hz_x1000; /* Watchdog clock in Hz multiplicated by 1000 */ + int wdd_min_hw_heartbeat_ms; + unsigned int ts_count; + struct pcf21xx_ts_config ts[PCF2127_MAX_TS_SUPPORTED]; + struct attribute_group attribute_group; +}; + struct pcf2127 { struct rtc_device *rtc; struct watchdog_device wdd; struct regmap *regmap; - time64_t ts; - bool ts_valid; + const struct pcf21xx_config *cfg; bool irq_enabled; + time64_t ts[PCF2127_MAX_TS_SUPPORTED]; /* Timestamp values. */ + bool ts_valid[PCF2127_MAX_TS_SUPPORTED]; /* Timestamp valid indication. */ }; /* @@ -117,27 +216,22 @@ struct pcf2127 { static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev); - unsigned char buf[10]; + unsigned char buf[7]; int ret; /* * Avoid reading CTRL2 register as it causes WD_VAL register * value to reset to 0 which means watchdog is stopped. */ - ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3, - (buf + PCF2127_REG_CTRL3), - ARRAY_SIZE(buf) - PCF2127_REG_CTRL3); + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->reg_time_base, + buf, sizeof(buf)); if (ret) { dev_err(dev, "%s: read error\n", __func__); return ret; } - if (buf[PCF2127_REG_CTRL3] & PCF2127_BIT_CTRL3_BLF) - dev_info(dev, - "low voltage detected, check/replace RTC battery.\n"); - /* Clock integrity is not guaranteed when OSF flag is set. */ - if (buf[PCF2127_REG_SC] & PCF2127_BIT_SC_OSF) { + if (buf[0] & PCF2127_BIT_SC_OSF) { /* * no need clear the flag here, * it will be cleared once the new date is saved @@ -148,20 +242,17 @@ static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm) } dev_dbg(dev, - "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, " + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", - __func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC], - buf[PCF2127_REG_MN], buf[PCF2127_REG_HR], - buf[PCF2127_REG_DM], buf[PCF2127_REG_DW], - buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]); - - tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F); - tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F); - tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */ - tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F); - tm->tm_wday = buf[PCF2127_REG_DW] & 0x07; - tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ - tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]); + __func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + tm->tm_sec = bcd2bin(buf[0] & 0x7F); + tm->tm_min = bcd2bin(buf[1] & 0x7F); + tm->tm_hour = bcd2bin(buf[2] & 0x3F); + tm->tm_mday = bcd2bin(buf[3] & 0x3F); + tm->tm_wday = buf[4] & 0x07; + tm->tm_mon = bcd2bin(buf[5] & 0x1F) - 1; + tm->tm_year = bcd2bin(buf[6]); tm->tm_year += 100; dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " @@ -198,14 +289,45 @@ static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm) /* year */ buf[i++] = bin2bcd(tm->tm_year - 100); - /* write register's data */ - err = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i); + /* Write access to time registers: + * PCF2127/29: no special action required. + * PCF2131: requires setting the STOP and CPR bits. STOP bit needs to + * be cleared after time registers are updated. + */ + if (pcf2127->cfg->type == PCF2131) { + err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1, + PCF2127_BIT_CTRL1_STOP, + PCF2127_BIT_CTRL1_STOP); + if (err) { + dev_dbg(dev, "setting STOP bit failed\n"); + return err; + } + + err = regmap_write(pcf2127->regmap, PCF2131_REG_SR_RESET, + PCF2131_SR_RESET_CPR_CMD); + if (err) { + dev_dbg(dev, "sending CPR cmd failed\n"); + return err; + } + } + + /* write time register's data */ + err = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->reg_time_base, buf, i); if (err) { - dev_err(dev, - "%s: err=%d", __func__, err); + dev_dbg(dev, "%s: err=%d", __func__, err); return err; } + if (pcf2127->cfg->type == PCF2131) { + /* Clear STOP bit (PCF2131 only) after write is completed. */ + err = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1, + PCF2127_BIT_CTRL1_STOP, 0); + if (err) { + dev_dbg(dev, "clearing STOP bit failed\n"); + return err; + } + } + return 0; } @@ -275,9 +397,16 @@ static int pcf2127_nvmem_write(void *priv, unsigned int offset, static int pcf2127_wdt_ping(struct watchdog_device *wdd) { + int wd_val; struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd); - return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL, wdd->timeout); + /* + * Compute counter value of WATCHDG_TIM_VAL to obtain desired period + * in seconds, depending on the source clock frequency. + */ + wd_val = ((wdd->timeout * pcf2127->cfg->wdd_clock_hz_x1000) / 1000) + 1; + + return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val, wd_val); } /* @@ -311,7 +440,7 @@ static int pcf2127_wdt_stop(struct watchdog_device *wdd) { struct pcf2127 *pcf2127 = watchdog_get_drvdata(wdd); - return regmap_write(pcf2127->regmap, PCF2127_REG_WD_VAL, + return regmap_write(pcf2127->regmap, pcf2127->cfg->reg_wd_val, PCF2127_WD_VAL_STOP); } @@ -339,9 +468,25 @@ static const struct watchdog_ops pcf2127_watchdog_ops = { .set_timeout = pcf2127_wdt_set_timeout, }; +/* + * Compute watchdog period, t, in seconds, from the WATCHDG_TIM_VAL register + * value, n, and the clock frequency, f1000, in Hz x 1000. + * + * The PCF2127/29 datasheet gives t as: + * t = n / f + * The PCF2131 datasheet gives t as: + * t = (n - 1) / f + * For both variants, the watchdog is triggered when the WATCHDG_TIM_VAL reaches + * the value 1, and not zero. Consequently, the equation from the PCF2131 + * datasheet seems to be the correct one for both variants. + */ +static int pcf2127_watchdog_get_period(int n, int f1000) +{ + return (1000 * (n - 1)) / f1000; +} + static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127) { - u32 wdd_timeout; int ret; if (!IS_ENABLED(CONFIG_WATCHDOG) || @@ -351,21 +496,35 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127) pcf2127->wdd.parent = dev; pcf2127->wdd.info = &pcf2127_wdt_info; pcf2127->wdd.ops = &pcf2127_watchdog_ops; - pcf2127->wdd.min_timeout = PCF2127_WD_VAL_MIN; - pcf2127->wdd.max_timeout = PCF2127_WD_VAL_MAX; - pcf2127->wdd.timeout = PCF2127_WD_VAL_DEFAULT; - pcf2127->wdd.min_hw_heartbeat_ms = 500; + + pcf2127->wdd.min_timeout = + pcf2127_watchdog_get_period( + 2, pcf2127->cfg->wdd_clock_hz_x1000); + pcf2127->wdd.max_timeout = + pcf2127_watchdog_get_period( + 255, pcf2127->cfg->wdd_clock_hz_x1000); + pcf2127->wdd.timeout = PCF2127_WD_DEFAULT_TIMEOUT_S; + + dev_dbg(dev, "%s clock = %d Hz / 1000\n", __func__, + pcf2127->cfg->wdd_clock_hz_x1000); + + pcf2127->wdd.min_hw_heartbeat_ms = pcf2127->cfg->wdd_min_hw_heartbeat_ms; pcf2127->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS; watchdog_set_drvdata(&pcf2127->wdd, pcf2127); /* Test if watchdog timer is started by bootloader */ - ret = regmap_read(pcf2127->regmap, PCF2127_REG_WD_VAL, &wdd_timeout); - if (ret) - return ret; + if (pcf2127->cfg->wd_val_reg_readable) { + u32 wdd_timeout; - if (wdd_timeout) - set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status); + ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_wd_val, + &wdd_timeout); + if (ret) + return ret; + + if (wdd_timeout) + set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status); + } return devm_watchdog_register_device(dev, &pcf2127->wdd); } @@ -386,8 +545,8 @@ static int pcf2127_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) if (ret) return ret; - ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf, - sizeof(buf)); + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->regs_alarm_base, + buf, sizeof(buf)); if (ret) return ret; @@ -437,8 +596,8 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) buf[3] = bin2bcd(alrm->time.tm_mday); buf[4] = PCF2127_BIT_ALARM_AE; /* Do not match on week day */ - ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_ALARM_SC, buf, - sizeof(buf)); + ret = regmap_bulk_write(pcf2127->regmap, pcf2127->cfg->regs_alarm_base, + buf, sizeof(buf)); if (ret) return ret; @@ -446,38 +605,35 @@ static int pcf2127_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) } /* - * This function reads ctrl2 register, caller is responsible for calling - * pcf2127_wdt_active_ping() + * This function reads one timestamp function data, caller is responsible for + * calling pcf2127_wdt_active_ping() */ -static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts) +static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts, + int ts_id) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev); struct rtc_time tm; int ret; - unsigned char data[25]; + unsigned char data[7]; - ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL1, data, - sizeof(data)); + ret = regmap_bulk_read(pcf2127->regmap, pcf2127->cfg->ts[ts_id].reg_base, + data, sizeof(data)); if (ret) { dev_err(dev, "%s: read error ret=%d\n", __func__, ret); return ret; } dev_dbg(dev, - "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n", - __func__, data[PCF2127_REG_CTRL1], data[PCF2127_REG_CTRL2], - data[PCF2127_REG_CTRL3], data[PCF2127_REG_TS_SC], - data[PCF2127_REG_TS_MN], data[PCF2127_REG_TS_HR], - data[PCF2127_REG_TS_DM], data[PCF2127_REG_TS_MO], - data[PCF2127_REG_TS_YR]); - - tm.tm_sec = bcd2bin(data[PCF2127_REG_TS_SC] & 0x7F); - tm.tm_min = bcd2bin(data[PCF2127_REG_TS_MN] & 0x7F); - tm.tm_hour = bcd2bin(data[PCF2127_REG_TS_HR] & 0x3F); - tm.tm_mday = bcd2bin(data[PCF2127_REG_TS_DM] & 0x3F); + "%s: raw data is ts_sc=%02x, ts_mn=%02x, ts_hr=%02x, ts_dm=%02x, ts_mo=%02x, ts_yr=%02x\n", + __func__, data[1], data[2], data[3], data[4], data[5], data[6]); + + tm.tm_sec = bcd2bin(data[1] & 0x7F); + tm.tm_min = bcd2bin(data[2] & 0x7F); + tm.tm_hour = bcd2bin(data[3] & 0x3F); + tm.tm_mday = bcd2bin(data[4] & 0x3F); /* TS_MO register (month) value range: 1-12 */ - tm.tm_mon = bcd2bin(data[PCF2127_REG_TS_MO] & 0x1F) - 1; - tm.tm_year = bcd2bin(data[PCF2127_REG_TS_YR]); + tm.tm_mon = bcd2bin(data[5] & 0x1F) - 1; + tm.tm_year = bcd2bin(data[6]); if (tm.tm_year < 70) tm.tm_year += 100; /* assume we are in 1970...2069 */ @@ -491,47 +647,84 @@ static int pcf2127_rtc_ts_read(struct device *dev, time64_t *ts) return 0; }; -static void pcf2127_rtc_ts_snapshot(struct device *dev) +static void pcf2127_rtc_ts_snapshot(struct device *dev, int ts_id) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev); int ret; + if (ts_id >= pcf2127->cfg->ts_count) + return; + /* Let userspace read the first timestamp */ - if (pcf2127->ts_valid) + if (pcf2127->ts_valid[ts_id]) return; - ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts); + ret = pcf2127_rtc_ts_read(dev, &pcf2127->ts[ts_id], ts_id); if (!ret) - pcf2127->ts_valid = true; + pcf2127->ts_valid[ts_id] = true; } static irqreturn_t pcf2127_rtc_irq(int irq, void *dev) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev); - unsigned int ctrl1, ctrl2; + unsigned int ctrl2; int ret = 0; - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1); - if (ret) - return IRQ_NONE; - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2); if (ret) return IRQ_NONE; - if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK)) - return IRQ_NONE; + if (pcf2127->cfg->ts_count == 1) { + /* PCF2127/29 */ + unsigned int ctrl1; + + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1); + if (ret) + return IRQ_NONE; + + if (!(ctrl1 & PCF2127_CTRL1_IRQ_MASK || ctrl2 & PCF2127_CTRL2_IRQ_MASK)) + return IRQ_NONE; + + if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2) + pcf2127_rtc_ts_snapshot(dev, 0); + + if (ctrl1 & PCF2127_CTRL1_IRQ_MASK) + regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1, + ctrl1 & ~PCF2127_CTRL1_IRQ_MASK); + + if (ctrl2 & PCF2127_CTRL2_IRQ_MASK) + regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2, + ctrl2 & ~PCF2127_CTRL2_IRQ_MASK); + } else { + /* PCF2131. */ + unsigned int ctrl4; + + ret = regmap_read(pcf2127->regmap, PCF2131_REG_CTRL4, &ctrl4); + if (ret) + return IRQ_NONE; + + if (!(ctrl4 & PCF2131_CTRL4_IRQ_MASK || ctrl2 & PCF2131_CTRL2_IRQ_MASK)) + return IRQ_NONE; - if (ctrl1 & PCF2127_BIT_CTRL1_TSF1 || ctrl2 & PCF2127_BIT_CTRL2_TSF2) - pcf2127_rtc_ts_snapshot(dev); + if (ctrl4 & PCF2131_CTRL4_IRQ_MASK) { + int i; + int tsf_bit = PCF2131_BIT_CTRL4_TSF1; /* Start at bit 7. */ - if (ctrl1 & PCF2127_CTRL1_IRQ_MASK) - regmap_write(pcf2127->regmap, PCF2127_REG_CTRL1, - ctrl1 & ~PCF2127_CTRL1_IRQ_MASK); + for (i = 0; i < pcf2127->cfg->ts_count; i++) { + if (ctrl4 & tsf_bit) + pcf2127_rtc_ts_snapshot(dev, i); - if (ctrl2 & PCF2127_CTRL2_IRQ_MASK) - regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2, - ctrl2 & ~PCF2127_CTRL2_IRQ_MASK); + tsf_bit = tsf_bit >> 1; + } + + regmap_write(pcf2127->regmap, PCF2131_REG_CTRL4, + ctrl4 & ~PCF2131_CTRL4_IRQ_MASK); + } + + if (ctrl2 & PCF2131_CTRL2_IRQ_MASK) + regmap_write(pcf2127->regmap, PCF2127_REG_CTRL2, + ctrl2 & ~PCF2131_CTRL2_IRQ_MASK); + } if (ctrl2 & PCF2127_BIT_CTRL2_AF) rtc_update_irq(pcf2127->rtc, 1, RTC_IRQF | RTC_AF); @@ -552,28 +745,41 @@ static const struct rtc_class_ops pcf2127_rtc_ops = { /* sysfs interface */ -static ssize_t timestamp0_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t timestamp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count, int ts_id) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent); int ret; + if (ts_id >= pcf2127->cfg->ts_count) + return 0; + if (pcf2127->irq_enabled) { - pcf2127->ts_valid = false; + pcf2127->ts_valid[ts_id] = false; } else { - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL1, - PCF2127_BIT_CTRL1_TSF1, 0); + /* Always clear GND interrupt bit. */ + ret = regmap_update_bits(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].gnd_detect_reg, + pcf2127->cfg->ts[ts_id].gnd_detect_bit, + 0); + if (ret) { - dev_err(dev, "%s: update ctrl1 ret=%d\n", __func__, ret); + dev_err(dev, "%s: update TS gnd detect ret=%d\n", __func__, ret); return ret; } - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2, - PCF2127_BIT_CTRL2_TSF2, 0); - if (ret) { - dev_err(dev, "%s: update ctrl2 ret=%d\n", __func__, ret); - return ret; + if (pcf2127->cfg->ts[ts_id].inter_detect_bit) { + /* Clear intermediate level interrupt bit if supported. */ + ret = regmap_update_bits(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].inter_detect_reg, + pcf2127->cfg->ts[ts_id].inter_detect_bit, + 0); + if (ret) { + dev_err(dev, "%s: update TS intermediate level detect ret=%d\n", + __func__, ret); + return ret; + } } ret = pcf2127_wdt_active_ping(&pcf2127->wdd); @@ -582,34 +788,84 @@ static ssize_t timestamp0_store(struct device *dev, } return count; +} + +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 0); }; -static ssize_t timestamp0_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t timestamp1_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 1); +}; + +static ssize_t timestamp2_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 2); +}; + +static ssize_t timestamp3_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return timestamp_store(dev, attr, buf, count, 3); +}; + +static ssize_t timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf, + int ts_id) { struct pcf2127 *pcf2127 = dev_get_drvdata(dev->parent); - unsigned int ctrl1, ctrl2; int ret; time64_t ts; + if (ts_id >= pcf2127->cfg->ts_count) + return 0; + if (pcf2127->irq_enabled) { - if (!pcf2127->ts_valid) + if (!pcf2127->ts_valid[ts_id]) return 0; - ts = pcf2127->ts; + ts = pcf2127->ts[ts_id]; } else { - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL1, &ctrl1); - if (ret) - return 0; + u8 valid_low = 0; + u8 valid_inter = 0; + unsigned int ctrl; - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL2, &ctrl2); + /* Check if TS input pin is driven to GND, supported by all + * variants. + */ + ret = regmap_read(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].gnd_detect_reg, + &ctrl); if (ret) return 0; - if (!(ctrl1 & PCF2127_BIT_CTRL1_TSF1) && - !(ctrl2 & PCF2127_BIT_CTRL2_TSF2)) + valid_low = ctrl & pcf2127->cfg->ts[ts_id].gnd_detect_bit; + + if (pcf2127->cfg->ts[ts_id].inter_detect_bit) { + /* Check if TS input pin is driven to intermediate level + * between GND and supply, if supported by variant. + */ + ret = regmap_read(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].inter_detect_reg, + &ctrl); + if (ret) + return 0; + + valid_inter = ctrl & pcf2127->cfg->ts[ts_id].inter_detect_bit; + } + + if (!valid_low && !valid_inter) return 0; - ret = pcf2127_rtc_ts_read(dev->parent, &ts); + ret = pcf2127_rtc_ts_read(dev->parent, &ts, ts_id); if (ret) return 0; @@ -618,21 +874,227 @@ static ssize_t timestamp0_show(struct device *dev, return ret; } return sprintf(buf, "%llu\n", (unsigned long long)ts); +} + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 0); +}; + +static ssize_t timestamp1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 1); +}; + +static ssize_t timestamp2_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 2); +}; + +static ssize_t timestamp3_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return timestamp_show(dev, attr, buf, 3); }; static DEVICE_ATTR_RW(timestamp0); +static DEVICE_ATTR_RW(timestamp1); +static DEVICE_ATTR_RW(timestamp2); +static DEVICE_ATTR_RW(timestamp3); static struct attribute *pcf2127_attrs[] = { &dev_attr_timestamp0.attr, NULL }; -static const struct attribute_group pcf2127_attr_group = { - .attrs = pcf2127_attrs, +static struct attribute *pcf2131_attrs[] = { + &dev_attr_timestamp0.attr, + &dev_attr_timestamp1.attr, + &dev_attr_timestamp2.attr, + &dev_attr_timestamp3.attr, + NULL }; +static struct pcf21xx_config pcf21xx_cfg[] = { + [PCF2127] = { + .type = PCF2127, + .max_register = 0x1d, + .has_nvmem = 1, + .has_bit_wd_ctl_cd0 = 1, + .wd_val_reg_readable = 1, + .has_int_a_b = 0, + .reg_time_base = PCF2127_REG_TIME_BASE, + .regs_alarm_base = PCF2127_REG_ALARM_BASE, + .reg_wd_ctl = PCF2127_REG_WD_CTL, + .reg_wd_val = PCF2127_REG_WD_VAL, + .reg_clkout = PCF2127_REG_CLKOUT, + .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000, + .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS, + .ts_count = 1, + .ts[0] = { + .reg_base = PCF2127_REG_TS1_BASE, + .gnd_detect_reg = PCF2127_REG_CTRL1, + .gnd_detect_bit = PCF2127_BIT_CTRL1_TSF1, + .inter_detect_reg = PCF2127_REG_CTRL2, + .inter_detect_bit = PCF2127_BIT_CTRL2_TSF2, + .ie_reg = PCF2127_REG_CTRL2, + .ie_bit = PCF2127_BIT_CTRL2_TSIE, + }, + .attribute_group = { + .attrs = pcf2127_attrs, + }, + }, + [PCF2129] = { + .type = PCF2129, + .max_register = 0x19, + .has_nvmem = 0, + .has_bit_wd_ctl_cd0 = 0, + .wd_val_reg_readable = 1, + .has_int_a_b = 0, + .reg_time_base = PCF2127_REG_TIME_BASE, + .regs_alarm_base = PCF2127_REG_ALARM_BASE, + .reg_wd_ctl = PCF2127_REG_WD_CTL, + .reg_wd_val = PCF2127_REG_WD_VAL, + .reg_clkout = PCF2127_REG_CLKOUT, + .wdd_clock_hz_x1000 = PCF2127_WD_CLOCK_HZ_X1000, + .wdd_min_hw_heartbeat_ms = PCF2127_WD_MIN_HW_HEARTBEAT_MS, + .ts_count = 1, + .ts[0] = { + .reg_base = PCF2127_REG_TS1_BASE, + .gnd_detect_reg = PCF2127_REG_CTRL1, + .gnd_detect_bit = PCF2127_BIT_CTRL1_TSF1, + .inter_detect_reg = PCF2127_REG_CTRL2, + .inter_detect_bit = PCF2127_BIT_CTRL2_TSF2, + .ie_reg = PCF2127_REG_CTRL2, + .ie_bit = PCF2127_BIT_CTRL2_TSIE, + }, + .attribute_group = { + .attrs = pcf2127_attrs, + }, + }, + [PCF2131] = { + .type = PCF2131, + .max_register = 0x36, + .has_nvmem = 0, + .has_bit_wd_ctl_cd0 = 0, + .wd_val_reg_readable = 0, + .has_int_a_b = 1, + .reg_time_base = PCF2131_REG_TIME_BASE, + .regs_alarm_base = PCF2131_REG_ALARM_BASE, + .reg_wd_ctl = PCF2131_REG_WD_CTL, + .reg_wd_val = PCF2131_REG_WD_VAL, + .reg_clkout = PCF2131_REG_CLKOUT, + .wdd_clock_hz_x1000 = PCF2131_WD_CLOCK_HZ_X1000, + .wdd_min_hw_heartbeat_ms = PCF2131_WD_MIN_HW_HEARTBEAT_MS, + .ts_count = 4, + .ts[0] = { + .reg_base = PCF2131_REG_TS1_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF1, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE1, + }, + .ts[1] = { + .reg_base = PCF2131_REG_TS2_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF2, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE2, + }, + .ts[2] = { + .reg_base = PCF2131_REG_TS3_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF3, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE3, + }, + .ts[3] = { + .reg_base = PCF2131_REG_TS4_BASE, + .gnd_detect_reg = PCF2131_REG_CTRL4, + .gnd_detect_bit = PCF2131_BIT_CTRL4_TSF4, + .inter_detect_bit = 0, + .ie_reg = PCF2131_REG_CTRL5, + .ie_bit = PCF2131_BIT_CTRL5_TSIE4, + }, + .attribute_group = { + .attrs = pcf2131_attrs, + }, + }, +}; + +/* + * Enable timestamp function and corresponding interrupt(s). + */ +static int pcf2127_enable_ts(struct device *dev, int ts_id) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int ret; + + if (ts_id >= pcf2127->cfg->ts_count) { + dev_err(dev, "%s: invalid tamper detection ID (%d)\n", + __func__, ts_id); + return -EINVAL; + } + + /* Enable timestamp function. */ + ret = regmap_update_bits(pcf2127->regmap, + pcf2127->cfg->ts[ts_id].reg_base, + PCF2127_BIT_TS_CTRL_TSOFF | + PCF2127_BIT_TS_CTRL_TSM, + PCF2127_BIT_TS_CTRL_TSM); + if (ret) { + dev_err(dev, "%s: tamper detection config (ts%d_ctrl) failed\n", + __func__, ts_id); + return ret; + } + + /* + * Enable interrupt generation when TSF timestamp flag is set. + * Interrupt signals are open-drain outputs and can be left floating if + * unused. + */ + ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->ts[ts_id].ie_reg, + pcf2127->cfg->ts[ts_id].ie_bit, + pcf2127->cfg->ts[ts_id].ie_bit); + if (ret) { + dev_err(dev, "%s: tamper detection TSIE%d config failed\n", + __func__, ts_id); + return ret; + } + + return ret; +} + +/* Route all interrupt sources to INT A pin. */ +static int pcf2127_configure_interrupt_pins(struct device *dev) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int ret; + + /* Mask bits need to be cleared to enable corresponding + * interrupt source. + */ + ret = regmap_write(pcf2127->regmap, + PCF2131_REG_INT_A_MASK1, 0); + if (ret) + return ret; + + ret = regmap_write(pcf2127->regmap, + PCF2131_REG_INT_A_MASK2, 0); + if (ret) + return ret; + + return ret; +} + static int pcf2127_probe(struct device *dev, struct regmap *regmap, - int alarm_irq, const char *name, bool is_pcf2127) + int alarm_irq, const struct pcf21xx_config *config) { struct pcf2127 *pcf2127; int ret = 0; @@ -645,6 +1107,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, return -ENOMEM; pcf2127->regmap = regmap; + pcf2127->cfg = config; dev_set_drvdata(dev, pcf2127); @@ -656,8 +1119,16 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, pcf2127->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; pcf2127->rtc->range_max = RTC_TIMESTAMP_END_2099; pcf2127->rtc->set_start_time = true; /* Sets actual start to 1970 */ - set_bit(RTC_FEATURE_ALARM_RES_2S, pcf2127->rtc->features); - clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features); + + /* + * PCF2127/29 do not work correctly when setting alarms at 1s intervals. + * PCF2131 is ok. + */ + if (pcf2127->cfg->type == PCF2127 || pcf2127->cfg->type == PCF2129) { + set_bit(RTC_FEATURE_ALARM_RES_2S, pcf2127->rtc->features); + clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf2127->rtc->features); + } + clear_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features); if (alarm_irq > 0) { @@ -688,7 +1159,16 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, set_bit(RTC_FEATURE_ALARM, pcf2127->rtc->features); } - if (is_pcf2127) { + if (pcf2127->cfg->has_int_a_b) { + /* Configure int A/B pins, independently of alarm_irq. */ + ret = pcf2127_configure_interrupt_pins(dev); + if (ret) { + dev_err(dev, "failed to configure interrupt pins\n"); + return ret; + } + } + + if (pcf2127->cfg->has_nvmem) { struct nvmem_config nvmem_cfg = { .priv = pcf2127, .reg_read = pcf2127_nvmem_read, @@ -703,15 +1183,17 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, * The "Power-On Reset Override" facility prevents the RTC to do a reset * after power on. For normal operation the PORO must be disabled. */ - regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1, + ret = regmap_clear_bits(pcf2127->regmap, PCF2127_REG_CTRL1, PCF2127_BIT_CTRL1_POR_OVRD); + if (ret < 0) + return ret; - ret = regmap_read(pcf2127->regmap, PCF2127_REG_CLKOUT, &val); + ret = regmap_read(pcf2127->regmap, pcf2127->cfg->reg_clkout, &val); if (ret < 0) return ret; if (!(val & PCF2127_BIT_CLKOUT_OTPR)) { - ret = regmap_set_bits(pcf2127->regmap, PCF2127_REG_CLKOUT, + ret = regmap_set_bits(pcf2127->regmap, pcf2127->cfg->reg_clkout, PCF2127_BIT_CLKOUT_OTPR); if (ret < 0) return ret; @@ -721,20 +1203,20 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, /* * Watchdog timer enabled and reset pin /RST activated when timed out. - * Select 1Hz clock source for watchdog timer. + * Select 1Hz clock source for watchdog timer (1/4Hz for PCF2131). * Note: Countdown timer disabled and not available. - * For pca2129, pcf2129, only bit[7] is for Symbol WD_CD + * For pca2129, pcf2129 and pcf2131, only bit[7] is for Symbol WD_CD * of register watchdg_tim_ctl. The bit[6] is labeled * as T. Bits labeled as T must always be written with * logic 0. */ - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_WD_CTL, + ret = regmap_update_bits(pcf2127->regmap, pcf2127->cfg->reg_wd_ctl, PCF2127_BIT_WD_CTL_CD1 | PCF2127_BIT_WD_CTL_CD0 | PCF2127_BIT_WD_CTL_TF1 | PCF2127_BIT_WD_CTL_TF0, PCF2127_BIT_WD_CTL_CD1 | - (is_pcf2127 ? PCF2127_BIT_WD_CTL_CD0 : 0) | + (pcf2127->cfg->has_bit_wd_ctl_cd0 ? PCF2127_BIT_WD_CTL_CD0 : 0) | PCF2127_BIT_WD_CTL_TF1); if (ret) { dev_err(dev, "%s: watchdog config (wd_ctl) failed\n", __func__); @@ -760,34 +1242,15 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, } /* - * Enable timestamp function and store timestamp of first trigger - * event until TSF1 and TSF2 interrupt flags are cleared. - */ - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_TS_CTRL, - PCF2127_BIT_TS_CTRL_TSOFF | - PCF2127_BIT_TS_CTRL_TSM, - PCF2127_BIT_TS_CTRL_TSM); - if (ret) { - dev_err(dev, "%s: tamper detection config (ts_ctrl) failed\n", - __func__); - return ret; - } - - /* - * Enable interrupt generation when TSF1 or TSF2 timestamp flags - * are set. Interrupt signal is an open-drain output and can be - * left floating if unused. + * Enable timestamp functions 1 to 4. */ - ret = regmap_update_bits(pcf2127->regmap, PCF2127_REG_CTRL2, - PCF2127_BIT_CTRL2_TSIE, - PCF2127_BIT_CTRL2_TSIE); - if (ret) { - dev_err(dev, "%s: tamper detection config (ctrl2) failed\n", - __func__); - return ret; + for (int i = 0; i < pcf2127->cfg->ts_count; i++) { + ret = pcf2127_enable_ts(dev, i); + if (ret) + return ret; } - ret = rtc_add_group(pcf2127->rtc, &pcf2127_attr_group); + ret = rtc_add_group(pcf2127->rtc, &pcf2127->cfg->attribute_group); if (ret) { dev_err(dev, "%s: tamper sysfs registering failed\n", __func__); @@ -799,9 +1262,10 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap, #ifdef CONFIG_OF static const struct of_device_id pcf2127_of_match[] = { - { .compatible = "nxp,pcf2127" }, - { .compatible = "nxp,pcf2129" }, - { .compatible = "nxp,pca2129" }, + { .compatible = "nxp,pcf2127", .data = &pcf21xx_cfg[PCF2127] }, + { .compatible = "nxp,pcf2129", .data = &pcf21xx_cfg[PCF2129] }, + { .compatible = "nxp,pca2129", .data = &pcf21xx_cfg[PCF2129] }, + { .compatible = "nxp,pcf2131", .data = &pcf21xx_cfg[PCF2131] }, {} }; MODULE_DEVICE_TABLE(of, pcf2127_of_match); @@ -886,26 +1350,41 @@ static const struct regmap_bus pcf2127_i2c_regmap = { static struct i2c_driver pcf2127_i2c_driver; static const struct i2c_device_id pcf2127_i2c_id[] = { - { "pcf2127", 1 }, - { "pcf2129", 0 }, - { "pca2129", 0 }, + { "pcf2127", PCF2127 }, + { "pcf2129", PCF2129 }, + { "pca2129", PCF2129 }, + { "pcf2131", PCF2131 }, { } }; MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); static int pcf2127_i2c_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_match_id(pcf2127_i2c_id, client); struct regmap *regmap; - static const struct regmap_config config = { + static struct regmap_config config = { .reg_bits = 8, .val_bits = 8, - .max_register = 0x1d, }; + const struct pcf21xx_config *variant; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; + if (client->dev.of_node) { + variant = of_device_get_match_data(&client->dev); + if (!variant) + return -ENODEV; + } else { + enum pcf21xx_type type = + i2c_match_id(pcf2127_i2c_id, client)->driver_data; + + if (type >= PCF21XX_LAST_ID) + return -ENODEV; + variant = &pcf21xx_cfg[type]; + } + + config.max_register = variant->max_register, + regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap, &client->dev, &config); if (IS_ERR(regmap)) { @@ -914,8 +1393,7 @@ static int pcf2127_i2c_probe(struct i2c_client *client) return PTR_ERR(regmap); } - return pcf2127_probe(&client->dev, regmap, client->irq, - pcf2127_i2c_driver.driver.name, id->driver_data); + return pcf2127_probe(&client->dev, regmap, client->irq, variant); } static struct i2c_driver pcf2127_i2c_driver = { @@ -953,17 +1431,32 @@ static void pcf2127_i2c_unregister_driver(void) #if IS_ENABLED(CONFIG_SPI_MASTER) static struct spi_driver pcf2127_spi_driver; +static const struct spi_device_id pcf2127_spi_id[]; static int pcf2127_spi_probe(struct spi_device *spi) { - static const struct regmap_config config = { + static struct regmap_config config = { .reg_bits = 8, .val_bits = 8, .read_flag_mask = 0xa0, .write_flag_mask = 0x20, - .max_register = 0x1d, }; struct regmap *regmap; + const struct pcf21xx_config *variant; + + if (spi->dev.of_node) { + variant = of_device_get_match_data(&spi->dev); + if (!variant) + return -ENODEV; + } else { + enum pcf21xx_type type = spi_get_device_id(spi)->driver_data; + + if (type >= PCF21XX_LAST_ID) + return -ENODEV; + variant = &pcf21xx_cfg[type]; + } + + config.max_register = variant->max_register, regmap = devm_regmap_init_spi(spi, &config); if (IS_ERR(regmap)) { @@ -972,15 +1465,14 @@ static int pcf2127_spi_probe(struct spi_device *spi) return PTR_ERR(regmap); } - return pcf2127_probe(&spi->dev, regmap, spi->irq, - pcf2127_spi_driver.driver.name, - spi_get_device_id(spi)->driver_data); + return pcf2127_probe(&spi->dev, regmap, spi->irq, variant); } static const struct spi_device_id pcf2127_spi_id[] = { - { "pcf2127", 1 }, - { "pcf2129", 0 }, - { "pca2129", 0 }, + { "pcf2127", PCF2127 }, + { "pcf2129", PCF2129 }, + { "pca2129", PCF2129 }, + { "pcf2131", PCF2131 }, { } }; MODULE_DEVICE_TABLE(spi, pcf2127_spi_id); @@ -1045,5 +1537,5 @@ static void __exit pcf2127_exit(void) module_exit(pcf2127_exit) MODULE_AUTHOR("Renaud Cerrato "); -MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver"); +MODULE_DESCRIPTION("NXP PCF2127/29/31 RTC driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index e517abfaee2a03d64882add2177e736952b42cfa..fdbc07f14036aff0399c15adf71d89a30fe7e2cb 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -514,49 +514,40 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063) } #endif -enum pcf85063_type { - PCF85063, - PCF85063TP, - PCF85063A, - RV8263, - PCF85063_LAST_ID +static const struct pcf85063_config config_pcf85063 = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0a, + }, }; -static struct pcf85063_config pcf85063_cfg[] = { - [PCF85063] = { - .regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = 0x0a, - }, - }, - [PCF85063TP] = { - .regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = 0x0a, - }, - }, - [PCF85063A] = { - .regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = 0x11, - }, - .has_alarms = 1, +static const struct pcf85063_config config_pcf85063tp = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0a, }, - [RV8263] = { - .regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = 0x11, - }, - .has_alarms = 1, - .force_cap_7000 = 1, +}; + +static const struct pcf85063_config config_pcf85063a = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, }, + .has_alarms = 1, }; -static const struct i2c_device_id pcf85063_ids[]; +static const struct pcf85063_config config_rv8263 = { + .regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x11, + }, + .has_alarms = 1, + .force_cap_7000 = 1, +}; static int pcf85063_probe(struct i2c_client *client) { @@ -579,17 +570,9 @@ static int pcf85063_probe(struct i2c_client *client) if (!pcf85063) return -ENOMEM; - if (client->dev.of_node) { - config = of_device_get_match_data(&client->dev); - if (!config) - return -ENODEV; - } else { - enum pcf85063_type type = - i2c_match_id(pcf85063_ids, client)->driver_data; - if (type >= PCF85063_LAST_ID) - return -ENODEV; - config = &pcf85063_cfg[type]; - } + config = i2c_get_match_data(client); + if (!config) + return -ENODEV; pcf85063->regmap = devm_regmap_init_i2c(client, &config->regmap); if (IS_ERR(pcf85063->regmap)) @@ -655,22 +638,22 @@ static int pcf85063_probe(struct i2c_client *client) } static const struct i2c_device_id pcf85063_ids[] = { - { "pca85073a", PCF85063A }, - { "pcf85063", PCF85063 }, - { "pcf85063tp", PCF85063TP }, - { "pcf85063a", PCF85063A }, - { "rv8263", RV8263 }, + { "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 }, + { "pcf85063tp", .driver_data = (kernel_ulong_t)&config_pcf85063tp }, + { "pcf85063a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { "rv8263", .driver_data = (kernel_ulong_t)&config_rv8263 }, {} }; MODULE_DEVICE_TABLE(i2c, pcf85063_ids); #ifdef CONFIG_OF static const struct of_device_id pcf85063_of_match[] = { - { .compatible = "nxp,pca85073a", .data = &pcf85063_cfg[PCF85063A] }, - { .compatible = "nxp,pcf85063", .data = &pcf85063_cfg[PCF85063] }, - { .compatible = "nxp,pcf85063tp", .data = &pcf85063_cfg[PCF85063TP] }, - { .compatible = "nxp,pcf85063a", .data = &pcf85063_cfg[PCF85063A] }, - { .compatible = "microcrystal,rv8263", .data = &pcf85063_cfg[RV8263] }, + { .compatible = "nxp,pca85073a", .data = &config_pcf85063a }, + { .compatible = "nxp,pcf85063", .data = &config_pcf85063 }, + { .compatible = "nxp,pcf85063tp", .data = &config_pcf85063tp }, + { .compatible = "nxp,pcf85063a", .data = &config_pcf85063a }, + { .compatible = "microcrystal,rv8263", .data = &config_rv8263 }, {} }; MODULE_DEVICE_TABLE(of, pcf85063_of_match); diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c index 65b8b1338dbb0b607d651df507bf43097d3debee..06194674d71c57510cf1747e8fae45c32e14f84c 100644 --- a/drivers/rtc/rtc-pcf85363.c +++ b/drivers/rtc/rtc-pcf85363.c @@ -15,7 +15,6 @@ #include #include #include -#include #include /* @@ -403,6 +402,7 @@ static int pcf85363_probe(struct i2c_client *client) }, }; int ret, i, err; + bool wakeup_source; if (data) config = data; @@ -432,25 +432,36 @@ static int pcf85363_probe(struct i2c_client *client) pcf85363->rtc->ops = &rtc_ops; pcf85363->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; pcf85363->rtc->range_max = RTC_TIMESTAMP_END_2099; - clear_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features); + + wakeup_source = device_property_read_bool(&client->dev, + "wakeup-source"); + if (client->irq > 0 || wakeup_source) { + regmap_write(pcf85363->regmap, CTRL_FLAGS, 0); + regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO, + PIN_IO_INTA_OUT, PIN_IO_INTAPM); + } if (client->irq > 0) { unsigned long irqflags = IRQF_TRIGGER_LOW; if (dev_fwnode(&client->dev)) irqflags = 0; - - regmap_write(pcf85363->regmap, CTRL_FLAGS, 0); - regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO, - PIN_IO_INTA_OUT, PIN_IO_INTAPM); ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, pcf85363_rtc_handle_irq, irqflags | IRQF_ONESHOT, "pcf85363", client); - if (ret) - dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); - else - set_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features); + if (ret) { + dev_warn(&client->dev, + "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + + if (client->irq > 0 || wakeup_source) { + device_init_wakeup(&client->dev, true); + set_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features); + } else { + clear_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features); } ret = devm_rtc_register_device(pcf85363->rtc); diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index eeacf480cf3621a0576a7e800b9adea88493534e..e400c78252e8276f427def6ee34a5bf8fd9dccb1 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c @@ -14,7 +14,6 @@ #include #include #include -#include #include "rtc-sa1100.h" diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index a5a6c8772ecd4beeb57498bf998c3460d13d3ba9..f8fab0205f8cc659932870ce15c0c3137ebbcccf 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include /* * Ricoh has a family of I2C based RTCs, which differ only slightly from @@ -826,8 +826,7 @@ static int rs5c372_probe(struct i2c_client *client) rs5c372->client = client; i2c_set_clientdata(client, rs5c372); if (client->dev.of_node) { - rs5c372->type = (enum rtc_type) - of_device_get_match_data(&client->dev); + rs5c372->type = (uintptr_t)of_device_get_match_data(&client->dev); } else { const struct i2c_device_id *id = i2c_match_id(rs5c372_id, client); rs5c372->type = id->driver_data; diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c index 076e56f4e01a524e56c34a54f0fd8e7a7d8d5e6c..2f001c59c61d54610a147bb2e7d1b24c26894e36 100644 --- a/drivers/rtc/rtc-rv3028.c +++ b/drivers/rtc/rtc-rv3028.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -855,11 +855,68 @@ static const struct regmap_config regmap_config = { .max_register = 0x37, }; +static u8 rv3028_set_trickle_charger(struct rv3028_data *rv3028, + struct i2c_client *client) +{ + int ret, val_old, val; + u32 ohms, chargeable; + + ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &val_old); + if (ret < 0) + return ret; + + /* mask out only trickle charger bits */ + val_old = val_old & (RV3028_BACKUP_TCE | RV3028_BACKUP_TCR_MASK); + val = val_old; + + /* setup trickle charger */ + if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms", + &ohms)) { + int i; + + for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++) + if (ohms == rv3028_trickle_resistors[i]) + break; + + if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { + /* enable trickle charger and its resistor */ + val = RV3028_BACKUP_TCE | i; + } else { + dev_warn(&client->dev, "invalid trickle resistor value\n"); + } + } + + if (!device_property_read_u32(&client->dev, "aux-voltage-chargeable", + &chargeable)) { + switch (chargeable) { + case 0: + val &= ~RV3028_BACKUP_TCE; + break; + case 1: + val |= RV3028_BACKUP_TCE; + break; + default: + dev_warn(&client->dev, + "unsupported aux-voltage-chargeable value\n"); + break; + } + } + + /* only update EEPROM if changes are necessary */ + if (val_old != val) { + ret = rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_TCE | + RV3028_BACKUP_TCR_MASK, val); + if (ret) + return ret; + } + + return ret; +} + static int rv3028_probe(struct i2c_client *client) { struct rv3028_data *rv3028; int ret, status; - u32 ohms; struct nvmem_config nvmem_cfg = { .name = "rv3028_nvram", .word_size = 1, @@ -937,24 +994,9 @@ static int rv3028_probe(struct i2c_client *client) if (ret) return ret; - /* setup trickle charger */ - if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms", - &ohms)) { - int i; - - for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++) - if (ohms == rv3028_trickle_resistors[i]) - break; - - if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { - ret = rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_TCE | - RV3028_BACKUP_TCR_MASK, RV3028_BACKUP_TCE | i); - if (ret) - return ret; - } else { - dev_warn(&client->dev, "invalid trickle resistor value\n"); - } - } + ret = rv3028_set_trickle_charger(rv3028, client); + if (ret) + return ret; ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group); if (ret) diff --git a/drivers/rtc/rtc-rv3032.c b/drivers/rtc/rtc-rv3032.c index 6b8eb2039a33368b3d203e20e1139953fadb250c..35b2e36b426a0de0469119e7ce729e7b87fc7bbf 100644 --- a/drivers/rtc/rtc-rv3032.c +++ b/drivers/rtc/rtc-rv3032.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 98679cae13e8409ae0d080e2e42fa886c3d8e1a1..1a3ec1bb5b8148dd945ad127f45e657cd4c5a882 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #define RV8803_I2C_TRY_COUNT 4 @@ -645,8 +645,7 @@ static int rv8803_probe(struct i2c_client *client) mutex_init(&rv8803->flags_lock); rv8803->client = client; if (client->dev.of_node) { - rv8803->type = (enum rv8803_type) - of_device_get_match_data(&client->dev); + rv8803->type = (uintptr_t)of_device_get_match_data(&client->dev); } else { const struct i2c_device_id *id = i2c_match_id(rv8803_id, client); diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c index 8702db6096ba4e7cb6c6a5f7a30233bc7395250e..834274db8c3fd3d2d8a98a639d77c75a4c0b1529 100644 --- a/drivers/rtc/rtc-rx6110.c +++ b/drivers/rtc/rtc-rx6110.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index 82881fd2e14a1aa844182ac9cd5d05e481d5f192..48efd61a114d40a59aed15087086ff37c529b225 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/rtc/rtc-rzn1.c b/drivers/rtc/rtc-rzn1.c index dca736caba85224454ca929601942c6984192490..56ebbd4d04814715c61ae1144220446d1fa403d7 100644 --- a/drivers/rtc/rtc-rzn1.c +++ b/drivers/rtc/rtc-rzn1.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -227,7 +227,7 @@ static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) return ret; /* We cannot set alarms more than one week ahead */ - farest = rtc_tm_to_time64(&tm_now) + (7 * 86400); + farest = rtc_tm_to_time64(&tm_now) + rtc->rtcdev->alarm_offset_max; alarm = rtc_tm_to_time64(tm); if (time_after(alarm, farest)) return -ERANGE; @@ -351,6 +351,7 @@ static int rzn1_rtc_probe(struct platform_device *pdev) rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099; + rtc->rtcdev->alarm_offset_max = 7 * 86400; rtc->rtcdev->ops = &rzn1_rtc_ops; set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features); clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 70e1a18e5efdfa8db211bda90199483b935b0991..282238818f63bd7f07b79dd834468f9dc44a72cf 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c index 3d36e11cff80c291fc8197612ab4e0f672c59a2f..76753c71d92eef3e1682abb3560026864bb6a962 100644 --- a/drivers/rtc/rtc-stm32.c +++ b/drivers/rtc/rtc-stm32.c @@ -6,11 +6,13 @@ #include #include +#include #include #include #include #include -#include +#include +#include #include #include #include @@ -89,6 +91,9 @@ /* Max STM32 RTC register offset is 0x3FC */ #define UNDEF_REG 0xFFFF +/* STM32 RTC driver time helpers */ +#define SEC_PER_DAY (24 * 60 * 60) + struct stm32_rtc; struct stm32_rtc_registers { @@ -114,6 +119,7 @@ struct stm32_rtc_data { void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags); bool has_pclk; bool need_dbp; + bool need_accuracy; }; struct stm32_rtc { @@ -158,10 +164,9 @@ static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) * slowest rtc_ck frequency may be 32kHz and highest should be * 1MHz, we poll every 10 us with a timeout of 100ms. */ - return readl_relaxed_poll_timeout_atomic( - rtc->base + regs->isr, - isr, (isr & STM32_RTC_ISR_INITF), - 10, 100000); + return readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr, isr, + (isr & STM32_RTC_ISR_INITF), + 10, 100000); } return 0; @@ -425,40 +430,42 @@ static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } -static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm) +static int stm32_rtc_valid_alrm(struct device *dev, struct rtc_time *tm) { - const struct stm32_rtc_registers *regs = &rtc->data->regs; - int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec; - unsigned int dr = readl_relaxed(rtc->base + regs->dr); - unsigned int tr = readl_relaxed(rtc->base + regs->tr); - - cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT; - cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT; - cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT; - cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT; - cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT; - cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT; + static struct rtc_time now; + time64_t max_alarm_time64; + int max_day_forward; + int next_month; + int next_year; /* * Assuming current date is M-D-Y H:M:S. * RTC alarm can't be set on a specific month and year. * So the valid alarm range is: * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S - * with a specific case for December... */ - if ((((tm->tm_year > cur_year) && - (tm->tm_mon == 0x1) && (cur_mon == 0x12)) || - ((tm->tm_year == cur_year) && - (tm->tm_mon <= cur_mon + 1))) && - ((tm->tm_mday > cur_day) || - ((tm->tm_mday == cur_day) && - ((tm->tm_hour > cur_hour) || - ((tm->tm_hour == cur_hour) && (tm->tm_min > cur_min)) || - ((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) && - (tm->tm_sec >= cur_sec)))))) - return 0; + stm32_rtc_read_time(dev, &now); + + /* + * Find the next month and the year of the next month. + * Note: tm_mon and next_month are from 0 to 11 + */ + next_month = now.tm_mon + 1; + if (next_month == 12) { + next_month = 0; + next_year = now.tm_year + 1; + } else { + next_year = now.tm_year; + } - return -EINVAL; + /* Find the maximum limit of alarm in days. */ + max_day_forward = rtc_month_days(now.tm_mon, now.tm_year) + - now.tm_mday + + min(rtc_month_days(next_month, next_year), now.tm_mday); + + /* Convert to timestamp and compare the alarm time and its upper limit */ + max_alarm_time64 = rtc_tm_to_time64(&now) + max_day_forward * SEC_PER_DAY; + return rtc_tm_to_time64(tm) <= max_alarm_time64 ? 0 : -EINVAL; } static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) @@ -469,17 +476,17 @@ static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) unsigned int cr, isr, alrmar; int ret = 0; - tm2bcd(tm); - /* * RTC alarm can't be set on a specific date, unless this date is * up to the same day of month next month. */ - if (stm32_rtc_valid_alrm(rtc, tm) < 0) { + if (stm32_rtc_valid_alrm(dev, tm) < 0) { dev_err(dev, "Alarm can be set only on upcoming month.\n"); return -EINVAL; } + tm2bcd(tm); + alrmar = 0; /* tm_year and tm_mon are not used because not supported by RTC */ alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) & @@ -545,6 +552,7 @@ static void stm32_rtc_clear_events(struct stm32_rtc *rtc, static const struct stm32_rtc_data stm32_rtc_data = { .has_pclk = false, .need_dbp = true, + .need_accuracy = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -566,6 +574,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { static const struct stm32_rtc_data stm32h7_rtc_data = { .has_pclk = true, .need_dbp = true, + .need_accuracy = false, .regs = { .tr = 0x00, .dr = 0x04, @@ -596,6 +605,7 @@ static void stm32mp1_rtc_clear_events(struct stm32_rtc *rtc, static const struct stm32_rtc_data stm32mp1_data = { .has_pclk = true, .need_dbp = false, + .need_accuracy = true, .regs = { .tr = 0x00, .dr = 0x04, @@ -628,7 +638,7 @@ static int stm32_rtc_init(struct platform_device *pdev, const struct stm32_rtc_registers *regs = &rtc->data->regs; unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr; unsigned int rate; - int ret = 0; + int ret; rate = clk_get_rate(rtc->rtc_ck); @@ -636,18 +646,32 @@ static int stm32_rtc_init(struct platform_device *pdev, pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT; pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT; - for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) { - pred_s = (rate / (pred_a + 1)) - 1; + if (rate > (pred_a_max + 1) * (pred_s_max + 1)) { + dev_err(&pdev->dev, "rtc_ck rate is too high: %dHz\n", rate); + return -EINVAL; + } + + if (rtc->data->need_accuracy) { + for (pred_a = 0; pred_a <= pred_a_max; pred_a++) { + pred_s = (rate / (pred_a + 1)) - 1; + + if (pred_s <= pred_s_max && ((pred_s + 1) * (pred_a + 1)) == rate) + break; + } + } else { + for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) { + pred_s = (rate / (pred_a + 1)) - 1; - if (((pred_s + 1) * (pred_a + 1)) == rate) - break; + if (((pred_s + 1) * (pred_a + 1)) == rate) + break; + } } /* * Can't find a 1Hz, so give priority to RTC power consumption * by choosing the higher possible value for prediv_a */ - if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) { + if (pred_s > pred_s_max || pred_a > pred_a_max) { pred_a = pred_a_max; pred_s = (rate / (pred_a + 1)) - 1; @@ -656,6 +680,20 @@ static int stm32_rtc_init(struct platform_device *pdev, "fast" : "slow"); } + cr = readl_relaxed(rtc->base + regs->cr); + + prer = readl_relaxed(rtc->base + regs->prer); + prer &= STM32_RTC_PRER_PRED_S | STM32_RTC_PRER_PRED_A; + + pred_s = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & + STM32_RTC_PRER_PRED_S; + pred_a = (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & + STM32_RTC_PRER_PRED_A; + + /* quit if there is nothing to initialize */ + if ((cr & STM32_RTC_CR_FMT) == 0 && prer == (pred_s | pred_a)) + return 0; + stm32_rtc_wpr_unlock(rtc); ret = stm32_rtc_enter_init_mode(rtc); @@ -665,13 +703,10 @@ static int stm32_rtc_init(struct platform_device *pdev, goto end; } - prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S; - writel_relaxed(prer, rtc->base + regs->prer); - prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A; - writel_relaxed(prer, rtc->base + regs->prer); + writel_relaxed(pred_s, rtc->base + regs->prer); + writel_relaxed(pred_a | pred_s, rtc->base + regs->prer); /* Force 24h time format */ - cr = readl_relaxed(rtc->base + regs->cr); cr &= ~STM32_RTC_CR_FMT; writel_relaxed(cr, rtc->base + regs->cr); @@ -730,16 +765,13 @@ static int stm32_rtc_probe(struct platform_device *pdev) rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL); } else { rtc->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(rtc->pclk)) { - dev_err(&pdev->dev, "no pclk clock"); - return PTR_ERR(rtc->pclk); - } + if (IS_ERR(rtc->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->pclk), "no pclk clock"); + rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck"); } - if (IS_ERR(rtc->rtc_ck)) { - dev_err(&pdev->dev, "no rtc_ck clock"); - return PTR_ERR(rtc->rtc_ck); - } + if (IS_ERR(rtc->rtc_ck)) + return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_ck), "no rtc_ck clock"); if (rtc->data->has_pclk) { ret = clk_prepare_enable(rtc->pclk); @@ -859,7 +891,6 @@ static void stm32_rtc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); } -#ifdef CONFIG_PM_SLEEP static int stm32_rtc_suspend(struct device *dev) { struct stm32_rtc *rtc = dev_get_drvdata(dev); @@ -890,10 +921,10 @@ static int stm32_rtc_resume(struct device *dev) return ret; } -#endif -static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops, - stm32_rtc_suspend, stm32_rtc_resume); +static const struct dev_pm_ops stm32_rtc_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_rtc_suspend, stm32_rtc_resume) +}; static struct platform_driver stm32_rtc_driver = { .probe = stm32_rtc_probe, diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 6f11b745f34d95bfb0df5ca8e5033cfacbf3b6f0..7566d0a44af82aab2647a644a94464511ea42aea 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c index 71548dd59a3a68ab529423db4d65b13b00515dcc..8e0c669061036c1beb6bd677f1dacb26cba8273c 100644 --- a/drivers/rtc/rtc-sun6i.c +++ b/drivers/rtc/rtc-sun6i.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -847,8 +846,6 @@ static int sun6i_rtc_probe(struct platform_device *pdev) if (ret) return ret; - dev_info(&pdev->dev, "RTC enabled\n"); - return 0; } diff --git a/drivers/rtc/rtc-sunplus.c b/drivers/rtc/rtc-sunplus.c index f33dc301f3019338bc344abbf04efa104bde7f19..20c7e97c2fc81523356dd0cb23d2fde02cd48be0 100644 --- a/drivers/rtc/rtc-sunplus.c +++ b/drivers/rtc/rtc-sunplus.c @@ -244,7 +244,7 @@ static int sp_rtc_probe(struct platform_device *plat_dev) sp_rtc->irq = platform_get_irq(plat_dev, 0); if (sp_rtc->irq < 0) - return dev_err_probe(&plat_dev->dev, sp_rtc->irq, "platform_get_irq failed\n"); + return sp_rtc->irq; ret = devm_request_irq(&plat_dev->dev, sp_rtc->irq, sp_rtc_irq_handler, IRQF_TRIGGER_RISING, "rtc irq", plat_dev); diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c index 5d019e3a835ad6f490e0476b39404f2c5640bbd6..5cab9953c44f5d84a7b142b0a27bb4f9ac3c7d4a 100644 --- a/drivers/rtc/rtc-sunxi.c +++ b/drivers/rtc/rtc-sunxi.c @@ -14,8 +14,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/drivers/rtc/rtc-ti-k3.c b/drivers/rtc/rtc-ti-k3.c index 0d90fe923355079be2d0104670aa658a75777d63..ec759d8f7023c62a3996ff6ff03a4bb53c2291c2 100644 --- a/drivers/rtc/rtc-ti-k3.c +++ b/drivers/rtc/rtc-ti-k3.c @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/rtc/rtc-tps6586x.c b/drivers/rtc/rtc-tps6586x.c index 9f14e247574704222e25507198ae39249efd4164..20faf08c254cc04c48c70a85101330616ef79133 100644 --- a/drivers/rtc/rtc-tps6586x.c +++ b/drivers/rtc/rtc-tps6586x.c @@ -252,6 +252,7 @@ static int tps6586x_rtc_probe(struct platform_device *pdev) rtc->rtc->ops = &tps6586x_rtc_ops; rtc->rtc->range_max = (1ULL << 30) - 1; /* 30-bit seconds */ + rtc->rtc->alarm_offset_max = ALM1_VALID_RANGE_IN_SEC; rtc->rtc->start_secs = mktime64(2009, 1, 1, 0, 0, 0); rtc->rtc->set_start_time = true; diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c index 75e4c2d777b9c716773c980434a47b8d9b5125d0..411ff66c04682fe76a15c15b0a7f7bba153d1976 100644 --- a/drivers/rtc/rtc-tps65910.c +++ b/drivers/rtc/rtc-tps65910.c @@ -406,11 +406,8 @@ static int tps65910_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tps_rtc); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n", - irq); - return -ENXIO; - } + if (irq < 0) + return irq; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c index 81b36948c2fa3e65aadc6fd37d160b05dd5798b3..13f8ce08243c8950a940fe46d9df4c5ae0228a6c 100644 --- a/drivers/rtc/rtc-twl.c +++ b/drivers/rtc/rtc-twl.c @@ -487,11 +487,24 @@ static const struct rtc_class_ops twl_rtc_ops = { .alarm_irq_enable = twl_rtc_alarm_irq_enable, }; +static int twl_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return twl_i2c_read((long)priv, val, offset, bytes); +} + +static int twl_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + return twl_i2c_write((long)priv, val, offset, bytes); +} + /*----------------------------------------------------------------------*/ static int twl_rtc_probe(struct platform_device *pdev) { struct twl_rtc *twl_rtc; + struct nvmem_config nvmem_cfg; struct device_node *np = pdev->dev.of_node; int ret = -EINVAL; int irq = platform_get_irq(pdev, 0); @@ -542,7 +555,6 @@ static int twl_rtc_probe(struct platform_device *pdev) REG_INT_MSK_STS_A); } - dev_info(&pdev->dev, "Enabling TWL-RTC\n"); ret = twl_rtc_write_u8(twl_rtc, BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG); if (ret < 0) @@ -564,11 +576,8 @@ static int twl_rtc_probe(struct platform_device *pdev) twl_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &twl_rtc_ops, THIS_MODULE); - if (IS_ERR(twl_rtc->rtc)) { - dev_err(&pdev->dev, "can't register RTC device, err %ld\n", - PTR_ERR(twl_rtc->rtc)); + if (IS_ERR(twl_rtc->rtc)) return PTR_ERR(twl_rtc->rtc); - } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, twl_rtc_interrupt, @@ -579,6 +588,30 @@ static int twl_rtc_probe(struct platform_device *pdev) return ret; } + memset(&nvmem_cfg, 0, sizeof(nvmem_cfg)); + nvmem_cfg.name = "twl-secured-"; + nvmem_cfg.type = NVMEM_TYPE_BATTERY_BACKED; + nvmem_cfg.reg_read = twl_nvram_read, + nvmem_cfg.reg_write = twl_nvram_write, + nvmem_cfg.word_size = 1; + nvmem_cfg.stride = 1; + if (twl_class_is_4030()) { + /* 20 bytes SECURED_REG area */ + nvmem_cfg.size = 20; + nvmem_cfg.priv = (void *)TWL_MODULE_SECURED_REG; + devm_rtc_nvmem_register(twl_rtc->rtc, &nvmem_cfg); + /* 8 bytes BACKUP area */ + nvmem_cfg.name = "twl-backup-"; + nvmem_cfg.size = 8; + nvmem_cfg.priv = (void *)TWL4030_MODULE_BACKUP; + devm_rtc_nvmem_register(twl_rtc->rtc, &nvmem_cfg); + } else { + /* 8 bytes SECURED_REG area */ + nvmem_cfg.size = 8; + nvmem_cfg.priv = (void *)TWL_MODULE_SECURED_REG; + devm_rtc_nvmem_register(twl_rtc->rtc, &nvmem_cfg); + } + return 0; } diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c index 947f8071803f1b2ed458232a3b20e2a1454c3255..3c773cff2b3930775eba6b3604855a88f34d079d 100644 --- a/drivers/rtc/rtc-wm8350.c +++ b/drivers/rtc/rtc-wm8350.c @@ -386,8 +386,6 @@ static int wm8350_rtc_probe(struct platform_device *pdev) /* enable the RTC if it's not already enabled */ power5 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); if (!(power5 & WM8350_RTC_TICK_ENA)) { - dev_info(wm8350->dev, "Starting RTC\n"); - wm8350_reg_unlock(wm8350); ret = wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, @@ -426,11 +424,8 @@ static int wm8350_rtc_probe(struct platform_device *pdev) wm_rtc->rtc = devm_rtc_device_register(&pdev->dev, "wm8350", &wm8350_rtc_ops, THIS_MODULE); - if (IS_ERR(wm_rtc->rtc)) { - ret = PTR_ERR(wm_rtc->rtc); - dev_err(&pdev->dev, "failed to register RTC: %d\n", ret); - return ret; - } + if (IS_ERR(wm_rtc->rtc)) + return PTR_ERR(wm_rtc->rtc); ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350_rtc_update_handler, 0, diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 620fab01b710b9d89f9d2ba97d7dd7cfd765deaa..c4e36650c42649ff28150ca2d24f93c659758794 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1378,16 +1378,12 @@ static ssize_t dasd_vendor_show(struct device *dev, static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL); -#define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 +\ - /* SSID */ 4 + 1 + /* unit addr */ 2 + 1 +\ - /* vduit */ 32 + 1) - static ssize_t dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) { + char uid_string[DASD_UID_STRLEN]; struct dasd_device *device; struct dasd_uid uid; - char uid_string[UID_STRLEN]; char ua_string[3]; device = dasd_device_from_cdev(to_ccwdev(dev)); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 8587e423169ecd2bfc01acc61119ef600cc4a83d..bd89b032968a4b747c64f5e1e9c95b799fab8d22 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1079,12 +1079,12 @@ static void dasd_eckd_get_uid_string(struct dasd_conf *conf, create_uid(conf, &uid); if (strlen(uid.vduit) > 0) - snprintf(print_uid, sizeof(*print_uid), + snprintf(print_uid, DASD_UID_STRLEN, "%s.%s.%04x.%02x.%s", uid.vendor, uid.serial, uid.ssid, uid.real_unit_addr, uid.vduit); else - snprintf(print_uid, sizeof(*print_uid), + snprintf(print_uid, DASD_UID_STRLEN, "%s.%s.%04x.%02x", uid.vendor, uid.serial, uid.ssid, uid.real_unit_addr); @@ -1093,8 +1093,8 @@ static void dasd_eckd_get_uid_string(struct dasd_conf *conf, static int dasd_eckd_check_cabling(struct dasd_device *device, void *conf_data, __u8 lpm) { + char print_path_uid[DASD_UID_STRLEN], print_device_uid[DASD_UID_STRLEN]; struct dasd_eckd_private *private = device->private; - char print_path_uid[60], print_device_uid[60]; struct dasd_conf path_conf; path_conf.data = conf_data; @@ -1293,9 +1293,9 @@ static void dasd_eckd_path_available_action(struct dasd_device *device, __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; __u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm; struct dasd_conf_data *conf_data; + char print_uid[DASD_UID_STRLEN]; struct dasd_conf path_conf; unsigned long flags; - char print_uid[60]; int rc, pos; opm = 0; @@ -5855,8 +5855,8 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, static int dasd_eckd_reload_device(struct dasd_device *device) { struct dasd_eckd_private *private = device->private; + char print_uid[DASD_UID_STRLEN]; int rc, old_base; - char print_uid[60]; struct dasd_uid uid; unsigned long flags; diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 0aa56351da720ebf8807a581ba9bd1d08b1a0a2b..8a4dbe9d774113836da6dc646a8f00ebac9ea910 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -259,6 +259,10 @@ struct dasd_uid { char vduit[33]; }; +#define DASD_UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 + \ + /* SSID */ 4 + 1 + /* unit addr */ 2 + 1 + \ + /* vduit */ 32 + 1) + /* * PPRC Status data */ diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 06bcb6c78909311619591ff10a74827e62c3dfc8..4b7ecd4fd4319c000d2a4f1101022eafae0f2291 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -411,13 +411,13 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch segment_unload(entry->segment_name); } list_del(&dev_info->lh); + up_write(&dcssblk_devices_sem); dax_remove_host(dev_info->gd); kill_dax(dev_info->dax_dev); put_dax(dev_info->dax_dev); del_gendisk(dev_info->gd); put_disk(dev_info->gd); - up_write(&dcssblk_devices_sem); if (device_remove_file_self(dev, attr)) { device_unregister(dev); @@ -790,18 +790,17 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch } list_del(&dev_info->lh); + /* unload all related segments */ + list_for_each_entry(entry, &dev_info->seg_list, lh) + segment_unload(entry->segment_name); + up_write(&dcssblk_devices_sem); + dax_remove_host(dev_info->gd); kill_dax(dev_info->dax_dev); put_dax(dev_info->dax_dev); del_gendisk(dev_info->gd); put_disk(dev_info->gd); - /* unload all related segments */ - list_for_each_entry(entry, &dev_info->seg_list, lh) - segment_unload(entry->segment_name); - - up_write(&dcssblk_devices_sem); - device_unregister(&dev_info->dev); put_device(&dev_info->dev); diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 9fa92e45e0ee6fd123464047a03d3882a32a5ef0..7207a7f5842a1164f9fe2f8eae7cef21ae5c7c2b 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -111,7 +111,7 @@ static inline unsigned long mon_mca_end(struct mon_msg *monmsg) static inline u8 mon_mca_type(struct mon_msg *monmsg, u8 index) { - return *((u8 *) mon_mca_start(monmsg) + monmsg->mca_offset + index); + return *((u8 *)__va(mon_mca_start(monmsg)) + monmsg->mca_offset + index); } static inline u32 mon_mca_size(struct mon_msg *monmsg) @@ -121,12 +121,12 @@ static inline u32 mon_mca_size(struct mon_msg *monmsg) static inline u32 mon_rec_start(struct mon_msg *monmsg) { - return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 4)); + return *((u32 *)(__va(mon_mca_start(monmsg)) + monmsg->mca_offset + 4)); } static inline u32 mon_rec_end(struct mon_msg *monmsg) { - return *((u32 *) (mon_mca_start(monmsg) + monmsg->mca_offset + 8)); + return *((u32 *)(__va(mon_mca_start(monmsg)) + monmsg->mca_offset + 8)); } static int mon_check_mca(struct mon_msg *monmsg) @@ -392,8 +392,7 @@ static ssize_t mon_read(struct file *filp, char __user *data, mce_start = mon_mca_start(monmsg) + monmsg->mca_offset; if ((monmsg->pos >= mce_start) && (monmsg->pos < mce_start + 12)) { count = min(count, (size_t) mce_start + 12 - monmsg->pos); - ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos, - count); + ret = copy_to_user(data, __va(monmsg->pos), count); if (ret) return -EFAULT; monmsg->pos += count; @@ -406,8 +405,7 @@ static ssize_t mon_read(struct file *filp, char __user *data, if (monmsg->pos <= mon_rec_end(monmsg)) { count = min(count, (size_t) mon_rec_end(monmsg) - monmsg->pos + 1); - ret = copy_to_user(data, (void *) (unsigned long) monmsg->pos, - count); + ret = copy_to_user(data, __va(monmsg->pos), count); if (ret) return -EFAULT; monmsg->pos += count; diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 34967e67249ecbaaaba362dda9046dbfdca47b80..a108f2bf5b3331b7a6aa5b5e2e7bc8c06d9dbc3e 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -49,8 +49,6 @@ int register_adapter_interrupt(struct airq_struct *airq) return -ENOMEM; airq->flags |= AIRQ_PTR_ALLOCATED; } - if (!airq->lsi_mask) - airq->lsi_mask = 0xff; snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%p", airq); CIO_TRACE_EVENT(4, dbf_txt); isc_register(airq->isc); @@ -98,7 +96,7 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy) head = &airq_lists[tpi_info->isc]; rcu_read_lock(); hlist_for_each_entry_rcu(airq, head, list) - if ((*airq->lsi_ptr & airq->lsi_mask) != 0) + if (*airq->lsi_ptr != 0) airq->handler(airq, tpi_info); rcu_read_unlock(); diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 4b23c9f7f3e54102a8ee0b97def8e9aa924a8925..ce04caa7913fb0bd4bf36c9abe81426823764859 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -366,7 +366,6 @@ static int zcdn_create(const char *name) { dev_t devt; int i, rc = 0; - char nodename[ZCDN_MAX_NAME]; struct zcdn_device *zcdndev; if (mutex_lock_interruptible(&ap_perms_mutex)) @@ -407,13 +406,11 @@ static int zcdn_create(const char *name) zcdndev->device.devt = devt; zcdndev->device.groups = zcdn_dev_attr_groups; if (name[0]) - strncpy(nodename, name, sizeof(nodename)); + rc = dev_set_name(&zcdndev->device, "%s", name); else - snprintf(nodename, sizeof(nodename), - ZCRYPT_NAME "_%d", (int)MINOR(devt)); - nodename[sizeof(nodename) - 1] = '\0'; - if (dev_set_name(&zcdndev->device, nodename)) { - rc = -EINVAL; + rc = dev_set_name(&zcdndev->device, ZCRYPT_NAME "_%d", (int)MINOR(devt)); + if (rc) { + kfree(zcdndev); goto unlockout; } rc = device_register(&zcdndev->device); diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index df782646e856fa8ea11e495583b3626af039ec41..ab2f35bc294da8a4655aff22c3342123f87e233f 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -518,12 +518,12 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, if (port) { put_device(&port->dev); retval = -EEXIST; - goto err_out; + goto err_put; } port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL); if (!port) - goto err_out; + goto err_put; rwlock_init(&port->unit_list_lock); INIT_LIST_HEAD(&port->unit_list); @@ -546,7 +546,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) { kfree(port); - goto err_out; + goto err_put; } retval = -EINVAL; @@ -563,7 +563,8 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, return port; -err_out: +err_put: zfcp_ccw_adapter_put(adapter); +err_out: return ERR_PTR(retval); } diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index 02922768b12981fa3eba18e2492e7fdb44427937..ac67576301bf53d20d7194e3ae38cba6a7bae979 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -250,7 +250,6 @@ static struct airq_info *new_airq_info(int index) info->airq.handler = virtio_airq_handler; info->summary_indicator_idx = index; info->airq.lsi_ptr = get_summary_indicator(info); - info->airq.lsi_mask = 0xff; info->airq.isc = VIRTIO_AIRQ_ISC; rc = register_adapter_interrupt(&info->airq); if (rc) { diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 4962ce989113a14abd5f84af82121158d04bef68..695a57d894cdd0cba13bd1462ca7f2bf4a368b9a 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -836,7 +836,7 @@ config SCSI_IMM config SCSI_IZIP_EPP16 bool "ppa/imm option - Use slow (but safe) EPP-16" - depends on SCSI_PPA || SCSI_IMM + depends on SCSI_IMM help EPP (Enhanced Parallel Port) is a standard for parallel ports which allows them to act as expansion buses that can handle up to 64 diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 3f062e4013ab6c3b9cb54e46f608bee974b5e9a1..013a9a334972ebd3b8578d83c19639627cd8b38d 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1451,7 +1451,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) #endif break; } - scsi_rescan_device(&device->sdev_gendev); + scsi_rescan_device(device); break; default: diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile index 243adb0a38d13312a133e79636c5edda39fe86d8..a3f2357a3f08b5640f41cbd52263892fae5fcbe1 100644 --- a/drivers/scsi/aic7xxx/aicasm/Makefile +++ b/drivers/scsi/aic7xxx/aicasm/Makefile @@ -61,23 +61,11 @@ $(OUTDIR)/aicdb.h: clean: rm -f $(clean-files) -# Create a dependency chain in generated files -# to avoid concurrent invocations of the single -# rule that builds them all. -$(OUTDIR)/aicasm_gram.c: $(OUTDIR)/aicasm_gram.h $(OUTDIR)/aicasm_gram.c $(OUTDIR)/aicasm_gram.h: aicasm_gram.y - $(YACC) $(YFLAGS) -b $(<:.y=) $< - mv $(<:.y=).tab.c $(OUTDIR)/$(<:.y=.c) - mv $(<:.y=).tab.h $(OUTDIR)/$(<:.y=.h) - -# Create a dependency chain in generated files -# to avoid concurrent invocations of the single -# rule that builds them all. -$(OUTDIR)/aicasm_macro_gram.c: $(OUTDIR)/aicasm_macro_gram.h + $(YACC) $(YFLAGS) -b $(<:.y=) $< -o $(OUTDIR)/$(<:.y=.c) + $(OUTDIR)/aicasm_macro_gram.c $(OUTDIR)/aicasm_macro_gram.h: aicasm_macro_gram.y - $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $< - mv $(<:.y=).tab.c $(OUTDIR)/$(<:.y=.c) - mv $(<:.y=).tab.h $(OUTDIR)/$(<:.y=.h) + $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $< -o $(OUTDIR)/$(<:.y=.c) $(OUTDIR)/aicasm_scan.c: aicasm_scan.l $(LEX) $(LFLAGS) -o $@ $< diff --git a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c index 975fcfcc0d8f665557aaea7393a04b009bea74bc..2b44eb5702eb79292a7350deadd1110417b651c2 100644 --- a/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c +++ b/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c @@ -52,6 +52,7 @@ #include #include #include +#include #include "aicasm_symbol.h" #include "aicasm.h" diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index 3dd1101434715afd0d8d9db8bf03b3a07f3709c3..9dda296c01523a1ca5cfaa5242e848fca54ecb0e 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -28,7 +28,7 @@ static int asd_get_user_sas_addr(struct asd_ha_struct *asd_ha) if (asd_ha->hw_prof.sas_addr[0]) return 0; - return sas_request_addr(asd_ha->sas_ha.core.shost, + return sas_request_addr(asd_ha->sas_ha.shost, asd_ha->hw_prof.sas_addr); } @@ -72,10 +72,8 @@ static int asd_init_phy(struct asd_phy *phy) struct asd_sas_phy *sas_phy = &phy->sas_phy; sas_phy->enabled = 1; - sas_phy->class = SAS; sas_phy->iproto = SAS_PROTOCOL_ALL; sas_phy->tproto = 0; - sas_phy->type = PHY_TYPE_PHYSICAL; sas_phy->role = PHY_ROLE_INITIATOR; sas_phy->oob_mode = OOB_NOT_CONNECTED; sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index f7f81f6c3fbfc228fdf618f7b1425239002b248b..8a3340d8d7ad41edc4964a7bab4a7e51f9716370 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -667,7 +667,6 @@ static int asd_register_sas_ha(struct asd_ha_struct *asd_ha) } asd_ha->sas_ha.sas_ha_name = (char *) asd_ha->name; - asd_ha->sas_ha.lldd_module = THIS_MODULE; asd_ha->sas_ha.sas_addr = &asd_ha->hw_prof.sas_addr[0]; for (i = 0; i < ASD_MAX_PHYS; i++) { @@ -688,8 +687,8 @@ static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha) err = sas_unregister_ha(&asd_ha->sas_ha); - sas_remove_host(asd_ha->sas_ha.core.shost); - scsi_host_put(asd_ha->sas_ha.core.shost); + sas_remove_host(asd_ha->sas_ha.shost); + scsi_host_put(asd_ha->sas_ha.shost); kfree(asd_ha->sas_ha.sas_phy); kfree(asd_ha->sas_ha.sas_port); @@ -739,7 +738,7 @@ static int asd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev)); SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha; - asd_ha->sas_ha.core.shost = shost; + asd_ha->sas_ha.shost = shost; shost->transportt = aic94xx_transport_template; shost->max_id = ~0; shost->max_lun = ~0; diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 7f02083001100146d6518afaa4be0cadbe965ff1..4bfd03724ad62eb46ebdec459b489ae9f9cacb47 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -388,14 +388,9 @@ static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task, flags |= data_dir_flags[task->data_dir]; scb->ata_task.ata_flags = flags; - scb->ata_task.retry_count = task->ata_task.retry_count; + scb->ata_task.retry_count = 0; - flags = 0; - if (task->ata_task.set_affil_pol) - flags |= SET_AFFIL_POLICY; - if (task->ata_task.stp_affil_pol) - flags |= STP_AFFIL_POLICY; - scb->ata_task.flags = flags; + scb->ata_task.flags = 0; } ascb->tasklet_complete = asd_task_tasklet_complete; @@ -485,9 +480,6 @@ static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task, scb->ssp_task.ssp_frame.tptt = cpu_to_be16(0xFFFF); memcpy(scb->ssp_task.ssp_cmd.lun, task->ssp_task.LUN, 8); - if (task->ssp_task.enable_first_burst) - scb->ssp_task.ssp_cmd.efb_prio_attr |= EFB_MASK; - scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_prio << 3); scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_attr & 7); memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c index 2cd12c7f06c6692ffadf4e9e981306f92b896d82..a66221c3b72f82501b5a1cc70f291fd45fbae546 100644 --- a/drivers/scsi/arcmsr/arcmsr_hba.c +++ b/drivers/scsi/arcmsr/arcmsr_hba.c @@ -1715,14 +1715,14 @@ static void arcmsr_shutdown(struct pci_dev *pdev) arcmsr_flush_adapter_cache(acb); } -static int arcmsr_module_init(void) +static int __init arcmsr_module_init(void) { int error = 0; error = pci_register_driver(&arcmsr_pci_driver); return error; } -static void arcmsr_module_exit(void) +static void __exit arcmsr_module_exit(void) { pci_unregister_driver(&arcmsr_pci_driver); } diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c index 8aeaddc93b1674ba334b5d31a0b2f95e4c53a28b..8d374ae863ba2b225fb93930b4f22785eebef54a 100644 --- a/drivers/scsi/be2iscsi/be_iscsi.c +++ b/drivers/scsi/be2iscsi/be_iscsi.c @@ -450,6 +450,10 @@ int beiscsi_iface_set_param(struct Scsi_Host *shost, } nla_for_each_attr(attrib, data, dt_len, rm_len) { + /* ignore nla_type as it is never used */ + if (nla_len(attrib) < sizeof(*iface_param)) + return -EINVAL; + iface_param = nla_data(attrib); if (iface_param->param_type != ISCSI_NET_PARAM) diff --git a/drivers/scsi/bfa/bfa_fc.h b/drivers/scsi/bfa/bfa_fc.h index a12d693065cef6a3c823cb8f98cc71ab7a4fa76c..1091aa4285333a061bed767be55882b6b0d094d5 100644 --- a/drivers/scsi/bfa/bfa_fc.h +++ b/drivers/scsi/bfa/bfa_fc.h @@ -800,7 +800,7 @@ struct fc_rscn_pl_s { u8 command; u8 pagelen; __be16 payldlen; - struct fc_rscn_event_s event[1]; + struct fc_rscn_event_s event[]; }; /* diff --git a/drivers/scsi/bfa/bfa_fcbuild.c b/drivers/scsi/bfa/bfa_fcbuild.c index 773c84af784c87350a4701e15dbbf6b76c2832e2..52303e8c716dd4220087197334697d7f6ff53fa4 100644 --- a/drivers/scsi/bfa/bfa_fcbuild.c +++ b/drivers/scsi/bfa/bfa_fcbuild.c @@ -1051,7 +1051,7 @@ fc_rscn_build(struct fchs_s *fchs, struct fc_rscn_pl_s *rscn, rscn->event[0].format = FC_RSCN_FORMAT_PORTID; rscn->event[0].portid = s_id; - return sizeof(struct fc_rscn_pl_s); + return struct_size(rscn, event, 1); } u16 diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c index 8f96049f62dda2882112687a9e22a3c4ab7655e6..5e7fb110bc3f38417baaf00006afbf2dad1453e3 100644 --- a/drivers/scsi/elx/libefc_sli/sli4.c +++ b/drivers/scsi/elx/libefc_sli/sli4.c @@ -2317,12 +2317,8 @@ sli_xmit_bls_rsp64_wqe(struct sli4 *sli, void *buf, SLI4_GENERIC_CONTEXT_VPI << SLI4_BLS_RSP_WQE_CT_SHFT; bls->context_tag = cpu_to_le16(params->vpi); - if (params->s_id != U32_MAX) - bls->local_n_port_id_dword |= - cpu_to_le32(params->s_id & 0x00ffffff); - else - bls->local_n_port_id_dword |= - cpu_to_le32(params->s_id & 0x00ffffff); + bls->local_n_port_id_dword |= + cpu_to_le32(params->s_id & 0x00ffffff); dw_ridflags = (dw_ridflags & ~SLI4_BLS_RSP_RID) | (params->d_id & SLI4_BLS_RSP_RID); diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 5c8d1ba3f8f3c9c2de41e7f111db57f2b4c3e63a..19eee108db02145e55d6bebc33a03e4fffba1ef5 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -319,16 +319,17 @@ static void fcoe_ctlr_announce(struct fcoe_ctlr *fip) { struct fcoe_fcf *sel; struct fcoe_fcf *fcf; + unsigned long flags; mutex_lock(&fip->ctlr_mutex); - spin_lock_bh(&fip->ctlr_lock); + spin_lock_irqsave(&fip->ctlr_lock, flags); kfree_skb(fip->flogi_req); fip->flogi_req = NULL; list_for_each_entry(fcf, &fip->fcfs, list) fcf->flogi_sent = 0; - spin_unlock_bh(&fip->ctlr_lock); + spin_unlock_irqrestore(&fip->ctlr_lock, flags); sel = fip->sel_fcf; if (sel && ether_addr_equal(sel->fcf_mac, fip->dest_addr)) @@ -699,6 +700,7 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, { struct fc_frame *fp; struct fc_frame_header *fh; + unsigned long flags; u16 old_xid; u8 op; u8 mac[ETH_ALEN]; @@ -732,11 +734,11 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, op = FIP_DT_FLOGI; if (fip->mode == FIP_MODE_VN2VN) break; - spin_lock_bh(&fip->ctlr_lock); + spin_lock_irqsave(&fip->ctlr_lock, flags); kfree_skb(fip->flogi_req); fip->flogi_req = skb; fip->flogi_req_send = 1; - spin_unlock_bh(&fip->ctlr_lock); + spin_unlock_irqrestore(&fip->ctlr_lock, flags); schedule_work(&fip->timer_work); return -EINPROGRESS; case ELS_FDISC: @@ -1705,10 +1707,11 @@ static int fcoe_ctlr_flogi_send_locked(struct fcoe_ctlr *fip) static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip) { struct fcoe_fcf *fcf; + unsigned long flags; int error; mutex_lock(&fip->ctlr_mutex); - spin_lock_bh(&fip->ctlr_lock); + spin_lock_irqsave(&fip->ctlr_lock, flags); LIBFCOE_FIP_DBG(fip, "re-sending FLOGI - reselect\n"); fcf = fcoe_ctlr_select(fip); if (!fcf || fcf->flogi_sent) { @@ -1719,7 +1722,7 @@ static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip) fcoe_ctlr_solicit(fip, NULL); error = fcoe_ctlr_flogi_send_locked(fip); } - spin_unlock_bh(&fip->ctlr_lock); + spin_unlock_irqrestore(&fip->ctlr_lock, flags); mutex_unlock(&fip->ctlr_mutex); return error; } @@ -1736,8 +1739,9 @@ static int fcoe_ctlr_flogi_retry(struct fcoe_ctlr *fip) static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip) { struct fcoe_fcf *fcf; + unsigned long flags; - spin_lock_bh(&fip->ctlr_lock); + spin_lock_irqsave(&fip->ctlr_lock, flags); fcf = fip->sel_fcf; if (!fcf || !fip->flogi_req_send) goto unlock; @@ -1764,7 +1768,7 @@ static void fcoe_ctlr_flogi_send(struct fcoe_ctlr *fip) } else /* XXX */ LIBFCOE_FIP_DBG(fip, "No FCF selected - defer send\n"); unlock: - spin_unlock_bh(&fip->ctlr_lock); + spin_unlock_irqrestore(&fip->ctlr_lock, flags); } /** diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index e51e92f932fa8bc2d491451589c682bd7652e250..22cef283b2b97e8da5bf35a82cfb7c4f63da710a 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -27,7 +27,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.6.0.55" +#define DRV_VERSION "1.6.0.57" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " @@ -236,6 +236,9 @@ struct fnic { unsigned int wq_count; unsigned int cq_count; + struct mutex sgreset_mutex; + spinlock_t sgreset_lock; /* lock for sgreset */ + struct scsi_cmnd *sgreset_sc; struct dentry *fnic_stats_debugfs_host; struct dentry *fnic_stats_debugfs_file; struct dentry *fnic_reset_debugfs_file; diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h index f4c8769df312ab97139549ce164694670436b77d..5895ead20e1420dab2792862faac28e4c021170a 100644 --- a/drivers/scsi/fnic/fnic_io.h +++ b/drivers/scsi/fnic/fnic_io.h @@ -52,6 +52,8 @@ struct fnic_io_req { unsigned long start_time; /* in jiffies */ struct completion *abts_done; /* completion for abts */ struct completion *dr_done; /* completion for device reset */ + unsigned int tag; + struct scsi_cmnd *sc; /* midlayer's cmd pointer */ }; enum fnic_port_speeds { diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 984bc5fc55e2939583da80082fb4001841189f70..f27f9319e0b2e748d3a2094c4e9341c388500183 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -754,6 +754,8 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) for (i = 0; i < FNIC_IO_LOCKS; i++) spin_lock_init(&fnic->io_req_lock[i]); + spin_lock_init(&fnic->sgreset_lock); + err = -ENOMEM; fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache); if (!fnic->io_req_pool) diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index be89ce96df46c8104f58e4f930825db6c05400b5..416d81954819f33e82571f68bdd50debc8d8e203 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1047,9 +1047,9 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, { u8 type; u8 hdr_status; - struct fcpio_tag tag; + struct fcpio_tag ftag; u32 id; - struct scsi_cmnd *sc; + struct scsi_cmnd *sc = NULL; struct fnic_io_req *io_req; struct fnic_stats *fnic_stats = &fnic->fnic_stats; struct abort_stats *abts_stats = &fnic->fnic_stats.abts_stats; @@ -1058,27 +1058,43 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned long flags; spinlock_t *io_lock; unsigned long start_time; + unsigned int tag; - fcpio_header_dec(&desc->hdr, &type, &hdr_status, &tag); - fcpio_tag_id_dec(&tag, &id); + fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag); + fcpio_tag_id_dec(&ftag, &id); - if ((id & FNIC_TAG_MASK) >= fnic->fnic_max_tag_id) { + tag = id & FNIC_TAG_MASK; + if (tag == fnic->fnic_max_tag_id) { + if (!(id & FNIC_TAG_DEV_RST)) { + shost_printk(KERN_ERR, fnic->lport->host, + "Tag out of range id 0x%x hdr status = %s\n", + id, fnic_fcpio_status_to_str(hdr_status)); + return; + } + } else if (tag > fnic->fnic_max_tag_id) { shost_printk(KERN_ERR, fnic->lport->host, - "Tag out of range tag %x hdr status = %s\n", - id, fnic_fcpio_status_to_str(hdr_status)); + "Tag out of range tag 0x%x hdr status = %s\n", + tag, fnic_fcpio_status_to_str(hdr_status)); return; } - sc = scsi_host_find_tag(fnic->lport->host, id & FNIC_TAG_MASK); + if ((tag == fnic->fnic_max_tag_id) && (id & FNIC_TAG_DEV_RST)) { + sc = fnic->sgreset_sc; + io_lock = &fnic->sgreset_lock; + } else { + sc = scsi_host_find_tag(fnic->lport->host, id & FNIC_TAG_MASK); + io_lock = fnic_io_lock_hash(fnic, sc); + } + WARN_ON_ONCE(!sc); if (!sc) { atomic64_inc(&fnic_stats->io_stats.sc_null); shost_printk(KERN_ERR, fnic->lport->host, "itmf_cmpl sc is null - hdr status = %s tag = 0x%x\n", - fnic_fcpio_status_to_str(hdr_status), id); + fnic_fcpio_status_to_str(hdr_status), tag); return; } - io_lock = fnic_io_lock_hash(fnic, sc); + spin_lock_irqsave(io_lock, flags); io_req = fnic_priv(sc)->io_req; WARN_ON_ONCE(!io_req); @@ -1089,7 +1105,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, shost_printk(KERN_ERR, fnic->lport->host, "itmf_cmpl io_req is null - " "hdr status = %s tag = 0x%x sc 0x%p\n", - fnic_fcpio_status_to_str(hdr_status), id, sc); + fnic_fcpio_status_to_str(hdr_status), tag, sc); return; } start_time = io_req->start_time; @@ -1938,6 +1954,10 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic, struct scsi_lun fc_lun; int ret = 0; unsigned long intr_flags; + unsigned int tag = scsi_cmd_to_rq(sc)->tag; + + if (tag == SCSI_NO_TAG) + tag = io_req->tag; spin_lock_irqsave(host->host_lock, intr_flags); if (unlikely(fnic_chk_state_flags_locked(fnic, @@ -1964,7 +1984,8 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic, /* fill in the lun info */ int_to_scsilun(sc->device->lun, &fc_lun); - fnic_queue_wq_copy_desc_itmf(wq, scsi_cmd_to_rq(sc)->tag | FNIC_TAG_DEV_RST, + tag |= FNIC_TAG_DEV_RST; + fnic_queue_wq_copy_desc_itmf(wq, tag, 0, FCPIO_ITMF_LUN_RESET, SCSI_NO_TAG, fc_lun.scsi_lun, io_req->port_id, fnic->config.ra_tov, fnic->config.ed_tov); @@ -2146,8 +2167,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic, .ret = SUCCESS, }; - if (new_sc) - iter_data.lr_sc = lr_sc; + iter_data.lr_sc = lr_sc; scsi_host_busy_iter(fnic->lport->host, fnic_pending_aborts_iter, &iter_data); @@ -2167,39 +2187,6 @@ static int fnic_clean_pending_aborts(struct fnic *fnic, return ret; } -/* - * fnic_scsi_host_start_tag - * Allocates tagid from host's tag list - **/ -static inline int -fnic_scsi_host_start_tag(struct fnic *fnic, struct scsi_cmnd *sc) -{ - struct request *rq = scsi_cmd_to_rq(sc); - struct request_queue *q = rq->q; - struct request *dummy; - - dummy = blk_mq_alloc_request(q, REQ_OP_WRITE, BLK_MQ_REQ_NOWAIT); - if (IS_ERR(dummy)) - return SCSI_NO_TAG; - - rq->tag = dummy->tag; - sc->host_scribble = (unsigned char *)dummy; - - return dummy->tag; -} - -/* - * fnic_scsi_host_end_tag - * frees tag allocated by fnic_scsi_host_start_tag. - **/ -static inline void -fnic_scsi_host_end_tag(struct fnic *fnic, struct scsi_cmnd *sc) -{ - struct request *dummy = (struct request *)sc->host_scribble; - - blk_mq_free_request(dummy); -} - /* * SCSI Eh thread issues a Lun Reset when one or more commands on a LUN * fail to get aborted. It calls driver's eh_device_reset with a SCSI command @@ -2222,7 +2209,6 @@ int fnic_device_reset(struct scsi_cmnd *sc) struct reset_stats *reset_stats; int tag = rq->tag; DECLARE_COMPLETION_ONSTACK(tm_done); - int tag_gen_flag = 0; /*to track tags allocated by fnic driver*/ bool new_sc = 0; /* Wait for rport to unblock */ @@ -2252,20 +2238,26 @@ int fnic_device_reset(struct scsi_cmnd *sc) } fnic_priv(sc)->flags = FNIC_DEVICE_RESET; - /* Allocate tag if not present */ if (unlikely(tag < 0)) { /* - * Really should fix the midlayer to pass in a proper - * request for ioctls... + * For device reset issued through sg3utils, we let + * only one LUN_RESET to go through and use a special + * tag equal to max_tag_id so that we don't have to allocate + * or free it. It won't interact with tags + * allocated by mid layer. */ - tag = fnic_scsi_host_start_tag(fnic, sc); - if (unlikely(tag == SCSI_NO_TAG)) - goto fnic_device_reset_end; - tag_gen_flag = 1; + mutex_lock(&fnic->sgreset_mutex); + tag = fnic->fnic_max_tag_id; new_sc = 1; - } - io_lock = fnic_io_lock_hash(fnic, sc); + fnic->sgreset_sc = sc; + io_lock = &fnic->sgreset_lock; + FNIC_SCSI_DBG(KERN_INFO, fnic->lport->host, + "fcid: 0x%x lun: 0x%llx flags: 0x%x tag: 0x%x Issuing sgreset\n", + rport->port_id, sc->device->lun, fnic_priv(sc)->flags, tag); + } else + io_lock = fnic_io_lock_hash(fnic, sc); + spin_lock_irqsave(io_lock, flags); io_req = fnic_priv(sc)->io_req; @@ -2281,6 +2273,8 @@ int fnic_device_reset(struct scsi_cmnd *sc) } memset(io_req, 0, sizeof(*io_req)); io_req->port_id = rport->port_id; + io_req->tag = tag; + io_req->sc = sc; fnic_priv(sc)->io_req = io_req; } io_req->dr_done = &tm_done; @@ -2434,9 +2428,10 @@ int fnic_device_reset(struct scsi_cmnd *sc) (u64)sc->cmnd[4] << 8 | sc->cmnd[5]), fnic_flags_and_state(sc)); - /* free tag if it is allocated */ - if (unlikely(tag_gen_flag)) - fnic_scsi_host_end_tag(fnic, sc); + if (new_sc) { + fnic->sgreset_sc = NULL; + mutex_unlock(&fnic->sgreset_mutex); + } FNIC_SCSI_DBG(KERN_DEBUG, fnic->lport->host, "Returning from device reset %s\n", diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index d2eddad099a2b0aaa17e09a6e9390996515d000d..0420bfe9bd42d34e1875ffd54e0c378f14c88f47 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -50,11 +50,6 @@ static irqreturn_t gvp11_intr(int irq, void *data) static int gvp11_xfer_mask = 0; -void gvp11_setup(char *str, int *ints) -{ - gvp11_xfer_mask = ints[1]; -} - static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd); diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 8f22ece957bd4d3b5b88a16f3d3ceb5730af64da..9472b9743aefbe85f0e8aeb6f55374f14db52cac 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -787,7 +787,7 @@ static int hisi_sas_init_device(struct domain_device *device) * However we don't need to issue a hard reset here for these * reasons: * a. When probing the device, libsas/libata already issues a - * hard reset in sas_probe_sata() -> ata_sas_async_probe(). + * hard reset in sas_probe_sata() -> ata_port_probe(). * Note that in hisi_sas_debug_I_T_nexus_reset() we take care * to issue a hard reset by checking the dev status (== INIT). * b. When resetting the controller, this is simply unnecessary. @@ -1018,10 +1018,8 @@ static void hisi_sas_phy_init(struct hisi_hba *hisi_hba, int phy_no) phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; phy->maximum_linkrate = hisi_hba->hw->phy_get_max_linkrate(); sas_phy->enabled = (phy_no < hisi_hba->n_phy) ? 1 : 0; - sas_phy->class = SAS; sas_phy->iproto = SAS_PROTOCOL_ALL; sas_phy->tproto = 0; - sas_phy->type = PHY_TYPE_PHYSICAL; sas_phy->role = PHY_ROLE_INITIATOR; sas_phy->oob_mode = OOB_NOT_CONNECTED; sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; @@ -1065,23 +1063,18 @@ EXPORT_SYMBOL_GPL(hisi_sas_phy_enable); static void hisi_sas_port_notify_formed(struct asd_sas_phy *sas_phy) { - struct sas_ha_struct *sas_ha = sas_phy->ha; - struct hisi_hba *hisi_hba = sas_ha->lldd_ha; struct hisi_sas_phy *phy = sas_phy->lldd_phy; struct asd_sas_port *sas_port = sas_phy->port; struct hisi_sas_port *port; - unsigned long flags; if (!sas_port) return; port = to_hisi_sas_port(sas_port); - spin_lock_irqsave(&hisi_hba->lock, flags); port->port_attached = 1; port->id = phy->port_id; phy->port = port; sas_port->lldd_port = port; - spin_unlock_irqrestore(&hisi_hba->lock, flags); } static void hisi_sas_do_release_task(struct hisi_hba *hisi_hba, struct sas_task *task, @@ -2519,10 +2512,9 @@ int hisi_sas_probe(struct platform_device *pdev, sha->sas_ha_name = DRV_NAME; sha->dev = hisi_hba->dev; - sha->lldd_module = THIS_MODULE; sha->sas_addr = &hisi_hba->sas_addr[0]; sha->num_phys = hisi_hba->n_phy; - sha->core.shost = hisi_hba->shost; + sha->shost = hisi_hba->shost; for (i = 0; i < hisi_hba->n_phy; i++) { sha->sas_phy[i] = &hisi_hba->phy[i].sas_phy; @@ -2564,12 +2556,12 @@ void hisi_sas_remove(struct platform_device *pdev) { struct sas_ha_struct *sha = platform_get_drvdata(pdev); struct hisi_hba *hisi_hba = sha->lldd_ha; - struct Scsi_Host *shost = sha->core.shost; + struct Scsi_Host *shost = sha->shost; del_timer_sync(&hisi_hba->timer); sas_unregister_ha(sha); - sas_remove_host(sha->core.shost); + sas_remove_host(shost); hisi_sas_free(hisi_hba); scsi_host_put(shost); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 94fbbceddc2e6faeb7f9219e7db0cc4ad7e8417d..3c555579f9a1cf04d2011c88efbdbe3abc808cb8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -960,7 +960,7 @@ static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; struct sas_tmf_task *tmf = slot->tmf; int has_data = 0, priority = !!tmf; - u8 *buf_cmd, fburst = 0; + u8 *buf_cmd; u32 dw1, dw2; /* create header */ @@ -1018,16 +1018,11 @@ static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, buf_cmd = hisi_sas_cmd_hdr_addr_mem(slot) + sizeof(struct ssp_frame_hdr); - if (task->ssp_task.enable_first_burst) { - fburst = (1 << 7); - dw2 |= 1 << CMD_HDR_FIRST_BURST_OFF; - } hdr->dw2 = cpu_to_le32(dw2); memcpy(buf_cmd, &task->ssp_task.LUN, 8); if (!tmf) { - buf_cmd[9] = fburst | task->ssp_task.task_attr | - (task->ssp_task.task_prio << 3); + buf_cmd[9] = task->ssp_task.task_attr; memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); } else { diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 87d8e408ccd1ced81601292d57eade447bab65a0..73b378837da7bd210a741ec9e28c72cb31ad885f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1798,8 +1798,7 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba, memcpy(buf_cmd, &task->ssp_task.LUN, 8); if (!tmf) { - buf_cmd[9] = task->ssp_task.task_attr | - (task->ssp_task.task_prio << 3); + buf_cmd[9] = task->ssp_task.task_attr; memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); } else { @@ -2026,6 +2025,11 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba, u16 dma_tx_err_type = le16_to_cpu(err_record->dma_tx_err_type); u16 sipc_rx_err_type = le16_to_cpu(err_record->sipc_rx_err_type); u32 dma_rx_err_type = le32_to_cpu(err_record->dma_rx_err_type); + struct hisi_sas_complete_v2_hdr *complete_queue = + hisi_hba->complete_hdr[slot->cmplt_queue]; + struct hisi_sas_complete_v2_hdr *complete_hdr = + &complete_queue[slot->cmplt_queue_slot]; + u32 dw0 = le32_to_cpu(complete_hdr->dw0); int error = -1; if (err_phase == 1) { @@ -2310,7 +2314,8 @@ static void slot_err_v2_hw(struct hisi_hba *hisi_hba, break; } } - hisi_sas_sata_done(task, slot); + if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) + hisi_sas_sata_done(task, slot); } break; default: @@ -2443,7 +2448,8 @@ static void slot_complete_v2_hw(struct hisi_hba *hisi_hba, case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: { ts->stat = SAS_SAM_STAT_GOOD; - hisi_sas_sata_done(task, slot); + if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) + hisi_sas_sata_done(task, slot); break; } default: diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 20e1607c62828b4c774ff1be7cabe7e6e7e85e23..bbb64ee6afd7c40ccdc49271785d3119549ce747 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1326,7 +1326,7 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, memcpy(buf_cmd, &task->ssp_task.LUN, 8); if (!tmf) { - buf_cmd[9] = ssp_task->task_attr | (ssp_task->task_prio << 3); + buf_cmd[9] = ssp_task->task_attr; memcpy(buf_cmd + 12, scsi_cmnd->cmnd, scsi_cmnd->cmd_len); } else { buf_cmd[10] = tmf->tmf; @@ -2257,7 +2257,8 @@ slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task, ts->stat = SAS_OPEN_REJECT; ts->open_rej_reason = SAS_OREJ_RSVD_RETRY; } - hisi_sas_sata_done(task, slot); + if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) + hisi_sas_sata_done(task, slot); break; case SAS_PROTOCOL_SMP: ts->stat = SAS_SAM_STAT_CHECK_CONDITION; @@ -2384,7 +2385,8 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba, case SAS_PROTOCOL_STP: case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: ts->stat = SAS_SAM_STAT_GOOD; - hisi_sas_sata_done(task, slot); + if (dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) + hisi_sas_sata_done(task, slot); break; default: ts->stat = SAS_SAM_STAT_CHECK_CONDITION; @@ -3104,21 +3106,25 @@ static const struct hisi_sas_debugfs_reg debugfs_ras_reg = { static void debugfs_snapshot_prepare_v3_hw(struct hisi_hba *hisi_hba) { - set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); - - hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); + struct Scsi_Host *shost = hisi_hba->shost; + scsi_block_requests(shost); wait_cmds_complete_timeout_v3_hw(hisi_hba, 100, 5000); + set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); hisi_sas_sync_cqs(hisi_hba); + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, 0); } static void debugfs_snapshot_restore_v3_hw(struct hisi_hba *hisi_hba) { + struct Scsi_Host *shost = hisi_hba->shost; + hisi_sas_write32(hisi_hba, DLVRY_QUEUE_ENABLE, (u32)((1ULL << hisi_hba->queue_count) - 1)); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); + scsi_unblock_requests(shost); } static void read_iost_itct_cache_v3_hw(struct hisi_hba *hisi_hba, @@ -4576,7 +4582,7 @@ static int debugfs_fifo_data_v3_hw_show(struct seq_file *s, void *p) debugfs_read_fifo_data_v3_hw(phy); debugfs_show_row_32_v3_hw(s, 0, HISI_SAS_FIFO_DATA_DW_SIZE * 4, - phy->fifo.rd_data); + (__le32 *)phy->fifo.rd_data); return 0; } @@ -4950,7 +4956,7 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) sha->sas_phy = arr_phy; sha->sas_port = arr_port; - sha->core.shost = shost; + sha->shost = shost; sha->lldd_ha = hisi_hba; shost->transportt = hisi_sas_stt; @@ -4967,7 +4973,6 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) sha->sas_ha_name = DRV_NAME; sha->dev = dev; - sha->lldd_module = THIS_MODULE; sha->sas_addr = &hisi_hba->sas_addr[0]; sha->num_phys = hisi_hba->n_phy; @@ -5055,14 +5060,14 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) struct device *dev = &pdev->dev; struct sas_ha_struct *sha = dev_get_drvdata(dev); struct hisi_hba *hisi_hba = sha->lldd_ha; - struct Scsi_Host *shost = sha->core.shost; + struct Scsi_Host *shost = sha->shost; pm_runtime_get_noresume(dev); del_timer_sync(&hisi_hba->timer); sas_unregister_ha(sha); flush_workqueue(hisi_hba->wq); - sas_remove_host(sha->core.shost); + sas_remove_host(shost); hisi_sas_v3_destroy_irqs(pdev, hisi_hba); hisi_sas_free(hisi_hba); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 198edf03f92977c9b5d149ecb4861abdc1441a3f..d7f51b84f3c788f0175648655673ca97b4c3b896 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -537,7 +537,7 @@ EXPORT_SYMBOL(scsi_host_alloc); static int __scsi_host_match(struct device *dev, const void *data) { struct Scsi_Host *p; - const unsigned short *hostnum = data; + const unsigned int *hostnum = data; p = class_to_shost(dev); return p->host_no == *hostnum; @@ -554,7 +554,7 @@ static int __scsi_host_match(struct device *dev, const void *data) * that scsi_host_get() took. The put_device() below dropped * the reference from class_find_device(). **/ -struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) +struct Scsi_Host *scsi_host_lookup(unsigned int hostnum) { struct device *cdev; struct Scsi_Host *shost = NULL; diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 6bc3f022630a286012cc73fc4b223160e0461624..52388374cf31591cf82af811a9e3fe0d00f7df58 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h @@ -306,7 +306,7 @@ static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) static inline struct Scsi_Host *to_shost(struct isci_host *ihost) { - return ihost->sas_ha.core.shost; + return ihost->sas_ha.shost; } #define for_each_isci_host(id, ihost, pdev) \ diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index ac1e04b86d8fed3426fa6eb3b7c878b7d7136152..6277162a028bb98378d66c34e7a9f47a20323027 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -250,7 +250,6 @@ static int isci_register_sas_ha(struct isci_host *isci_host) return -ENOMEM; sas_ha->sas_ha_name = DRV_NAME; - sas_ha->lldd_module = THIS_MODULE; sas_ha->sas_addr = &isci_host->phys[0].sas_addr[0]; for (i = 0; i < SCI_MAX_PHYS; i++) { @@ -264,9 +263,7 @@ static int isci_register_sas_ha(struct isci_host *isci_host) sas_ha->strict_wide_ports = 1; - sas_register_ha(sas_ha); - - return 0; + return sas_register_ha(sas_ha); } static void isci_unregister(struct isci_host *isci_host) @@ -575,7 +572,7 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) goto err_shost; SHOST_TO_SAS_HA(shost) = &ihost->sas_ha; - ihost->sas_ha.core.shost = shost; + ihost->sas_ha.shost = shost; shost->transportt = isci_transport_template; shost->max_id = ~0; @@ -730,7 +727,7 @@ static int isci_resume(struct device *dev) sas_prep_resume_ha(&ihost->sas_ha); isci_host_init(ihost); - isci_host_start(ihost->sas_ha.core.shost); + isci_host_start(ihost->sas_ha.shost); wait_for_start(ihost); sas_resume_ha(&ihost->sas_ha); diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c index aa8787343e8313618ce21f214508e95a9fa8b0d8..743a3c64b0da10b512449d6964e3bab8510d4e45 100644 --- a/drivers/scsi/isci/phy.c +++ b/drivers/scsi/isci/phy.c @@ -1404,10 +1404,8 @@ void isci_phy_init(struct isci_phy *iphy, struct isci_host *ihost, int index) iphy->sas_phy.ha = &ihost->sas_ha; iphy->sas_phy.lldd_phy = iphy; iphy->sas_phy.enabled = 1; - iphy->sas_phy.class = SAS; iphy->sas_phy.iproto = SAS_PROTOCOL_ALL; iphy->sas_phy.tproto = 0; - iphy->sas_phy.type = PHY_TYPE_PHYSICAL; iphy->sas_phy.role = PHY_ROLE_INITIATOR; iphy->sas_phy.oob_mode = OOB_NOT_CONNECTED; iphy->sas_phy.linkrate = SAS_LINK_RATE_UNKNOWN; diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 6370cdbfba08c407f90503b54905bc1b7454f192..a7b3243b471d5b3b1180abd8ac709e2b97e777fe 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -180,7 +180,7 @@ static void sci_io_request_build_ssp_command_iu(struct isci_request *ireq) cmd_iu->_r_a = 0; cmd_iu->_r_b = 0; cmd_iu->en_fburst = 0; /* unsupported */ - cmd_iu->task_prio = task->ssp_task.task_prio; + cmd_iu->task_prio = 0; cmd_iu->task_attr = task->ssp_task.task_attr; cmd_iu->_r_c = 0; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 9ab8555180a3a0bd159b621a57c99bcb8f0413ae..8e14cea15f980829e99afa2c43bf6872fcfd965c 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -724,6 +724,10 @@ iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, return -EEXIST; } + err = -EINVAL; + if (!sk_is_tcp(sock->sk)) + goto free_socket; + err = iscsi_conn_bind(cls_session, cls_conn, is_leading); if (err) goto free_socket; diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 77714a495cbbae2c18068befaa157ff6bb2eb9fa..12e2653846e3f0d93a9dee938eee0ba65df169e2 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -162,7 +162,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct domain_device *dev = ap->private_data; struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *host = sas_ha->core.shost; + struct Scsi_Host *host = sas_ha->shost; struct sas_internal *i = to_sas_internal(host->transportt); /* TODO: we should try to remove that unlock */ @@ -201,12 +201,14 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->data_dir = qc->dma_dir; } task->scatter = qc->sg; - task->ata_task.retry_count = 1; qc->lldd_task = task; task->ata_task.use_ncq = ata_is_ncq(qc->tf.protocol); task->ata_task.dma_xfer = ata_is_dma(qc->tf.protocol); + if (qc->flags & ATA_QCFLAG_RESULT_TF) + task->ata_task.return_fis_on_success = 1; + if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, task); @@ -235,7 +237,7 @@ static void sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) { - return to_sas_internal(dev->port->ha->core.shost->transportt); + return to_sas_internal(dev->port->ha->shost->transportt); } static int sas_get_ata_command_set(struct domain_device *dev) @@ -565,8 +567,6 @@ static struct ata_port_operations sas_sata_ops = { .qc_prep = ata_noop_qc_prep, .qc_issue = sas_ata_qc_issue, .qc_fill_rtf = sas_ata_qc_fill_rtf, - .port_start = ata_sas_port_start, - .port_stop = ata_sas_port_stop, .set_dmamode = sas_ata_set_dmamode, .sched_eh = sas_ata_sched_eh, .end_eh = sas_ata_end_eh, @@ -584,7 +584,7 @@ static struct ata_port_info sata_port_info = { int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; - struct Scsi_Host *shost = ha->core.shost; + struct Scsi_Host *shost = ha->shost; struct ata_host *ata_host; struct ata_port *ap; int rc; @@ -607,9 +607,6 @@ int sas_ata_init(struct domain_device *found_dev) ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - rc = ata_sas_port_init(ap); - if (rc) - goto destroy_port; rc = ata_sas_tport_add(ata_host->dev, ap); if (rc) @@ -621,7 +618,7 @@ int sas_ata_init(struct domain_device *found_dev) return 0; destroy_port: - ata_sas_port_destroy(ap); + kfree(ap); free_host: ata_host_put(ata_host); return rc; @@ -655,7 +652,7 @@ void sas_probe_sata(struct asd_sas_port *port) if (!dev_is_sata(dev)) continue; - ata_sas_async_probe(dev->sata_dev.ap); + ata_port_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -822,7 +819,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) struct sas_ha_struct *ha = dev->port->ha; sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); - ata_scsi_port_error_handler(ha->core.shost, ap); + ata_scsi_port_error_handler(ha->shost, ap); sas_put_device(dev); } diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 8c6afe724944bdb0d76813aa54945e9bd4688254..ff7b63b10aeb372939d12c95fb4dc1be490c6f1e 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -170,7 +170,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) { int res = 0; struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; struct sas_internal *i = to_sas_internal(shost->transportt); if (!i->dft->lldd_dev_found) @@ -192,7 +192,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) void sas_notify_lldd_dev_gone(struct domain_device *dev) { struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; struct sas_internal *i = to_sas_internal(shost->transportt); if (!i->dft->lldd_dev_gone) @@ -234,7 +234,7 @@ static void sas_suspend_devices(struct work_struct *work) struct domain_device *dev; struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; - struct Scsi_Host *shost = port->ha->core.shost; + struct Scsi_Host *shost = port->ha->shost; struct sas_internal *si = to_sas_internal(shost->transportt); clear_bit(DISCE_SUSPEND, &port->disc.pending); @@ -301,7 +301,7 @@ void sas_free_device(struct kref *kref) if (dev_is_sata(dev) && dev->sata_dev.ap) { ata_sas_tport_delete(dev->sata_dev.ap); - ata_sas_port_destroy(dev->sata_dev.ap); + kfree(dev->sata_dev.ap); ata_host_put(dev->sata_dev.ata_host); dev->sata_dev.ata_host = NULL; dev->sata_dev.ap = NULL; @@ -373,7 +373,7 @@ static bool sas_abort_cmd(struct request *req, void *data) static void sas_abort_device_scsi_cmds(struct domain_device *dev) { struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; if (dev_is_expander(dev->dev_type)) return; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index adcac57aaee6446750d645f6b6b09412fb3c0ddb..a2204674b6808fa3df84288d7f5b134680232fd1 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -37,7 +37,7 @@ static int smp_execute_task_sg(struct domain_device *dev, int res, retry; struct sas_task *task = NULL; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); struct sas_ha_struct *ha = dev->port->ha; pm_runtime_get_sync(ha->dev); diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 32cdc969b736af3bd718da1b276db830afbd2d55..2ecb8535634c1558b53772423daaec75fe6ee247 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -114,7 +114,7 @@ static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, u8 reg_type, u8 reg_index, u8 reg_count, u8 *req_data) { - struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); + struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt); int written; if (i->dft->lldd_write_gpio == NULL) { @@ -182,7 +182,7 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, enum sas_linkrate max, u8 *resp_data) { struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); struct sas_phy_linkrates rates; struct asd_sas_phy *asd_phy; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index f2c05ebeb72ffbe992455a99f9292ecf7568f2d5..8586dc79f2a0bc12b78971c9bef899add77cdefb 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -183,7 +183,7 @@ static int sas_get_linkerrors(struct sas_phy *phy) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); } @@ -232,7 +232,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) return 0; @@ -266,7 +266,7 @@ int sas_phy_enable(struct sas_phy *phy, int enable) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); if (enable) ret = transport_sas_phy_reset(phy, 0); @@ -303,7 +303,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); } else { @@ -339,7 +339,7 @@ int sas_set_phy_speed(struct sas_phy *phy, struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE, rates); @@ -438,7 +438,7 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) /* all phys are back up or timed out, turn on i/o so we can * flush out disks that did not return */ - scsi_unblock_requests(ha->core.shost); + scsi_unblock_requests(ha->shost); if (drain) sas_drain_work(ha); clear_bit(SAS_HA_RESUMING, &ha->state); @@ -468,7 +468,7 @@ void sas_suspend_ha(struct sas_ha_struct *ha) int i; sas_disable_events(ha); - scsi_block_requests(ha->core.shost); + scsi_block_requests(ha->shost); for (i = 0; i < ha->num_phys; i++) { struct asd_sas_port *port = ha->sas_port[i]; @@ -641,7 +641,7 @@ struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, struct asd_sas_event *event; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); event = kmem_cache_zalloc(sas_event_cache, gfp_flags); if (!event) diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 6f593fa69b586335ef3c7ab40e9cb3348fea759c..a6dc7dc07fce3799d03cc74cbc1b06a926b28377 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -41,13 +41,7 @@ struct sas_phy_data { void sas_scsi_recover_host(struct Scsi_Host *shost); -int sas_show_class(enum sas_class class, char *buf); -int sas_show_proto(enum sas_protocol proto, char *buf); -int sas_show_linkrate(enum sas_linkrate linkrate, char *buf); -int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf); - int sas_register_phys(struct sas_ha_struct *sas_ha); -void sas_unregister_phys(struct sas_ha_struct *sas_ha); struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags); void sas_free_event(struct asd_sas_event *event); @@ -91,7 +85,6 @@ int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, u8 *sas_addr, enum sas_device_type *type); int sas_try_ata_reset(struct asd_sas_phy *phy); -void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); void sas_destruct_devices(struct asd_sas_port *port); diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index a0d592d11dfb117442d93bc2ecfdf689496fe9bc..57494ac97076d834a33771f70dd33be309af6541 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -38,7 +38,7 @@ static void sas_phye_oob_error(struct work_struct *work) struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); sas_deform_port(phy, 1); @@ -66,7 +66,7 @@ static void sas_phye_spinup_hold(struct work_struct *work) struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); phy->error = 0; i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); @@ -95,7 +95,7 @@ static void sas_phye_shutdown(struct work_struct *work) struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); if (phy->enabled) { int ret; @@ -131,7 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) spin_lock_init(&phy->sas_prim_lock); phy->frame_rcvd_size = 0; - phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); + phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i); if (!phy->phy) return -ENOMEM; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index 11599c0e3fc3443f9c3a49466dd80f6a8275d782..e3f2ed913419a1520a656cfb16198e81aa9122e2 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -28,7 +28,7 @@ static void sas_resume_port(struct asd_sas_phy *phy) struct domain_device *dev, *n; struct asd_sas_port *port = phy->port; struct sas_ha_struct *sas_ha = phy->ha; - struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + struct sas_internal *si = to_sas_internal(sas_ha->shost->transportt); if (si->dft->lldd_port_formed) si->dft->lldd_port_formed(phy); @@ -83,7 +83,6 @@ static void sas_form_port_add_phy(struct asd_sas_port *port, memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE); if (*(u64 *)port->attached_sas_addr == 0) { - port->class = phy->class; memcpy(port->attached_sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); port->iproto = phy->iproto; @@ -109,7 +108,7 @@ static void sas_form_port(struct asd_sas_phy *phy) struct asd_sas_port *port = phy->port; struct domain_device *port_dev = NULL; struct sas_internal *si = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); unsigned long flags; if (port) { @@ -212,7 +211,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *si = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); struct domain_device *dev; unsigned long flags; @@ -249,7 +248,6 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) INIT_LIST_HEAD(&port->phy_list); memset(port->sas_addr, 0, SAS_ADDR_SIZE); memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE); - port->class = 0; port->iproto = 0; port->tproto = 0; port->oob_mode = 0; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 94c5f14f3c16dbaaf54804120bdb34a0da958f7e..9047cfcd1072ecc7b272bf925254c7d43413b169 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -142,7 +142,6 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, task->dev = dev; task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */ - task->ssp_task.retry_count = 1; int_to_scsilun(cmd->device->lun, &lun); memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); task->ssp_task.task_attr = TASK_ATTR_SIMPLE; @@ -279,7 +278,7 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) unsigned long flags; int i, res; struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); + to_sas_internal(task->dev->port->ha->shost->transportt); for (i = 0; i < 5; i++) { pr_notice("%s: aborting task 0x%p\n", __func__, task); @@ -327,7 +326,7 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) int res = TMF_RESP_FUNC_FAILED; struct scsi_lun lun; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); int_to_scsilun(cmd->device->lun, &lun); @@ -355,7 +354,7 @@ static int sas_recover_I_T(struct domain_device *dev) { int res = TMF_RESP_FUNC_FAILED; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); pr_notice("I_T nexus reset for dev %016llx\n", SAS_ADDR(dev->sas_addr)); @@ -387,37 +386,7 @@ struct sas_phy *sas_get_local_phy(struct domain_device *dev) } EXPORT_SYMBOL_GPL(sas_get_local_phy); -static void sas_wait_eh(struct domain_device *dev) -{ - struct sas_ha_struct *ha = dev->port->ha; - DEFINE_WAIT(wait); - - if (dev_is_sata(dev)) { - ata_port_wait_eh(dev->sata_dev.ap); - return; - } - retry: - spin_lock_irq(&ha->lock); - - while (test_bit(SAS_DEV_EH_PENDING, &dev->state)) { - prepare_to_wait(&ha->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&ha->lock); - schedule(); - spin_lock_irq(&ha->lock); - } - finish_wait(&ha->eh_wait_q, &wait); - - spin_unlock_irq(&ha->lock); - - /* make sure SCSI EH is complete */ - if (scsi_host_in_recovery(ha->core.shost)) { - msleep(10); - goto retry; - } -} - -static int sas_queue_reset(struct domain_device *dev, int reset_type, - u64 lun, int wait) +static int sas_queue_reset(struct domain_device *dev, int reset_type, u64 lun) { struct sas_ha_struct *ha = dev->port->ha; int scheduled = 0, tries = 100; @@ -425,8 +394,6 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, /* ata: promote lun reset to bus reset */ if (dev_is_sata(dev)) { sas_ata_schedule_reset(dev); - if (wait) - sas_ata_wait_eh(dev); return SUCCESS; } @@ -440,13 +407,10 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, set_bit(SAS_DEV_EH_PENDING, &dev->state); set_bit(reset_type, &dev->state); int_to_scsilun(lun, &dev->ssp_dev.reset_lun); - scsi_schedule_eh(ha->core.shost); + scsi_schedule_eh(ha->shost); } spin_unlock_irq(&ha->lock); - if (wait) - sas_wait_eh(dev); - if (scheduled) return SUCCESS; } @@ -499,7 +463,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) struct sas_internal *i = to_sas_internal(host->transportt); if (current != host->ehandler) - return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun, 0); + return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun); int_to_scsilun(cmd->device->lun, &lun); @@ -522,7 +486,7 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) struct sas_internal *i = to_sas_internal(host->transportt); if (current != host->ehandler) - return sas_queue_reset(dev, SAS_DEV_RESET, 0, 0); + return sas_queue_reset(dev, SAS_DEV_RESET, 0); if (!i->dft->lldd_I_T_nexus_reset) return FAILED; @@ -925,7 +889,7 @@ static int sas_execute_internal_abort(struct domain_device *device, unsigned int qid, void *data) { struct sas_ha_struct *ha = device->port->ha; - struct sas_internal *i = to_sas_internal(ha->core.shost->transportt); + struct sas_internal *i = to_sas_internal(ha->shost->transportt); struct sas_task *task = NULL; int res, retry; @@ -1015,7 +979,7 @@ int sas_execute_tmf(struct domain_device *device, void *parameter, { struct sas_task *task; struct sas_internal *i = - to_sas_internal(device->port->ha->core.shost->transportt); + to_sas_internal(device->port->ha->shost->transportt); int res, retry; for (retry = 0; retry < TASK_RETRY; retry++) { diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 9a89636843693f2d83d8096070333dde4ce0d4c3..af15f7a22d2584d650158b487e9dd09ff344e2a8 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -309,7 +309,6 @@ struct lpfc_hba; #define LPFC_VMID_TIMER 300 /* timer interval in seconds */ #define LPFC_MAX_VMID_SIZE 256 -#define LPFC_COMPRESS_VMID_SIZE 16 union lpfc_vmid_io_tag { u32 app_id; /* App Id vmid */ @@ -667,7 +666,7 @@ struct lpfc_vport { uint32_t cfg_first_burst_size; uint32_t dev_loss_tmo_changed; /* VMID parameters */ - u8 lpfc_vmid_host_uuid[LPFC_COMPRESS_VMID_SIZE]; + u8 lpfc_vmid_host_uuid[16]; u32 max_vmid; /* maximum VMIDs allowed per port */ u32 cur_vmid_cnt; /* Current VMID count */ #define LPFC_MIN_VMID 4 @@ -872,6 +871,7 @@ enum lpfc_irq_chann_mode { enum lpfc_hba_bit_flags { FABRIC_COMANDS_BLOCKED, HBA_PCI_ERR, + MBX_TMO_ERR, }; struct lpfc_hba { @@ -1708,6 +1708,25 @@ lpfc_next_online_cpu(const struct cpumask *mask, unsigned int start) return cpu_it; } +/** + * lpfc_next_present_cpu - Finds next present CPU after n + * @n: the cpu prior to search + * + * Note: If no next present cpu, then fallback to first present cpu. + * + **/ +static inline unsigned int lpfc_next_present_cpu(int n) +{ + unsigned int cpu; + + cpu = cpumask_next(n, cpu_present_mask); + + if (cpu >= nr_cpu_ids) + cpu = cpumask_first(cpu_present_mask); + + return cpu; +} + /** * lpfc_sli4_mod_hba_eq_delay - update EQ delay * @phba: Pointer to HBA context object. diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 21c7ecd3ede55d9a489cd60ce944f29abf9ec541..b1c9107d3408364ef6c0ecf4ccec30acfdca8f2b 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -2127,11 +2127,12 @@ lpfc_get_hba_info(struct lpfc_hba *phba, uint32_t *mrpi, uint32_t *arpi, uint32_t *mvpi, uint32_t *avpi) { - struct lpfc_mbx_read_config *rd_config; LPFC_MBOXQ_t *pmboxq; MAILBOX_t *pmb; int rc = 0; - uint32_t max_vpi; + struct lpfc_sli4_hba *sli4_hba; + struct lpfc_max_cfg_param *max_cfg_param; + u16 rsrc_ext_cnt, rsrc_ext_size, max_vpi; /* * prevent udev from issuing mailbox commands until the port is @@ -2167,31 +2168,65 @@ lpfc_get_hba_info(struct lpfc_hba *phba, } if (phba->sli_rev == LPFC_SLI_REV4) { - rd_config = &pmboxq->u.mqe.un.rd_config; - if (mrpi) - *mrpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config); - if (arpi) - *arpi = bf_get(lpfc_mbx_rd_conf_rpi_count, rd_config) - - phba->sli4_hba.max_cfg_param.rpi_used; - if (mxri) - *mxri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config); - if (axri) - *axri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config) - - phba->sli4_hba.max_cfg_param.xri_used; + sli4_hba = &phba->sli4_hba; + max_cfg_param = &sli4_hba->max_cfg_param; + + /* Normally, extents are not used */ + if (!phba->sli4_hba.extents_in_use) { + if (mrpi) + *mrpi = max_cfg_param->max_rpi; + if (mxri) + *mxri = max_cfg_param->max_xri; + if (mvpi) { + max_vpi = max_cfg_param->max_vpi; + + /* Limit the max we support */ + if (max_vpi > LPFC_MAX_VPI) + max_vpi = LPFC_MAX_VPI; + *mvpi = max_vpi; + } + } else { /* Extents in use */ + if (mrpi) { + if (lpfc_sli4_get_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_RPI, + &rsrc_ext_cnt, + &rsrc_ext_size)) { + rc = 0; + goto free_pmboxq; + } + + *mrpi = rsrc_ext_cnt * rsrc_ext_size; + } - /* Account for differences with SLI-3. Get vpi count from - * mailbox data and subtract one for max vpi value. - */ - max_vpi = (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) > 0) ? - (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) - 1) : 0; + if (mxri) { + if (lpfc_sli4_get_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_XRI, + &rsrc_ext_cnt, + &rsrc_ext_size)) { + rc = 0; + goto free_pmboxq; + } - /* Limit the max we support */ - if (max_vpi > LPFC_MAX_VPI) - max_vpi = LPFC_MAX_VPI; - if (mvpi) - *mvpi = max_vpi; - if (avpi) - *avpi = max_vpi - phba->sli4_hba.max_cfg_param.vpi_used; + *mxri = rsrc_ext_cnt * rsrc_ext_size; + } + + if (mvpi) { + if (lpfc_sli4_get_avail_extnt_rsrc(phba, + LPFC_RSC_TYPE_FCOE_VPI, + &rsrc_ext_cnt, + &rsrc_ext_size)) { + rc = 0; + goto free_pmboxq; + } + + max_vpi = rsrc_ext_cnt * rsrc_ext_size; + + /* Limit the max we support */ + if (max_vpi > LPFC_MAX_VPI) + max_vpi = LPFC_MAX_VPI; + *mvpi = max_vpi; + } + } } else { if (mrpi) *mrpi = pmb->un.varRdConfig.max_rpi; @@ -2212,8 +2247,12 @@ lpfc_get_hba_info(struct lpfc_hba *phba, } } + /* Success */ + rc = 1; + +free_pmboxq: mempool_free(pmboxq, phba->mbox_mem_pool); - return 1; + return rc; } /** @@ -2265,10 +2304,19 @@ lpfc_used_rpi_show(struct device *dev, struct device_attribute *attr, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - uint32_t cnt, acnt; + struct lpfc_sli4_hba *sli4_hba; + struct lpfc_max_cfg_param *max_cfg_param; + u32 cnt = 0, acnt = 0; - if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, &acnt, NULL, NULL)) - return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); + if (phba->sli_rev == LPFC_SLI_REV4) { + sli4_hba = &phba->sli4_hba; + max_cfg_param = &sli4_hba->max_cfg_param; + return scnprintf(buf, PAGE_SIZE, "%d\n", + max_cfg_param->rpi_used); + } else { + if (lpfc_get_hba_info(phba, NULL, NULL, &cnt, &acnt, NULL, NULL)) + return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); + } return scnprintf(buf, PAGE_SIZE, "Unknown\n"); } @@ -2321,10 +2369,19 @@ lpfc_used_xri_show(struct device *dev, struct device_attribute *attr, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - uint32_t cnt, acnt; + struct lpfc_sli4_hba *sli4_hba; + struct lpfc_max_cfg_param *max_cfg_param; + u32 cnt = 0, acnt = 0; - if (lpfc_get_hba_info(phba, &cnt, &acnt, NULL, NULL, NULL, NULL)) - return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); + if (phba->sli_rev == LPFC_SLI_REV4) { + sli4_hba = &phba->sli4_hba; + max_cfg_param = &sli4_hba->max_cfg_param; + return scnprintf(buf, PAGE_SIZE, "%d\n", + max_cfg_param->xri_used); + } else { + if (lpfc_get_hba_info(phba, &cnt, &acnt, NULL, NULL, NULL, NULL)) + return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); + } return scnprintf(buf, PAGE_SIZE, "Unknown\n"); } @@ -2377,10 +2434,19 @@ lpfc_used_vpi_show(struct device *dev, struct device_attribute *attr, struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; struct lpfc_hba *phba = vport->phba; - uint32_t cnt, acnt; + struct lpfc_sli4_hba *sli4_hba; + struct lpfc_max_cfg_param *max_cfg_param; + u32 cnt = 0, acnt = 0; - if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, &acnt)) - return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); + if (phba->sli_rev == LPFC_SLI_REV4) { + sli4_hba = &phba->sli4_hba; + max_cfg_param = &sli4_hba->max_cfg_param; + return scnprintf(buf, PAGE_SIZE, "%d\n", + max_cfg_param->vpi_used); + } else { + if (lpfc_get_hba_info(phba, NULL, NULL, NULL, NULL, &cnt, &acnt)) + return scnprintf(buf, PAGE_SIZE, "%d\n", (cnt - acnt)); + } return scnprintf(buf, PAGE_SIZE, "Unknown\n"); } diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 474834f313a7b5413a89d895bf94289da33218ef..baae1f8279e0cbe66128d815974803b56783f09e 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1557,7 +1557,8 @@ lpfc_cmpl_ct_cmd_gft_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ndlp->nlp_fc4_type |= NLP_FC4_FCP; if (fc4_data_1 & LPFC_FC4_TYPE_BITMASK) ndlp->nlp_fc4_type |= NLP_FC4_NVME; - lpfc_printf_vlog(vport, KERN_INFO, LOG_DISCOVERY, + lpfc_printf_vlog(vport, KERN_INFO, + LOG_DISCOVERY | LOG_NODE, "3064 Setting ndlp x%px, DID x%06x " "with FC4 x%08x, Data: x%08x x%08x " "%d\n", @@ -1568,14 +1569,21 @@ lpfc_cmpl_ct_cmd_gft_id(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (ndlp->nlp_state == NLP_STE_REG_LOGIN_ISSUE && ndlp->nlp_fc4_type) { ndlp->nlp_prev_state = NLP_STE_REG_LOGIN_ISSUE; - - lpfc_nlp_set_state(vport, ndlp, - NLP_STE_PRLI_ISSUE); - lpfc_issue_els_prli(vport, ndlp, 0); + /* This is a fabric topology so if discovery + * started with an unsolicited PLOGI, don't + * send a PRLI. Targets don't issue PLOGI or + * PRLI when acting as a target. Likely this is + * an initiator function. + */ + if (!(ndlp->nlp_flag & NLP_RCV_PLOGI)) { + lpfc_nlp_set_state(vport, ndlp, + NLP_STE_PRLI_ISSUE); + lpfc_issue_els_prli(vport, ndlp, 0); + } } else if (!ndlp->nlp_fc4_type) { /* If fc4 type is still unknown, then LOGO */ lpfc_printf_vlog(vport, KERN_INFO, - LOG_DISCOVERY, + LOG_DISCOVERY | LOG_NODE, "6443 Sending LOGO ndlp x%px," "DID x%06x with fc4_type: " "x%08x, state: %d\n", diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 7f9b221e7c34ae25e663ef30d98075f4f1e83446..ea9b42225e629dd5840e29c8bce622aef7736444 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -6073,7 +6073,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) phba->hba_debugfs_root, phba, &lpfc_debugfs_op_multixripools); - if (!phba->debug_multixri_pools) { + if (IS_ERR(phba->debug_multixri_pools)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "0527 Cannot create debugfs multixripools\n"); goto debug_failed; @@ -6085,7 +6085,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, phba, &lpfc_cgn_buffer_op); - if (!phba->debug_cgn_buffer) { + if (IS_ERR(phba->debug_cgn_buffer)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "6527 Cannot create debugfs " "cgn_buffer\n"); @@ -6098,7 +6098,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, phba, &lpfc_rx_monitor_op); - if (!phba->debug_rx_monitor) { + if (IS_ERR(phba->debug_rx_monitor)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "6528 Cannot create debugfs " "rx_monitor\n"); @@ -6111,7 +6111,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, 0644, phba->hba_debugfs_root, phba, &lpfc_debugfs_ras_log); - if (!phba->debug_ras_log) { + if (IS_ERR(phba->debug_ras_log)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "6148 Cannot create debugfs" " ras_log\n"); @@ -6132,7 +6132,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_lockstat); - if (!phba->debug_lockstat) { + if (IS_ERR(phba->debug_lockstat)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "4610 Can't create debugfs lockstat\n"); goto debug_failed; @@ -6358,7 +6358,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_scsistat); - if (!vport->debug_scsistat) { + if (IS_ERR(vport->debug_scsistat)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "4611 Cannot create debugfs scsistat\n"); goto debug_failed; @@ -6369,7 +6369,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_ioktime); - if (!vport->debug_ioktime) { + if (IS_ERR(vport->debug_ioktime)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "0815 Cannot create debugfs ioktime\n"); goto debug_failed; diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 2bad9954c355f66ce0c78b3447c42072a8b1f604..54e47f2682358ab027ed90e5ffee0349f9fa3f9d 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1041,7 +1041,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, !(ndlp->fc4_xpt_flags & SCSI_XPT_REGD)) lpfc_nlp_put(ndlp); - lpfc_printf_vlog(vport, KERN_WARNING, LOG_TRACE_EVENT, + lpfc_printf_vlog(vport, KERN_WARNING, LOG_ELS, "0150 FLOGI failure Status:x%x/x%x " "xri x%x TMO:x%x refcnt %d\n", ulp_status, ulp_word4, cmdiocb->sli4_xritag, @@ -1091,7 +1091,6 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (!lpfc_error_lost_link(vport, ulp_status, ulp_word4)) lpfc_issue_reg_vfi(vport); - lpfc_nlp_put(ndlp); goto out; } goto flogifail; @@ -1332,7 +1331,8 @@ lpfc_issue_els_flogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (phba->cfg_vmid_priority_tagging) { sp->cmn.priority_tagging = 1; /* lpfc_vmid_host_uuid is combination of wwpn and wwnn */ - if (uuid_is_null((uuid_t *)vport->lpfc_vmid_host_uuid)) { + if (!memchr_inv(vport->lpfc_vmid_host_uuid, 0, + sizeof(vport->lpfc_vmid_host_uuid))) { memcpy(vport->lpfc_vmid_host_uuid, phba->wwpn, sizeof(phba->wwpn)); memcpy(&vport->lpfc_vmid_host_uuid[8], phba->wwnn, @@ -2377,10 +2377,10 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, /* PRLI failed */ lpfc_printf_vlog(vport, mode, loglevel, "2754 PRLI failure DID:%06X Status:x%x/x%x, " - "data: x%x x%x\n", + "data: x%x x%x x%x\n", ndlp->nlp_DID, ulp_status, ulp_word4, ndlp->nlp_state, - ndlp->fc4_prli_sent); + ndlp->fc4_prli_sent, ndlp->nlp_flag); /* Do not call DSM for lpfc_els_abort'ed ELS cmds */ if (!lpfc_error_lost_link(vport, ulp_status, ulp_word4)) @@ -2391,14 +2391,16 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, * mismatch typically caused by an RSCN. Skip any * processing to allow recovery. */ - if (ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE && - ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE) { + if ((ndlp->nlp_state >= NLP_STE_PLOGI_ISSUE && + ndlp->nlp_state <= NLP_STE_REG_LOGIN_ISSUE) || + (ndlp->nlp_state == NLP_STE_NPR_NODE && + ndlp->nlp_flag & NLP_DELAY_TMO)) { lpfc_printf_vlog(vport, KERN_WARNING, LOG_NODE, - "2784 PRLI cmpl: state mismatch " + "2784 PRLI cmpl: Allow Node recovery " "DID x%06x nstate x%x nflag x%x\n", ndlp->nlp_DID, ndlp->nlp_state, ndlp->nlp_flag); - goto out; + goto out; } /* @@ -6166,11 +6168,25 @@ lpfc_els_rsp_prli_acc(struct lpfc_vport *vport, struct lpfc_iocbq *oldiocb, npr->TaskRetryIdReq = 1; } npr->acceptRspCode = PRLI_REQ_EXECUTED; - npr->estabImagePair = 1; + + /* Set image pair for complementary pairs only. */ + if (ndlp->nlp_type & NLP_FCP_TARGET) + npr->estabImagePair = 1; + else + npr->estabImagePair = 0; npr->readXferRdyDis = 1; npr->ConfmComplAllowed = 1; npr->prliType = PRLI_FCP_TYPE; npr->initiatorFunc = 1; + + /* Xmit PRLI ACC response tag */ + lpfc_printf_vlog(vport, KERN_INFO, + LOG_ELS | LOG_NODE | LOG_DISCOVERY, + "6014 FCP issue PRLI ACC imgpair %d " + "retry %d task %d\n", + npr->estabImagePair, + npr->Retry, npr->TaskRetryIdReq); + } else if (prli_fc4_req == PRLI_NVME_TYPE) { /* Respond with an NVME PRLI Type */ npr_nvme = (struct lpfc_nvme_prli *) pcmd; @@ -9588,11 +9604,13 @@ void lpfc_els_flush_cmd(struct lpfc_vport *vport) { LIST_HEAD(abort_list); + LIST_HEAD(cancel_list); struct lpfc_hba *phba = vport->phba; struct lpfc_sli_ring *pring; struct lpfc_iocbq *tmp_iocb, *piocb; u32 ulp_command; unsigned long iflags = 0; + bool mbx_tmo_err; lpfc_fabric_abort_vport(vport); @@ -9614,15 +9632,16 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) if (phba->sli_rev == LPFC_SLI_REV4) spin_lock(&pring->ring_lock); + mbx_tmo_err = test_bit(MBX_TMO_ERR, &phba->bit_flags); /* First we need to issue aborts to outstanding cmds on txcmpl */ list_for_each_entry_safe(piocb, tmp_iocb, &pring->txcmplq, list) { - if (piocb->cmd_flag & LPFC_IO_LIBDFC) + if (piocb->cmd_flag & LPFC_IO_LIBDFC && !mbx_tmo_err) continue; if (piocb->vport != vport) continue; - if (piocb->cmd_flag & LPFC_DRIVER_ABORTED) + if (piocb->cmd_flag & LPFC_DRIVER_ABORTED && !mbx_tmo_err) continue; /* On the ELS ring we can have ELS_REQUESTs or @@ -9641,8 +9660,8 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) */ if (phba->link_state == LPFC_LINK_DOWN) piocb->cmd_cmpl = lpfc_cmpl_els_link_down; - } - if (ulp_command == CMD_GEN_REQUEST64_CR) + } else if (ulp_command == CMD_GEN_REQUEST64_CR || + mbx_tmo_err) list_add_tail(&piocb->dlist, &abort_list); } @@ -9654,11 +9673,19 @@ lpfc_els_flush_cmd(struct lpfc_vport *vport) list_for_each_entry_safe(piocb, tmp_iocb, &abort_list, dlist) { spin_lock_irqsave(&phba->hbalock, iflags); list_del_init(&piocb->dlist); - lpfc_sli_issue_abort_iotag(phba, pring, piocb, NULL); + if (mbx_tmo_err) + list_move_tail(&piocb->list, &cancel_list); + else + lpfc_sli_issue_abort_iotag(phba, pring, piocb, NULL); + spin_unlock_irqrestore(&phba->hbalock, iflags); } - /* Make sure HBA is alive */ - lpfc_issue_hb_tmo(phba); + if (!list_empty(&cancel_list)) + lpfc_sli_cancel_iocbs(phba, &cancel_list, IOSTAT_LOCAL_REJECT, + IOERR_SLI_ABORTED); + else + /* Make sure HBA is alive */ + lpfc_issue_hb_tmo(phba); if (!list_empty(&abort_list)) lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, @@ -12331,9 +12358,10 @@ lpfc_vmid_uvem(struct lpfc_vport *vport, elsiocb->vmid_tag.vmid_context = vmid_context; pcmd = (u8 *)elsiocb->cmd_dmabuf->virt; - if (uuid_is_null((uuid_t *)vport->lpfc_vmid_host_uuid)) + if (!memchr_inv(vport->lpfc_vmid_host_uuid, 0, + sizeof(vport->lpfc_vmid_host_uuid))) memcpy(vport->lpfc_vmid_host_uuid, vmid->host_vmid, - LPFC_COMPRESS_VMID_SIZE); + sizeof(vport->lpfc_vmid_host_uuid)); *((u32 *)(pcmd)) = ELS_CMD_UVEM; len = (u32 *)(pcmd + 4); @@ -12343,13 +12371,13 @@ lpfc_vmid_uvem(struct lpfc_vport *vport, vem_id_desc->tag = be32_to_cpu(VEM_ID_DESC_TAG); vem_id_desc->length = be32_to_cpu(LPFC_UVEM_VEM_ID_DESC_SIZE); memcpy(vem_id_desc->vem_id, vport->lpfc_vmid_host_uuid, - LPFC_COMPRESS_VMID_SIZE); + sizeof(vem_id_desc->vem_id)); inst_desc = (struct instantiated_ve_desc *)(pcmd + 32); inst_desc->tag = be32_to_cpu(INSTANTIATED_VE_DESC_TAG); inst_desc->length = be32_to_cpu(LPFC_UVEM_VE_MAP_DESC_SIZE); memcpy(inst_desc->global_vem_id, vmid->host_vmid, - LPFC_COMPRESS_VMID_SIZE); + sizeof(inst_desc->global_vem_id)); bf_set(lpfc_instantiated_nport_id, inst_desc, vport->fc_myDID); bf_set(lpfc_instantiated_local_id, inst_desc, diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index fdd7f69d87efebb51ce54b579ac5670fbf6c9bb7..5154eeaee0ec325c8f293ee52e291ea5d9b7aa2f 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -169,29 +169,45 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NODE, "3181 dev_loss_callbk x%06x, rport x%px flg x%x " - "load_flag x%x refcnt %d state %d xpt x%x\n", + "load_flag x%x refcnt %u state %d xpt x%x\n", ndlp->nlp_DID, ndlp->rport, ndlp->nlp_flag, vport->load_flag, kref_read(&ndlp->kref), ndlp->nlp_state, ndlp->fc4_xpt_flags); - /* Don't schedule a worker thread event if the vport is going down. - * The teardown process cleans up the node via lpfc_drop_node. - */ + /* Don't schedule a worker thread event if the vport is going down. */ if (vport->load_flag & FC_UNLOADING) { - ((struct lpfc_rport_data *)rport->dd_data)->pnode = NULL; + spin_lock_irqsave(&ndlp->lock, iflags); ndlp->rport = NULL; - ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD; - /* clear the NLP_XPT_REGD if the node is not registered - * with nvme-fc + /* The scsi_transport is done with the rport so lpfc cannot + * call to unregister. Remove the scsi transport reference + * and clean up the SCSI transport node details. */ - if (ndlp->fc4_xpt_flags == NLP_XPT_REGD) - ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD; + if (ndlp->fc4_xpt_flags & (NLP_XPT_REGD | SCSI_XPT_REGD)) { + ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD; + + /* NVME transport-registered rports need the + * NLP_XPT_REGD flag to complete an unregister. + */ + if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD)) + ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD; + spin_unlock_irqrestore(&ndlp->lock, iflags); + lpfc_nlp_put(ndlp); + spin_lock_irqsave(&ndlp->lock, iflags); + } - /* Remove the node reference from remote_port_add now. - * The driver will not call remote_port_delete. + /* Only 1 thread can drop the initial node reference. If + * another thread has set NLP_DROPPED, this thread is done. */ - lpfc_nlp_put(ndlp); + if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD) && + !(ndlp->nlp_flag & NLP_DROPPED)) { + ndlp->nlp_flag |= NLP_DROPPED; + spin_unlock_irqrestore(&ndlp->lock, iflags); + lpfc_nlp_put(ndlp); + return; + } + + spin_unlock_irqrestore(&ndlp->lock, iflags); return; } @@ -4686,7 +4702,8 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) spin_lock_irqsave(&ndlp->lock, iflags); if (!(ndlp->fc4_xpt_flags & NLP_XPT_REGD)) { spin_unlock_irqrestore(&ndlp->lock, iflags); - lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI, + lpfc_printf_vlog(vport, KERN_INFO, + LOG_ELS | LOG_NODE | LOG_DISCOVERY, "0999 %s Not regd: ndlp x%px rport x%px DID " "x%x FLG x%x XPT x%x\n", __func__, ndlp, ndlp->rport, ndlp->nlp_DID, @@ -4702,9 +4719,10 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) vport->phba->nport_event_cnt++; lpfc_unregister_remote_port(ndlp); } else if (!ndlp->rport) { - lpfc_printf_vlog(vport, KERN_INFO, LOG_SLI, + lpfc_printf_vlog(vport, KERN_INFO, + LOG_ELS | LOG_NODE | LOG_DISCOVERY, "1999 %s NDLP in devloss x%px DID x%x FLG x%x" - " XPT x%x refcnt %d\n", + " XPT x%x refcnt %u\n", __func__, ndlp, ndlp->nlp_DID, ndlp->nlp_flag, ndlp->fc4_xpt_flags, kref_read(&ndlp->kref)); @@ -4954,22 +4972,29 @@ lpfc_drop_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) { /* * Use of lpfc_drop_node and UNUSED list: lpfc_drop_node should - * be used if we wish to issue the "last" lpfc_nlp_put() to remove - * the ndlp from the vport. The ndlp marked as UNUSED on the list - * until ALL other outstanding threads have completed. We check - * that the ndlp not already in the UNUSED state before we proceed. + * be used when lpfc wants to remove the "last" lpfc_nlp_put() to + * release the ndlp from the vport when conditions are correct. */ if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) return; lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNUSED_NODE); - ndlp->nlp_flag |= NLP_DROPPED; if (vport->phba->sli_rev == LPFC_SLI_REV4) { lpfc_cleanup_vports_rrqs(vport, ndlp); lpfc_unreg_rpi(vport, ndlp); } - lpfc_nlp_put(ndlp); - return; + /* NLP_DROPPED means another thread already removed the initial + * reference from lpfc_nlp_init. If set, don't drop it again and + * introduce an imbalance. + */ + spin_lock_irq(&ndlp->lock); + if (!(ndlp->nlp_flag & NLP_DROPPED)) { + ndlp->nlp_flag |= NLP_DROPPED; + spin_unlock_irq(&ndlp->lock); + lpfc_nlp_put(ndlp); + return; + } + spin_unlock_irq(&ndlp->lock); } /* @@ -5757,8 +5782,11 @@ lpfc_setup_disc_node(struct lpfc_vport *vport, uint32_t did) (NLP_FCP_TARGET | NLP_NVME_TARGET))) return NULL; - lpfc_disc_state_machine(vport, ndlp, NULL, - NLP_EVT_DEVICE_RECOVERY); + if (ndlp->nlp_state > NLP_STE_UNUSED_NODE && + ndlp->nlp_state < NLP_STE_PRLI_ISSUE) { + lpfc_disc_state_machine(vport, ndlp, NULL, + NLP_EVT_DEVICE_RECOVERY); + } spin_lock_irq(&ndlp->lock); ndlp->nlp_flag |= NLP_NPR_2B_DISC; diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index aaea3e31944d07ab7933bf0e93b1dadd592a5ba3..2108b4cb781575b4c805d9ac4d7bb94f319fa3ba 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -764,6 +764,8 @@ typedef struct _PRLI { /* Structure is in Big Endian format */ #define PRLI_PREDEF_CONFIG 0x5 #define PRLI_PARTIAL_SUCCESS 0x6 #define PRLI_INVALID_PAGE_CNT 0x7 +#define PRLI_INV_SRV_PARM 0x8 + uint8_t word0Reserved3; /* FC Parm Word 0, bit 0:7 */ uint32_t origProcAssoc; /* FC Parm Word 1, bit 0:31 */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 3221a934066bbb7e33159f2d8ed253fc12378586..9e59c050103d64f3e09e5a2da12ea5384fa7c7a9 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2123,7 +2123,7 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba) en_rn_msg = false; } else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 && reg_err2 == SLIPORT_ERR2_REG_FORCED_DUMP) - lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "3144 Port Down: Debug Dump\n"); else if (reg_err1 == SLIPORT_ERR1_REG_ERR_CODE_2 && reg_err2 == SLIPORT_ERR2_REG_FUNC_PROVISON) @@ -7550,6 +7550,8 @@ lpfc_disable_pci_dev(struct lpfc_hba *phba) void lpfc_reset_hba(struct lpfc_hba *phba) { + int rc = 0; + /* If resets are disabled then set error state and return. */ if (!phba->cfg_enable_hba_reset) { phba->link_state = LPFC_HBA_ERROR; @@ -7560,13 +7562,25 @@ lpfc_reset_hba(struct lpfc_hba *phba) if (phba->sli.sli_flag & LPFC_SLI_ACTIVE) { lpfc_offline_prep(phba, LPFC_MBX_WAIT); } else { + if (test_bit(MBX_TMO_ERR, &phba->bit_flags)) { + /* Perform a PCI function reset to start from clean */ + rc = lpfc_pci_function_reset(phba); + lpfc_els_flush_all_cmd(phba); + } lpfc_offline_prep(phba, LPFC_MBX_NO_WAIT); lpfc_sli_flush_io_rings(phba); } lpfc_offline(phba); - lpfc_sli_brdrestart(phba); - lpfc_online(phba); - lpfc_unblock_mgmt_io(phba); + clear_bit(MBX_TMO_ERR, &phba->bit_flags); + if (unlikely(rc)) { + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "8888 PCI function reset failed rc %x\n", + rc); + } else { + lpfc_sli_brdrestart(phba); + lpfc_online(phba); + lpfc_unblock_mgmt_io(phba); + } } /** @@ -12498,10 +12512,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) (new_cpup->eq != LPFC_VECTOR_MAP_EMPTY) && (new_cpup->phys_id == cpup->phys_id)) goto found_same; - new_cpu = cpumask_next( - new_cpu, cpu_present_mask); - if (new_cpu >= nr_cpu_ids) - new_cpu = first_cpu; + new_cpu = lpfc_next_present_cpu(new_cpu); } /* At this point, we leave the CPU as unassigned */ continue; @@ -12513,9 +12524,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) * chance of having multiple unassigned CPU entries * selecting the same IRQ. */ - start_cpu = cpumask_next(new_cpu, cpu_present_mask); - if (start_cpu >= nr_cpu_ids) - start_cpu = first_cpu; + start_cpu = lpfc_next_present_cpu(new_cpu); lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "3337 Set Affinity: CPU %d " @@ -12548,10 +12557,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) if (!(new_cpup->flag & LPFC_CPU_MAP_UNASSIGN) && (new_cpup->eq != LPFC_VECTOR_MAP_EMPTY)) goto found_any; - new_cpu = cpumask_next( - new_cpu, cpu_present_mask); - if (new_cpu >= nr_cpu_ids) - new_cpu = first_cpu; + new_cpu = lpfc_next_present_cpu(new_cpu); } /* We should never leave an entry unassigned */ lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -12567,9 +12573,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) * chance of having multiple unassigned CPU entries * selecting the same IRQ. */ - start_cpu = cpumask_next(new_cpu, cpu_present_mask); - if (start_cpu >= nr_cpu_ids) - start_cpu = first_cpu; + start_cpu = lpfc_next_present_cpu(new_cpu); lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "3338 Set Affinity: CPU %d " @@ -12640,9 +12644,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) new_cpup->core_id == cpup->core_id) { goto found_hdwq; } - new_cpu = cpumask_next(new_cpu, cpu_present_mask); - if (new_cpu >= nr_cpu_ids) - new_cpu = first_cpu; + new_cpu = lpfc_next_present_cpu(new_cpu); } /* If we can't match both phys_id and core_id, @@ -12654,10 +12656,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) if (new_cpup->hdwq != LPFC_VECTOR_MAP_EMPTY && new_cpup->phys_id == cpup->phys_id) goto found_hdwq; - - new_cpu = cpumask_next(new_cpu, cpu_present_mask); - if (new_cpu >= nr_cpu_ids) - new_cpu = first_cpu; + new_cpu = lpfc_next_present_cpu(new_cpu); } /* Otherwise just round robin on cfg_hdw_queue */ @@ -12666,9 +12665,7 @@ lpfc_cpu_affinity_check(struct lpfc_hba *phba, int vectors) goto logit; found_hdwq: /* We found an available entry, copy the IRQ info */ - start_cpu = cpumask_next(new_cpu, cpu_present_mask); - if (start_cpu >= nr_cpu_ids) - start_cpu = first_cpu; + start_cpu = lpfc_next_present_cpu(new_cpu); cpup->hdwq = new_cpup->hdwq; logit: lpfc_printf_log(phba, KERN_INFO, LOG_INIT, diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index b86ff9fcdf0c6a909197cf1e2d6824ccdb28007b..1eb7f7e60bba553a01bccc940cc1a72c893dc9e2 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2023 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -879,23 +879,34 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, spin_unlock_irq(shost->host_lock); lpfc_retry_pport_discovery(phba); } - } else if ((!(ndlp->nlp_type & NLP_FABRIC) && - ((ndlp->nlp_type & NLP_FCP_TARGET) || - (ndlp->nlp_type & NLP_NVME_TARGET) || - (vport->fc_flag & FC_PT2PT))) || - (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) { - /* Only try to re-login if this is NOT a Fabric Node - * AND the remote NPORT is a FCP/NVME Target or we - * are in pt2pt mode. NLP_STE_ADISC_ISSUE is a special - * case for LOGO as a response to ADISC behavior. - */ - mod_timer(&ndlp->nlp_delayfunc, - jiffies + msecs_to_jiffies(1000 * 1)); - spin_lock_irq(&ndlp->lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; - spin_unlock_irq(&ndlp->lock); - - ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; + } else { + lpfc_printf_vlog(vport, KERN_INFO, + LOG_NODE | LOG_ELS | LOG_DISCOVERY, + "3203 LOGO recover nport x%06x state x%x " + "ntype x%x fc_flag x%x\n", + ndlp->nlp_DID, ndlp->nlp_state, + ndlp->nlp_type, vport->fc_flag); + + /* Special cases for rports that recover post LOGO. */ + if ((!(ndlp->nlp_type == NLP_FABRIC) && + (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_NVME_TARGET) || + vport->fc_flag & FC_PT2PT)) || + (ndlp->nlp_state >= NLP_STE_ADISC_ISSUE || + ndlp->nlp_state <= NLP_STE_PRLI_ISSUE)) { + mod_timer(&ndlp->nlp_delayfunc, + jiffies + msecs_to_jiffies(1000 * 1)); + spin_lock_irq(&ndlp->lock); + ndlp->nlp_flag |= NLP_DELAY_TMO; + spin_unlock_irq(&ndlp->lock); + ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; + lpfc_printf_vlog(vport, KERN_INFO, + LOG_NODE | LOG_ELS | LOG_DISCOVERY, + "3204 Start nlpdelay on DID x%06x " + "nflag x%x lastels x%x ref cnt %u", + ndlp->nlp_DID, ndlp->nlp_flag, + ndlp->nlp_last_elscmd, + kref_read(&ndlp->kref)); + } } out: /* Unregister from backend, could have been skipped due to ADISC */ @@ -1854,7 +1865,6 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *) arg; LPFC_MBOXQ_t *mb; LPFC_MBOXQ_t *nextmb; - struct lpfc_nodelist *ns_ndlp; cmdiocb = (struct lpfc_iocbq *) arg; @@ -1882,13 +1892,6 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_vport *vport, } spin_unlock_irq(&phba->hbalock); - /* software abort if any GID_FT is outstanding */ - if (vport->cfg_enable_fc4_type != LPFC_ENABLE_FCP) { - ns_ndlp = lpfc_findnode_did(vport, NameServer_DID); - if (ns_ndlp) - lpfc_els_abort(phba, ns_ndlp); - } - lpfc_rcv_logo(vport, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -2148,6 +2151,7 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_nvme_prli *nvpr; void *temp_ptr; u32 ulp_status; + bool acc_imode_sps = false; cmdiocb = (struct lpfc_iocbq *) arg; rspiocb = cmdiocb->rsp_iocb; @@ -2182,22 +2186,32 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, goto out_err; } - if (npr && (npr->acceptRspCode == PRLI_REQ_EXECUTED) && - (npr->prliType == PRLI_FCP_TYPE)) { - lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_DISC, - "6028 FCP NPR PRLI Cmpl Init %d Target %d\n", - npr->initiatorFunc, - npr->targetFunc); - if (npr->initiatorFunc) - ndlp->nlp_type |= NLP_FCP_INITIATOR; - if (npr->targetFunc) { - ndlp->nlp_type |= NLP_FCP_TARGET; - if (npr->writeXferRdyDis) - ndlp->nlp_flag |= NLP_FIRSTBURST; + if (npr && npr->prliType == PRLI_FCP_TYPE) { + lpfc_printf_vlog(vport, KERN_INFO, + LOG_ELS | LOG_NODE | LOG_DISCOVERY, + "6028 FCP NPR PRLI Cmpl Init %d Target %d " + "EIP %d AccCode x%x\n", + npr->initiatorFunc, npr->targetFunc, + npr->estabImagePair, npr->acceptRspCode); + + if (npr->acceptRspCode == PRLI_INV_SRV_PARM) { + /* Strict initiators don't establish an image pair. */ + if (npr->initiatorFunc && !npr->targetFunc && + !npr->estabImagePair) + acc_imode_sps = true; } - if (npr->Retry) - ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + if (npr->acceptRspCode == PRLI_REQ_EXECUTED || acc_imode_sps) { + if (npr->initiatorFunc) + ndlp->nlp_type |= NLP_FCP_INITIATOR; + if (npr->targetFunc) { + ndlp->nlp_type |= NLP_FCP_TARGET; + if (npr->writeXferRdyDis) + ndlp->nlp_flag |= NLP_FIRSTBURST; + } + if (npr->Retry) + ndlp->nlp_fcp_info |= NLP_FCP_2_DEVICE; + } } else if (nvpr && (bf_get_be32(prli_acc_rsp_code, nvpr) == PRLI_REQ_EXECUTED) && diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 8db7cb99903dbdf45e070f8a1a5e62450e867333..96e11a26c297eb3949fbdd8d51932c2e703b09fe 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -228,8 +228,7 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) spin_unlock_irq(&ndlp->lock); /* On a devloss timeout event, one more put is executed provided the - * NVME and SCSI rport unregister requests are complete. If the vport - * is unloading, this extra put is executed by lpfc_drop_node. + * NVME and SCSI rport unregister requests are complete. */ if (!(ndlp->fc4_xpt_flags & fc4_xpt_flags)) lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); @@ -1864,7 +1863,6 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, struct lpfc_nvme_fcpreq_priv *freqpriv; unsigned long flags; int ret_val; - struct nvme_fc_cmd_iu *cp; /* Validate pointers. LLDD fault handling with transport does * have timing races. @@ -1988,16 +1986,10 @@ lpfc_nvme_fcp_abort(struct nvme_fc_local_port *pnvme_lport, return; } - /* - * Get Command Id from cmd to plug into response. This - * code is not needed in the next NVME Transport drop. - */ - cp = (struct nvme_fc_cmd_iu *)lpfc_nbuf->nvmeCmd->cmdaddr; lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS, "6138 Transport Abort NVME Request Issued for " - "ox_id x%x nvme opcode x%x nvme cmd_id x%x\n", - nvmereq_wqe->sli4_xritag, cp->sqe.common.opcode, - cp->sqe.common.command_id); + "ox_id x%x\n", + nvmereq_wqe->sli4_xritag); return; out_unlock: @@ -2510,8 +2502,9 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_printf_vlog(vport, KERN_ERR, LOG_TRACE_EVENT, "6031 RemotePort Registration failed " - "err: %d, DID x%06x\n", - ret, ndlp->nlp_DID); + "err: %d, DID x%06x ref %u\n", + ret, ndlp->nlp_DID, kref_read(&ndlp->kref)); + lpfc_nlp_put(ndlp); } return ret; @@ -2573,11 +2566,7 @@ lpfc_nvme_rescan_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * nvme_transport perspective. Loss of an rport just means IO cannot * be sent and recovery is completely up to the initator. * For now, the driver just unbinds the DID and port_role so that - * no further IO can be issued. Changes are planned for later. - * - * Notes - the ndlp reference count is not decremented here since - * since there is no nvme_transport api for devloss. Node ref count - * is only adjusted in driver unload. + * no further IO can be issued. */ void lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) @@ -2652,6 +2641,21 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) "6167 NVME unregister failed %d " "port_state x%x\n", ret, remoteport->port_state); + + if (vport->load_flag & FC_UNLOADING) { + /* Only 1 thread can drop the initial node + * reference. Check if another thread has set + * NLP_DROPPED. + */ + spin_lock_irq(&ndlp->lock); + if (!(ndlp->nlp_flag & NLP_DROPPED)) { + ndlp->nlp_flag |= NLP_DROPPED; + spin_unlock_irq(&ndlp->lock); + lpfc_nlp_put(ndlp); + return; + } + spin_unlock_irq(&ndlp->lock); + } } } return; diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index dff4584d338bae91746595a5be3c4bf51c9e297e..425328d9c2d80b567ac3aa452625286fa368f417 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1620,10 +1620,7 @@ lpfc_nvmet_setup_io_context(struct lpfc_hba *phba) cpu = cpumask_first(cpu_present_mask); continue; } - cpu = cpumask_next(cpu, cpu_present_mask); - if (cpu == nr_cpu_ids) - cpu = cpumask_first(cpu_present_mask); - + cpu = lpfc_next_present_cpu(cpu); } for_each_present_cpu(i) { diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 58d10f8f75a78fc80fc99767c96f17db6c6092e7..4dfadf254a72710564136371032ff460e77a1051 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -3935,6 +3935,8 @@ void lpfc_poll_eratt(struct timer_list *t) uint64_t sli_intr, cnt; phba = from_timer(phba, t, eratt_poll); + if (!(phba->hba_flag & HBA_SETUP)) + return; /* Here we will also keep track of interrupts per sec of the hba */ sli_intr = phba->sli.slistat.sli_intr; @@ -7693,7 +7695,9 @@ lpfc_sli4_repost_sgl_list(struct lpfc_hba *phba, spin_unlock_irq(&phba->hbalock); } else { lpfc_printf_log(phba, KERN_ERR, LOG_TRACE_EVENT, - "3161 Failure to post sgl to port.\n"); + "3161 Failure to post sgl to port,status %x " + "blkcnt %d totalcnt %d postcnt %d\n", + status, block_cnt, total_cnt, post_cnt); return -EIO; } @@ -8478,6 +8482,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) spin_unlock_irq(&phba->hbalock); } } + phba->hba_flag &= ~HBA_SETUP; lpfc_sli4_dip(phba); @@ -9282,6 +9287,7 @@ lpfc_mbox_timeout_handler(struct lpfc_hba *phba) * would get IOCB_ERROR from lpfc_sli_issue_iocb, allowing * it to fail all outstanding SCSI IO. */ + set_bit(MBX_TMO_ERR, &phba->bit_flags); spin_lock_irq(&phba->pport->work_port_lock); phba->pport->work_port_events &= ~WORKER_MBOX_TMO; spin_unlock_irq(&phba->pport->work_port_lock); diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 6f35491aed0fd1615a50743dbf8e229fb3f86c16..13a547277f97f077870cb4185585fd59a501a4bf 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "14.2.0.13" +#define LPFC_DRIVER_VERSION "14.2.0.14" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index ef2b6380e19a670aacfa8f4567f143c0283d4c01..bc867da650b610ac6776b0b1b67a1d1729560ba5 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -438,7 +438,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) // set up PCI related soft state and other pre-known parameters - adapter->unique_id = pdev->bus->number << 8 | pdev->devfn; + adapter->unique_id = pci_dev_id(pdev); adapter->irq = pdev->irq; adapter->pdev = pdev; diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 3554f6b07727324ac5a8259be01bd4ecb8db6565..94abba57582d54ff0394029855f86b47d0861c2a 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -2332,7 +2332,7 @@ struct megasas_instance { u32 support_morethan256jbod; /* FW support for more than 256 PD/JBOD */ bool use_seqnum_jbod_fp; /* Added for PD sequence */ bool smp_affinity_enable; - spinlock_t crashdump_lock; + struct mutex crashdump_lock; struct megasas_register_set __iomem *reg_set; u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY]; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 050eed8e26846f4855c6fe0953240b2e3627d1d8..e1aa667dae662dc8b83d6eb928e71b7a248c8cfb 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3271,14 +3271,13 @@ fw_crash_buffer_store(struct device *cdev, struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; int val = 0; - unsigned long flags; if (kstrtoint(buf, 0, &val) != 0) return -EINVAL; - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); instance->fw_crash_buffer_offset = val; - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return strlen(buf); } @@ -3293,24 +3292,23 @@ fw_crash_buffer_show(struct device *cdev, unsigned long dmachunk = CRASH_DMA_BUF_SIZE; unsigned long chunk_left_bytes; unsigned long src_addr; - unsigned long flags; u32 buff_offset; - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); buff_offset = instance->fw_crash_buffer_offset; if (!instance->crash_dump_buf || !((instance->fw_crash_state == AVAILABLE) || (instance->fw_crash_state == COPYING))) { dev_err(&instance->pdev->dev, "Firmware crash dump is not available\n"); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return -EINVAL; } if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) { dev_err(&instance->pdev->dev, "Firmware crash dump offset is out of range\n"); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return 0; } @@ -3322,7 +3320,7 @@ fw_crash_buffer_show(struct device *cdev, src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] + (buff_offset % dmachunk); memcpy(buf, (void *)src_addr, size); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return size; } @@ -3347,7 +3345,6 @@ fw_crash_state_store(struct device *cdev, struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; int val = 0; - unsigned long flags; if (kstrtoint(buf, 0, &val) != 0) return -EINVAL; @@ -3361,9 +3358,9 @@ fw_crash_state_store(struct device *cdev, instance->fw_crash_state = val; if ((val == COPIED) || (val == COPY_ERROR)) { - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); megasas_free_host_crash_buffer(instance); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); if (val == COPY_ERROR) dev_info(&instance->pdev->dev, "application failed to " "copy Firmware crash dump\n"); @@ -7422,7 +7419,7 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance) init_waitqueue_head(&instance->int_cmd_wait_q); init_waitqueue_head(&instance->abort_cmd_wait_q); - spin_lock_init(&instance->crashdump_lock); + mutex_init(&instance->crashdump_lock); spin_lock_init(&instance->mfi_pool_lock); spin_lock_init(&instance->hba_lock); spin_lock_init(&instance->stream_lock); @@ -7518,7 +7515,7 @@ static int megasas_probe_one(struct pci_dev *pdev, */ instance->pdev = pdev; instance->host = host; - instance->unique_id = pdev->bus->number << 8 | pdev->devfn; + instance->unique_id = pci_dev_id(pdev); instance->init_id = MEGASAS_DEFAULT_INIT_ID; megasas_set_adapter_type(instance); diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h index 2fc196499c8923a611b0baa94c9fd0b427cd647c..35f81af40f5118eb59862ea7b90a90c451360f55 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_cnfg.h @@ -1482,7 +1482,7 @@ struct mpi3_security_page0 { #define MPI3_SECURITY1_KEY_RECORD_MAX 1 #endif #ifndef MPI3_SECURITY1_PAD_MAX -#define MPI3_SECURITY1_PAD_MAX 1 +#define MPI3_SECURITY1_PAD_MAX 4 #endif union mpi3_security1_key_data { __le32 dword[128]; diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h index f5e9c2309ce6da61b444991b27782593d591c453..1e4a60fc655f87f02bd40a5a77d15a82604a892b 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_ioc.h @@ -600,6 +600,7 @@ struct mpi3_event_data_pcie_error_threshold { __le16 threshold_count; __le16 attached_dev_handle; __le16 reserved12; + __le32 reserved14; }; #define MPI3_EVENT_PCI_ERROR_RC_THRESHOLD_EXCEEDED (0x00) diff --git a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h index 441cfc2c7f0957888212ed844650a0176c846265..1e0a3dcaf723220c709ecf162fcc77ef4cfec342 100644 --- a/drivers/scsi/mpi3mr/mpi/mpi30_transport.h +++ b/drivers/scsi/mpi3mr/mpi/mpi30_transport.h @@ -18,7 +18,7 @@ union mpi3_version_union { #define MPI3_VERSION_MAJOR (3) #define MPI3_VERSION_MINOR (0) -#define MPI3_VERSION_UNIT (27) +#define MPI3_VERSION_UNIT (28) #define MPI3_VERSION_DEV (0) #define MPI3_DEVHANDLE_INVALID (0xffff) struct mpi3_sysif_oper_queue_indexes { diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index 0afb687402e15cf02cf515ef5687eeacfeab6920..ae98d15c30b1dd0c367fb53d3fdb4f11172fb17d 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -55,8 +55,8 @@ extern struct list_head mrioc_list; extern int prot_mask; extern atomic64_t event_counter; -#define MPI3MR_DRIVER_VERSION "8.4.1.0.0" -#define MPI3MR_DRIVER_RELDATE "16-March-2023" +#define MPI3MR_DRIVER_VERSION "8.5.0.0.0" +#define MPI3MR_DRIVER_RELDATE "24-July-2023" #define MPI3MR_DRIVER_NAME "mpi3mr" #define MPI3MR_DRIVER_LICENSE "GPL" @@ -66,11 +66,12 @@ extern atomic64_t event_counter; #define MPI3MR_NAME_LENGTH 32 #define IOCNAME "%s: " -#define MPI3MR_MAX_SECTORS 2048 +#define MPI3MR_DEFAULT_MAX_IO_SIZE (1 * 1024 * 1024) /* Definitions for internal SGL and Chain SGL buffers */ #define MPI3MR_PAGE_SIZE_4K 4096 -#define MPI3MR_SG_DEPTH (MPI3MR_PAGE_SIZE_4K / sizeof(struct mpi3_sge_common)) +#define MPI3MR_DEFAULT_SGL_ENTRIES 256 +#define MPI3MR_MAX_SGL_ENTRIES 2048 /* Definitions for MAX values for shost */ #define MPI3MR_MAX_CMDS_LUN 128 @@ -206,6 +207,9 @@ extern atomic64_t event_counter; */ #define MPI3MR_MAX_APP_XFER_SECTORS (2048 + 512) +#define MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS 256 +#define MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS 2048 + /** * struct mpi3mr_nvme_pt_sge - Structure to store SGEs for NVMe * Encapsulated commands. @@ -323,6 +327,7 @@ struct mpi3mr_ioc_facts { u16 max_perids; u16 max_pds; u16 max_sasexpanders; + u32 max_data_length; u16 max_sasinitiators; u16 max_enclosures; u16 max_pcie_switches; @@ -676,6 +681,7 @@ enum mpi3mr_dev_state { * @io_unit_port: IO Unit port ID * @non_stl: Is this device not to be attached with SAS TL * @io_throttle_enabled: I/O throttling needed or not + * @wslen: Write same max length * @q_depth: Device specific Queue Depth * @wwid: World wide ID * @enclosure_logical_id: Enclosure logical identifier @@ -698,6 +704,7 @@ struct mpi3mr_tgt_dev { u8 io_unit_port; u8 non_stl; u8 io_throttle_enabled; + u16 wslen; u16 q_depth; u64 wwid; u64 enclosure_logical_id; @@ -751,6 +758,8 @@ static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s) * @dev_removed: Device removed in the Firmware * @dev_removedelay: Device is waiting to be removed in FW * @dev_type: Device type + * @dev_nvme_dif: Device is NVMe DIF enabled + * @wslen: Write same max length * @io_throttle_enabled: I/O throttling needed or not * @io_divert: Flag indicates io divert is on or off for the dev * @throttle_group: Pointer to throttle group info @@ -767,6 +776,8 @@ struct mpi3mr_stgt_priv_data { u8 dev_removed; u8 dev_removedelay; u8 dev_type; + u8 dev_nvme_dif; + u16 wslen; u8 io_throttle_enabled; u8 io_divert; struct mpi3mr_throttle_group_info *throttle_group; @@ -782,12 +793,14 @@ struct mpi3mr_stgt_priv_data { * @ncq_prio_enable: NCQ priority enable for SATA device * @pend_count: Counter to track pending I/Os during error * handling + * @wslen: Write same max length */ struct mpi3mr_sdev_priv_data { struct mpi3mr_stgt_priv_data *tgt_priv_data; u32 lun_id; u8 ncq_prio_enable; u32 pend_count; + u16 wslen; }; /** @@ -959,6 +972,7 @@ struct scmd_priv { * @stop_drv_processing: Stop all command processing * @device_refresh_on: Don't process the events until devices are refreshed * @max_host_ios: Maximum host I/O count + * @max_sgl_entries: Max SGL entries per I/O * @chain_buf_count: Chain buffer count * @chain_buf_pool: Chain buffer pool * @chain_sgl_list: Chain SGL list @@ -1129,6 +1143,7 @@ struct mpi3mr_ioc { u16 max_host_ios; spinlock_t tgtdev_lock; struct list_head tgtdev_list; + u16 max_sgl_entries; u32 chain_buf_count; struct dma_pool *chain_buf_pool; diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index 5fa07d6ee5b8ee5b52b77c826577b66f98c094b2..f039f1d9864776205ff03fb0173517afafef0f75 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -1163,6 +1163,12 @@ mpi3mr_revalidate_factsdata(struct mpi3mr_ioc *mrioc) return -EPERM; } + if (mrioc->shost->max_sectors != (mrioc->facts.max_data_length / 512)) + ioc_err(mrioc, "Warning: The maximum data transfer length\n" + "\tchanged after reset: previous(%d), new(%d),\n" + "the driver cannot change this at run time\n", + mrioc->shost->max_sectors * 512, mrioc->facts.max_data_length); + if ((mrioc->sas_transport_enabled) && (mrioc->facts.ioc_capabilities & MPI3_IOCFACTS_CAPABILITY_MULTIPATH_ENABLED)) ioc_err(mrioc, @@ -2343,8 +2349,8 @@ static int mpi3mr_sync_timestamp(struct mpi3mr_ioc *mrioc) ioc_err(mrioc, "Issue IOUCTL time_stamp: command timed out\n"); mrioc->init_cmds.is_waiting = 0; if (!(mrioc->init_cmds.state & MPI3MR_CMD_RESET)) - mpi3mr_soft_reset_handler(mrioc, - MPI3MR_RESET_FROM_TSU_TIMEOUT, 1); + mpi3mr_check_rh_fault_ioc(mrioc, + MPI3MR_RESET_FROM_TSU_TIMEOUT); retval = -1; goto out_unlock; } @@ -2856,6 +2862,7 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, le16_to_cpu(facts_data->max_pcie_switches); mrioc->facts.max_sasexpanders = le16_to_cpu(facts_data->max_sas_expanders); + mrioc->facts.max_data_length = le16_to_cpu(facts_data->max_data_length); mrioc->facts.max_sasinitiators = le16_to_cpu(facts_data->max_sas_initiators); mrioc->facts.max_enclosures = le16_to_cpu(facts_data->max_enclosures); @@ -2893,13 +2900,18 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, mrioc->facts.io_throttle_high = le16_to_cpu(facts_data->io_throttle_high); + if (mrioc->facts.max_data_length == + MPI3_IOCFACTS_MAX_DATA_LENGTH_NOT_REPORTED) + mrioc->facts.max_data_length = MPI3MR_DEFAULT_MAX_IO_SIZE; + else + mrioc->facts.max_data_length *= MPI3MR_PAGE_SIZE_4K; /* Store in 512b block count */ if (mrioc->facts.io_throttle_data_length) mrioc->io_throttle_data_length = (mrioc->facts.io_throttle_data_length * 2 * 4); else /* set the length to 1MB + 1K to disable throttle */ - mrioc->io_throttle_data_length = MPI3MR_MAX_SECTORS + 2; + mrioc->io_throttle_data_length = (mrioc->facts.max_data_length / 512) + 2; mrioc->io_throttle_high = (mrioc->facts.io_throttle_high * 2 * 1024); mrioc->io_throttle_low = (mrioc->facts.io_throttle_low * 2 * 1024); @@ -2914,9 +2926,9 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, ioc_info(mrioc, "SGEModMask 0x%x SGEModVal 0x%x SGEModShift 0x%x ", mrioc->facts.sge_mod_mask, mrioc->facts.sge_mod_value, mrioc->facts.sge_mod_shift); - ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x\n", + ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x max_data_len (%d)\n", mrioc->facts.dma_mask, (facts_flags & - MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK)); + MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK), mrioc->facts.max_data_length); ioc_info(mrioc, "max_dev_per_throttle_group(%d), max_throttle_groups(%d)\n", mrioc->facts.max_dev_per_tg, mrioc->facts.max_io_throttle_group); @@ -3359,8 +3371,8 @@ int mpi3mr_process_event_ack(struct mpi3mr_ioc *mrioc, u8 event, if (!(mrioc->init_cmds.state & MPI3MR_CMD_COMPLETE)) { ioc_err(mrioc, "Issue EvtNotify: command timed out\n"); if (!(mrioc->init_cmds.state & MPI3MR_CMD_RESET)) - mpi3mr_soft_reset_handler(mrioc, - MPI3MR_RESET_FROM_EVTACK_TIMEOUT, 1); + mpi3mr_check_rh_fault_ioc(mrioc, + MPI3MR_RESET_FROM_EVTACK_TIMEOUT); retval = -1; goto out_unlock; } @@ -3414,7 +3426,14 @@ static int mpi3mr_alloc_chain_bufs(struct mpi3mr_ioc *mrioc) if (!mrioc->chain_sgl_list) goto out_failed; - sz = MPI3MR_PAGE_SIZE_4K; + if (mrioc->max_sgl_entries > (mrioc->facts.max_data_length / + MPI3MR_PAGE_SIZE_4K)) + mrioc->max_sgl_entries = mrioc->facts.max_data_length / + MPI3MR_PAGE_SIZE_4K; + sz = mrioc->max_sgl_entries * sizeof(struct mpi3_sge_common); + ioc_info(mrioc, "number of sgl entries=%d chain buffer size=%dKB\n", + mrioc->max_sgl_entries, sz/1024); + mrioc->chain_buf_pool = dma_pool_create("chain_buf pool", &mrioc->pdev->dev, sz, 16, 0); if (!mrioc->chain_buf_pool) { @@ -3813,7 +3832,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc) } mrioc->max_host_ios = mrioc->facts.max_reqs - MPI3MR_INTERNAL_CMDS_RESVD; - + mrioc->shost->max_sectors = mrioc->facts.max_data_length / 512; mrioc->num_io_throttle_group = mrioc->facts.max_io_throttle_group; atomic_set(&mrioc->pend_large_data_sz, 0); diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index d627355303d790de4c2e5e763ca8406ace2fc240..89ba015c5d7e8d215d256aa92b3a65803b626a86 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -33,6 +33,12 @@ static int logging_level; module_param(logging_level, int, 0); MODULE_PARM_DESC(logging_level, " bits for enabling additional logging info (default=0)"); +static int max_sgl_entries = MPI3MR_DEFAULT_SGL_ENTRIES; +module_param(max_sgl_entries, int, 0444); +MODULE_PARM_DESC(max_sgl_entries, + "Preferred max number of SG entries to be used for a single I/O\n" + "The actual value will be determined by the driver\n" + "(Minimum=256, Maximum=2048, default=256)"); /* Forward declarations*/ static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, @@ -424,6 +430,7 @@ void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc) tgt_priv->io_throttle_enabled = 0; tgt_priv->io_divert = 0; tgt_priv->throttle_group = NULL; + tgt_priv->wslen = 0; if (tgtdev->host_exposed) atomic_set(&tgt_priv->block_io, 1); } @@ -1034,6 +1041,19 @@ mpi3mr_update_sdev(struct scsi_device *sdev, void *data) void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc) { struct mpi3mr_tgt_dev *tgtdev, *tgtdev_next; + struct mpi3mr_stgt_priv_data *tgt_priv; + + dprint_reset(mrioc, "refresh target devices: check for removals\n"); + list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list, + list) { + if ((tgtdev->dev_handle == MPI3MR_INVALID_DEV_HANDLE) && + tgtdev->host_exposed && tgtdev->starget && + tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + tgt_priv->dev_removed = 1; + atomic_set(&tgt_priv->block_io, 0); + } + } list_for_each_entry_safe(tgtdev, tgtdev_next, &mrioc->tgtdev_list, list) { @@ -1102,6 +1122,18 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, tgtdev->io_throttle_enabled = (flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0; + switch (flags & MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK) { + case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB: + tgtdev->wslen = MPI3MR_WRITE_SAME_MAX_LEN_256_BLKS; + break; + case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_2048_LB: + tgtdev->wslen = MPI3MR_WRITE_SAME_MAX_LEN_2048_BLKS; + break; + case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_NO_LIMIT: + default: + tgtdev->wslen = 0; + break; + } if (tgtdev->starget && tgtdev->starget->hostdata) { scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) @@ -1113,6 +1145,7 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, tgtdev->io_throttle_enabled; if (is_added == true) atomic_set(&scsi_tgt_priv_data->block_io, 0); + scsi_tgt_priv_data->wslen = tgtdev->wslen; } switch (dev_pg0->access_status) { @@ -3413,7 +3446,7 @@ static int mpi3mr_prepare_sg_scmd(struct mpi3mr_ioc *mrioc, scsi_bufflen(scmd)); return -ENOMEM; } - if (sges_left > MPI3MR_SG_DEPTH) { + if (sges_left > mrioc->max_sgl_entries) { sdev_printk(KERN_ERR, scmd->device, "scsi_dma_map returned unsupported sge count %d!\n", sges_left); @@ -3933,6 +3966,48 @@ void mpi3mr_wait_for_host_io(struct mpi3mr_ioc *mrioc, u32 timeout) mpi3mr_get_fw_pending_ios(mrioc)); } +/** + * mpi3mr_setup_divert_ws - Setup Divert IO flag for write same + * @mrioc: Adapter instance reference + * @scmd: SCSI command reference + * @scsiio_req: MPI3 SCSI IO request + * @scsiio_flags: Pointer to MPI3 SCSI IO Flags + * @wslen: write same max length + * + * Gets values of unmap, ndob and number of blocks from write + * same scsi io and based on these values it sets divert IO flag + * and reason for diverting IO to firmware. + * + * Return: Nothing + */ +static inline void mpi3mr_setup_divert_ws(struct mpi3mr_ioc *mrioc, + struct scsi_cmnd *scmd, struct mpi3_scsi_io_request *scsiio_req, + u32 *scsiio_flags, u16 wslen) +{ + u8 unmap = 0, ndob = 0; + u8 opcode = scmd->cmnd[0]; + u32 num_blocks = 0; + u16 sa = (scmd->cmnd[8] << 8) | (scmd->cmnd[9]); + + if (opcode == WRITE_SAME_16) { + unmap = scmd->cmnd[1] & 0x08; + ndob = scmd->cmnd[1] & 0x01; + num_blocks = get_unaligned_be32(scmd->cmnd + 10); + } else if ((opcode == VARIABLE_LENGTH_CMD) && (sa == WRITE_SAME_32)) { + unmap = scmd->cmnd[10] & 0x08; + ndob = scmd->cmnd[10] & 0x01; + num_blocks = get_unaligned_be32(scmd->cmnd + 28); + } else + return; + + if ((unmap) && (ndob) && (num_blocks > wslen)) { + scsiio_req->msg_flags |= + MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE; + *scsiio_flags |= + MPI3_SCSIIO_FLAGS_DIVERT_REASON_WRITE_SAME_TOO_LARGE; + } +} + /** * mpi3mr_eh_host_reset - Host reset error handling callback * @scmd: SCSI command reference @@ -4430,7 +4505,6 @@ static int mpi3mr_target_alloc(struct scsi_target *starget) unsigned long flags; int retval = 0; struct sas_rphy *rphy = NULL; - bool update_stgt_priv_data = false; scsi_tgt_priv_data = kzalloc(sizeof(*scsi_tgt_priv_data), GFP_KERNEL); if (!scsi_tgt_priv_data) @@ -4439,39 +4513,50 @@ static int mpi3mr_target_alloc(struct scsi_target *starget) starget->hostdata = scsi_tgt_priv_data; spin_lock_irqsave(&mrioc->tgtdev_lock, flags); - if (starget->channel == mrioc->scsi_device_channel) { tgt_dev = __mpi3mr_get_tgtdev_by_perst_id(mrioc, starget->id); - if (tgt_dev && !tgt_dev->is_hidden) - update_stgt_priv_data = true; - else + if (tgt_dev && !tgt_dev->is_hidden) { + scsi_tgt_priv_data->starget = starget; + scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle; + scsi_tgt_priv_data->perst_id = tgt_dev->perst_id; + scsi_tgt_priv_data->dev_type = tgt_dev->dev_type; + scsi_tgt_priv_data->tgt_dev = tgt_dev; + tgt_dev->starget = starget; + atomic_set(&scsi_tgt_priv_data->block_io, 0); + retval = 0; + if ((tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_PCIE) && + ((tgt_dev->dev_spec.pcie_inf.dev_info & + MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) == + MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_NVME_DEVICE) && + ((tgt_dev->dev_spec.pcie_inf.dev_info & + MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_MASK) != + MPI3_DEVICE0_PCIE_DEVICE_INFO_PITYPE_0)) + scsi_tgt_priv_data->dev_nvme_dif = 1; + scsi_tgt_priv_data->io_throttle_enabled = tgt_dev->io_throttle_enabled; + scsi_tgt_priv_data->wslen = tgt_dev->wslen; + if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_VD) + scsi_tgt_priv_data->throttle_group = tgt_dev->dev_spec.vd_inf.tg; + } else retval = -ENXIO; } else if (mrioc->sas_transport_enabled && !starget->channel) { rphy = dev_to_rphy(starget->dev.parent); tgt_dev = __mpi3mr_get_tgtdev_by_addr_and_rphy(mrioc, rphy->identify.sas_address, rphy); if (tgt_dev && !tgt_dev->is_hidden && !tgt_dev->non_stl && - (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA)) - update_stgt_priv_data = true; - else + (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_SAS_SATA)) { + scsi_tgt_priv_data->starget = starget; + scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle; + scsi_tgt_priv_data->perst_id = tgt_dev->perst_id; + scsi_tgt_priv_data->dev_type = tgt_dev->dev_type; + scsi_tgt_priv_data->tgt_dev = tgt_dev; + scsi_tgt_priv_data->io_throttle_enabled = tgt_dev->io_throttle_enabled; + scsi_tgt_priv_data->wslen = tgt_dev->wslen; + tgt_dev->starget = starget; + atomic_set(&scsi_tgt_priv_data->block_io, 0); + retval = 0; + } else retval = -ENXIO; } - - if (update_stgt_priv_data) { - scsi_tgt_priv_data->starget = starget; - scsi_tgt_priv_data->dev_handle = tgt_dev->dev_handle; - scsi_tgt_priv_data->perst_id = tgt_dev->perst_id; - scsi_tgt_priv_data->dev_type = tgt_dev->dev_type; - scsi_tgt_priv_data->tgt_dev = tgt_dev; - tgt_dev->starget = starget; - atomic_set(&scsi_tgt_priv_data->block_io, 0); - retval = 0; - scsi_tgt_priv_data->io_throttle_enabled = - tgt_dev->io_throttle_enabled; - if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_VD) - scsi_tgt_priv_data->throttle_group = - tgt_dev->dev_spec.vd_inf.tg; - } spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); return retval; @@ -4732,6 +4817,10 @@ static int mpi3mr_qcmd(struct Scsi_Host *shost, mpi3mr_setup_eedp(mrioc, scmd, scsiio_req); + if (stgt_priv_data->wslen) + mpi3mr_setup_divert_ws(mrioc, scmd, scsiio_req, &scsiio_flags, + stgt_priv_data->wslen); + memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len); scsiio_req->data_length = cpu_to_le32(scsi_bufflen(scmd)); scsiio_req->dev_handle = cpu_to_le16(dev_handle); @@ -4818,10 +4907,10 @@ static const struct scsi_host_template mpi3mr_driver_template = { .no_write_same = 1, .can_queue = 1, .this_id = -1, - .sg_tablesize = MPI3MR_SG_DEPTH, + .sg_tablesize = MPI3MR_DEFAULT_SGL_ENTRIES, /* max xfer supported is 1M (2K in 512 byte sized sectors) */ - .max_sectors = 2048, + .max_sectors = (MPI3MR_DEFAULT_MAX_IO_SIZE / 512), .cmd_per_lun = MPI3MR_MAX_CMDS_LUN, .max_segment_size = 0xffffffff, .track_queue_depth = 1, @@ -5004,6 +5093,16 @@ mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id) mrioc->pdev = pdev; mrioc->stop_bsgs = 1; + mrioc->max_sgl_entries = max_sgl_entries; + if (max_sgl_entries > MPI3MR_MAX_SGL_ENTRIES) + mrioc->max_sgl_entries = MPI3MR_MAX_SGL_ENTRIES; + else if (max_sgl_entries < MPI3MR_DEFAULT_SGL_ENTRIES) + mrioc->max_sgl_entries = MPI3MR_DEFAULT_SGL_ENTRIES; + else { + mrioc->max_sgl_entries /= MPI3MR_DEFAULT_SGL_ENTRIES; + mrioc->max_sgl_entries *= MPI3MR_DEFAULT_SGL_ENTRIES; + } + /* init shost parameters */ shost->max_cmd_len = MPI3MR_MAX_CDB_LENGTH; shost->max_lun = -1; @@ -5068,7 +5167,7 @@ mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id) shost->nr_maps = 3; shost->can_queue = mrioc->max_host_ios; - shost->sg_tablesize = MPI3MR_SG_DEPTH; + shost->sg_tablesize = mrioc->max_sgl_entries; shost->max_id = mrioc->facts.max_perids + 1; retval = scsi_add_host(shost, &pdev->dev); diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h index ed3923f8db4ffa149fe432c44dad84f86f11b450..6de35b32223c2ddce4bf82069ae3692d0195136c 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2.h @@ -199,7 +199,7 @@ * *****************************************************************************/ -typedef volatile struct _MPI2_SYSTEM_INTERFACE_REGS { +typedef struct _MPI2_SYSTEM_INTERFACE_REGS { U32 Doorbell; /*0x00 */ U32 WriteSequence; /*0x04 */ U32 HostDiagnostic; /*0x08 */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 53f5492579cb7edfab4b48e70c6c92e352cc40e2..61a32bf00747e6151c063893038c6527bc2ab2f9 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -138,6 +138,9 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); static void _base_clear_outstanding_commands(struct MPT3SAS_ADAPTER *ioc); +static u32 +_base_readl_ext_retry(const void __iomem *addr); + /** * mpt3sas_base_check_cmd_timeout - Function * to check timeout and command termination due @@ -201,7 +204,7 @@ module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug, * while reading the system interface register. */ static inline u32 -_base_readl_aero(const volatile void __iomem *addr) +_base_readl_aero(const void __iomem *addr) { u32 i = 0, ret_val; @@ -213,8 +216,22 @@ _base_readl_aero(const volatile void __iomem *addr) return ret_val; } +static u32 +_base_readl_ext_retry(const void __iomem *addr) +{ + u32 i, ret_val; + + for (i = 0 ; i < 30 ; i++) { + ret_val = readl(addr); + if (ret_val == 0) + continue; + } + + return ret_val; +} + static inline u32 -_base_readl(const volatile void __iomem *addr) +_base_readl(const void __iomem *addr) { return readl(addr); } @@ -940,7 +957,7 @@ mpt3sas_halt_firmware(struct MPT3SAS_ADAPTER *ioc) dump_stack(); - doorbell = ioc->base_readl(&ioc->chip->Doorbell); + doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt3sas_print_fault_code(ioc, doorbell & MPI2_DOORBELL_DATA_MASK); @@ -6686,7 +6703,7 @@ mpt3sas_base_get_iocstate(struct MPT3SAS_ADAPTER *ioc, int cooked) { u32 s, sc; - s = ioc->base_readl(&ioc->chip->Doorbell); + s = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); sc = s & MPI2_IOC_STATE_MASK; return cooked ? sc : s; } @@ -6831,7 +6848,7 @@ _base_wait_for_doorbell_ack(struct MPT3SAS_ADAPTER *ioc, int timeout) __func__, count, timeout)); return 0; } else if (int_status & MPI2_HIS_IOC2SYS_DB_STATUS) { - doorbell = ioc->base_readl(&ioc->chip->Doorbell); + doorbell = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); if ((doorbell & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt3sas_print_fault_code(ioc, doorbell); @@ -6871,7 +6888,7 @@ _base_wait_for_doorbell_not_used(struct MPT3SAS_ADAPTER *ioc, int timeout) count = 0; cntdn = 1000 * timeout; do { - doorbell_reg = ioc->base_readl(&ioc->chip->Doorbell); + doorbell_reg = ioc->base_readl_ext_retry(&ioc->chip->Doorbell); if (!(doorbell_reg & MPI2_DOORBELL_USED)) { dhsprintk(ioc, ioc_info(ioc, "%s: successful count(%d), timeout(%d)\n", @@ -7019,7 +7036,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, __le32 *mfp; /* make sure doorbell is not in use */ - if ((ioc->base_readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { + if ((ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_USED)) { ioc_err(ioc, "doorbell is in use (line=%d)\n", __LINE__); return -EFAULT; } @@ -7068,7 +7085,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } /* read the first two 16-bits, it gives the total length of the reply */ - reply[0] = le16_to_cpu(ioc->base_readl(&ioc->chip->Doorbell) + reply[0] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); if ((_base_wait_for_doorbell_int(ioc, 5))) { @@ -7076,7 +7093,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, __LINE__); return -EFAULT; } - reply[1] = le16_to_cpu(ioc->base_readl(&ioc->chip->Doorbell) + reply[1] = le16_to_cpu(ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); @@ -7087,10 +7104,10 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, return -EFAULT; } if (i >= reply_bytes/2) /* overflow case */ - ioc->base_readl(&ioc->chip->Doorbell); + ioc->base_readl_ext_retry(&ioc->chip->Doorbell); else reply[i] = le16_to_cpu( - ioc->base_readl(&ioc->chip->Doorbell) + ioc->base_readl_ext_retry(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); } @@ -7949,7 +7966,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc) goto out; } - host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic); + host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic); drsprintk(ioc, ioc_info(ioc, "wrote magic sequence: count(%d), host_diagnostic(0x%08x)\n", count, host_diagnostic)); @@ -7969,7 +7986,7 @@ _base_diag_reset(struct MPT3SAS_ADAPTER *ioc) for (count = 0; count < (300000000 / MPI2_HARD_RESET_PCIE_SECOND_READ_DELAY_MICRO_SEC); count++) { - host_diagnostic = ioc->base_readl(&ioc->chip->HostDiagnostic); + host_diagnostic = ioc->base_readl_ext_retry(&ioc->chip->HostDiagnostic); if (host_diagnostic == 0xFFFFFFFF) { ioc_info(ioc, @@ -8359,10 +8376,13 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) ioc->rdpq_array_enable_assigned = 0; ioc->use_32bit_dma = false; ioc->dma_mask = 64; - if (ioc->is_aero_ioc) + if (ioc->is_aero_ioc) { ioc->base_readl = &_base_readl_aero; - else + ioc->base_readl_ext_retry = &_base_readl_ext_retry; + } else { ioc->base_readl = &_base_readl; + ioc->base_readl_ext_retry = &_base_readl; + } r = mpt3sas_base_map_resources(ioc); if (r) goto out_free_resources; diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 05364aa15ecdb86b9da1688adf0d7a50dc38270a..1be0850ca17aa1f9c5e05218b6e6d82b294428e8 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -994,7 +994,7 @@ typedef void (*NVME_BUILD_PRP)(struct MPT3SAS_ADAPTER *ioc, u16 smid, typedef void (*PUT_SMID_IO_FP_HIP) (struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 funcdep); typedef void (*PUT_SMID_DEFAULT) (struct MPT3SAS_ADAPTER *ioc, u16 smid); -typedef u32 (*BASE_READ_REG) (const volatile void __iomem *addr); +typedef u32 (*BASE_READ_REG) (const void __iomem *addr); /* * To get high iops reply queue's msix index when high iops mode is enabled * else get the msix index of general reply queues. @@ -1618,6 +1618,7 @@ struct MPT3SAS_ADAPTER { u8 diag_trigger_active; u8 atomic_desc_capable; BASE_READ_REG base_readl; + BASE_READ_REG base_readl_ext_retry; struct SL_WH_MASTER_TRIGGER_T diag_trigger_master; struct SL_WH_EVENT_TRIGGERS_T diag_trigger_event; struct SL_WH_SCSI_TRIGGERS_T diag_trigger_scsi; diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 49e2a5e7ce5493d79639f9cdb9932a930ee27a42..43ebb331e21673b68acee5b3824c0ae35e0b8500 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -84,10 +84,8 @@ static void mvs_phy_init(struct mvs_info *mvi, int phy_id) phy->port = NULL; timer_setup(&phy->timer, NULL, 0); sas_phy->enabled = (phy_id < mvi->chip->n_phy) ? 1 : 0; - sas_phy->class = SAS; sas_phy->iproto = SAS_PROTOCOL_ALL; sas_phy->tproto = 0; - sas_phy->type = PHY_TYPE_PHYSICAL; sas_phy->role = PHY_ROLE_INITIATOR; sas_phy->oob_mode = OOB_NOT_CONNECTED; sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; @@ -416,7 +414,7 @@ static int mvs_prep_sas_ha_init(struct Scsi_Host *shost, sha->sas_phy = arr_phy; sha->sas_port = arr_port; - sha->core.shost = shost; + sha->shost = shost; sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL); if (!sha->lldd_ha) @@ -458,7 +456,6 @@ static void mvs_post_sas_ha_init(struct Scsi_Host *shost, sha->sas_ha_name = DRV_NAME; sha->dev = mvi->dev; - sha->lldd_module = THIS_MODULE; sha->sas_addr = &mvi->sas_addr[0]; sha->num_phys = nr_core * chip_info->n_phy; @@ -473,7 +470,7 @@ static void mvs_post_sas_ha_init(struct Scsi_Host *shost, shost->sg_tablesize = min_t(u16, SG_ALL, MVS_MAX_SG); shost->can_queue = can_queue; mvi->shost->cmd_per_lun = MVS_QUEUE_SIZE; - sha->core.shost = mvi->shost; + sha->shost = mvi->shost; } static void mvs_init_sas_add(struct mvs_info *mvi) diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 9978c424214cda590152c65ca5ea6d8c541dd69b..1444b1f1c4c886a85f2379aa078d3730014668d3 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -564,7 +564,7 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, void *buf_prd; struct ssp_frame_hdr *ssp_hdr; void *buf_tmp; - u8 *buf_cmd, *buf_oaf, fburst = 0; + u8 *buf_cmd, *buf_oaf; dma_addr_t buf_tmp_dma; u32 flags; u32 resp_len, req_len, i, tag = tei->tag; @@ -582,10 +582,6 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, (phy_mask << TXQ_PHY_SHIFT)); flags = MCH_RETRY; - if (task->ssp_task.enable_first_burst) { - flags |= MCH_FBURST; - fburst = (1 << 7); - } if (is_tmf) flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT); else @@ -667,8 +663,7 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, memcpy(buf_cmd, &task->ssp_task.LUN, 8); if (ssp_hdr->frame_type != SSP_TASK) { - buf_cmd[9] = fburst | task->ssp_task.task_attr | - (task->ssp_task.task_prio << 3); + buf_cmd[9] = task->ssp_task.task_attr; memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); } else{ diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c index 73aa7059b55692a9add12c47ef436031394ae9fb..d9d366ec17dc975a9890ecf6441d7c9837e226e0 100644 --- a/drivers/scsi/mvumi.c +++ b/drivers/scsi/mvumi.c @@ -1500,7 +1500,7 @@ static void mvumi_rescan_devices(struct mvumi_hba *mhba, int id) sdev = scsi_device_lookup(mhba->shost, 0, id, 0); if (sdev) { - scsi_rescan_device(&sdev->sdev_gendev); + scsi_rescan_device(sdev); scsi_device_put(sdev); } } @@ -2490,7 +2490,7 @@ static int mvumi_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) mhba->pdev = pdev; mhba->shost = host; - mhba->unique_id = pdev->bus->number << 8 | pdev->devfn; + mhba->unique_id = pci_dev_id(pdev); ret = mvumi_init_fw(mhba); if (ret) diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 73cd25f30ca580b2941622a9b6d6b0b36edbfe4b..90069c7b164256bb98ee745e8a8da8244ae05ef1 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -4053,9 +4053,6 @@ static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len); ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id); ssp_cmd.tag = cpu_to_le32(tag); - if (task->ssp_task.enable_first_burst) - ssp_cmd.ssp_iu.efb_prio_attr |= 0x80; - ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3); ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7); memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); @@ -4095,7 +4092,7 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha, u32 hdr_tag, ncg_tag = 0; u64 phys_addr; u32 ATAP = 0x0; - u32 dir; + u32 dir, retfis = 0; u32 opc = OPC_INB_SATA_HOST_OPSTART; memset(&sata_cmd, 0, sizeof(sata_cmd)); @@ -4124,8 +4121,11 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha, sata_cmd.tag = cpu_to_le32(tag); sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id); sata_cmd.data_len = cpu_to_le32(task->total_xfer_len); - sata_cmd.ncqtag_atap_dir_m = - cpu_to_le32(((ncg_tag & 0xff)<<16)|((ATAP & 0x3f) << 10) | dir); + if (task->ata_task.return_fis_on_success) + retfis = 1; + sata_cmd.retfis_ncqtag_atap_dir_m = + cpu_to_le32((retfis << 24) | ((ncg_tag & 0xff) << 16) | + ((ATAP & 0x3f) << 10) | dir); sata_cmd.sata_fis = task->ata_task.fis; if (likely(!task->ata_task.device_control_reg_update)) sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */ @@ -4180,7 +4180,7 @@ pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) payload.sas_identify.dev_type = SAS_END_DEVICE; payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL; memcpy(payload.sas_identify.sas_addr, - pm8001_ha->sas_addr, SAS_ADDR_SIZE); + &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE); payload.sas_identify.phy_id = phy_id; return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h index 961d0465b923fcc9d40685dd438396f31c77f6b0..fc2127dcb58d905bf79a6dbd1082bce20757e351 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.h +++ b/drivers/scsi/pm8001/pm8001_hwi.h @@ -515,7 +515,7 @@ struct sata_start_req { __le32 tag; __le32 device_id; __le32 data_len; - __le32 ncqtag_atap_dir_m; + __le32 retfis_ncqtag_atap_dir_m; struct host_to_dev_fis sata_fis; u32 reserved1; u32 reserved2; diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 4995e1ef4e0e51da6b0f3d47c88e846b0598a48a..443a3176c6c0c9911658e7d7453f976b7a66a0d9 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -162,10 +162,8 @@ static void pm8001_phy_init(struct pm8001_hba_info *pm8001_ha, int phy_id) phy->minimum_linkrate = SAS_LINK_RATE_1_5_GBPS; phy->maximum_linkrate = SAS_LINK_RATE_6_0_GBPS; sas_phy->enabled = (phy_id < pm8001_ha->chip->n_phy) ? 1 : 0; - sas_phy->class = SAS; sas_phy->iproto = SAS_PROTOCOL_ALL; sas_phy->tproto = 0; - sas_phy->type = PHY_TYPE_PHYSICAL; sas_phy->role = PHY_ROLE_INITIATOR; sas_phy->oob_mode = OOB_NOT_CONNECTED; sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN; @@ -275,7 +273,6 @@ static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id) return ret; } -static u32 pm8001_setup_irq(struct pm8001_hba_info *pm8001_ha); static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha); /** @@ -296,13 +293,6 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha, pm8001_dbg(pm8001_ha, INIT, "pm8001_alloc: PHY:%x\n", pm8001_ha->chip->n_phy); - /* Setup Interrupt */ - rc = pm8001_setup_irq(pm8001_ha); - if (rc) { - pm8001_dbg(pm8001_ha, FAIL, - "pm8001_setup_irq failed [ret: %d]\n", rc); - goto err_out; - } /* Request Interrupt */ rc = pm8001_request_irq(pm8001_ha); if (rc) @@ -654,10 +644,9 @@ static void pm8001_post_sas_ha_init(struct Scsi_Host *shost, sha->sas_ha_name = DRV_NAME; sha->dev = pm8001_ha->dev; sha->strict_wide_ports = 1; - sha->lldd_module = THIS_MODULE; sha->sas_addr = &pm8001_ha->sas_addr[0]; sha->num_phys = chip_info->n_phy; - sha->core.shost = shost; + sha->shost = shost; } /** @@ -1034,47 +1023,38 @@ static u32 pm8001_request_msix(struct pm8001_hba_info *pm8001_ha) } #endif -static u32 pm8001_setup_irq(struct pm8001_hba_info *pm8001_ha) -{ - struct pci_dev *pdev; - - pdev = pm8001_ha->pdev; - -#ifdef PM8001_USE_MSIX - if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) - return pm8001_setup_msix(pm8001_ha); - pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n"); -#endif - return 0; -} - /** * pm8001_request_irq - register interrupt * @pm8001_ha: our ha struct. */ static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) { - struct pci_dev *pdev; + struct pci_dev *pdev = pm8001_ha->pdev; +#ifdef PM8001_USE_MSIX int rc; - pdev = pm8001_ha->pdev; + if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) { + rc = pm8001_setup_msix(pm8001_ha); + if (rc) { + pm8001_dbg(pm8001_ha, FAIL, + "pm8001_setup_irq failed [ret: %d]\n", rc); + return rc; + } -#ifdef PM8001_USE_MSIX - if (pdev->msix_cap && pci_msi_enabled()) - return pm8001_request_msix(pm8001_ha); - else { - pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n"); - goto intx; + if (pdev->msix_cap && pci_msi_enabled()) + return pm8001_request_msix(pm8001_ha); } + + pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n"); #endif -intx: /* initialize the INT-X interrupt */ pm8001_ha->irq_vector[0].irq_id = 0; pm8001_ha->irq_vector[0].drv_inst = pm8001_ha; - rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED, - pm8001_ha->name, SHOST_TO_SAS_HA(pm8001_ha->shost)); - return rc; + + return request_irq(pdev->irq, pm8001_interrupt_handler_intx, + IRQF_SHARED, pm8001_ha->name, + SHOST_TO_SAS_HA(pm8001_ha->shost)); } /** diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 953572fc0d9eb64e61b826b0d51d9e70523577b7..2fadd353f1c13df1890fcd9894a732f24f008bd9 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -702,8 +702,6 @@ int pm8001_mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb); int pm8001_mpi_general_event(struct pm8001_hba_info *pm8001_ha, void *piomb); int pm8001_mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb); -struct sas_task *pm8001_alloc_task(void); -void pm8001_free_task(struct sas_task *task); void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag); struct pm8001_device *pm8001_find_dev(struct pm8001_hba_info *pm8001_ha, u32 device_id); diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 39a12ee94a72ffe68d2ff3784af12ae18da1a401..3afd9443c4254afd5507f26683996978fda80ac0 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -3671,10 +3671,12 @@ static int mpi_set_controller_config_resp(struct pm8001_hba_info *pm8001_ha, (struct set_ctrl_cfg_resp *)(piomb + 4); u32 status = le32_to_cpu(pPayload->status); u32 err_qlfr_pgcd = le32_to_cpu(pPayload->err_qlfr_pgcd); + u32 tag = le32_to_cpu(pPayload->tag); pm8001_dbg(pm8001_ha, MSG, "SET CONTROLLER RESP: status 0x%x qlfr_pgcd 0x%x\n", status, err_qlfr_pgcd); + pm8001_tag_free(pm8001_ha, tag); return 0; } @@ -4316,9 +4318,6 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, ssp_cmd.data_len = cpu_to_le32(task->total_xfer_len); ssp_cmd.device_id = cpu_to_le32(pm8001_dev->device_id); ssp_cmd.tag = cpu_to_le32(tag); - if (task->ssp_task.enable_first_burst) - ssp_cmd.ssp_iu.efb_prio_attr = 0x80; - ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_prio << 3); ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7); memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); @@ -4457,7 +4456,7 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, u64 phys_addr, end_addr; u32 end_addr_high, end_addr_low; u32 ATAP = 0x0; - u32 dir; + u32 dir, retfis = 0; u32 opc = OPC_INB_SATA_HOST_OPSTART; memset(&sata_cmd, 0, sizeof(sata_cmd)); @@ -4487,7 +4486,8 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, sata_cmd.tag = cpu_to_le32(tag); sata_cmd.device_id = cpu_to_le32(pm8001_ha_dev->device_id); sata_cmd.data_len = cpu_to_le32(task->total_xfer_len); - + if (task->ata_task.return_fis_on_success) + retfis = 1; sata_cmd.sata_fis = task->ata_task.fis; if (likely(!task->ata_task.device_control_reg_update)) sata_cmd.sata_fis.flags |= 0x80;/* C=1: update ATA cmd reg */ @@ -4500,12 +4500,10 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, "Encryption enabled.Sending Encrypt SATA cmd 0x%x\n", sata_cmd.sata_fis.command); opc = OPC_INB_SATA_DIF_ENC_IO; - - /* set encryption bit */ - sata_cmd.ncqtag_atap_dir_m_dad = - cpu_to_le32(((ncg_tag & 0xff)<<16)| - ((ATAP & 0x3f) << 10) | 0x20 | dir); - /* dad (bit 0-1) is 0 */ + /* set encryption bit; dad (bits 0-1) is 0 */ + sata_cmd.retfis_ncqtag_atap_dir_m_dad = + cpu_to_le32((retfis << 24) | ((ncg_tag & 0xff) << 16) | + ((ATAP & 0x3f) << 10) | 0x20 | dir); /* fill in PRD (scatter/gather) table, if any */ if (task->num_scatter > 1) { pm8001_chip_make_sg(task->scatter, @@ -4568,11 +4566,10 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, pm8001_dbg(pm8001_ha, IO, "Sending Normal SATA command 0x%x inb %x\n", sata_cmd.sata_fis.command, q_index); - /* dad (bit 0-1) is 0 */ - sata_cmd.ncqtag_atap_dir_m_dad = - cpu_to_le32(((ncg_tag & 0xff)<<16) | - ((ATAP & 0x3f) << 10) | dir); - + /* dad (bits 0-1) is 0 */ + sata_cmd.retfis_ncqtag_atap_dir_m_dad = + cpu_to_le32((retfis << 24) | ((ncg_tag & 0xff) << 16) | + ((ATAP & 0x3f) << 10) | dir); /* fill in PRD (scatter/gather) table, if any */ if (task->num_scatter > 1) { pm8001_chip_make_sg(task->scatter, @@ -4676,7 +4673,7 @@ pm80xx_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) payload.sas_identify.dev_type = SAS_END_DEVICE; payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL; memcpy(payload.sas_identify.sas_addr, - &pm8001_ha->sas_addr, SAS_ADDR_SIZE); + &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE); payload.sas_identify.phy_id = phy_id; return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, diff --git a/drivers/scsi/pm8001/pm80xx_hwi.h b/drivers/scsi/pm8001/pm80xx_hwi.h index acf6e3005b842ed6bc7f66a6b24bab7e6ddebf3c..eb8fd37b20661e3f303a3c69340c9d0adbaae569 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.h +++ b/drivers/scsi/pm8001/pm80xx_hwi.h @@ -731,7 +731,7 @@ struct sata_start_req { __le32 tag; __le32 device_id; __le32 data_len; - __le32 ncqtag_atap_dir_m_dad; + __le32 retfis_ncqtag_atap_dir_m_dad; struct host_to_dev_fis sata_fis; u32 reserved1; u32 reserved2; /* dword 11. rsvd for normal I/O. */ diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 9415a481947048d2874599b58c2e6925d84c5688..50dc30051f221f537332ee3022d4d746b3108ae9 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3584,8 +3584,7 @@ static ssize_t pmcraid_show_adapter_id( struct Scsi_Host *shost = class_to_shost(dev); struct pmcraid_instance *pinstance = (struct pmcraid_instance *)shost->hostdata; - u32 adapter_id = (pinstance->pdev->bus->number << 8) | - pinstance->pdev->devfn; + u32 adapter_id = pci_dev_id(pinstance->pdev); u32 aen_group = pmcraid_event_family.id; return snprintf(buf, PAGE_SIZE, diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 909c49541984a1ea67c7d381f21d9c8540515388..d592ee9170c11f373971028a804f44a6ba6042b7 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -45,6 +45,11 @@ typedef struct { #include "ppa.h" +static unsigned int mode = PPA_AUTODETECT; +module_param(mode, uint, 0644); +MODULE_PARM_DESC(mode, "Transfer mode (0 = Autodetect, 1 = SPP 4-bit, " + "2 = SPP 8-bit, 3 = EPP 8-bit, 4 = EPP 16-bit, 5 = EPP 32-bit"); + static struct scsi_pointer *ppa_scsi_pointer(struct scsi_cmnd *cmd) { return scsi_cmd_priv(cmd); @@ -157,7 +162,7 @@ static int ppa_show_info(struct seq_file *m, struct Scsi_Host *host) return 0; } -static int device_check(ppa_struct *dev); +static int device_check(ppa_struct *dev, bool autodetect); #if PPA_DEBUG > 0 #define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\ @@ -302,13 +307,10 @@ static int ppa_out(ppa_struct *dev, char *buffer, int len) case PPA_EPP_8: epp_reset(ppb); w_ctr(ppb, 0x4); -#ifdef CONFIG_SCSI_IZIP_EPP16 - if (!(((long) buffer | len) & 0x01)) - outsw(ppb + 4, buffer, len >> 1); -#else - if (!(((long) buffer | len) & 0x03)) + if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03)) outsl(ppb + 4, buffer, len >> 2); -#endif + else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01)) + outsw(ppb + 4, buffer, len >> 1); else outsb(ppb + 4, buffer, len); w_ctr(ppb, 0xc); @@ -355,13 +357,10 @@ static int ppa_in(ppa_struct *dev, char *buffer, int len) case PPA_EPP_8: epp_reset(ppb); w_ctr(ppb, 0x24); -#ifdef CONFIG_SCSI_IZIP_EPP16 - if (!(((long) buffer | len) & 0x01)) - insw(ppb + 4, buffer, len >> 1); -#else - if (!(((long) buffer | len) & 0x03)) + if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03)) insl(ppb + 4, buffer, len >> 2); -#endif + else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01)) + insw(ppb + 4, buffer, len >> 1); else insb(ppb + 4, buffer, len); w_ctr(ppb, 0x2c); @@ -469,6 +468,27 @@ static int ppa_init(ppa_struct *dev) { int retv; unsigned short ppb = dev->base; + bool autodetect = dev->mode == PPA_AUTODETECT; + + if (autodetect) { + int modes = dev->dev->port->modes; + int ppb_hi = dev->dev->port->base_hi; + + /* Mode detection works up the chain of speed + * This avoids a nasty if-then-else-if-... tree + */ + dev->mode = PPA_NIBBLE; + + if (modes & PARPORT_MODE_TRISTATE) + dev->mode = PPA_PS2; + + if (modes & PARPORT_MODE_ECP) { + w_ecr(ppb_hi, 0x20); + dev->mode = PPA_PS2; + } + if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP)) + w_ecr(ppb_hi, 0x80); + } ppa_disconnect(dev); ppa_connect(dev, CONNECT_NORMAL); @@ -492,7 +512,7 @@ static int ppa_init(ppa_struct *dev) if (retv) return -EIO; - return device_check(dev); + return device_check(dev, autodetect); } static inline int ppa_send_command(struct scsi_cmnd *cmd) @@ -637,7 +657,7 @@ static void ppa_interrupt(struct work_struct *work) case DID_OK: break; case DID_NO_CONNECT: - printk(KERN_DEBUG "ppa: no device at SCSI ID %i\n", cmd->device->target); + printk(KERN_DEBUG "ppa: no device at SCSI ID %i\n", scmd_id(cmd)); break; case DID_BUS_BUSY: printk(KERN_DEBUG "ppa: BUS BUSY - EPP timeout detected\n"); @@ -883,7 +903,7 @@ static int ppa_reset(struct scsi_cmnd *cmd) return SUCCESS; } -static int device_check(ppa_struct *dev) +static int device_check(ppa_struct *dev, bool autodetect) { /* This routine looks for a device and then attempts to use EPP to send a command. If all goes as planned then EPP is available. */ @@ -895,8 +915,8 @@ static int device_check(ppa_struct *dev) old_mode = dev->mode; for (loop = 0; loop < 8; loop++) { /* Attempt to use EPP for Test Unit Ready */ - if ((ppb & 0x0007) == 0x0000) - dev->mode = PPA_EPP_32; + if (autodetect && (ppb & 0x0007) == 0x0000) + dev->mode = PPA_EPP_8; second_pass: ppa_connect(dev, CONNECT_EPP_MAYBE); @@ -924,7 +944,7 @@ static int device_check(ppa_struct *dev) udelay(1000); ppa_disconnect(dev); udelay(1000); - if (dev->mode == PPA_EPP_32) { + if (dev->mode != old_mode) { dev->mode = old_mode; goto second_pass; } @@ -947,7 +967,7 @@ static int device_check(ppa_struct *dev) udelay(1000); ppa_disconnect(dev); udelay(1000); - if (dev->mode == PPA_EPP_32) { + if (dev->mode != old_mode) { dev->mode = old_mode; goto second_pass; } @@ -1026,7 +1046,6 @@ static int __ppa_attach(struct parport *pb) DEFINE_WAIT(wait); ppa_struct *dev, *temp; int ports; - int modes, ppb, ppb_hi; int err = -ENOMEM; struct pardev_cb ppa_cb; @@ -1034,7 +1053,7 @@ static int __ppa_attach(struct parport *pb) if (!dev) return -ENOMEM; dev->base = -1; - dev->mode = PPA_AUTODETECT; + dev->mode = mode < PPA_UNKNOWN ? mode : PPA_AUTODETECT; dev->recon_tmo = PPA_RECON_TMO; init_waitqueue_head(&waiting); temp = find_parent(); @@ -1069,25 +1088,8 @@ static int __ppa_attach(struct parport *pb) } dev->waiting = NULL; finish_wait(&waiting, &wait); - ppb = dev->base = dev->dev->port->base; - ppb_hi = dev->dev->port->base_hi; - w_ctr(ppb, 0x0c); - modes = dev->dev->port->modes; - - /* Mode detection works up the chain of speed - * This avoids a nasty if-then-else-if-... tree - */ - dev->mode = PPA_NIBBLE; - - if (modes & PARPORT_MODE_TRISTATE) - dev->mode = PPA_PS2; - - if (modes & PARPORT_MODE_ECP) { - w_ecr(ppb_hi, 0x20); - dev->mode = PPA_PS2; - } - if ((modes & PARPORT_MODE_EPP) && (modes & PARPORT_MODE_ECP)) - w_ecr(ppb_hi, 0x80); + dev->base = dev->dev->port->base; + w_ctr(dev->base, 0x0c); /* Done configuration */ diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h index 6a1f8a2d70ebac4804d27f41b0b02128bc1a7947..098bcf7b9eb4d3c7fc249e9fe4ebc14ae3cac0c6 100644 --- a/drivers/scsi/ppa.h +++ b/drivers/scsi/ppa.h @@ -107,11 +107,7 @@ static char *PPA_MODE_STRING[] = "PS/2", "EPP 8 bit", "EPP 16 bit", -#ifdef CONFIG_SCSI_IZIP_EPP16 - "EPP 16 bit", -#else "EPP 32 bit", -#endif "Unknown"}; /* other options */ diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index c5c0bbdafc4ea1e7b67e36cf094782920acb12dd..1619cc33034f219a882bdf6728839c721a8d6a30 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -548,7 +548,6 @@ extern void qedf_get_generic_tlv_data(void *dev, struct qed_generic_tlvs *data); extern void qedf_wq_grcdump(struct work_struct *work); void qedf_stag_change_work(struct work_struct *work); void qedf_ctx_soft_reset(struct fc_lport *lport); -extern void qedf_board_disable_work(struct work_struct *work); extern void qedf_schedule_hw_err_handler(void *dev, enum qed_hw_err_type err_type); diff --git a/drivers/scsi/qedf/qedf_dbg.h b/drivers/scsi/qedf/qedf_dbg.h index f4d81127239ebfd2b8c9d06c09884c9aa4d41cb0..5ec2b817c694a9846c337c88f6e9b59382dbaa1b 100644 --- a/drivers/scsi/qedf/qedf_dbg.h +++ b/drivers/scsi/qedf/qedf_dbg.h @@ -59,6 +59,8 @@ extern uint qedf_debug; #define QEDF_LOG_NOTICE 0x40000000 /* Notice logs */ #define QEDF_LOG_WARN 0x80000000 /* Warning logs */ +#define QEDF_DEBUGFS_LOG_LEN (2 * PAGE_SIZE) + /* Debug context structure */ struct qedf_dbg_ctx { unsigned int host_no; diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c index a3ed681c8ce3f89ca3377643e27a381c3f8ef5aa..451fd236bfd0585f4a7fb86e5c6848f8002b098a 100644 --- a/drivers/scsi/qedf/qedf_debugfs.c +++ b/drivers/scsi/qedf/qedf_debugfs.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "qedf.h" #include "qedf_dbg.h" @@ -98,7 +99,9 @@ static ssize_t qedf_dbg_fp_int_cmd_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { + ssize_t ret; size_t cnt = 0; + char *cbuf; int id; struct qedf_fastpath *fp = NULL; struct qedf_dbg_ctx *qedf_dbg = @@ -108,19 +111,25 @@ qedf_dbg_fp_int_cmd_read(struct file *filp, char __user *buffer, size_t count, QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n"); - cnt = sprintf(buffer, "\nFastpath I/O completions\n\n"); + cbuf = vmalloc(QEDF_DEBUGFS_LOG_LEN); + if (!cbuf) + return 0; + + cnt += scnprintf(cbuf + cnt, QEDF_DEBUGFS_LOG_LEN - cnt, "\nFastpath I/O completions\n\n"); for (id = 0; id < qedf->num_queues; id++) { fp = &(qedf->fp_array[id]); if (fp->sb_id == QEDF_SB_ID_NULL) continue; - cnt += sprintf((buffer + cnt), "#%d: %lu\n", id, - fp->completions); + cnt += scnprintf(cbuf + cnt, QEDF_DEBUGFS_LOG_LEN - cnt, + "#%d: %lu\n", id, fp->completions); } - cnt = min_t(int, count, cnt - *ppos); - *ppos += cnt; - return cnt; + ret = simple_read_from_buffer(buffer, count, ppos, cbuf, cnt); + + vfree(cbuf); + + return ret; } static ssize_t @@ -138,15 +147,14 @@ qedf_dbg_debug_cmd_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { int cnt; + char cbuf[32]; struct qedf_dbg_ctx *qedf_dbg = (struct qedf_dbg_ctx *)filp->private_data; QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "debug mask=0x%x\n", qedf_debug); - cnt = sprintf(buffer, "debug mask = 0x%x\n", qedf_debug); + cnt = scnprintf(cbuf, sizeof(cbuf), "debug mask = 0x%x\n", qedf_debug); - cnt = min_t(int, count, cnt - *ppos); - *ppos += cnt; - return cnt; + return simple_read_from_buffer(buffer, count, ppos, cbuf, cnt); } static ssize_t @@ -185,18 +193,17 @@ qedf_dbg_stop_io_on_error_cmd_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { int cnt; + char cbuf[7]; struct qedf_dbg_ctx *qedf_dbg = (struct qedf_dbg_ctx *)filp->private_data; struct qedf_ctx *qedf = container_of(qedf_dbg, struct qedf_ctx, dbg_ctx); QEDF_INFO(qedf_dbg, QEDF_LOG_DEBUGFS, "entered\n"); - cnt = sprintf(buffer, "%s\n", + cnt = scnprintf(cbuf, sizeof(cbuf), "%s\n", qedf->stop_io_on_error ? "true" : "false"); - cnt = min_t(int, count, cnt - *ppos); - *ppos += cnt; - return cnt; + return simple_read_from_buffer(buffer, count, ppos, cbuf, cnt); } static ssize_t diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 4750ec5789a80db9f07060599d3705ddc6bcc062..10fe3383855c00558e0ffc26268023001376f796 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1904,6 +1904,7 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) goto drop_rdata_kref; } + spin_lock_irqsave(&fcport->rport_lock, flags); if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) || test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) { @@ -1911,17 +1912,20 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) "io_req xid=0x%x sc_cmd=%p already in cleanup or abort processing or already completed.\n", io_req->xid, io_req->sc_cmd); rc = 1; + spin_unlock_irqrestore(&fcport->rport_lock, flags); goto drop_rdata_kref; } + /* Set the command type to abort */ + io_req->cmd_type = QEDF_ABTS; + spin_unlock_irqrestore(&fcport->rport_lock, flags); + kref_get(&io_req->refcount); xid = io_req->xid; qedf->control_requests++; qedf->packet_aborts++; - /* Set the command type to abort */ - io_req->cmd_type = QEDF_ABTS; io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts; set_bit(QEDF_CMD_IN_ABORT, &io_req->flags); @@ -2210,7 +2214,9 @@ int qedf_initiate_cleanup(struct qedf_ioreq *io_req, refcount, fcport, fcport->rdata->ids.port_id); /* Cleanup cmds re-use the same TID as the original I/O */ + spin_lock_irqsave(&fcport->rport_lock, flags); io_req->cmd_type = QEDF_CLEANUP; + spin_unlock_irqrestore(&fcport->rport_lock, flags); io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts; init_completion(&io_req->cleanup_done); diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 7825765c936cd651b276af2507799664b5cedeb1..91f3f1d7098eb5b4b0680fb127ebc1978ec08f53 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -2805,6 +2805,8 @@ void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe) struct qedf_ioreq *io_req; struct qedf_rport *fcport; u32 comp_type; + u8 io_comp_type; + unsigned long flags; comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) & FCOE_CQE_CQE_TYPE_MASK; @@ -2838,11 +2840,14 @@ void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe) return; } + spin_lock_irqsave(&fcport->rport_lock, flags); + io_comp_type = io_req->cmd_type; + spin_unlock_irqrestore(&fcport->rport_lock, flags); switch (comp_type) { case FCOE_GOOD_COMPLETION_CQE_TYPE: atomic_inc(&fcport->free_sqes); - switch (io_req->cmd_type) { + switch (io_comp_type) { case QEDF_SCSI_CMD: qedf_scsi_completion(qedf, cqe, io_req); break; diff --git a/drivers/scsi/qedi/qedi_gbl.h b/drivers/scsi/qedi/qedi_gbl.h index 0e316cc24b190e292a879be69e4cdbb4c2d403e9..772218445a56eef9f666caff556bbee5b83f248a 100644 --- a/drivers/scsi/qedi/qedi_gbl.h +++ b/drivers/scsi/qedi/qedi_gbl.h @@ -67,8 +67,6 @@ void qedi_trace_io(struct qedi_ctx *qedi, struct iscsi_task *task, int qedi_alloc_id(struct qedi_portid_tbl *id_tbl, u16 id); u16 qedi_alloc_new_id(struct qedi_portid_tbl *id_tbl); void qedi_free_id(struct qedi_portid_tbl *id_tbl, u16 id); -int qedi_create_sysfs_ctx_attr(struct qedi_ctx *qedi); -void qedi_remove_sysfs_ctx_attr(struct qedi_ctx *qedi); void qedi_clearsq(struct qedi_ctx *qedi, struct qedi_conn *qedi_conn, struct iscsi_task *task); diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index b00222459607a847c9ea3f113b8a3dd7c677a4c5..44449c70a375f34feff9c97df33044831bb7bbc8 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -3093,8 +3093,6 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) vha->flags.difdix_supported = 1; ql_dbg(ql_dbg_user, vha, 0x7082, "Registered for DIF/DIX type 1 and 3 protection.\n"); - if (ql2xenabledif == 1) - prot = SHOST_DIX_TYPE0_PROTECTION; scsi_host_set_prot(vha->host, prot | SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index d7e8454304ceea98feddf77802104a32ba1e0c1c..691ef827a5ab3eb467bfced7dad33fa08dce0b29 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -12,13 +12,12 @@ * ---------------------------------------------------------------------- * | Module Init and Probe | 0x0199 | | * | Mailbox commands | 0x1206 | 0x11a5-0x11ff | - * | Device Discovery | 0x2134 | 0x210e-0x2115 | - * | | | 0x211c-0x2128 | - * | | | 0x212c-0x2134 | + * | Device Discovery | 0x2134 | 0x2112-0x2115 | + * | | | 0x2127-0x2128 | * | Queue Command and IO tracing | 0x3074 | 0x300b | * | | | 0x3027-0x3028 | * | | | 0x303d-0x3041 | - * | | | 0x302d,0x3033 | + * | | | 0x302e,0x3033 | * | | | 0x3036,0x3038 | * | | | 0x303a | * | DPC Thread | 0x4023 | 0x4002,0x4013 | diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index 70482b55d240ecf720ada456713c32ab22c2c856..54f0a412226f6676327334024d0f5aa34c4d5c2f 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -368,6 +368,7 @@ ql_log_qp(uint32_t, struct qla_qpair *, int32_t, const char *fmt, ...); #define ql_dbg_tgt_tmr 0x00001000 /* Target mode task management */ #define ql_dbg_tgt_dif 0x00000800 /* Target mode dif */ #define ql_dbg_edif 0x00000400 /* edif and purex debug */ +#define ql_dbg_unsol 0x00000100 /* Unsolicited path debug */ extern int qla27xx_dump_mpi_ram(struct qla_hw_data *, uint32_t, uint32_t *, uint32_t, void **); diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 4ae38305c15a115730dfdf4615a21aa2c11f49f9..deb642607deb6f6d3c0eead3e7688e9381af2e12 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -346,6 +346,12 @@ struct name_list_extended { u8 sent; }; +struct qla_nvme_fc_rjt { + struct fcnvme_ls_rjt *c; + dma_addr_t cdma; + u16 size; +}; + struct els_reject { struct fc_els_ls_rjt *c; dma_addr_t cdma; @@ -466,6 +472,7 @@ static inline be_id_t port_id_to_be_id(port_id_t port_id) } struct tmf_arg { + struct list_head tmf_elem; struct qla_qpair *qpair; struct fc_port *fcport; struct scsi_qla_host *vha; @@ -502,6 +509,20 @@ struct ct_arg { port_id_t id; }; +struct qla_nvme_lsrjt_pt_arg { + struct fc_port *fcport; + u8 opcode; + u8 vp_idx; + u8 reason; + u8 explanation; + __le16 nport_handle; + u16 control_flags; + __le16 ox_id; + __le32 xchg_address; + u32 tx_byte_count, rx_byte_count; + dma_addr_t tx_addr, rx_addr; +}; + /* * SRB extensions. */ @@ -610,13 +631,16 @@ struct srb_iocb { void *desc; /* These are only used with ls4 requests */ - int cmd_len; - int rsp_len; + __le32 cmd_len; + __le32 rsp_len; dma_addr_t cmd_dma; dma_addr_t rsp_dma; enum nvmefc_fcp_datadir dir; uint32_t dl; uint32_t timeout_sec; + __le32 exchange_address; + __le16 nport_handle; + __le16 ox_id; struct list_head entry; } nvme; struct { @@ -706,6 +730,10 @@ typedef struct srb { struct fc_port *fcport; struct scsi_qla_host *vha; unsigned int start_timer:1; + unsigned int abort:1; + unsigned int aborted:1; + unsigned int completed:1; + unsigned int unsol_rsp:1; uint32_t handle; uint16_t flags; @@ -2541,7 +2569,7 @@ enum rscn_addr_format { typedef struct fc_port { struct list_head list; struct scsi_qla_host *vha; - struct list_head tmf_pending; + struct list_head unsol_ctx_head; unsigned int conf_compl_supported:1; unsigned int deleted:2; @@ -2562,9 +2590,6 @@ typedef struct fc_port { unsigned int do_prli_nvme:1; uint8_t nvme_flag; - uint8_t active_tmf; -#define MAX_ACTIVE_TMF 8 - uint8_t node_name[WWN_SIZE]; uint8_t port_name[WWN_SIZE]; port_id_t d_id; @@ -3745,6 +3770,16 @@ struct qla_fw_resources { u16 pad; }; +struct qla_fw_res { + u16 iocb_total; + u16 iocb_limit; + atomic_t iocb_used; + + u16 exch_total; + u16 exch_limit; + atomic_t exch_used; +}; + #define QLA_IOCB_PCT_LIMIT 95 struct qla_buf_pool { @@ -3790,6 +3825,12 @@ struct qla_qpair { uint16_t id; /* qp number used with FW */ uint16_t vp_idx; /* vport ID */ + + uint16_t dsd_inuse; + uint16_t dsd_avail; + struct list_head dsd_list; +#define NUM_DSD_CHAIN 4096 + mempool_t *srb_mempool; struct pci_dev *pdev; @@ -4387,7 +4428,6 @@ struct qla_hw_data { uint8_t aen_mbx_count; atomic_t num_pend_mbx_stage1; atomic_t num_pend_mbx_stage2; - atomic_t num_pend_mbx_stage3; uint16_t frame_payload_size; uint32_t login_retry_count; @@ -4656,6 +4696,8 @@ struct qla_hw_data { uint32_t flt_region_aux_img_status_sec; }; uint8_t active_image; + uint8_t active_tmf; +#define MAX_ACTIVE_TMF 8 /* Needed for BEACON */ uint16_t beacon_blink_led; @@ -4670,6 +4712,8 @@ struct qla_hw_data { struct qla_msix_entry *msix_entries; + struct list_head tmf_pending; + struct list_head tmf_active; struct list_head vp_list; /* list of VP */ unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) / sizeof(unsigned long)]; @@ -4713,11 +4757,6 @@ struct qla_hw_data { struct fw_blob *hablob; struct qla82xx_legacy_intr_set nx_legacy_intr; - uint16_t gbl_dsd_inuse; - uint16_t gbl_dsd_avail; - struct list_head gbl_dsd_list; -#define NUM_DSD_CHAIN 4096 - uint8_t fw_type; uint32_t file_prd_off; /* File firmware product offset */ @@ -4799,6 +4838,8 @@ struct qla_hw_data { struct els_reject elsrej; u8 edif_post_stop_cnt_down; struct qla_vp_map *vp_map; + struct qla_nvme_fc_rjt lsrjt; + struct qla_fw_res fwres ____cacheline_aligned; }; #define RX_ELS_SIZE (roundup(sizeof(struct enode) + ELS_MAX_PAYLOAD, SMP_CACHE_BYTES)) @@ -4831,6 +4872,7 @@ struct active_regions { * is variable) starting at "iocb". */ struct purex_item { + void *purls_context; struct list_head list; struct scsi_qla_host *vha; void (*process_item)(struct scsi_qla_host *vha, diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 1925cc6897b6833c2cc4e263dcd99b58e034cb0e..a7a364760b8002bd9b4bb02270d5e83fe4c5b3f0 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -116,7 +116,7 @@ qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp) sprintf(wwn, "pn-%016llx", wwn_to_u64(fp->port_name)); fp->dfs_rport_dir = debugfs_create_dir(wwn, vha->dfs_rport_root); - if (!fp->dfs_rport_dir) + if (IS_ERR(fp->dfs_rport_dir)) return; if (NVME_TARGET(vha->hw, fp)) debugfs_create_file("dev_loss_tmo", 0600, fp->dfs_rport_dir, @@ -276,6 +276,16 @@ qla_dfs_fw_resource_cnt_show(struct seq_file *s, void *unused) seq_printf(s, "estimate exchange used[%d] high water limit [%d] n", exch_used, ha->base_qpair->fwres.exch_limit); + + if (ql2xenforce_iocb_limit == 2) { + iocbs_used = atomic_read(&ha->fwres.iocb_used); + exch_used = atomic_read(&ha->fwres.exch_used); + seq_printf(s, " estimate iocb2 used [%d] high water limit [%d]\n", + iocbs_used, ha->fwres.iocb_limit); + + seq_printf(s, " estimate exchange2 used[%d] high water limit [%d] \n", + exch_used, ha->fwres.exch_limit); + } } return 0; @@ -698,14 +708,14 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha) if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha)) { ha->tgt.dfs_naqp = debugfs_create_file("naqp", 0400, ha->dfs_dir, vha, &dfs_naqp_ops); - if (!ha->tgt.dfs_naqp) { + if (IS_ERR(ha->tgt.dfs_naqp)) { ql_log(ql_log_warn, vha, 0xd011, "Unable to create debugFS naqp node.\n"); goto out; } } vha->dfs_rport_root = debugfs_create_dir("rports", ha->dfs_dir); - if (!vha->dfs_rport_root) { + if (IS_ERR(vha->dfs_rport_root)) { ql_log(ql_log_warn, vha, 0xd012, "Unable to create debugFS rports node.\n"); goto out; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index ba7831f24734f176de8d6575d5d42aa5d9f0478d..09cb9413670a5e4034ea13792d8806fdab6cbc73 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -48,8 +48,6 @@ extern int qla24xx_els_dcmd2_iocb(scsi_qla_host_t *, int, fc_port_t *, bool); extern void qla2x00_els_dcmd2_free(scsi_qla_host_t *vha, struct els_plogi *els_plogi); -extern void qla2x00_update_fcports(scsi_qla_host_t *); - extern int qla2x00_abort_isp(scsi_qla_host_t *); extern void qla2x00_abort_isp_cleanup(scsi_qla_host_t *); extern void qla2x00_quiesce_io(scsi_qla_host_t *); @@ -143,6 +141,7 @@ void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess); void qla_edif_clear_appdata(struct scsi_qla_host *vha, struct fc_port *fcport); const char *sc_to_str(uint16_t cmd); +void qla_adjust_iocb_limit(scsi_qla_host_t *vha); /* * Global Data in qla_os.c source file. @@ -205,8 +204,6 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_post_async_adisc_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); -extern int qla2x00_post_async_adisc_done_work(struct scsi_qla_host *, - fc_port_t *, uint16_t *); extern int qla2x00_set_exlogins_buffer(struct scsi_qla_host *); extern void qla2x00_free_exlogin_buffer(struct qla_hw_data *); extern int qla2x00_set_exchoffld_buffer(struct scsi_qla_host *); @@ -216,7 +213,6 @@ extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); extern struct scsi_qla_host *qla2x00_create_host(const struct scsi_host_template *, struct qla_hw_data *); -extern void qla2x00_free_host(struct scsi_qla_host *); extern void qla2x00_relogin(struct scsi_qla_host *); extern void qla2x00_do_work(struct scsi_qla_host *); extern void qla2x00_free_fcports(struct scsi_qla_host *); @@ -238,13 +234,10 @@ extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha); extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); extern void qla2x00_disable_board_on_pci_error(struct work_struct *); -extern void qla_eeh_work(struct work_struct *); extern void qla2x00_sp_compl(srb_t *sp, int); extern void qla2xxx_qpair_sp_free_dma(srb_t *sp); extern void qla2xxx_qpair_sp_compl(srb_t *sp, int); extern void qla24xx_sched_upd_fcport(fc_port_t *); -void qla2x00_handle_login_done_event(struct scsi_qla_host *, fc_port_t *, - uint16_t *); int qla24xx_post_gnl_work(struct scsi_qla_host *, fc_port_t *); int qla24xx_post_relogin_work(struct scsi_qla_host *vha); void qla2x00_wait_for_sess_deletion(scsi_qla_host_t *); @@ -610,7 +603,11 @@ qla2xxx_msix_rsp_q_hs(int irq, void *dev_id); fc_port_t *qla2x00_find_fcport_by_loopid(scsi_qla_host_t *, uint16_t); fc_port_t *qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *, u8 *, u8); fc_port_t *qla2x00_find_fcport_by_nportid(scsi_qla_host_t *, port_id_t *, u8); -void __qla_consume_iocb(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp); +void qla24xx_queue_purex_item(scsi_qla_host_t *, struct purex_item *, + void (*process_item)(struct scsi_qla_host *, + struct purex_item *)); +void __qla_consume_iocb(struct scsi_qla_host *, void **, struct rsp_que **); +void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp); /* * Global Function Prototypes in qla_sup.c source file. @@ -673,9 +670,11 @@ extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t); extern void qla2xxx_flash_npiv_conf(scsi_qla_host_t *); extern int qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *); extern int qla2x00_mailbox_passthru(struct bsg_job *bsg_job); -int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha, void **pkt, - struct rsp_que **rsp, u8 *buf, u32 buf_len); - +int qla2x00_sys_ld_info(struct bsg_job *bsg_job); +int __qla_copy_purex_to_buffer(struct scsi_qla_host *, void **, + struct rsp_que **, u8 *, u32); +struct purex_item *qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, + void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order); int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in, uint16_t *mbx_out); @@ -728,7 +727,6 @@ int qla24xx_post_gpsc_work(struct scsi_qla_host *, fc_port_t *); int qla24xx_async_gpsc(scsi_qla_host_t *, fc_port_t *); void qla24xx_handle_gpsc_event(scsi_qla_host_t *, struct event_arg *); int qla2x00_mgmt_svr_login(scsi_qla_host_t *); -void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea); int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport, bool); int qla24xx_async_gpnft(scsi_qla_host_t *, u8, srb_t *); void qla24xx_async_gpnft_done(scsi_qla_host_t *, srb_t *); @@ -851,7 +849,6 @@ extern void qla2x00_start_iocbs(struct scsi_qla_host *, struct req_que *); /* Interrupt related */ extern irqreturn_t qla82xx_intr_handler(int, void *); -extern irqreturn_t qla82xx_msi_handler(int, void *); extern irqreturn_t qla82xx_msix_default(int, void *); extern irqreturn_t qla82xx_msix_rsp_q(int, void *); extern void qla82xx_enable_intrs(struct qla_hw_data *); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 367fba27fe699310f0b17f1b51a8e87a36522aac..a314cfc5b263f223e4548c4647e1d645208db69f 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -508,6 +508,7 @@ static void qla24xx_handle_adisc_event(scsi_qla_host_t *vha, struct event_arg *ea) { struct fc_port *fcport = ea->fcport; + unsigned long flags; ql_dbg(ql_dbg_disc, vha, 0x20d2, "%s %8phC DS %d LS %d rc %d login %d|%d rscn %d|%d lid %d\n", @@ -522,9 +523,15 @@ void qla24xx_handle_adisc_event(scsi_qla_host_t *vha, struct event_arg *ea) ql_dbg(ql_dbg_disc, vha, 0x2066, "%s %8phC: adisc fail: post delete\n", __func__, ea->fcport->port_name); + + spin_lock_irqsave(&vha->work_lock, flags); /* deleted = 0 & logout_on_delete = force fw cleanup */ - fcport->deleted = 0; + if (fcport->deleted == QLA_SESS_DELETED) + fcport->deleted = 0; + fcport->logout_on_delete = 1; + spin_unlock_irqrestore(&vha->work_lock, flags); + qlt_schedule_sess_for_deletion(ea->fcport); return; } @@ -1134,7 +1141,7 @@ int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport) u16 *mb; if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT)) - return rval; + goto done; ql_dbg(ql_dbg_disc, vha, 0x20d9, "Async-gnlist WWPN %8phC \n", fcport->port_name); @@ -1188,8 +1195,9 @@ int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport) done_free_sp: /* ref: INIT */ kref_put(&sp->cmd_kref, qla2x00_sp_release); + fcport->flags &= ~(FCF_ASYNC_SENT); done: - fcport->flags &= ~(FCF_ASYNC_ACTIVE | FCF_ASYNC_SENT); + fcport->flags &= ~(FCF_ASYNC_ACTIVE); return rval; } @@ -1446,7 +1454,6 @@ void __qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags); ea->fcport->login_gen++; - ea->fcport->deleted = 0; ea->fcport->logout_on_delete = 1; if (!ea->fcport->login_succ && !IS_SW_RESV_ADDR(ea->fcport->d_id)) { @@ -1996,12 +2003,11 @@ qla2x00_tmf_iocb_timeout(void *data) int rc, h; unsigned long flags; - if (sp->type == SRB_MARKER) { - complete(&tmf->u.tmf.comp); - return; - } + if (sp->type == SRB_MARKER) + rc = QLA_FUNCTION_FAILED; + else + rc = qla24xx_async_abort_cmd(sp, false); - rc = qla24xx_async_abort_cmd(sp, false); if (rc) { spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) { @@ -2032,10 +2038,14 @@ static void qla_marker_sp_done(srb_t *sp, int res) complete(&tmf->u.tmf.comp); } -#define START_SP_W_RETRIES(_sp, _rval) \ +#define START_SP_W_RETRIES(_sp, _rval, _chip_gen, _login_gen) \ {\ int cnt = 5; \ do { \ + if (_chip_gen != sp->vha->hw->chip_reset || _login_gen != sp->fcport->login_gen) {\ + _rval = EINVAL; \ + break; \ + } \ _rval = qla2x00_start_sp(_sp); \ if (_rval == EAGAIN) \ msleep(1); \ @@ -2058,6 +2068,7 @@ qla26xx_marker(struct tmf_arg *arg) srb_t *sp; int rval = QLA_FUNCTION_FAILED; fc_port_t *fcport = arg->fcport; + u32 chip_gen, login_gen; if (TMF_NOT_READY(arg->fcport)) { ql_dbg(ql_dbg_taskm, vha, 0x8039, @@ -2067,6 +2078,9 @@ qla26xx_marker(struct tmf_arg *arg) return QLA_SUSPENDED; } + chip_gen = vha->hw->chip_reset; + login_gen = fcport->login_gen; + /* ref: INIT */ sp = qla2xxx_get_qpair_sp(vha, arg->qpair, fcport, GFP_KERNEL); if (!sp) @@ -2084,7 +2098,7 @@ qla26xx_marker(struct tmf_arg *arg) tm_iocb->u.tmf.loop_id = fcport->loop_id; tm_iocb->u.tmf.vp_index = vha->vp_idx; - START_SP_W_RETRIES(sp, rval); + START_SP_W_RETRIES(sp, rval, chip_gen, login_gen); ql_dbg(ql_dbg_taskm, vha, 0x8006, "Async-marker hdl=%x loop-id=%x portid=%06x modifier=%x lun=%lld qp=%d rval %d.\n", @@ -2123,6 +2137,17 @@ static void qla2x00_tmf_sp_done(srb_t *sp, int res) complete(&tmf->u.tmf.comp); } +static int qla_tmf_wait(struct tmf_arg *arg) +{ + /* there are only 2 types of error handling that reaches here, lun or target reset */ + if (arg->flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET | TCF_CLEAR_TASK_SET)) + return qla2x00_eh_wait_for_pending_commands(arg->vha, + arg->fcport->d_id.b24, arg->lun, WAIT_LUN); + else + return qla2x00_eh_wait_for_pending_commands(arg->vha, + arg->fcport->d_id.b24, arg->lun, WAIT_TARGET); +} + static int __qla2x00_async_tm_cmd(struct tmf_arg *arg) { @@ -2130,8 +2155,9 @@ __qla2x00_async_tm_cmd(struct tmf_arg *arg) struct srb_iocb *tm_iocb; srb_t *sp; int rval = QLA_FUNCTION_FAILED; - fc_port_t *fcport = arg->fcport; + u32 chip_gen, login_gen; + u64 jif; if (TMF_NOT_READY(arg->fcport)) { ql_dbg(ql_dbg_taskm, vha, 0x8032, @@ -2141,6 +2167,9 @@ __qla2x00_async_tm_cmd(struct tmf_arg *arg) return QLA_SUSPENDED; } + chip_gen = vha->hw->chip_reset; + login_gen = fcport->login_gen; + /* ref: INIT */ sp = qla2xxx_get_qpair_sp(vha, arg->qpair, fcport, GFP_KERNEL); if (!sp) @@ -2158,7 +2187,7 @@ __qla2x00_async_tm_cmd(struct tmf_arg *arg) tm_iocb->u.tmf.flags = arg->flags; tm_iocb->u.tmf.lun = arg->lun; - START_SP_W_RETRIES(sp, rval); + START_SP_W_RETRIES(sp, rval, chip_gen, login_gen); ql_dbg(ql_dbg_taskm, vha, 0x802f, "Async-tmf hdl=%x loop-id=%x portid=%06x ctrl=%x lun=%lld qp=%d rval=%x.\n", @@ -2176,8 +2205,26 @@ __qla2x00_async_tm_cmd(struct tmf_arg *arg) "TM IOCB failed (%x).\n", rval); } - if (!test_bit(UNLOADING, &vha->dpc_flags) && !IS_QLAFX00(vha->hw)) - rval = qla26xx_marker(arg); + if (!test_bit(UNLOADING, &vha->dpc_flags) && !IS_QLAFX00(vha->hw)) { + jif = jiffies; + if (qla_tmf_wait(arg)) { + ql_log(ql_log_info, vha, 0x803e, + "Waited %u ms Nexus=%ld:%06x:%llu.\n", + jiffies_to_msecs(jiffies - jif), vha->host_no, + fcport->d_id.b24, arg->lun); + } + + if (chip_gen == vha->hw->chip_reset && login_gen == fcport->login_gen) { + rval = qla26xx_marker(arg); + } else { + ql_log(ql_log_info, vha, 0x803e, + "Skip Marker due to disruption. Nexus=%ld:%06x:%llu.\n", + vha->host_no, fcport->d_id.b24, arg->lun); + rval = QLA_FUNCTION_FAILED; + } + } + if (tm_iocb->u.tmf.data) + rval = tm_iocb->u.tmf.data; done_free_sp: /* ref: INIT */ @@ -2186,30 +2233,42 @@ __qla2x00_async_tm_cmd(struct tmf_arg *arg) return rval; } -static void qla_put_tmf(fc_port_t *fcport) +static void qla_put_tmf(struct tmf_arg *arg) { - struct scsi_qla_host *vha = fcport->vha; + struct scsi_qla_host *vha = arg->vha; struct qla_hw_data *ha = vha->hw; unsigned long flags; spin_lock_irqsave(&ha->tgt.sess_lock, flags); - fcport->active_tmf--; + ha->active_tmf--; + list_del(&arg->tmf_elem); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); } static -int qla_get_tmf(fc_port_t *fcport) +int qla_get_tmf(struct tmf_arg *arg) { - struct scsi_qla_host *vha = fcport->vha; + struct scsi_qla_host *vha = arg->vha; struct qla_hw_data *ha = vha->hw; unsigned long flags; + fc_port_t *fcport = arg->fcport; int rc = 0; - LIST_HEAD(tmf_elem); + struct tmf_arg *t; spin_lock_irqsave(&ha->tgt.sess_lock, flags); - list_add_tail(&tmf_elem, &fcport->tmf_pending); + list_for_each_entry(t, &ha->tmf_active, tmf_elem) { + if (t->fcport == arg->fcport && t->lun == arg->lun) { + /* reject duplicate TMF */ + ql_log(ql_log_warn, vha, 0x802c, + "found duplicate TMF. Nexus=%ld:%06x:%llu.\n", + vha->host_no, fcport->d_id.b24, arg->lun); + spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); + return -EINVAL; + } + } - while (fcport->active_tmf >= MAX_ACTIVE_TMF) { + list_add_tail(&arg->tmf_elem, &ha->tmf_pending); + while (ha->active_tmf >= MAX_ACTIVE_TMF) { spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); msleep(1); @@ -2221,15 +2280,17 @@ int qla_get_tmf(fc_port_t *fcport) rc = EIO; break; } - if (fcport->active_tmf < MAX_ACTIVE_TMF && - list_is_first(&tmf_elem, &fcport->tmf_pending)) + if (ha->active_tmf < MAX_ACTIVE_TMF && + list_is_first(&arg->tmf_elem, &ha->tmf_pending)) break; } - list_del(&tmf_elem); + list_del(&arg->tmf_elem); - if (!rc) - fcport->active_tmf++; + if (!rc) { + ha->active_tmf++; + list_add_tail(&arg->tmf_elem, &ha->tmf_active); + } spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); @@ -2241,9 +2302,8 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint64_t lun, uint32_t tag) { struct scsi_qla_host *vha = fcport->vha; - struct qla_qpair *qpair; struct tmf_arg a; - int i, rval = QLA_SUCCESS; + int rval = QLA_SUCCESS; if (TMF_NOT_READY(fcport)) return QLA_SUSPENDED; @@ -2251,47 +2311,22 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint64_t lun, a.vha = fcport->vha; a.fcport = fcport; a.lun = lun; + a.flags = flags; + INIT_LIST_HEAD(&a.tmf_elem); + if (flags & (TCF_LUN_RESET|TCF_ABORT_TASK_SET|TCF_CLEAR_TASK_SET|TCF_CLEAR_ACA)) { a.modifier = MK_SYNC_ID_LUN; - - if (qla_get_tmf(fcport)) - return QLA_FUNCTION_FAILED; } else { a.modifier = MK_SYNC_ID; } - if (vha->hw->mqenable) { - for (i = 0; i < vha->hw->num_qpairs; i++) { - qpair = vha->hw->queue_pair_map[i]; - if (!qpair) - continue; - - if (TMF_NOT_READY(fcport)) { - ql_log(ql_log_warn, vha, 0x8026, - "Unable to send TM due to disruption.\n"); - rval = QLA_SUSPENDED; - break; - } - - a.qpair = qpair; - a.flags = flags|TCF_NOTMCMD_TO_TARGET; - rval = __qla2x00_async_tm_cmd(&a); - if (rval) - break; - } - } - - if (rval) - goto bailout; + if (qla_get_tmf(&a)) + return QLA_FUNCTION_FAILED; a.qpair = vha->hw->base_qpair; - a.flags = flags; rval = __qla2x00_async_tm_cmd(&a); -bailout: - if (a.modifier == MK_SYNC_ID_LUN) - qla_put_tmf(fcport); - + qla_put_tmf(&a); return rval; } @@ -4147,39 +4182,61 @@ qla24xx_detect_sfp(scsi_qla_host_t *vha) return ha->flags.lr_detected; } -void qla_init_iocb_limit(scsi_qla_host_t *vha) +static void __qla_adjust_iocb_limit(struct qla_qpair *qpair) { - u16 i, num_qps; - u32 limit; - struct qla_hw_data *ha = vha->hw; + u8 num_qps; + u16 limit; + struct qla_hw_data *ha = qpair->vha->hw; num_qps = ha->num_qpairs + 1; limit = (ha->orig_fw_iocb_count * QLA_IOCB_PCT_LIMIT) / 100; - ha->base_qpair->fwres.iocbs_total = ha->orig_fw_iocb_count; - ha->base_qpair->fwres.iocbs_limit = limit; - ha->base_qpair->fwres.iocbs_qp_limit = limit / num_qps; - ha->base_qpair->fwres.iocbs_used = 0; + qpair->fwres.iocbs_total = ha->orig_fw_iocb_count; + qpair->fwres.iocbs_limit = limit; + qpair->fwres.iocbs_qp_limit = limit / num_qps; + + qpair->fwres.exch_total = ha->orig_fw_xcb_count; + qpair->fwres.exch_limit = (ha->orig_fw_xcb_count * + QLA_IOCB_PCT_LIMIT) / 100; +} - ha->base_qpair->fwres.exch_total = ha->orig_fw_xcb_count; - ha->base_qpair->fwres.exch_limit = (ha->orig_fw_xcb_count * - QLA_IOCB_PCT_LIMIT) / 100; +void qla_init_iocb_limit(scsi_qla_host_t *vha) +{ + u8 i; + struct qla_hw_data *ha = vha->hw; + + __qla_adjust_iocb_limit(ha->base_qpair); + ha->base_qpair->fwres.iocbs_used = 0; ha->base_qpair->fwres.exch_used = 0; for (i = 0; i < ha->max_qpairs; i++) { if (ha->queue_pair_map[i]) { - ha->queue_pair_map[i]->fwres.iocbs_total = - ha->orig_fw_iocb_count; - ha->queue_pair_map[i]->fwres.iocbs_limit = limit; - ha->queue_pair_map[i]->fwres.iocbs_qp_limit = - limit / num_qps; + __qla_adjust_iocb_limit(ha->queue_pair_map[i]); ha->queue_pair_map[i]->fwres.iocbs_used = 0; - ha->queue_pair_map[i]->fwres.exch_total = ha->orig_fw_xcb_count; - ha->queue_pair_map[i]->fwres.exch_limit = - (ha->orig_fw_xcb_count * QLA_IOCB_PCT_LIMIT) / 100; ha->queue_pair_map[i]->fwres.exch_used = 0; } } + + ha->fwres.iocb_total = ha->orig_fw_iocb_count; + ha->fwres.iocb_limit = (ha->orig_fw_iocb_count * QLA_IOCB_PCT_LIMIT) / 100; + ha->fwres.exch_total = ha->orig_fw_xcb_count; + ha->fwres.exch_limit = (ha->orig_fw_xcb_count * QLA_IOCB_PCT_LIMIT) / 100; + + atomic_set(&ha->fwres.iocb_used, 0); + atomic_set(&ha->fwres.exch_used, 0); +} + +void qla_adjust_iocb_limit(scsi_qla_host_t *vha) +{ + u8 i; + struct qla_hw_data *ha = vha->hw; + + __qla_adjust_iocb_limit(ha->base_qpair); + + for (i = 0; i < ha->max_qpairs; i++) { + if (ha->queue_pair_map[i]) + __qla_adjust_iocb_limit(ha->queue_pair_map[i]); + } } /** @@ -4777,15 +4834,16 @@ qla2x00_init_rings(scsi_qla_host_t *vha) if (ha->flags.edif_enabled) mid_init_cb->init_cb.frame_payload_size = cpu_to_le16(ELS_MAX_PAYLOAD); + QLA_FW_STARTED(ha); rval = qla2x00_init_firmware(vha, ha->init_cb_size); next_check: if (rval) { + QLA_FW_STOPPED(ha); ql_log(ql_log_fatal, vha, 0x00d2, "Init Firmware **** FAILED ****.\n"); } else { ql_dbg(ql_dbg_init, vha, 0x00d3, "Init Firmware -- success.\n"); - QLA_FW_STARTED(ha); vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0; } @@ -5506,7 +5564,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) INIT_WORK(&fcport->reg_work, qla_register_fcport_fn); INIT_LIST_HEAD(&fcport->gnl_entry); INIT_LIST_HEAD(&fcport->list); - INIT_LIST_HEAD(&fcport->tmf_pending); + INIT_LIST_HEAD(&fcport->unsol_ctx_head); INIT_LIST_HEAD(&fcport->sess_cmd_list); spin_lock_init(&fcport->sess_cmd_lock); @@ -5549,7 +5607,7 @@ static void qla_get_login_template(scsi_qla_host_t *vha) __be32 *q; memset(ha->init_cb, 0, ha->init_cb_size); - sz = min_t(int, sizeof(struct fc_els_csp), ha->init_cb_size); + sz = min_t(int, sizeof(struct fc_els_flogi), ha->init_cb_size); rval = qla24xx_get_port_login_templ(vha, ha->init_cb_dma, ha->init_cb, sz); if (rval != QLA_SUCCESS) { @@ -6090,6 +6148,8 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) void qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) { + unsigned long flags; + if (IS_SW_RESV_ADDR(fcport->d_id)) return; @@ -6099,7 +6159,11 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) qla2x00_set_fcport_disc_state(fcport, DSC_UPD_FCPORT); fcport->login_retry = vha->hw->login_retry_count; fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); + + spin_lock_irqsave(&vha->work_lock, flags); fcport->deleted = 0; + spin_unlock_irqrestore(&vha->work_lock, flags); + if (vha->hw->current_topology == ISP_CFG_NL) fcport->logout_on_delete = 0; else @@ -7337,14 +7401,15 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) } /* purge MBox commands */ - if (atomic_read(&ha->num_pend_mbx_stage3)) { + spin_lock_irqsave(&ha->hardware_lock, flags); + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags)) { clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); complete(&ha->mbx_intr_comp); } + spin_unlock_irqrestore(&ha->hardware_lock, flags); i = 0; - while (atomic_read(&ha->num_pend_mbx_stage3) || - atomic_read(&ha->num_pend_mbx_stage2) || + while (atomic_read(&ha->num_pend_mbx_stage2) || atomic_read(&ha->num_pend_mbx_stage1)) { msleep(20); i++; @@ -9590,6 +9655,7 @@ struct qla_qpair *qla2xxx_create_qpair(struct scsi_qla_host *vha, int qos, qpair->vp_idx = vp_idx; qpair->fw_started = ha->flags.fw_started; INIT_LIST_HEAD(&qpair->hints_list); + INIT_LIST_HEAD(&qpair->dsd_list); qpair->chip_reset = ha->base_qpair->chip_reset; qpair->enable_class_2 = ha->base_qpair->enable_class_2; qpair->enable_explicit_conf = @@ -9718,6 +9784,19 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair) if (ret != QLA_SUCCESS) goto fail; + if (!list_empty(&qpair->dsd_list)) { + struct dsd_dma *dsd_ptr, *tdsd_ptr; + + /* clean up allocated prev pool */ + list_for_each_entry_safe(dsd_ptr, tdsd_ptr, + &qpair->dsd_list, list) { + dma_pool_free(ha->dl_dma_pool, dsd_ptr->dsd_addr, + dsd_ptr->dsd_list_dma); + list_del(&dsd_ptr->list); + kfree(dsd_ptr); + } + } + mutex_lock(&ha->mq_lock); ha->queue_pair_map[qpair->id] = NULL; clear_bit(qpair->id, ha->qpair_qid_map); diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 0167e85ba05876d4173514271905c21ff68c92f8..a4a56ab0ba7473f9c6258ed56e8d8bf03ca9aa7a 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -386,6 +386,7 @@ enum { RESOURCE_IOCB = BIT_0, RESOURCE_EXCH = BIT_1, /* exchange */ RESOURCE_FORCE = BIT_2, + RESOURCE_HA = BIT_3, }; static inline int @@ -393,7 +394,7 @@ qla_get_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores) { u16 iocbs_used, i; u16 exch_used; - struct qla_hw_data *ha = qp->vha->hw; + struct qla_hw_data *ha = qp->hw; if (!ql2xenforce_iocb_limit) { iores->res_type = RESOURCE_NONE; @@ -428,15 +429,69 @@ qla_get_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores) return -ENOSPC; } } + + if (ql2xenforce_iocb_limit == 2) { + if ((iores->iocb_cnt + atomic_read(&ha->fwres.iocb_used)) >= + ha->fwres.iocb_limit) { + iores->res_type = RESOURCE_NONE; + return -ENOSPC; + } + + if (iores->res_type & RESOURCE_EXCH) { + if ((iores->exch_cnt + atomic_read(&ha->fwres.exch_used)) >= + ha->fwres.exch_limit) { + iores->res_type = RESOURCE_NONE; + return -ENOSPC; + } + } + } + force: qp->fwres.iocbs_used += iores->iocb_cnt; qp->fwres.exch_used += iores->exch_cnt; + if (ql2xenforce_iocb_limit == 2) { + atomic_add(iores->iocb_cnt, &ha->fwres.iocb_used); + atomic_add(iores->exch_cnt, &ha->fwres.exch_used); + iores->res_type |= RESOURCE_HA; + } return 0; } +/* + * decrement to zero. This routine will not decrement below zero + * @v: pointer of type atomic_t + * @amount: amount to decrement from v + */ +static void qla_atomic_dtz(atomic_t *v, int amount) +{ + int c, old, dec; + + c = atomic_read(v); + for (;;) { + dec = c - amount; + if (unlikely(dec < 0)) + dec = 0; + + old = atomic_cmpxchg((v), c, dec); + if (likely(old == c)) + break; + c = old; + } +} + static inline void qla_put_fw_resources(struct qla_qpair *qp, struct iocb_resource *iores) { + struct qla_hw_data *ha = qp->hw; + + if (iores->res_type & RESOURCE_HA) { + if (iores->res_type & RESOURCE_IOCB) + qla_atomic_dtz(&ha->fwres.iocb_used, iores->iocb_cnt); + + if (iores->res_type & RESOURCE_EXCH) + qla_atomic_dtz(&ha->fwres.exch_used, iores->exch_cnt); + } + if (iores->res_type & RESOURCE_IOCB) { if (qp->fwres.iocbs_used >= iores->iocb_cnt) { qp->fwres.iocbs_used -= iores->iocb_cnt; @@ -522,7 +577,7 @@ fcport_is_bigger(fc_port_t *fcport) static inline struct qla_qpair * qla_mapq_nvme_select_qpair(struct qla_hw_data *ha, struct qla_qpair *qpair) { - int cpuid = smp_processor_id(); + int cpuid = raw_smp_processor_id(); if (qpair->cpuid != cpuid && ha->qp_cpu_map[cpuid]) { diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 730d8609276c6dd6657685a78d9f20c427bec8a3..df90169f82440a3f665164e55153d9b560231b4b 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -11,6 +11,7 @@ #include +static int qla_start_scsi_type6(srb_t *sp); /** * qla2x00_get_cmd_direction() - Determine control_flag data direction. * @sp: SCSI command @@ -590,8 +591,6 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, uint16_t tot_dsds) { struct dsd64 *cur_dsd = NULL, *next_dsd; - scsi_qla_host_t *vha; - struct qla_hw_data *ha; struct scsi_cmnd *cmd; struct scatterlist *cur_seg; uint8_t avail_dsds; @@ -613,9 +612,6 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, return 0; } - vha = sp->vha; - ha = vha->hw; - /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA); @@ -636,14 +632,13 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, tot_dsds -= avail_dsds; dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE; - dsd_ptr = list_first_entry(&ha->gbl_dsd_list, - struct dsd_dma, list); + dsd_ptr = list_first_entry(&qpair->dsd_list, struct dsd_dma, list); next_dsd = dsd_ptr->dsd_addr; list_del(&dsd_ptr->list); - ha->gbl_dsd_avail--; + qpair->dsd_avail--; list_add_tail(&dsd_ptr->list, &ctx->dsd_list); ctx->dsd_use_cnt++; - ha->gbl_dsd_inuse++; + qpair->dsd_inuse++; if (first_iocb) { first_iocb = 0; @@ -1722,6 +1717,8 @@ qla24xx_dif_start_scsi(srb_t *sp) if (scsi_get_prot_op(cmd) == SCSI_PROT_NORMAL) { if (cmd->cmd_len <= 16) return qla24xx_start_scsi(sp); + else + return qla_start_scsi_type6(sp); } /* Setup device pointers. */ @@ -2101,6 +2098,8 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp) if (scsi_get_prot_op(cmd) == SCSI_PROT_NORMAL) { if (cmd->cmd_len <= 16) return qla2xxx_start_scsi_mq(sp); + else + return qla_start_scsi_type6(sp); } spin_lock_irqsave(&qpair->qp_lock, flags); @@ -3368,6 +3367,7 @@ qla82xx_start_scsi(srb_t *sp) struct qla_hw_data *ha = vha->hw; struct req_que *req = NULL; struct rsp_que *rsp = NULL; + struct qla_qpair *qpair = sp->qpair; /* Setup device pointers. */ reg = &ha->iobase->isp82; @@ -3416,18 +3416,18 @@ qla82xx_start_scsi(srb_t *sp) uint16_t i; more_dsd_lists = qla24xx_calc_dsd_lists(tot_dsds); - if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN) { + if ((more_dsd_lists + qpair->dsd_inuse) >= NUM_DSD_CHAIN) { ql_dbg(ql_dbg_io, vha, 0x300d, "Num of DSD list %d is than %d for cmd=%p.\n", - more_dsd_lists + ha->gbl_dsd_inuse, NUM_DSD_CHAIN, + more_dsd_lists + qpair->dsd_inuse, NUM_DSD_CHAIN, cmd); goto queuing_error; } - if (more_dsd_lists <= ha->gbl_dsd_avail) + if (more_dsd_lists <= qpair->dsd_avail) goto sufficient_dsds; else - more_dsd_lists -= ha->gbl_dsd_avail; + more_dsd_lists -= qpair->dsd_avail; for (i = 0; i < more_dsd_lists; i++) { dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); @@ -3447,8 +3447,8 @@ qla82xx_start_scsi(srb_t *sp) "for cmd=%p.\n", cmd); goto queuing_error; } - list_add_tail(&dsd_ptr->list, &ha->gbl_dsd_list); - ha->gbl_dsd_avail++; + list_add_tail(&dsd_ptr->list, &qpair->dsd_list); + qpair->dsd_avail++; } sufficient_dsds: @@ -3767,21 +3767,28 @@ qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt) nvme = &sp->u.iocb_cmd; cmd_pkt->entry_type = PT_LS4_REQUEST; cmd_pkt->entry_count = 1; - cmd_pkt->control_flags = cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT); - cmd_pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec); - cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + if (sp->unsol_rsp) { + cmd_pkt->control_flags = + cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT); + cmd_pkt->nport_handle = nvme->u.nvme.nport_handle; + cmd_pkt->exchange_address = nvme->u.nvme.exchange_address; + } else { + cmd_pkt->control_flags = + cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT); + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->rx_dseg_count = cpu_to_le16(1); + cmd_pkt->rx_byte_count = nvme->u.nvme.rsp_len; + cmd_pkt->dsd[1].length = nvme->u.nvme.rsp_len; + put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address); + } + cmd_pkt->tx_dseg_count = cpu_to_le16(1); - cmd_pkt->tx_byte_count = cpu_to_le32(nvme->u.nvme.cmd_len); - cmd_pkt->dsd[0].length = cpu_to_le32(nvme->u.nvme.cmd_len); + cmd_pkt->tx_byte_count = nvme->u.nvme.cmd_len; + cmd_pkt->dsd[0].length = nvme->u.nvme.cmd_len; put_unaligned_le64(nvme->u.nvme.cmd_dma, &cmd_pkt->dsd[0].address); - - cmd_pkt->rx_dseg_count = cpu_to_le16(1); - cmd_pkt->rx_byte_count = cpu_to_le32(nvme->u.nvme.rsp_len); - cmd_pkt->dsd[1].length = cpu_to_le32(nvme->u.nvme.rsp_len); - put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address); } static void @@ -3882,6 +3889,7 @@ qla_marker_iocb(srb_t *sp, struct mrk_entry_24xx *mrk) { mrk->entry_type = MARKER_TYPE; mrk->modifier = sp->u.iocb_cmd.u.tmf.modifier; + mrk->handle = make_handle(sp->qpair->req->id, sp->handle); if (sp->u.iocb_cmd.u.tmf.modifier != MK_SYNC_ALL) { mrk->nport_handle = cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id); int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun, (struct scsi_lun *)&mrk->lun); @@ -4197,3 +4205,267 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds) return rval; } + +/** + * qla_start_scsi_type6() - Send a SCSI command to the ISP + * @sp: command to send to the ISP + * + * Returns non-zero if a failure occurred, else zero. + */ +static int +qla_start_scsi_type6(srb_t *sp) +{ + int nseg; + unsigned long flags; + uint32_t *clr_ptr; + uint32_t handle; + struct cmd_type_6 *cmd_pkt; + uint16_t cnt; + uint16_t req_cnt; + uint16_t tot_dsds; + struct req_que *req = NULL; + struct rsp_que *rsp; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct scsi_qla_host *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_qpair *qpair = sp->qpair; + uint16_t more_dsd_lists = 0; + struct dsd_dma *dsd_ptr; + uint16_t i; + __be32 *fcp_dl; + uint8_t additional_cdb_len; + struct ct6_dsd *ctx; + + /* Acquire qpair specific lock */ + spin_lock_irqsave(&qpair->qp_lock, flags); + + /* Setup qpair pointers */ + req = qpair->req; + rsp = qpair->rsp; + + /* So we know we haven't pci_map'ed anything yet */ + tot_dsds = 0; + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (__qla2x00_marker(vha, qpair, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) { + spin_unlock_irqrestore(&qpair->qp_lock, flags); + return QLA_FUNCTION_FAILED; + } + vha->marker_needed = 0; + } + + handle = qla2xxx_get_next_handle(req); + if (handle == 0) + goto queuing_error; + + /* Map the sg table so we have an accurate count of sg entries needed */ + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + } else { + nseg = 0; + } + + tot_dsds = nseg; + + /* eventhough driver only need 1 T6 IOCB, FW still convert DSD to Continueation IOCB */ + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + + sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; + sp->iores.exch_cnt = 1; + sp->iores.iocb_cnt = req_cnt; + + if (qla_get_fw_resources(sp->qpair, &sp->iores)) + goto queuing_error; + + more_dsd_lists = qla24xx_calc_dsd_lists(tot_dsds); + if ((more_dsd_lists + qpair->dsd_inuse) >= NUM_DSD_CHAIN) { + ql_dbg(ql_dbg_io, vha, 0x3028, + "Num of DSD list %d is than %d for cmd=%p.\n", + more_dsd_lists + qpair->dsd_inuse, NUM_DSD_CHAIN, cmd); + goto queuing_error; + } + + if (more_dsd_lists <= qpair->dsd_avail) + goto sufficient_dsds; + else + more_dsd_lists -= qpair->dsd_avail; + + for (i = 0; i < more_dsd_lists; i++) { + dsd_ptr = kzalloc(sizeof(*dsd_ptr), GFP_ATOMIC); + if (!dsd_ptr) { + ql_log(ql_log_fatal, vha, 0x3029, + "Failed to allocate memory for dsd_dma for cmd=%p.\n", cmd); + goto queuing_error; + } + INIT_LIST_HEAD(&dsd_ptr->list); + + dsd_ptr->dsd_addr = dma_pool_alloc(ha->dl_dma_pool, + GFP_ATOMIC, &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + kfree(dsd_ptr); + ql_log(ql_log_fatal, vha, 0x302a, + "Failed to allocate memory for dsd_addr for cmd=%p.\n", cmd); + goto queuing_error; + } + list_add_tail(&dsd_ptr->list, &qpair->dsd_list); + qpair->dsd_avail++; + } + +sufficient_dsds: + req_cnt = 1; + + if (req->cnt < (req_cnt + 2)) { + if (IS_SHADOW_REG_CAPABLE(ha)) { + cnt = *req->out_ptr; + } else { + cnt = (uint16_t)rd_reg_dword_relaxed(req->req_q_out); + if (qla2x00_check_reg16_for_disconnect(vha, cnt)) + goto queuing_error; + } + + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - (req->ring_index - cnt); + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + } + + ctx = &sp->u.scmd.ct6_ctx; + + memset(ctx, 0, sizeof(struct ct6_dsd)); + ctx->fcp_cmnd = dma_pool_zalloc(ha->fcp_cmnd_dma_pool, + GFP_ATOMIC, &ctx->fcp_cmnd_dma); + if (!ctx->fcp_cmnd) { + ql_log(ql_log_fatal, vha, 0x3031, + "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd); + goto queuing_error; + } + + /* Initialize the DSD list and dma handle */ + INIT_LIST_HEAD(&ctx->dsd_list); + ctx->dsd_use_cnt = 0; + + if (cmd->cmd_len > 16) { + additional_cdb_len = cmd->cmd_len - 16; + if (cmd->cmd_len % 4 || + cmd->cmd_len > QLA_CDB_BUF_SIZE) { + /* + * SCSI command bigger than 16 bytes must be + * multiple of 4 or too big. + */ + ql_log(ql_log_warn, vha, 0x3033, + "scsi cmd len %d not multiple of 4 for cmd=%p.\n", + cmd->cmd_len, cmd); + goto queuing_error_fcp_cmnd; + } + ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4; + } else { + additional_cdb_len = 0; + ctx->fcp_cmnd_len = 12 + 16 + 4; + } + + /* Build command packet. */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; + + cmd_pkt = (struct cmd_type_6 *)req->ring_ptr; + cmd_pkt->handle = make_handle(req->id, handle); + + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number */ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->vha->vp_idx; + + /* Build IOCB segments */ + qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds); + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + + /* build FCP_CMND IU */ + int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun); + ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len; + + if (cmd->sc_data_direction == DMA_TO_DEVICE) + ctx->fcp_cmnd->additional_cdb_len |= 1; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + ctx->fcp_cmnd->additional_cdb_len |= 2; + + /* Populate the FCP_PRIO. */ + if (ha->flags.fcp_prio_enabled) + ctx->fcp_cmnd->task_attribute |= + sp->fcport->fcp_prio << 3; + + memcpy(ctx->fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); + + fcp_dl = (__be32 *)(ctx->fcp_cmnd->cdb + 16 + + additional_cdb_len); + *fcp_dl = htonl((uint32_t)scsi_bufflen(cmd)); + + cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(ctx->fcp_cmnd_len); + put_unaligned_le64(ctx->fcp_cmnd_dma, + &cmd_pkt->fcp_cmnd_dseg_address); + + sp->flags |= SRB_FCP_CMND_DMA_VALID; + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + + wmb(); + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else { + req->ring_ptr++; + } + + sp->qpair->cmd_cnt++; + sp->flags |= SRB_DMA_VALID; + + /* Set chip new ring index. */ + wrt_reg_dword(req->req_q_in, req->ring_index); + + /* Manage unprocessed RIO/ZIO commands in response queue. */ + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&qpair->qp_lock, flags); + + return QLA_SUCCESS; + +queuing_error_fcp_cmnd: + dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, ctx->fcp_cmnd_dma); + +queuing_error: + if (tot_dsds) + scsi_dma_unmap(cmd); + + qla_put_fw_resources(sp->qpair, &sp->iores); + + if (sp->u.scmd.crc_ctx) { + mempool_free(sp->u.scmd.crc_ctx, ha->ctx_mempool); + sp->u.scmd.crc_ctx = NULL; + } + + spin_unlock_irqrestore(&qpair->qp_lock, flags); + + return QLA_FUNCTION_FAILED; +} diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 656700f79325981bb4e3b94c55cc73ad39f654f2..d48007e182881b1fcc3aada1327fe77e600d3e2b 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -56,6 +56,22 @@ const char *const port_state_str[] = { [FCS_ONLINE] = "ONLINE" }; +#define SFP_DISABLE_LASER_INITIATED 0x15 /* Sub code of 8070 AEN */ +#define SFP_ENABLE_LASER_INITIATED 0x16 /* Sub code of 8070 AEN */ + +static inline void display_Laser_info(scsi_qla_host_t *vha, + u16 mb1, u16 mb2, u16 mb3) { + + if (mb1 == SFP_DISABLE_LASER_INITIATED) + ql_log(ql_log_warn, vha, 0xf0a2, + "SFP temperature (%d C) reached/exceeded the threshold (%d C). Laser is disabled.\n", + mb3, mb2); + if (mb1 == SFP_ENABLE_LASER_INITIATED) + ql_log(ql_log_warn, vha, 0xf0a3, + "SFP temperature (%d C) reached normal operating level. Laser is enabled.\n", + mb3); +} + static void qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt) { @@ -823,6 +839,135 @@ qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb) } } +/** + * qla27xx_copy_multiple_pkt() - Copy over purex/purls packets that can + * span over multiple IOCBs. + * @vha: SCSI driver HA context + * @pkt: ELS packet + * @rsp: Response queue + * @is_purls: True, for Unsolicited Received FC-NVMe LS rsp IOCB + * false, for Unsolicited Received ELS IOCB + * @byte_order: True, to change the byte ordering of iocb payload + */ +struct purex_item * +qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, + struct rsp_que **rsp, bool is_purls, + bool byte_order) +{ + struct purex_entry_24xx *purex = NULL; + struct pt_ls4_rx_unsol *purls = NULL; + struct rsp_que *rsp_q = *rsp; + sts_cont_entry_t *new_pkt; + uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0; + uint16_t buffer_copy_offset = 0, payload_size = 0; + uint16_t entry_count, entry_count_remaining; + struct purex_item *item; + void *iocb_pkt = NULL; + + if (is_purls) { + purls = *pkt; + total_bytes = (le16_to_cpu(purls->frame_size) & 0x0FFF) - + PURX_ELS_HEADER_SIZE; + entry_count = entry_count_remaining = purls->entry_count; + payload_size = sizeof(purls->payload); + } else { + purex = *pkt; + total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) - + PURX_ELS_HEADER_SIZE; + entry_count = entry_count_remaining = purex->entry_count; + payload_size = sizeof(purex->els_frame_payload); + } + + pending_bytes = total_bytes; + no_bytes = (pending_bytes > payload_size) ? payload_size : + pending_bytes; + ql_dbg(ql_dbg_async, vha, 0x509a, + "%s LS, frame_size 0x%x, entry count %d\n", + (is_purls ? "PURLS" : "FPIN"), total_bytes, entry_count); + + item = qla24xx_alloc_purex_item(vha, total_bytes); + if (!item) + return item; + + iocb_pkt = &item->iocb; + + if (is_purls) + memcpy(iocb_pkt, &purls->payload[0], no_bytes); + else + memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes); + buffer_copy_offset += no_bytes; + pending_bytes -= no_bytes; + --entry_count_remaining; + + if (is_purls) + ((response_t *)purls)->signature = RESPONSE_PROCESSED; + else + ((response_t *)purex)->signature = RESPONSE_PROCESSED; + wmb(); + + do { + while ((total_bytes > 0) && (entry_count_remaining > 0)) { + if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) { + ql_dbg(ql_dbg_async, vha, 0x5084, + "Ran out of IOCBs, partial data 0x%x\n", + buffer_copy_offset); + cpu_relax(); + continue; + } + + new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr; + *pkt = new_pkt; + + if (new_pkt->entry_type != STATUS_CONT_TYPE) { + ql_log(ql_log_warn, vha, 0x507a, + "Unexpected IOCB type, partial data 0x%x\n", + buffer_copy_offset); + break; + } + + rsp_q->ring_index++; + if (rsp_q->ring_index == rsp_q->length) { + rsp_q->ring_index = 0; + rsp_q->ring_ptr = rsp_q->ring; + } else { + rsp_q->ring_ptr++; + } + no_bytes = (pending_bytes > sizeof(new_pkt->data)) ? + sizeof(new_pkt->data) : pending_bytes; + if ((buffer_copy_offset + no_bytes) <= total_bytes) { + memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset), + new_pkt->data, no_bytes); + buffer_copy_offset += no_bytes; + pending_bytes -= no_bytes; + --entry_count_remaining; + } else { + ql_log(ql_log_warn, vha, 0x5044, + "Attempt to copy more that we got, optimizing..%x\n", + buffer_copy_offset); + memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset), + new_pkt->data, + total_bytes - buffer_copy_offset); + } + + ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED; + wmb(); + } + + if (pending_bytes != 0 || entry_count_remaining != 0) { + ql_log(ql_log_fatal, vha, 0x508b, + "Dropping partial FPIN, underrun bytes = 0x%x, entry cnts 0x%x\n", + total_bytes, entry_count_remaining); + qla24xx_free_purex_item(item); + return NULL; + } + } while (entry_count_remaining > 0); + + if (byte_order) + host_to_fcp_swap((uint8_t *)&item->iocb, total_bytes); + + return item; +} + int qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry) { @@ -958,7 +1103,7 @@ qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size) return item; } -static void +void qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt, void (*process_item)(struct scsi_qla_host *vha, struct purex_item *pkt)) @@ -1121,8 +1266,12 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) unsigned long flags; fc_port_t *fcport = NULL; - if (!vha->hw->flags.fw_started) + if (!vha->hw->flags.fw_started) { + ql_log(ql_log_warn, vha, 0x50ff, + "Dropping AEN - %04x %04x %04x %04x.\n", + mb[0], mb[1], mb[2], mb[3]); return; + } /* Setup to process RIO completion. */ handle_cnt = 0; @@ -1794,6 +1943,8 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) break; case MBA_TEMPERATURE_ALERT: + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) + display_Laser_info(vha, mb[1], mb[2], mb[3]); ql_dbg(ql_dbg_async, vha, 0x505e, "TEMPERATURE ALERT: %04x %04x %04x\n", mb[1], mb[2], mb[3]); break; @@ -2539,7 +2690,6 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) case CS_PORT_BUSY: case CS_INCOMPLETE: case CS_PORT_UNAVAILABLE: - case CS_TIMEOUT: case CS_RESET: if (atomic_read(&fcport->state) == FCS_ONLINE) { ql_dbg(ql_dbg_disc, fcport->vha, 0x3021, @@ -3808,13 +3958,14 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, struct qla_hw_data *ha = vha->hw; struct purex_entry_24xx *purex_entry; struct purex_item *pure_item; + struct pt_ls4_rx_unsol *p; u16 rsp_in = 0, cur_ring_index; int is_shadow_hba; if (!ha->flags.fw_started) return; - if (rsp->qpair->cpuid != smp_processor_id() || !rsp->qpair->rcv_intr) { + if (rsp->qpair->cpuid != raw_smp_processor_id() || !rsp->qpair->rcv_intr) { rsp->qpair->rcv_intr = 1; if (!rsp->qpair->cpu_mapped) @@ -3980,7 +4131,19 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, qla28xx_sa_update_iocb_entry(vha, rsp->req, (struct sa_update_28xx *)pkt); break; - + case PT_LS4_UNSOL: + p = (void *)pkt; + if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) { + rsp->ring_ptr = (response_t *)pkt; + rsp->ring_index = cur_ring_index; + + ql_dbg(ql_dbg_init, vha, 0x2124, + "Defer processing UNSOL LS req opcode %#x...\n", + p->payload[0]); + return; + } + qla2xxx_process_purls_iocb((void **)&pkt, &rsp); + break; default: /* Type Not Supported. */ ql_dbg(ql_dbg_async, vha, 0x5042, @@ -4305,7 +4468,7 @@ qla2xxx_msix_rsp_q(int irq, void *dev_id) } ha = qpair->hw; - queue_work_on(smp_processor_id(), ha->wq, &qpair->q_work); + queue_work(ha->wq, &qpair->q_work); return IRQ_HANDLED; } @@ -4331,7 +4494,7 @@ qla2xxx_msix_rsp_q_hs(int irq, void *dev_id) wrt_reg_dword(®->hccr, HCCRX_CLR_RISC_INT); spin_unlock_irqrestore(&ha->hardware_lock, flags); - queue_work_on(smp_processor_id(), ha->wq, &qpair->q_work); + queue_work(ha->wq, &qpair->q_work); return IRQ_HANDLED; } diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 254fd4c64262890b177dab830c08f1e474e227ea..21ec32b4fb2809321ee6498255e63662cc094fb0 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -273,7 +273,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) spin_unlock_irqrestore(&ha->hardware_lock, flags); wait_time = jiffies; - atomic_inc(&ha->num_pend_mbx_stage3); if (!wait_for_completion_timeout(&ha->mbx_intr_comp, mcp->tov * HZ)) { ql_dbg(ql_dbg_mbx, vha, 0x117a, @@ -290,7 +289,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) spin_unlock_irqrestore(&ha->hardware_lock, flags); atomic_dec(&ha->num_pend_mbx_stage2); - atomic_dec(&ha->num_pend_mbx_stage3); rval = QLA_ABORTED; goto premature_exit; } @@ -302,11 +300,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) ha->flags.mbox_busy = 0; spin_unlock_irqrestore(&ha->hardware_lock, flags); atomic_dec(&ha->num_pend_mbx_stage2); - atomic_dec(&ha->num_pend_mbx_stage3); rval = QLA_ABORTED; goto premature_exit; } - atomic_dec(&ha->num_pend_mbx_stage3); if (time_after(jiffies, wait_time + 5 * HZ)) ql_log(ql_log_warn, vha, 0x1015, "cmd=0x%x, waited %d msecs\n", @@ -2213,6 +2209,9 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1054, "Entered %s.\n", __func__); + if (!ha->flags.fw_started) + return QLA_FUNCTION_FAILED; + mcp->mb[0] = MBC_GET_FIRMWARE_STATE; mcp->out_mb = MBX_0; if (IS_FWI2_CAPABLE(vha->hw)) diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 86e85f2f4782f6c1f52febe57645e448c7e111b6..a8ddf356e6626038fcaf17df560259bad772fec3 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -12,6 +12,26 @@ #include static struct nvme_fc_port_template qla_nvme_fc_transport; +static int qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, + struct qla_qpair *qp, + struct qla_nvme_lsrjt_pt_arg *a, + bool is_xchg_terminate); + +struct qla_nvme_unsol_ctx { + struct list_head elem; + struct scsi_qla_host *vha; + struct fc_port *fcport; + struct srb *sp; + struct nvmefc_ls_rsp lsrsp; + struct nvmefc_ls_rsp *fd_rsp; + struct work_struct lsrsp_work; + struct work_struct abort_work; + __le32 exchange_address; + __le16 nport_handle; + __le16 ox_id; + int comp_status; + spinlock_t cmd_lock; +}; int qla_nvme_register_remote(struct scsi_qla_host *vha, struct fc_port *fcport) { @@ -132,6 +152,7 @@ static int qla_nvme_alloc_queue(struct nvme_fc_local_port *lport, "Failed to allocate qpair\n"); return -EINVAL; } + qla_adjust_iocb_limit(vha); } *handle = qpair; @@ -215,6 +236,55 @@ static void qla_nvme_sp_ls_done(srb_t *sp, int res) schedule_work(&priv->ls_work); } +static void qla_nvme_release_lsrsp_cmd_kref(struct kref *kref) +{ + struct srb *sp = container_of(kref, struct srb, cmd_kref); + struct qla_nvme_unsol_ctx *uctx = sp->priv; + struct nvmefc_ls_rsp *fd_rsp; + unsigned long flags; + + if (!uctx) { + qla2x00_rel_sp(sp); + return; + } + + spin_lock_irqsave(&uctx->cmd_lock, flags); + uctx->sp = NULL; + sp->priv = NULL; + spin_unlock_irqrestore(&uctx->cmd_lock, flags); + + fd_rsp = uctx->fd_rsp; + + list_del(&uctx->elem); + + fd_rsp->done(fd_rsp); + kfree(uctx); + qla2x00_rel_sp(sp); +} + +static void qla_nvme_lsrsp_complete(struct work_struct *work) +{ + struct qla_nvme_unsol_ctx *uctx = + container_of(work, struct qla_nvme_unsol_ctx, lsrsp_work); + + kref_put(&uctx->sp->cmd_kref, qla_nvme_release_lsrsp_cmd_kref); +} + +static void qla_nvme_sp_lsrsp_done(srb_t *sp, int res) +{ + struct qla_nvme_unsol_ctx *uctx = sp->priv; + + if (WARN_ON_ONCE(kref_read(&sp->cmd_kref) == 0)) + return; + + if (res) + res = -EINVAL; + + uctx->comp_status = res; + INIT_WORK(&uctx->lsrsp_work, qla_nvme_lsrsp_complete); + schedule_work(&uctx->lsrsp_work); +} + /* it assumed that QPair lock is held. */ static void qla_nvme_sp_done(srb_t *sp, int res) { @@ -287,6 +357,92 @@ static void qla_nvme_abort_work(struct work_struct *work) kref_put(&sp->cmd_kref, sp->put_fn); } +static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_rsp *fd_resp) +{ + struct qla_nvme_unsol_ctx *uctx = container_of(fd_resp, + struct qla_nvme_unsol_ctx, lsrsp); + struct qla_nvme_rport *qla_rport = rport->private; + fc_port_t *fcport = qla_rport->fcport; + struct scsi_qla_host *vha = uctx->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_nvme_lsrjt_pt_arg a; + struct srb_iocb *nvme; + srb_t *sp; + int rval = QLA_FUNCTION_FAILED; + uint8_t cnt = 0; + + if (!fcport || fcport->deleted) + goto out; + + if (!ha->flags.fw_started) + goto out; + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); + if (!sp) + goto out; + + sp->type = SRB_NVME_LS; + sp->name = "nvme_ls"; + sp->done = qla_nvme_sp_lsrsp_done; + sp->put_fn = qla_nvme_release_lsrsp_cmd_kref; + sp->priv = (void *)uctx; + sp->unsol_rsp = 1; + uctx->sp = sp; + spin_lock_init(&uctx->cmd_lock); + nvme = &sp->u.iocb_cmd; + uctx->fd_rsp = fd_resp; + nvme->u.nvme.desc = fd_resp; + nvme->u.nvme.dir = 0; + nvme->u.nvme.dl = 0; + nvme->u.nvme.timeout_sec = 0; + nvme->u.nvme.cmd_dma = fd_resp->rspdma; + nvme->u.nvme.cmd_len = cpu_to_le32(fd_resp->rsplen); + nvme->u.nvme.rsp_len = 0; + nvme->u.nvme.rsp_dma = 0; + nvme->u.nvme.exchange_address = uctx->exchange_address; + nvme->u.nvme.nport_handle = uctx->nport_handle; + nvme->u.nvme.ox_id = uctx->ox_id; + dma_sync_single_for_device(&ha->pdev->dev, nvme->u.nvme.cmd_dma, + fd_resp->rsplen, DMA_TO_DEVICE); + + ql_dbg(ql_dbg_unsol, vha, 0x2122, + "Unsol lsreq portid=%06x %8phC exchange_address 0x%x ox_id 0x%x hdl 0x%x\n", + fcport->d_id.b24, fcport->port_name, uctx->exchange_address, + uctx->ox_id, uctx->nport_handle); +retry: + rval = qla2x00_start_sp(sp); + switch (rval) { + case QLA_SUCCESS: + break; + case EAGAIN: + msleep(PURLS_MSLEEP_INTERVAL); + cnt++; + if (cnt < PURLS_RETRY_COUNT) + goto retry; + + fallthrough; + default: + ql_dbg(ql_log_warn, vha, 0x2123, + "Failed to xmit Unsol ls response = %d\n", rval); + rval = -EIO; + qla2x00_rel_sp(sp); + goto out; + } + + return 0; +out: + memset((void *)&a, 0, sizeof(a)); + a.vp_idx = vha->vp_idx; + a.nport_handle = uctx->nport_handle; + a.xchg_address = uctx->exchange_address; + qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true); + kfree(uctx); + return rval; +} + static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport, struct nvme_fc_remote_port *rport, struct nvmefc_ls_req *fd) { @@ -348,8 +504,8 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, nvme->u.nvme.desc = fd; nvme->u.nvme.dir = 0; nvme->u.nvme.dl = 0; - nvme->u.nvme.cmd_len = fd->rqstlen; - nvme->u.nvme.rsp_len = fd->rsplen; + nvme->u.nvme.cmd_len = cpu_to_le32(fd->rqstlen); + nvme->u.nvme.rsp_len = cpu_to_le32(fd->rsplen); nvme->u.nvme.rsp_dma = fd->rspdma; nvme->u.nvme.timeout_sec = fd->timeout; nvme->u.nvme.cmd_dma = fd->rqstdma; @@ -667,7 +823,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport, rval = qla2x00_start_nvme_mq(sp); if (rval != QLA_SUCCESS) { - ql_log(ql_log_warn, vha, 0x212d, + ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x212d, "qla2x00_start_nvme_mq failed = %d\n", rval); sp->priv = NULL; priv->sp = NULL; @@ -719,6 +875,7 @@ static struct nvme_fc_port_template qla_nvme_fc_transport = { .ls_abort = qla_nvme_ls_abort, .fcp_io = qla_nvme_post_cmd, .fcp_abort = qla_nvme_fcp_abort, + .xmt_ls_rsp = qla_nvme_xmt_ls_rsp, .map_queues = qla_nvme_map_queues, .max_hw_queues = DEF_NVME_HW_QUEUES, .max_sgl_segments = 1024, @@ -923,3 +1080,247 @@ inline void qla_wait_nvme_release_cmd_kref(srb_t *orig_sp) return; kref_put(&orig_sp->cmd_kref, orig_sp->put_fn); } + +static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason, + u8 explanation, u8 vendor) +{ + struct fcnvme_ls_rjt *rjt = buf; + + rjt->w0.ls_cmd = FCNVME_LSDESC_RQST; + rjt->desc_list_len = fcnvme_lsdesc_len(sizeof(struct fcnvme_ls_rjt)); + rjt->rqst.desc_tag = cpu_to_be32(FCNVME_LSDESC_RQST); + rjt->rqst.desc_len = + fcnvme_lsdesc_len(sizeof(struct fcnvme_lsdesc_rqst)); + rjt->rqst.w0.ls_cmd = ls_cmd; + rjt->rjt.desc_tag = cpu_to_be32(FCNVME_LSDESC_RJT); + rjt->rjt.desc_len = fcnvme_lsdesc_len(sizeof(struct fcnvme_lsdesc_rjt)); + rjt->rjt.reason_code = reason; + rjt->rjt.reason_explanation = explanation; + rjt->rjt.vendor = vendor; +} + +static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha, + struct pt_ls4_request *lsrjt_iocb, + struct qla_nvme_lsrjt_pt_arg *a) +{ + lsrjt_iocb->entry_type = PT_LS4_REQUEST; + lsrjt_iocb->entry_count = 1; + lsrjt_iocb->sys_define = 0; + lsrjt_iocb->entry_status = 0; + lsrjt_iocb->handle = QLA_SKIP_HANDLE; + lsrjt_iocb->nport_handle = a->nport_handle; + lsrjt_iocb->exchange_address = a->xchg_address; + lsrjt_iocb->vp_index = a->vp_idx; + + lsrjt_iocb->control_flags = cpu_to_le16(a->control_flags); + + put_unaligned_le64(a->tx_addr, &lsrjt_iocb->dsd[0].address); + lsrjt_iocb->dsd[0].length = cpu_to_le32(a->tx_byte_count); + lsrjt_iocb->tx_dseg_count = cpu_to_le16(1); + lsrjt_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count); + + put_unaligned_le64(a->rx_addr, &lsrjt_iocb->dsd[1].address); + lsrjt_iocb->dsd[1].length = 0; + lsrjt_iocb->rx_dseg_count = 0; + lsrjt_iocb->rx_byte_count = 0; +} + +static int +qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp, + struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate) +{ + struct pt_ls4_request *lsrjt_iocb; + + lsrjt_iocb = __qla2x00_alloc_iocbs(qp, NULL); + if (!lsrjt_iocb) { + ql_log(ql_log_warn, vha, 0x210e, + "qla2x00_alloc_iocbs failed.\n"); + return QLA_FUNCTION_FAILED; + } + + if (!is_xchg_terminate) { + qla_nvme_fc_format_rjt((void *)vha->hw->lsrjt.c, a->opcode, + a->reason, a->explanation, 0); + + a->tx_byte_count = sizeof(struct fcnvme_ls_rjt); + a->tx_addr = vha->hw->lsrjt.cdma; + a->control_flags = CF_LS4_RESPONDER << CF_LS4_SHIFT; + + ql_dbg(ql_dbg_unsol, vha, 0x211f, + "Sending nvme fc ls reject ox_id %04x op %04x\n", + a->ox_id, a->opcode); + ql_dump_buffer(ql_dbg_unsol + ql_dbg_verbose, vha, 0x210f, + vha->hw->lsrjt.c, sizeof(*vha->hw->lsrjt.c)); + } else { + a->tx_byte_count = 0; + a->control_flags = CF_LS4_RESPONDER_TERM << CF_LS4_SHIFT; + ql_dbg(ql_dbg_unsol, vha, 0x2110, + "Terminate nvme ls xchg 0x%x\n", a->xchg_address); + } + + qla_nvme_lsrjt_pt_iocb(vha, lsrjt_iocb, a); + /* flush iocb to mem before notifying hw doorbell */ + wmb(); + qla2x00_start_iocbs(vha, qp->req); + return 0; +} + +/* + * qla2xxx_process_purls_pkt() - Pass-up Unsolicited + * Received FC-NVMe Link Service pkt to nvme_fc_rcv_ls_req(). + * LLDD need to provide memory for response buffer, which + * will be used to reference the exchange corresponding + * to the LS when issuing an ls response. LLDD will have to free + * response buffer in lport->ops->xmt_ls_rsp(). + * + * @vha: SCSI qla host + * @item: ptr to purex_item + */ +static void +qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item) +{ + struct qla_nvme_unsol_ctx *uctx = item->purls_context; + struct qla_nvme_lsrjt_pt_arg a; + int ret = 1; + +#if (IS_ENABLED(CONFIG_NVME_FC)) + ret = nvme_fc_rcv_ls_req(uctx->fcport->nvme_remote_port, &uctx->lsrsp, + &item->iocb, item->size); +#endif + if (ret) { + ql_dbg(ql_dbg_unsol, vha, 0x2125, "NVMe transport ls_req failed\n"); + memset((void *)&a, 0, sizeof(a)); + a.vp_idx = vha->vp_idx; + a.nport_handle = uctx->nport_handle; + a.xchg_address = uctx->exchange_address; + qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true); + list_del(&uctx->elem); + kfree(uctx); + } +} + +static scsi_qla_host_t * +qla2xxx_get_vha_from_vp_idx(struct qla_hw_data *ha, uint16_t vp_index) +{ + scsi_qla_host_t *base_vha, *vha, *tvp; + unsigned long flags; + + base_vha = pci_get_drvdata(ha->pdev); + + if (!vp_index && !ha->num_vhosts) + return base_vha; + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry_safe(vha, tvp, &ha->vp_list, list) { + if (vha->vp_idx == vp_index) { + spin_unlock_irqrestore(&ha->vport_slock, flags); + return vha; + } + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + + return NULL; +} + +void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp) +{ + struct nvme_fc_remote_port *rport; + struct qla_nvme_rport *qla_rport; + struct qla_nvme_lsrjt_pt_arg a; + struct pt_ls4_rx_unsol *p = *pkt; + struct qla_nvme_unsol_ctx *uctx; + struct rsp_que *rsp_q = *rsp; + struct qla_hw_data *ha; + scsi_qla_host_t *vha; + fc_port_t *fcport = NULL; + struct purex_item *item; + port_id_t d_id = {0}; + port_id_t id = {0}; + u8 *opcode; + bool xmt_reject = false; + + ha = rsp_q->hw; + + vha = qla2xxx_get_vha_from_vp_idx(ha, p->vp_index); + if (!vha) { + ql_log(ql_log_warn, NULL, 0x2110, "Invalid vp index %d\n", p->vp_index); + WARN_ON_ONCE(1); + return; + } + + memset((void *)&a, 0, sizeof(a)); + opcode = (u8 *)&p->payload[0]; + a.opcode = opcode[3]; + a.vp_idx = p->vp_index; + a.nport_handle = p->nport_handle; + a.ox_id = p->ox_id; + a.xchg_address = p->exchange_address; + + id.b.domain = p->s_id.domain; + id.b.area = p->s_id.area; + id.b.al_pa = p->s_id.al_pa; + d_id.b.domain = p->d_id[2]; + d_id.b.area = p->d_id[1]; + d_id.b.al_pa = p->d_id[0]; + + fcport = qla2x00_find_fcport_by_nportid(vha, &id, 0); + if (!fcport) { + ql_dbg(ql_dbg_unsol, vha, 0x211e, + "Failed to find sid=%06x did=%06x\n", + id.b24, d_id.b24); + a.reason = FCNVME_RJT_RC_INV_ASSOC; + a.explanation = FCNVME_RJT_EXP_NONE; + xmt_reject = true; + goto out; + } + rport = fcport->nvme_remote_port; + qla_rport = rport->private; + + item = qla27xx_copy_multiple_pkt(vha, pkt, rsp, true, false); + if (!item) { + a.reason = FCNVME_RJT_RC_LOGIC; + a.explanation = FCNVME_RJT_EXP_NONE; + xmt_reject = true; + goto out; + } + + uctx = kzalloc(sizeof(*uctx), GFP_ATOMIC); + if (!uctx) { + ql_log(ql_log_info, vha, 0x2126, "Failed allocate memory\n"); + a.reason = FCNVME_RJT_RC_LOGIC; + a.explanation = FCNVME_RJT_EXP_NONE; + xmt_reject = true; + kfree(item); + goto out; + } + + uctx->vha = vha; + uctx->fcport = fcport; + uctx->exchange_address = p->exchange_address; + uctx->nport_handle = p->nport_handle; + uctx->ox_id = p->ox_id; + qla_rport->uctx = uctx; + INIT_LIST_HEAD(&uctx->elem); + list_add_tail(&uctx->elem, &fcport->unsol_ctx_head); + item->purls_context = (void *)uctx; + + ql_dbg(ql_dbg_unsol, vha, 0x2121, + "PURLS OP[%01x] size %d xchg addr 0x%x portid %06x\n", + item->iocb.iocb[3], item->size, uctx->exchange_address, + fcport->d_id.b24); + /* +48 0 1 2 3 4 5 6 7 8 9 A B C D E F + * ----- ----------------------------------------------- + * 0000: 00 00 00 05 28 00 00 00 07 00 00 00 08 00 00 00 + * 0010: ab ec 0f cc 00 00 8d 7d 05 00 00 00 10 00 00 00 + * 0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + */ + ql_dump_buffer(ql_dbg_unsol + ql_dbg_verbose, vha, 0x2120, + &item->iocb, item->size); + + qla24xx_queue_purex_item(vha, item, qla2xxx_process_purls_pkt); +out: + if (xmt_reject) { + qla_nvme_ls_reject_iocb(vha, (*rsp)->qpair, &a, false); + __qla_consume_iocb(vha, pkt, rsp); + } +} diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h index d299478371b21b3c3f11f2a23fb3494a3d697cec..a253ac55171bdda8cdc75cdc95cfde6418fdc11c 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.h +++ b/drivers/scsi/qla2xxx/qla_nvme.h @@ -21,6 +21,7 @@ #define Q2T_NVME_NUM_TAGS 2048 #define QLA_MAX_FC_SEGMENTS 64 +struct qla_nvme_unsol_ctx; struct scsi_qla_host; struct qla_hw_data; struct req_que; @@ -37,6 +38,7 @@ struct nvme_private { struct qla_nvme_rport { struct fc_port *fcport; + struct qla_nvme_unsol_ctx *uctx; }; #define COMMAND_NVME 0x88 /* Command Type FC-NVMe IOCB */ @@ -75,6 +77,9 @@ struct cmd_nvme { struct dsd64 nvme_dsd; }; +#define PURLS_MSLEEP_INTERVAL 1 +#define PURLS_RETRY_COUNT 5 + #define PT_LS4_REQUEST 0x89 /* Link Service pass-through IOCB (request) */ struct pt_ls4_request { uint8_t entry_type; @@ -118,21 +123,19 @@ struct pt_ls4_rx_unsol { __le32 exchange_address; uint8_t d_id[3]; uint8_t r_ctl; - be_id_t s_id; + le_id_t s_id; uint8_t cs_ctl; uint8_t f_ctl[3]; uint8_t type; __le16 seq_cnt; uint8_t df_ctl; uint8_t seq_id; - __le16 rx_id; - __le16 ox_id; - __le32 param; - __le32 desc0; + __le16 rx_id; + __le16 ox_id; + __le32 desc0; #define PT_LS4_PAYLOAD_OFFSET 0x2c #define PT_LS4_FIRST_PACKET_LEN 20 - __le32 desc_len; - __le32 payload[3]; + __le32 payload[5]; }; /* diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h index 6dc80c8ddf796635bca994914512b376102eda2a..5d1bdc15b75c2f209e0bc8efaa35e4ffcc6ab1b0 100644 --- a/drivers/scsi/qla2xxx/qla_nx.h +++ b/drivers/scsi/qla2xxx/qla_nx.h @@ -857,7 +857,9 @@ struct fcp_cmnd { uint8_t task_attribute; uint8_t task_management; uint8_t additional_cdb_len; - uint8_t cdb[260]; /* 256 for CDB len and 4 for FCP_DL */ +#define QLA_CDB_BUF_SIZE 256 +#define QLA_FCP_DL_SIZE 4 + uint8_t cdb[QLA_CDB_BUF_SIZE + QLA_FCP_DL_SIZE]; /* 256 for CDB len and 4 for FCP_DL */ }; struct dsd_dma { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 877e4f446709d5fb6792b670c290fa9519220f13..50db08265c51fdf373bb7d9afb8ce251c000bd2d 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -44,10 +44,11 @@ module_param(ql2xfulldump_on_mpifail, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql2xfulldump_on_mpifail, "Set this to take full dump on MPI hang."); -int ql2xenforce_iocb_limit = 1; +int ql2xenforce_iocb_limit = 2; module_param(ql2xenforce_iocb_limit, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql2xenforce_iocb_limit, - "Enforce IOCB throttling, to avoid FW congestion. (default: 1)"); + "Enforce IOCB throttling, to avoid FW congestion. (default: 2) " + "1: track usage per queue, 2: track usage per adapter"); /* * CT6 CTX allocation cache @@ -432,6 +433,7 @@ static void qla_init_base_qpair(struct scsi_qla_host *vha, struct req_que *req, ha->base_qpair->msix = &ha->msix_entries[QLA_MSIX_RSP_Q]; ha->base_qpair->srb_mempool = ha->srb_mempool; INIT_LIST_HEAD(&ha->base_qpair->hints_list); + INIT_LIST_HEAD(&ha->base_qpair->dsd_list); ha->base_qpair->enable_class_2 = ql2xenableclass2; /* init qpair to this cpu. Will adjust at run time. */ qla_cpu_update(rsp->qpair, raw_smp_processor_id()); @@ -750,9 +752,9 @@ void qla2x00_sp_free_dma(srb_t *sp) dma_pool_free(ha->fcp_cmnd_dma_pool, ctx1->fcp_cmnd, ctx1->fcp_cmnd_dma); - list_splice(&ctx1->dsd_list, &ha->gbl_dsd_list); - ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt; - ha->gbl_dsd_avail += ctx1->dsd_use_cnt; + list_splice(&ctx1->dsd_list, &sp->qpair->dsd_list); + sp->qpair->dsd_inuse -= ctx1->dsd_use_cnt; + sp->qpair->dsd_avail += ctx1->dsd_use_cnt; } if (sp->flags & SRB_GOT_BUF) @@ -836,9 +838,9 @@ void qla2xxx_qpair_sp_free_dma(srb_t *sp) dma_pool_free(ha->fcp_cmnd_dma_pool, ctx1->fcp_cmnd, ctx1->fcp_cmnd_dma); - list_splice(&ctx1->dsd_list, &ha->gbl_dsd_list); - ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt; - ha->gbl_dsd_avail += ctx1->dsd_use_cnt; + list_splice(&ctx1->dsd_list, &sp->qpair->dsd_list); + sp->qpair->dsd_inuse -= ctx1->dsd_use_cnt; + sp->qpair->dsd_avail += ctx1->dsd_use_cnt; sp->flags &= ~SRB_FCP_CMND_DMA_VALID; } @@ -1488,8 +1490,9 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) goto eh_reset_failed; } err = 3; - if (qla2x00_eh_wait_for_pending_commands(vha, sdev->id, - sdev->lun, WAIT_LUN) != QLA_SUCCESS) { + if (qla2x00_eh_wait_for_pending_commands(vha, fcport->d_id.b24, + cmd->device->lun, + WAIT_LUN) != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x800d, "wait for pending cmds failed for cmd=%p.\n", cmd); goto eh_reset_failed; @@ -1555,8 +1558,8 @@ qla2xxx_eh_target_reset(struct scsi_cmnd *cmd) goto eh_reset_failed; } err = 3; - if (qla2x00_eh_wait_for_pending_commands(vha, sdev->id, - 0, WAIT_TARGET) != QLA_SUCCESS) { + if (qla2x00_eh_wait_for_pending_commands(vha, fcport->d_id.b24, 0, + WAIT_TARGET) != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x800d, "wait for pending cmds failed for cmd=%p.\n", cmd); goto eh_reset_failed; @@ -3006,9 +3009,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->max_exchg = FW_MAX_EXCHANGES_CNT; atomic_set(&ha->num_pend_mbx_stage1, 0); atomic_set(&ha->num_pend_mbx_stage2, 0); - atomic_set(&ha->num_pend_mbx_stage3, 0); atomic_set(&ha->zio_threshold, DEFAULT_ZIO_THRESHOLD); ha->last_zio_threshold = DEFAULT_ZIO_THRESHOLD; + INIT_LIST_HEAD(&ha->tmf_pending); + INIT_LIST_HEAD(&ha->tmf_active); /* Assign ISP specific operations. */ if (IS_QLA2100(ha)) { @@ -3285,6 +3289,13 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host->max_id = ha->max_fibre_devices; host->cmd_per_lun = 3; host->unique_id = host->host_no; + + if (ql2xenabledif && ql2xenabledif != 2) { + ql_log(ql_log_warn, base_vha, 0x302d, + "Invalid value for ql2xenabledif, resetting it to default (2)\n"); + ql2xenabledif = 2; + } + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) host->max_cmd_len = 32; else @@ -3521,8 +3532,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) base_vha->flags.difdix_supported = 1; ql_dbg(ql_dbg_init, base_vha, 0x00f1, "Registering for DIF/DIX type 1 and 3 protection.\n"); - if (ql2xenabledif == 1) - prot = SHOST_DIX_TYPE0_PROTECTION; if (ql2xprotmask) scsi_host_set_prot(host, ql2xprotmask); else @@ -4399,7 +4408,6 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, "sf_init_cb=%p.\n", ha->sf_init_cb); } - INIT_LIST_HEAD(&ha->gbl_dsd_list); /* Get consistent memory allocated for Async Port-Database. */ if (!IS_FWI2_CAPABLE(ha)) { @@ -4454,8 +4462,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16; ha->elsrej.c = dma_alloc_coherent(&ha->pdev->dev, - ha->elsrej.size, &ha->elsrej.cdma, GFP_KERNEL); - + ha->elsrej.size, + &ha->elsrej.cdma, + GFP_KERNEL); if (!ha->elsrej.c) { ql_dbg_pci(ql_dbg_init, ha->pdev, 0xffff, "Alloc failed for els reject cmd.\n"); @@ -4464,8 +4473,21 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ha->elsrej.c->er_cmd = ELS_LS_RJT; ha->elsrej.c->er_reason = ELS_RJT_LOGIC; ha->elsrej.c->er_explan = ELS_EXPL_UNAB_DATA; + + ha->lsrjt.size = sizeof(struct fcnvme_ls_rjt); + ha->lsrjt.c = dma_alloc_coherent(&ha->pdev->dev, ha->lsrjt.size, + &ha->lsrjt.cdma, GFP_KERNEL); + if (!ha->lsrjt.c) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0xffff, + "Alloc failed for nvme fc reject cmd.\n"); + goto fail_lsrjt; + } + return 0; +fail_lsrjt: + dma_free_coherent(&ha->pdev->dev, ha->elsrej.size, + ha->elsrej.c, ha->elsrej.cdma); fail_elsrej: dma_pool_destroy(ha->purex_dma_pool); fail_flt: @@ -4931,18 +4953,16 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->gid_list = NULL; ha->gid_list_dma = 0; - if (IS_QLA82XX(ha)) { - if (!list_empty(&ha->gbl_dsd_list)) { - struct dsd_dma *dsd_ptr, *tdsd_ptr; - - /* clean up allocated prev pool */ - list_for_each_entry_safe(dsd_ptr, - tdsd_ptr, &ha->gbl_dsd_list, list) { - dma_pool_free(ha->dl_dma_pool, - dsd_ptr->dsd_addr, dsd_ptr->dsd_list_dma); - list_del(&dsd_ptr->list); - kfree(dsd_ptr); - } + if (!list_empty(&ha->base_qpair->dsd_list)) { + struct dsd_dma *dsd_ptr, *tdsd_ptr; + + /* clean up allocated prev pool */ + list_for_each_entry_safe(dsd_ptr, tdsd_ptr, + &ha->base_qpair->dsd_list, list) { + dma_pool_free(ha->dl_dma_pool, dsd_ptr->dsd_addr, + dsd_ptr->dsd_list_dma); + list_del(&dsd_ptr->list); + kfree(dsd_ptr); } } @@ -4997,6 +5017,12 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->elsrej.c = NULL; } + if (ha->lsrjt.c) { + dma_free_coherent(&ha->pdev->dev, ha->lsrjt.size, ha->lsrjt.c, + ha->lsrjt.cdma); + ha->lsrjt.c = NULL; + } + ha->init_cb = NULL; ha->init_cb_dma = 0; diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 5258b07687a94812072c3ab4d0e955d398dcd2c7..2ef2dbac0db2739d82c590f4afd987e893230931 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1068,10 +1068,6 @@ void qlt_free_session_done(struct work_struct *work) (struct imm_ntfy_from_isp *)sess->iocb, SRB_NACK_LOGO); } - spin_lock_irqsave(&vha->work_lock, flags); - sess->flags &= ~FCF_ASYNC_SENT; - spin_unlock_irqrestore(&vha->work_lock, flags); - spin_lock_irqsave(&ha->tgt.sess_lock, flags); if (sess->se_sess) { sess->se_sess = NULL; @@ -1081,7 +1077,6 @@ void qlt_free_session_done(struct work_struct *work) qla2x00_set_fcport_disc_state(sess, DSC_DELETED); sess->fw_login_state = DSC_LS_PORT_UNAVAIL; - sess->deleted = QLA_SESS_DELETED; if (sess->login_succ && !IS_SW_RESV_ADDR(sess->d_id)) { vha->fcport_count--; @@ -1133,10 +1128,15 @@ void qlt_free_session_done(struct work_struct *work) sess->explicit_logout = 0; spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); - sess->free_pending = 0; qla2x00_dfs_remove_rport(vha, sess); + spin_lock_irqsave(&vha->work_lock, flags); + sess->flags &= ~FCF_ASYNC_SENT; + sess->deleted = QLA_SESS_DELETED; + sess->free_pending = 0; + spin_unlock_irqrestore(&vha->work_lock, flags); + ql_dbg(ql_dbg_disc, vha, 0xf001, "Unregistration of sess %p %8phC finished fcp_cnt %d\n", sess, sess->port_name, vha->fcport_count); @@ -1185,12 +1185,12 @@ void qlt_unreg_sess(struct fc_port *sess) * management from being sent. */ sess->flags |= FCF_ASYNC_SENT; + sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; spin_unlock_irqrestore(&sess->vha->work_lock, flags); if (sess->se_sess) vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); - sess->deleted = QLA_SESS_DELETION_IN_PROGRESS; qla2x00_set_fcport_disc_state(sess, DSC_DELETE_PEND); sess->last_rscn_gen = sess->rscn_gen; sess->last_login_gen = sess->login_gen; @@ -4425,8 +4425,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq, &cmd->work); } else if (ha->msix_count) { if (cmd->atio.u.isp24.fcp_cmnd.rddata) - queue_work_on(smp_processor_id(), qla_tgt_wq, - &cmd->work); + queue_work(qla_tgt_wq, &cmd->work); else queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq, &cmd->work); diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index e3771923b0d7d6cffde7c00275610cddad5fb594..d903563e969eb32a0a079f3ea8c3504678afc65a 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -6,9 +6,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "10.02.08.400-k" +#define QLA2XXX_VERSION "10.02.09.100-k" #define QLA_DRIVER_MAJOR_VER 10 #define QLA_DRIVER_MINOR_VER 2 -#define QLA_DRIVER_PATCH_VER 8 -#define QLA_DRIVER_BETA_VER 400 +#define QLA_DRIVER_PATCH_VER 9 +#define QLA_DRIVER_BETA_VER 100 diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 3b5ba4b47b3b2717708b64f6e85fec986513070a..68a0e6a2fb6e98fa60a68c81e56fa87e4a1c1bcb 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -310,7 +310,7 @@ static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd) cmd->trc_flags |= TRC_CMD_DONE; INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free); - queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); } /* @@ -547,7 +547,7 @@ static void tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd) cmd->trc_flags |= TRC_DATA_IN; cmd->cmd_in_wq = 1; INIT_WORK(&cmd->work, tcm_qla2xxx_handle_data_work); - queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); } static int tcm_qla2xxx_chk_dif_tags(uint32_t tag) diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index b2a3988e1e1594603307f19dd43d87ec33398d0e..675332e49a7b063c54c3d265e5f56006f59ffa8a 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -968,6 +968,11 @@ static int qla4xxx_set_chap_entry(struct Scsi_Host *shost, void *data, int len) memset(&chap_rec, 0, sizeof(chap_rec)); nla_for_each_attr(attr, data, len, rem) { + if (nla_len(attr) < sizeof(*param_info)) { + rc = -EINVAL; + goto exit_set_chap; + } + param_info = nla_data(attr); switch (param_info->param) { @@ -2750,6 +2755,11 @@ qla4xxx_iface_set_param(struct Scsi_Host *shost, void *data, uint32_t len) } nla_for_each_attr(attr, data, len, rem) { + if (nla_len(attr) < sizeof(*iface_param)) { + rval = -EINVAL; + goto exit_init_fw_cb; + } + iface_param = nla_data(attr); if (iface_param->param_type == ISCSI_NET_PARAM) { @@ -8104,6 +8114,11 @@ qla4xxx_sysfs_ddb_set_param(struct iscsi_bus_flash_session *fnode_sess, memset((void *)&chap_tbl, 0, sizeof(chap_tbl)); nla_for_each_attr(attr, data, len, rem) { + if (nla_len(attr) < sizeof(*fnode_param)) { + rc = -EINVAL; + goto exit_set_param; + } + fnode_param = nla_data(attr); switch (fnode_param->param) { diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 1e8fbd4572485994cd2d8d839b7ce908c65c5f91..3b95f7a6216fe72b602b0e9546e7b085f93763a4 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -843,7 +843,7 @@ static int qpti_map_queues(struct qlogicpti *qpti) return 0; } -const char *qlogicpti_info(struct Scsi_Host *host) +static const char *qlogicpti_info(struct Scsi_Host *host) { static char buf[80]; struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index d0911bc28663a8142d0fb0ae69dbad1348efa385..89367c4bf0ef5e8f2018fc725b6ffae1b87fa2f3 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -613,6 +613,17 @@ void scsi_cdl_check(struct scsi_device *sdev) bool cdl_supported; unsigned char *buf; + /* + * Support for CDL was defined in SPC-5. Ignore devices reporting an + * lower SPC version. This also avoids problems with old drives choking + * on MAINTENANCE_IN / MI_REPORT_SUPPORTED_OPERATION_CODES with a + * service action specified, as done in scsi_cdl_check_cmd(). + */ + if (sdev->scsi_level < SCSI_SPC_5) { + sdev->cdl_supported = 0; + return; + } + buf = kmalloc(SCSI_CDL_CHECK_BUF_LEN, GFP_KERNEL); if (!buf) { sdev->cdl_supported = 0; diff --git a/drivers/scsi/scsi_debugfs.c b/drivers/scsi/scsi_debugfs.c index 217b70c678c3ad322ddfd23b6a992fb0e977202a..f795848b316c4a5ea4a5bdbf18733b19f0188795 100644 --- a/drivers/scsi/scsi_debugfs.c +++ b/drivers/scsi/scsi_debugfs.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "scsi_debugfs.h" #define SCSI_CMD_FLAG_NAME(name)[const_ilog2(SCMD_##name)] = #name @@ -33,14 +34,33 @@ static int scsi_flags_show(struct seq_file *m, const unsigned long flags, void scsi_show_rq(struct seq_file *m, struct request *rq) { - struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq), *cmd2; + struct Scsi_Host *shost = cmd->device->host; int alloc_ms = jiffies_to_msecs(jiffies - cmd->jiffies_at_alloc); int timeout_ms = jiffies_to_msecs(rq->timeout); + const char *list_info = NULL; char buf[80] = "(?)"; + spin_lock_irq(shost->host_lock); + list_for_each_entry(cmd2, &shost->eh_abort_list, eh_entry) { + if (cmd == cmd2) { + list_info = "on eh_abort_list"; + goto unlock; + } + } + list_for_each_entry(cmd2, &shost->eh_cmd_q, eh_entry) { + if (cmd == cmd2) { + list_info = "on eh_cmd_q"; + goto unlock; + } + } +unlock: + spin_unlock_irq(shost->host_lock); + __scsi_format_command(buf, sizeof(buf), cmd->cmnd, cmd->cmd_len); - seq_printf(m, ", .cmd=%s, .retries=%d, .result = %#x, .flags=", buf, - cmd->retries, cmd->result); + seq_printf(m, ", .cmd=%s, .retries=%d, .allowed=%d, .result = %#x, %s%s.flags=", + buf, cmd->retries, cmd->allowed, cmd->result, + list_info ? : "", list_info ? ", " : ""); scsi_flags_show(m, cmd->flags, scsi_cmd_flags, ARRAY_SIZE(scsi_cmd_flags)); seq_printf(m, ", .timeout=%d.%03d, allocated %d.%03d s ago", diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 59176946ab560813a7989487f563f67890d7ab84..c2f647a7c1b050e0862a9bd3b0ad0a2a88ba535b 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2454,7 +2454,7 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) envp[idx++] = "SDEV_MEDIA_CHANGE=1"; break; case SDEV_EVT_INQUIRY_CHANGE_REPORTED: - scsi_rescan_device(&sdev->sdev_gendev); + scsi_rescan_device(sdev); envp[idx++] = "SDEV_UA=INQUIRY_DATA_HAS_CHANGED"; break; case SDEV_EVT_CAPACITY_CHANGE_REPORTED: diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index f42388ecb02488d3042a63c816ac897e975b1062..3f0dfb97db6bd1b88755db1fb50dd6e968e385c6 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -103,7 +103,6 @@ bool scsi_noretry_cmd(struct scsi_cmnd *scmd); void scsi_eh_done(struct scsi_cmnd *scmd); /* scsi_lib.c */ -extern int scsi_maybe_unblock_host(struct scsi_device *sdev); extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd); extern void scsi_queue_insert(struct scsi_cmnd *cmd, int reason); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); @@ -138,7 +137,6 @@ extern int scsi_complete_async_scans(void); extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, unsigned int, u64, enum scsi_scan_mode); extern void scsi_forget_host(struct Scsi_Host *); -extern void scsi_rescan_device(struct device *); /* scsi_sysctl.c */ #ifdef CONFIG_SYSCTL @@ -155,7 +153,6 @@ extern int scsi_sysfs_add_host(struct Scsi_Host *); extern int scsi_sysfs_register(void); extern void scsi_sysfs_unregister(void); extern void scsi_sysfs_device_initialize(struct scsi_device *); -extern int scsi_sysfs_target_initialize(struct scsi_device *); extern struct scsi_transport_template blank_transport_template; extern void __scsi_remove_device(struct scsi_device *); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index aa13feb17c62605a15379cae725514acf2a8a09c..902655d7594768d6865a4f5ccde4a11170f2263d 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -822,7 +822,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, * device is attached at LUN 0 (SCSI_SCAN_TARGET_PRESENT) so * non-zero LUNs can be scanned. */ - sdev->scsi_level = inq_result[2] & 0x07; + sdev->scsi_level = inq_result[2] & 0x0f; if (sdev->scsi_level >= 2 || (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1)) sdev->scsi_level++; @@ -1619,12 +1619,24 @@ int scsi_add_device(struct Scsi_Host *host, uint channel, } EXPORT_SYMBOL(scsi_add_device); -void scsi_rescan_device(struct device *dev) +int scsi_rescan_device(struct scsi_device *sdev) { - struct scsi_device *sdev = to_scsi_device(dev); + struct device *dev = &sdev->sdev_gendev; + int ret = 0; device_lock(dev); + /* + * Bail out if the device is not running. Otherwise, the rescan may + * block waiting for commands to be executed, with us holding the + * device lock. This can result in a potential deadlock in the power + * management core code when system resume is on-going. + */ + if (sdev->sdev_state != SDEV_RUNNING) { + ret = -EWOULDBLOCK; + goto unlock; + } + scsi_attach_vpd(sdev); scsi_cdl_check(sdev); @@ -1638,7 +1650,11 @@ void scsi_rescan_device(struct device *dev) drv->rescan(dev); module_put(dev->driver->owner); } + +unlock: device_unlock(dev); + + return ret; } EXPORT_SYMBOL(scsi_rescan_device); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 60317676e45f19bf7328d84422cb7f5f079bcb13..24f6eefb68030d39869934e6fbefc9fce11d640b 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -747,7 +747,7 @@ static ssize_t store_rescan_field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - scsi_rescan_device(dev); + scsi_rescan_device(to_scsi_device(dev)); return count; } static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); @@ -840,7 +840,7 @@ store_state_field(struct device *dev, struct device_attribute *attr, * waiting for pending I/O to finish. */ blk_mq_run_hw_queues(sdev->request_queue, true); - scsi_rescan_device(dev); + scsi_rescan_device(sdev); } return ret == 0 ? count : -EINVAL; diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index e527ece12453a1f0c5eb0df2986432fd11eb8391..3075b2ddf7a6975e203f9453aa56e1feead0ffc2 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -3014,14 +3014,15 @@ iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev } static int -iscsi_if_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) +iscsi_if_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen) { char *data = (char*)ev + sizeof(*ev); struct iscsi_cls_conn *conn; struct iscsi_cls_session *session; int err = 0, value = 0, state; - if (ev->u.set_param.len > PAGE_SIZE) + if (ev->u.set_param.len > rlen || + ev->u.set_param.len > PAGE_SIZE) return -EINVAL; session = iscsi_session_lookup(ev->u.set_param.sid); @@ -3029,6 +3030,10 @@ iscsi_if_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) if (!conn || !session) return -EINVAL; + /* data will be regarded as NULL-ended string, do length check */ + if (strlen(data) > ev->u.set_param.len) + return -EINVAL; + switch (ev->u.set_param.param) { case ISCSI_PARAM_SESS_RECOVERY_TMO: sscanf(data, "%d", &value); @@ -3118,7 +3123,7 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport, static int iscsi_if_transport_ep(struct iscsi_transport *transport, - struct iscsi_uevent *ev, int msg_type) + struct iscsi_uevent *ev, int msg_type, u32 rlen) { struct iscsi_endpoint *ep; int rc = 0; @@ -3126,7 +3131,10 @@ iscsi_if_transport_ep(struct iscsi_transport *transport, switch (msg_type) { case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST: case ISCSI_UEVENT_TRANSPORT_EP_CONNECT: - rc = iscsi_if_ep_connect(transport, ev, msg_type); + if (rlen < sizeof(struct sockaddr)) + rc = -EINVAL; + else + rc = iscsi_if_ep_connect(transport, ev, msg_type); break; case ISCSI_UEVENT_TRANSPORT_EP_POLL: if (!transport->ep_poll) @@ -3150,12 +3158,15 @@ iscsi_if_transport_ep(struct iscsi_transport *transport, static int iscsi_tgt_dscvr(struct iscsi_transport *transport, - struct iscsi_uevent *ev) + struct iscsi_uevent *ev, u32 rlen) { struct Scsi_Host *shost; struct sockaddr *dst_addr; int err; + if (rlen < sizeof(*dst_addr)) + return -EINVAL; + if (!transport->tgt_dscvr) return -EINVAL; @@ -3176,7 +3187,7 @@ iscsi_tgt_dscvr(struct iscsi_transport *transport, static int iscsi_set_host_param(struct iscsi_transport *transport, - struct iscsi_uevent *ev) + struct iscsi_uevent *ev, u32 rlen) { char *data = (char*)ev + sizeof(*ev); struct Scsi_Host *shost; @@ -3185,7 +3196,8 @@ iscsi_set_host_param(struct iscsi_transport *transport, if (!transport->set_host_param) return -ENOSYS; - if (ev->u.set_host_param.len > PAGE_SIZE) + if (ev->u.set_host_param.len > rlen || + ev->u.set_host_param.len > PAGE_SIZE) return -EINVAL; shost = scsi_host_lookup(ev->u.set_host_param.host_no); @@ -3195,6 +3207,10 @@ iscsi_set_host_param(struct iscsi_transport *transport, return -ENODEV; } + /* see similar check in iscsi_if_set_param() */ + if (strlen(data) > ev->u.set_host_param.len) + return -EINVAL; + err = transport->set_host_param(shost, ev->u.set_host_param.param, data, ev->u.set_host_param.len); scsi_host_put(shost); @@ -3202,12 +3218,15 @@ iscsi_set_host_param(struct iscsi_transport *transport, } static int -iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev) +iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen) { struct Scsi_Host *shost; struct iscsi_path *params; int err; + if (rlen < sizeof(*params)) + return -EINVAL; + if (!transport->set_path) return -ENOSYS; @@ -3267,12 +3286,15 @@ iscsi_set_iface_params(struct iscsi_transport *transport, } static int -iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev) +iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen) { struct Scsi_Host *shost; struct sockaddr *dst_addr; int err; + if (rlen < sizeof(*dst_addr)) + return -EINVAL; + if (!transport->send_ping) return -ENOSYS; @@ -3770,13 +3792,12 @@ iscsi_get_host_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) } static int iscsi_if_transport_conn(struct iscsi_transport *transport, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, u32 pdu_len) { struct iscsi_uevent *ev = nlmsg_data(nlh); struct iscsi_cls_session *session; struct iscsi_cls_conn *conn = NULL; struct iscsi_endpoint *ep; - uint32_t pdu_len; int err = 0; switch (nlh->nlmsg_type) { @@ -3861,8 +3882,6 @@ static int iscsi_if_transport_conn(struct iscsi_transport *transport, break; case ISCSI_UEVENT_SEND_PDU: - pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev); - if ((ev->u.send_pdu.hdr_size > pdu_len) || (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { err = -EINVAL; @@ -3892,6 +3911,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) struct iscsi_internal *priv; struct iscsi_cls_session *session; struct iscsi_endpoint *ep = NULL; + u32 rlen; if (!netlink_capable(skb, CAP_SYS_ADMIN)) return -EPERM; @@ -3911,6 +3931,13 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) portid = NETLINK_CB(skb).portid; + /* + * Even though the remaining payload may not be regarded as nlattr, + * (like address or something else), calculate the remaining length + * here to ease following length checks. + */ + rlen = nlmsg_attrlen(nlh, sizeof(*ev)); + switch (nlh->nlmsg_type) { case ISCSI_UEVENT_CREATE_SESSION: err = iscsi_if_create_session(priv, ep, ev, @@ -3967,7 +3994,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) err = -EINVAL; break; case ISCSI_UEVENT_SET_PARAM: - err = iscsi_if_set_param(transport, ev); + err = iscsi_if_set_param(transport, ev, rlen); break; case ISCSI_UEVENT_CREATE_CONN: case ISCSI_UEVENT_DESTROY_CONN: @@ -3975,7 +4002,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) case ISCSI_UEVENT_START_CONN: case ISCSI_UEVENT_BIND_CONN: case ISCSI_UEVENT_SEND_PDU: - err = iscsi_if_transport_conn(transport, nlh); + err = iscsi_if_transport_conn(transport, nlh, rlen); break; case ISCSI_UEVENT_GET_STATS: err = iscsi_if_get_stats(transport, nlh); @@ -3984,23 +4011,22 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) case ISCSI_UEVENT_TRANSPORT_EP_POLL: case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST: - err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type); + err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type, rlen); break; case ISCSI_UEVENT_TGT_DSCVR: - err = iscsi_tgt_dscvr(transport, ev); + err = iscsi_tgt_dscvr(transport, ev, rlen); break; case ISCSI_UEVENT_SET_HOST_PARAM: - err = iscsi_set_host_param(transport, ev); + err = iscsi_set_host_param(transport, ev, rlen); break; case ISCSI_UEVENT_PATH_UPDATE: - err = iscsi_set_path(transport, ev); + err = iscsi_set_path(transport, ev, rlen); break; case ISCSI_UEVENT_SET_IFACE_PARAMS: - err = iscsi_set_iface_params(transport, ev, - nlmsg_attrlen(nlh, sizeof(*ev))); + err = iscsi_set_iface_params(transport, ev, rlen); break; case ISCSI_UEVENT_PING: - err = iscsi_send_ping(transport, ev); + err = iscsi_send_ping(transport, ev, rlen); break; case ISCSI_UEVENT_GET_CHAP: err = iscsi_get_chap(transport, nlh); @@ -4009,13 +4035,10 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) err = iscsi_delete_chap(transport, ev); break; case ISCSI_UEVENT_SET_FLASHNODE_PARAMS: - err = iscsi_set_flashnode_param(transport, ev, - nlmsg_attrlen(nlh, - sizeof(*ev))); + err = iscsi_set_flashnode_param(transport, ev, rlen); break; case ISCSI_UEVENT_NEW_FLASHNODE: - err = iscsi_new_flashnode(transport, ev, - nlmsg_attrlen(nlh, sizeof(*ev))); + err = iscsi_new_flashnode(transport, ev, rlen); break; case ISCSI_UEVENT_DEL_FLASHNODE: err = iscsi_del_flashnode(transport, ev); @@ -4030,8 +4053,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) err = iscsi_logout_flashnode_sid(transport, ev); break; case ISCSI_UEVENT_SET_CHAP: - err = iscsi_set_chap(transport, ev, - nlmsg_attrlen(nlh, sizeof(*ev))); + err = iscsi_set_chap(transport, ev, rlen); break; case ISCSI_UEVENT_GET_HOST_STATS: err = iscsi_get_host_stats(transport, nlh); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3c668cfb146d390fa23b2bc1d13c7841cec79fc5..83b6a3f3863bc072df64dfe5ed34f7b52833ef2a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -104,19 +104,7 @@ static void sd_config_discard(struct scsi_disk *, unsigned int); static void sd_config_write_same(struct scsi_disk *); static int sd_revalidate_disk(struct gendisk *); static void sd_unlock_native_capacity(struct gendisk *disk); -static int sd_probe(struct device *); -static int sd_remove(struct device *); static void sd_shutdown(struct device *); -static int sd_suspend_system(struct device *); -static int sd_suspend_runtime(struct device *); -static int sd_resume_system(struct device *); -static int sd_resume_runtime(struct device *); -static void sd_rescan(struct device *); -static blk_status_t sd_init_command(struct scsi_cmnd *SCpnt); -static void sd_uninit_command(struct scsi_cmnd *SCpnt); -static int sd_done(struct scsi_cmnd *); -static void sd_eh_reset(struct scsi_cmnd *); -static int sd_eh_action(struct scsi_cmnd *, int); static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer); static void scsi_disk_release(struct device *cdev); @@ -213,18 +201,63 @@ cache_type_store(struct device *dev, struct device_attribute *attr, } static ssize_t -manage_start_stop_show(struct device *dev, struct device_attribute *attr, - char *buf) +manage_start_stop_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct scsi_disk *sdkp = to_scsi_disk(dev); struct scsi_device *sdp = sdkp->device; - return sprintf(buf, "%u\n", sdp->manage_start_stop); + return sysfs_emit(buf, "%u\n", + sdp->manage_system_start_stop && + sdp->manage_runtime_start_stop); } +static DEVICE_ATTR_RO(manage_start_stop); static ssize_t -manage_start_stop_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +manage_system_start_stop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + struct scsi_device *sdp = sdkp->device; + + return sysfs_emit(buf, "%u\n", sdp->manage_system_start_stop); +} + +static ssize_t +manage_system_start_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + struct scsi_device *sdp = sdkp->device; + bool v; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (kstrtobool(buf, &v)) + return -EINVAL; + + sdp->manage_system_start_stop = v; + + return count; +} +static DEVICE_ATTR_RW(manage_system_start_stop); + +static ssize_t +manage_runtime_start_stop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_disk *sdkp = to_scsi_disk(dev); + struct scsi_device *sdp = sdkp->device; + + return sysfs_emit(buf, "%u\n", sdp->manage_runtime_start_stop); +} + +static ssize_t +manage_runtime_start_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct scsi_disk *sdkp = to_scsi_disk(dev); struct scsi_device *sdp = sdkp->device; @@ -236,11 +269,11 @@ manage_start_stop_store(struct device *dev, struct device_attribute *attr, if (kstrtobool(buf, &v)) return -EINVAL; - sdp->manage_start_stop = v; + sdp->manage_runtime_start_stop = v; return count; } -static DEVICE_ATTR_RW(manage_start_stop); +static DEVICE_ATTR_RW(manage_runtime_start_stop); static ssize_t allow_restart_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -572,6 +605,8 @@ static struct attribute *sd_disk_attrs[] = { &dev_attr_FUA.attr, &dev_attr_allow_restart.attr, &dev_attr_manage_start_stop.attr, + &dev_attr_manage_system_start_stop.attr, + &dev_attr_manage_runtime_start_stop.attr, &dev_attr_protection_type.attr, &dev_attr_protection_mode.attr, &dev_attr_app_tag_own.attr, @@ -592,33 +627,6 @@ static struct class sd_disk_class = { .dev_groups = sd_disk_groups, }; -static const struct dev_pm_ops sd_pm_ops = { - .suspend = sd_suspend_system, - .resume = sd_resume_system, - .poweroff = sd_suspend_system, - .restore = sd_resume_system, - .runtime_suspend = sd_suspend_runtime, - .runtime_resume = sd_resume_runtime, -}; - -static struct scsi_driver sd_template = { - .gendrv = { - .name = "sd", - .owner = THIS_MODULE, - .probe = sd_probe, - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .remove = sd_remove, - .shutdown = sd_shutdown, - .pm = &sd_pm_ops, - }, - .rescan = sd_rescan, - .init_command = sd_init_command, - .uninit_command = sd_uninit_command, - .done = sd_done, - .eh_action = sd_eh_action, - .eh_reset = sd_eh_reset, -}; - /* * Don't request a new module, as that could deadlock in multipath * environment. @@ -3733,7 +3741,8 @@ static int sd_remove(struct device *dev) device_del(&sdkp->disk_dev); del_gendisk(sdkp->disk); - sd_shutdown(dev); + if (!sdkp->suspended) + sd_shutdown(dev); put_disk(sdkp->disk); return 0; @@ -3810,13 +3819,20 @@ static void sd_shutdown(struct device *dev) sd_sync_cache(sdkp, NULL); } - if (system_state != SYSTEM_RESTART && sdkp->device->manage_start_stop) { + if (system_state != SYSTEM_RESTART && + sdkp->device->manage_system_start_stop) { sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n"); sd_start_stop_device(sdkp, 0); } } -static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) +static inline bool sd_do_start_stop(struct scsi_device *sdev, bool runtime) +{ + return (sdev->manage_system_start_stop && !runtime) || + (sdev->manage_runtime_start_stop && runtime); +} + +static int sd_suspend_common(struct device *dev, bool runtime) { struct scsi_disk *sdkp = dev_get_drvdata(dev); struct scsi_sense_hdr sshdr; @@ -3848,15 +3864,18 @@ static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) } } - if (sdkp->device->manage_start_stop) { + if (sd_do_start_stop(sdkp->device, runtime)) { if (!sdkp->device->silence_suspend) sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n"); /* an error is not worth aborting a system sleep */ ret = sd_start_stop_device(sdkp, 0); - if (ignore_stop_errors) + if (!runtime) ret = 0; } + if (!ret) + sdkp->suspended = true; + return ret; } @@ -3865,15 +3884,15 @@ static int sd_suspend_system(struct device *dev) if (pm_runtime_suspended(dev)) return 0; - return sd_suspend_common(dev, true); + return sd_suspend_common(dev, false); } static int sd_suspend_runtime(struct device *dev) { - return sd_suspend_common(dev, false); + return sd_suspend_common(dev, true); } -static int sd_resume(struct device *dev) +static int sd_resume(struct device *dev, bool runtime) { struct scsi_disk *sdkp = dev_get_drvdata(dev); int ret = 0; @@ -3881,16 +3900,21 @@ static int sd_resume(struct device *dev) if (!sdkp) /* E.g.: runtime resume at the start of sd_probe() */ return 0; - if (!sdkp->device->manage_start_stop) + if (!sd_do_start_stop(sdkp->device, runtime)) { + sdkp->suspended = false; return 0; + } if (!sdkp->device->no_start_on_resume) { sd_printk(KERN_NOTICE, sdkp, "Starting disk\n"); ret = sd_start_stop_device(sdkp, 1); } - if (!ret) + if (!ret) { opal_unlock_from_suspend(sdkp->opal_dev); + sdkp->suspended = false; + } + return ret; } @@ -3899,7 +3923,7 @@ static int sd_resume_system(struct device *dev) if (pm_runtime_suspended(dev)) return 0; - return sd_resume(dev); + return sd_resume(dev, false); } static int sd_resume_runtime(struct device *dev) @@ -3926,9 +3950,36 @@ static int sd_resume_runtime(struct device *dev) "Failed to clear sense data\n"); } - return sd_resume(dev); + return sd_resume(dev, true); } +static const struct dev_pm_ops sd_pm_ops = { + .suspend = sd_suspend_system, + .resume = sd_resume_system, + .poweroff = sd_suspend_system, + .restore = sd_resume_system, + .runtime_suspend = sd_suspend_runtime, + .runtime_resume = sd_resume_runtime, +}; + +static struct scsi_driver sd_template = { + .gendrv = { + .name = "sd", + .owner = THIS_MODULE, + .probe = sd_probe, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .remove = sd_remove, + .shutdown = sd_shutdown, + .pm = &sd_pm_ops, + }, + .rescan = sd_rescan, + .init_command = sd_init_command, + .uninit_command = sd_uninit_command, + .done = sd_done, + .eh_action = sd_eh_action, + .eh_reset = sd_eh_reset, +}; + /** * init_sd - entry point for this driver (both when built in or when * a module). diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 5eea762f84d188e15b516b05f0f98ba3892209a5..409dda5350d10fb898b7bad49889ec165f257511 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -131,6 +131,7 @@ struct scsi_disk { u8 provisioning_mode; u8 zeroing_mode; u8 nr_actuators; /* Number of actuators */ + bool suspended; /* Disk is suspended (stopped) */ unsigned ATO : 1; /* state of disk ATO bit */ unsigned cache_override : 1; /* temp override of WCE,RCD */ unsigned WCE : 1; /* state of disk WCE bit */ diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index e392eaf5b2bf70cfac506019cad805cf401ca777..041940183516969318839f297583f16ab98b7b71 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -710,7 +710,7 @@ typedef u32 pqi_index_t; #define SOP_TMF_COMPLETE 0x0 #define SOP_TMF_REJECTED 0x4 #define SOP_TMF_FUNCTION_SUCCEEDED 0x8 -#define SOP_RC_INCORRECT_LOGICAL_UNIT 0x9 +#define SOP_TMF_INCORRECT_LOGICAL_UNIT 0x9 /* additional CDB bytes usage field codes */ #define SOP_ADDITIONAL_CDB_BYTES_0 0 /* 16-byte CDB */ @@ -1085,7 +1085,16 @@ struct pqi_stream_data { u32 last_accessed; }; -#define PQI_MAX_LUNS_PER_DEVICE 256 +#define PQI_MAX_LUNS_PER_DEVICE 256 + +struct pqi_tmf_work { + struct work_struct work_struct; + struct scsi_cmnd *scmd; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + u8 lun; + u8 scsi_opcode; +}; struct pqi_scsi_dev { int devtype; /* as reported by INQUIRY command */ @@ -1111,6 +1120,7 @@ struct pqi_scsi_dev { u8 erase_in_progress : 1; bool aio_enabled; /* only valid for physical disks */ bool in_remove; + bool in_reset[PQI_MAX_LUNS_PER_DEVICE]; bool device_offline; u8 vendor[8]; /* bytes 8-15 of inquiry data */ u8 model[16]; /* bytes 16-31 of inquiry data */ @@ -1149,6 +1159,8 @@ struct pqi_scsi_dev { struct pqi_stream_data stream_data[NUM_STREAMS_PER_LUN]; atomic_t scsi_cmds_outstanding[PQI_MAX_LUNS_PER_DEVICE]; unsigned int raid_bypass_cnt; + + struct pqi_tmf_work tmf_work[PQI_MAX_LUNS_PER_DEVICE]; }; /* VPD inquiry pages */ diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 6aaaa7ebca377bf4f1fb50b4bdd8f0d3e8924b34..9a58df9312fa7e4151ca07537391b030d91a2289 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -33,11 +33,11 @@ #define BUILD_TIMESTAMP #endif -#define DRIVER_VERSION "2.1.22-040" +#define DRIVER_VERSION "2.1.24-046" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 -#define DRIVER_RELEASE 22 -#define DRIVER_REVISION 40 +#define DRIVER_RELEASE 24 +#define DRIVER_REVISION 46 #define DRIVER_NAME "Microchip SmartPQI Driver (v" \ DRIVER_VERSION BUILD_TIMESTAMP ")" @@ -48,6 +48,8 @@ #define PQI_POST_RESET_DELAY_SECS 5 #define PQI_POST_OFA_RESET_DELAY_UPON_TIMEOUT_SECS 10 +#define PQI_NO_COMPLETION ((void *)-1) + MODULE_AUTHOR("Microchip"); MODULE_DESCRIPTION("Driver for Microchip Smart Family Controller version " DRIVER_VERSION); @@ -96,6 +98,7 @@ static int pqi_ofa_host_memory_update(struct pqi_ctrl_info *ctrl_info); static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun, unsigned long timeout_msecs); static void pqi_fail_all_outstanding_requests(struct pqi_ctrl_info *ctrl_info); +static void pqi_tmf_worker(struct work_struct *work); /* for flags argument to pqi_submit_raid_request_synchronous() */ #define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 @@ -455,6 +458,21 @@ static inline bool pqi_device_in_remove(struct pqi_scsi_dev *device) return device->in_remove; } +static inline void pqi_device_reset_start(struct pqi_scsi_dev *device, u8 lun) +{ + device->in_reset[lun] = true; +} + +static inline void pqi_device_reset_done(struct pqi_scsi_dev *device, u8 lun) +{ + device->in_reset[lun] = false; +} + +static inline bool pqi_device_in_reset(struct pqi_scsi_dev *device, u8 lun) +{ + return device->in_reset[lun]; +} + static inline int pqi_event_type_to_event_index(unsigned int event_type) { int index; @@ -2137,6 +2155,15 @@ static inline bool pqi_is_device_added(struct pqi_scsi_dev *device) return device->sdev != NULL; } +static inline void pqi_init_device_tmf_work(struct pqi_scsi_dev *device) +{ + unsigned int lun; + struct pqi_tmf_work *tmf_work; + + for (lun = 0, tmf_work = device->tmf_work; lun < PQI_MAX_LUNS_PER_DEVICE; lun++, tmf_work++) + INIT_WORK(&tmf_work->work_struct, pqi_tmf_worker); +} + static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *new_device_list[], unsigned int num_new_devices) { @@ -2217,6 +2244,7 @@ static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, list_add_tail(&device->add_list_entry, &add_list); /* To prevent this device structure from being freed later. */ device->keep_device = true; + pqi_init_device_tmf_work(device); } spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); @@ -2257,7 +2285,7 @@ static void pqi_update_device_list(struct pqi_ctrl_info *ctrl_info, device->advertised_queue_depth = device->queue_depth; scsi_change_queue_depth(device->sdev, device->advertised_queue_depth); if (device->rescan) { - scsi_rescan_device(&device->sdev->sdev_gendev); + scsi_rescan_device(device->sdev); device->rescan = false; } } @@ -3330,7 +3358,7 @@ static int pqi_interpret_task_management_response(struct pqi_ctrl_info *ctrl_inf case SOP_TMF_REJECTED: rc = -EAGAIN; break; - case SOP_RC_INCORRECT_LOGICAL_UNIT: + case SOP_TMF_INCORRECT_LOGICAL_UNIT: rc = -ENODEV; break; default: @@ -5628,7 +5656,6 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, int rc; struct pqi_io_request *io_request; struct pqi_aio_path_request *request; - struct pqi_scsi_dev *device; io_request = pqi_alloc_io_request(ctrl_info, scmd); if (!io_request) @@ -5648,9 +5675,8 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, request->command_priority = io_high_prio; put_unaligned_le16(io_request->index, &request->request_id); request->error_index = request->request_id; - device = scmd->device->hostdata; - if (!pqi_is_logical_device(device) && ctrl_info->multi_lun_device_supported) - put_unaligned_le64(((scmd->device->lun) << 8), &request->lun_number); + if (!raid_bypass && ctrl_info->multi_lun_device_supported) + put_unaligned_le64(scmd->device->lun << 8, &request->lun_number); if (cdb_length > sizeof(request->cdb)) cdb_length = sizeof(request->cdb); request->cdb_length = cdb_length; @@ -5850,6 +5876,7 @@ static inline bool pqi_is_bypass_eligible_request(struct scsi_cmnd *scmd) void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd) { struct pqi_scsi_dev *device; + struct completion *wait; if (!scmd->device) { set_host_byte(scmd, DID_NO_CONNECT); @@ -5863,6 +5890,10 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd) } atomic_dec(&device->scsi_cmds_outstanding[scmd->device->lun]); + + wait = (struct completion *)xchg(&scmd->host_scribble, NULL); + if (wait != PQI_NO_COMPLETION) + complete(wait); } static bool pqi_is_parity_write_stream(struct pqi_ctrl_info *ctrl_info, @@ -5948,6 +5979,9 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scm u16 hw_queue; struct pqi_queue_group *queue_group; bool raid_bypassed; + u8 lun; + + scmd->host_scribble = PQI_NO_COMPLETION; device = scmd->device->hostdata; @@ -5957,7 +5991,9 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scm return 0; } - atomic_inc(&device->scsi_cmds_outstanding[scmd->device->lun]); + lun = (u8)scmd->device->lun; + + atomic_inc(&device->scsi_cmds_outstanding[lun]); ctrl_info = shost_to_hba(shost); @@ -5967,7 +6003,7 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scm return 0; } - if (pqi_ctrl_blocked(ctrl_info)) { + if (pqi_ctrl_blocked(ctrl_info) || pqi_device_in_reset(device, lun)) { rc = SCSI_MLQUEUE_HOST_BUSY; goto out; } @@ -6002,8 +6038,10 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scm } out: - if (rc) - atomic_dec(&device->scsi_cmds_outstanding[scmd->device->lun]); + if (rc) { + scmd->host_scribble = NULL; + atomic_dec(&device->scsi_cmds_outstanding[lun]); + } return rc; } @@ -6097,7 +6135,7 @@ static int pqi_wait_until_inbound_queues_empty(struct pqi_ctrl_info *ctrl_info) } static void pqi_fail_io_queued_for_device(struct pqi_ctrl_info *ctrl_info, - struct pqi_scsi_dev *device) + struct pqi_scsi_dev *device, u8 lun) { unsigned int i; unsigned int path; @@ -6127,6 +6165,9 @@ static void pqi_fail_io_queued_for_device(struct pqi_ctrl_info *ctrl_info, if (scsi_device != device) continue; + if ((u8)scmd->device->lun != lun) + continue; + list_del(&io_request->request_list_entry); set_host_byte(scmd, DID_RESET); pqi_free_io_request(io_request); @@ -6224,15 +6265,13 @@ static int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info, #define PQI_LUN_RESET_FIRMWARE_TIMEOUT_SECS 30 -static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd) +static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun) { int rc; struct pqi_io_request *io_request; DECLARE_COMPLETION_ONSTACK(wait); struct pqi_task_management_request *request; - struct pqi_scsi_dev *device; - device = scmd->device->hostdata; io_request = pqi_alloc_io_request(ctrl_info, NULL); io_request->io_complete_callback = pqi_lun_reset_complete; io_request->context = &wait; @@ -6247,7 +6286,7 @@ static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd memcpy(request->lun_number, device->scsi3addr, sizeof(request->lun_number)); if (!pqi_is_logical_device(device) && ctrl_info->multi_lun_device_supported) - request->ml_device_lun_number = (u8)scmd->device->lun; + request->ml_device_lun_number = lun; request->task_management_function = SOP_TASK_MANAGEMENT_LUN_RESET; if (ctrl_info->tmf_iu_timeout_supported) put_unaligned_le16(PQI_LUN_RESET_FIRMWARE_TIMEOUT_SECS, &request->timeout); @@ -6255,7 +6294,7 @@ static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd pqi_start_io(ctrl_info, &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, io_request); - rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, (u8)scmd->device->lun, &wait); + rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, lun, &wait); if (rc == 0) rc = io_request->status; @@ -6269,18 +6308,16 @@ static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd #define PQI_LUN_RESET_PENDING_IO_TIMEOUT_MSECS (10 * 60 * 1000) #define PQI_LUN_RESET_FAILED_PENDING_IO_TIMEOUT_MSECS (2 * 60 * 1000) -static int pqi_lun_reset_with_retries(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd) +static int pqi_lun_reset_with_retries(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun) { int reset_rc; int wait_rc; unsigned int retries; unsigned long timeout_msecs; - struct pqi_scsi_dev *device; - device = scmd->device->hostdata; for (retries = 0;;) { - reset_rc = pqi_lun_reset(ctrl_info, scmd); - if (reset_rc == 0 || reset_rc == -ENODEV || ++retries > PQI_LUN_RESET_RETRIES) + reset_rc = pqi_lun_reset(ctrl_info, device, lun); + if (reset_rc == 0 || reset_rc == -ENODEV || reset_rc == -ENXIO || ++retries > PQI_LUN_RESET_RETRIES) break; msleep(PQI_LUN_RESET_RETRY_INTERVAL_MSECS); } @@ -6288,60 +6325,51 @@ static int pqi_lun_reset_with_retries(struct pqi_ctrl_info *ctrl_info, struct sc timeout_msecs = reset_rc ? PQI_LUN_RESET_FAILED_PENDING_IO_TIMEOUT_MSECS : PQI_LUN_RESET_PENDING_IO_TIMEOUT_MSECS; - wait_rc = pqi_device_wait_for_pending_io(ctrl_info, device, scmd->device->lun, timeout_msecs); + wait_rc = pqi_device_wait_for_pending_io(ctrl_info, device, lun, timeout_msecs); if (wait_rc && reset_rc == 0) reset_rc = wait_rc; return reset_rc == 0 ? SUCCESS : FAILED; } -static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd) +static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun) { int rc; - struct pqi_scsi_dev *device; - device = scmd->device->hostdata; pqi_ctrl_block_requests(ctrl_info); pqi_ctrl_wait_until_quiesced(ctrl_info); - pqi_fail_io_queued_for_device(ctrl_info, device); + pqi_fail_io_queued_for_device(ctrl_info, device, lun); rc = pqi_wait_until_inbound_queues_empty(ctrl_info); + pqi_device_reset_start(device, lun); + pqi_ctrl_unblock_requests(ctrl_info); if (rc) rc = FAILED; else - rc = pqi_lun_reset_with_retries(ctrl_info, scmd); - pqi_ctrl_unblock_requests(ctrl_info); + rc = pqi_lun_reset_with_retries(ctrl_info, device, lun); + pqi_device_reset_done(device, lun); return rc; } -static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) +static int pqi_device_reset_handler(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun, struct scsi_cmnd *scmd, u8 scsi_opcode) { int rc; - struct Scsi_Host *shost; - struct pqi_ctrl_info *ctrl_info; - struct pqi_scsi_dev *device; - - shost = scmd->device->host; - ctrl_info = shost_to_hba(shost); - device = scmd->device->hostdata; mutex_lock(&ctrl_info->lun_reset_mutex); dev_err(&ctrl_info->pci_dev->dev, - "resetting scsi %d:%d:%d:%d due to cmd 0x%02x\n", - shost->host_no, - device->bus, device->target, (u32)scmd->device->lun, - scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff); + "resetting scsi %d:%d:%d:%u SCSI cmd at %p due to cmd opcode 0x%02x\n", + ctrl_info->scsi_host->host_no, device->bus, device->target, lun, scmd, scsi_opcode); pqi_check_ctrl_health(ctrl_info); if (pqi_ctrl_offline(ctrl_info)) rc = FAILED; else - rc = pqi_device_reset(ctrl_info, scmd); + rc = pqi_device_reset(ctrl_info, device, lun); dev_err(&ctrl_info->pci_dev->dev, - "reset of scsi %d:%d:%d:%d: %s\n", - shost->host_no, device->bus, device->target, (u32)scmd->device->lun, + "reset of scsi %d:%d:%d:%u: %s\n", + ctrl_info->scsi_host->host_no, device->bus, device->target, lun, rc == SUCCESS ? "SUCCESS" : "FAILED"); mutex_unlock(&ctrl_info->lun_reset_mutex); @@ -6349,6 +6377,77 @@ static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) return rc; } +static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) +{ + struct Scsi_Host *shost; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + u8 scsi_opcode; + + shost = scmd->device->host; + ctrl_info = shost_to_hba(shost); + device = scmd->device->hostdata; + scsi_opcode = scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff; + + return pqi_device_reset_handler(ctrl_info, device, (u8)scmd->device->lun, scmd, scsi_opcode); +} + +static void pqi_tmf_worker(struct work_struct *work) +{ + struct pqi_tmf_work *tmf_work; + struct scsi_cmnd *scmd; + + tmf_work = container_of(work, struct pqi_tmf_work, work_struct); + scmd = (struct scsi_cmnd *)xchg(&tmf_work->scmd, NULL); + + pqi_device_reset_handler(tmf_work->ctrl_info, tmf_work->device, tmf_work->lun, scmd, tmf_work->scsi_opcode); +} + +static int pqi_eh_abort_handler(struct scsi_cmnd *scmd) +{ + struct Scsi_Host *shost; + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + struct pqi_tmf_work *tmf_work; + DECLARE_COMPLETION_ONSTACK(wait); + + shost = scmd->device->host; + ctrl_info = shost_to_hba(shost); + device = scmd->device->hostdata; + + dev_err(&ctrl_info->pci_dev->dev, + "attempting TASK ABORT on scsi %d:%d:%d:%d for SCSI cmd at %p\n", + shost->host_no, device->bus, device->target, (int)scmd->device->lun, scmd); + + if (cmpxchg(&scmd->host_scribble, PQI_NO_COMPLETION, (void *)&wait) == NULL) { + dev_err(&ctrl_info->pci_dev->dev, + "scsi %d:%d:%d:%d for SCSI cmd at %p already completed\n", + shost->host_no, device->bus, device->target, (int)scmd->device->lun, scmd); + scmd->result = DID_RESET << 16; + goto out; + } + + tmf_work = &device->tmf_work[scmd->device->lun]; + + if (cmpxchg(&tmf_work->scmd, NULL, scmd) == NULL) { + tmf_work->ctrl_info = ctrl_info; + tmf_work->device = device; + tmf_work->lun = (u8)scmd->device->lun; + tmf_work->scsi_opcode = scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff; + schedule_work(&tmf_work->work_struct); + } + + wait_for_completion(&wait); + + dev_err(&ctrl_info->pci_dev->dev, + "TASK ABORT on scsi %d:%d:%d:%d for SCSI cmd at %p: SUCCESS\n", + shost->host_no, device->bus, device->target, (int)scmd->device->lun, scmd); + +out: + + return SUCCESS; +} + static int pqi_slave_alloc(struct scsi_device *sdev) { struct pqi_scsi_dev *device; @@ -6470,21 +6569,21 @@ static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *ar struct pci_dev *pci_dev; u32 subsystem_vendor; u32 subsystem_device; - cciss_pci_info_struct pciinfo; + cciss_pci_info_struct pci_info; if (!arg) return -EINVAL; pci_dev = ctrl_info->pci_dev; - pciinfo.domain = pci_domain_nr(pci_dev->bus); - pciinfo.bus = pci_dev->bus->number; - pciinfo.dev_fn = pci_dev->devfn; + pci_info.domain = pci_domain_nr(pci_dev->bus); + pci_info.bus = pci_dev->bus->number; + pci_info.dev_fn = pci_dev->devfn; subsystem_vendor = pci_dev->subsystem_vendor; subsystem_device = pci_dev->subsystem_device; - pciinfo.board_id = ((subsystem_device << 16) & 0xffff0000) | subsystem_vendor; + pci_info.board_id = ((subsystem_device << 16) & 0xffff0000) | subsystem_vendor; - if (copy_to_user(arg, &pciinfo, sizeof(pciinfo))) + if (copy_to_user(arg, &pci_info, sizeof(pci_info))) return -EFAULT; return 0; @@ -7362,6 +7461,7 @@ static const struct scsi_host_template pqi_driver_template = { .scan_finished = pqi_scan_finished, .this_id = -1, .eh_device_reset_handler = pqi_eh_device_reset_handler, + .eh_abort_handler = pqi_eh_abort_handler, .ioctl = pqi_ioctl, .slave_alloc = pqi_slave_alloc, .slave_configure = pqi_slave_configure, @@ -8904,6 +9004,52 @@ static void pqi_ctrl_offline_worker(struct work_struct *work) pqi_take_ctrl_offline_deferred(ctrl_info); } +static char *pqi_ctrl_shutdown_reason_to_string(enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason) +{ + char *string; + + switch (ctrl_shutdown_reason) { + case PQI_IQ_NOT_DRAINED_TIMEOUT: + string = "inbound queue not drained timeout"; + break; + case PQI_LUN_RESET_TIMEOUT: + string = "LUN reset timeout"; + break; + case PQI_IO_PENDING_POST_LUN_RESET_TIMEOUT: + string = "I/O pending timeout after LUN reset"; + break; + case PQI_NO_HEARTBEAT: + string = "no controller heartbeat detected"; + break; + case PQI_FIRMWARE_KERNEL_NOT_UP: + string = "firmware kernel not ready"; + break; + case PQI_OFA_RESPONSE_TIMEOUT: + string = "OFA response timeout"; + break; + case PQI_INVALID_REQ_ID: + string = "invalid request ID"; + break; + case PQI_UNMATCHED_REQ_ID: + string = "unmatched request ID"; + break; + case PQI_IO_PI_OUT_OF_RANGE: + string = "I/O queue producer index out of range"; + break; + case PQI_EVENT_PI_OUT_OF_RANGE: + string = "event queue producer index out of range"; + break; + case PQI_UNEXPECTED_IU_TYPE: + string = "unexpected IU type"; + break; + default: + string = "unknown reason"; + break; + } + + return string; +} + static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info, enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason) { @@ -8916,7 +9062,9 @@ static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info, if (!pqi_disable_ctrl_shutdown) sis_shutdown_ctrl(ctrl_info, ctrl_shutdown_reason); pci_disable_device(ctrl_info->pci_dev); - dev_err(&ctrl_info->pci_dev->dev, "controller offline\n"); + dev_err(&ctrl_info->pci_dev->dev, + "controller offline: reason code 0x%x (%s)\n", + ctrl_shutdown_reason, pqi_ctrl_shutdown_reason_to_string(ctrl_shutdown_reason)); schedule_work(&ctrl_info->ctrl_offline_work); } @@ -9062,7 +9210,7 @@ static void pqi_shutdown(struct pci_dev *pci_dev) rc = pqi_flush_cache(ctrl_info, shutdown_event); if (rc) dev_err(&pci_dev->dev, - "unable to flush controller cache\n"); + "unable to flush controller cache during shutdown\n"); pqi_crash_if_pending_command(ctrl_info); pqi_reset(ctrl_info); diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 14d7981ddcdd192ac729ed41f9387d38900e34f6..338aa8c429682ce2dd58c45b342228efadb81783 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -414,6 +414,8 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) if (cmdstatp->have_sense && cmdstatp->sense_hdr.asc == 0 && cmdstatp->sense_hdr.ascq == 0x17) STp->cleaning_req = 1; /* ASC and ASCQ => cleaning requested */ + if (cmdstatp->have_sense && scode == UNIT_ATTENTION && cmdstatp->sense_hdr.asc == 0x29) + STp->pos_unknown = 1; /* ASC => power on / reset */ STp->pos_unknown |= STp->device->was_reset; diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 047ffaf7d42a9e7c37bc541f12e24df00ae497c5..a95936b18f695e3ef796098866ea07101e9e346d 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -316,6 +316,9 @@ enum storvsc_request_type { #define SRB_STATUS_ABORTED 0x02 #define SRB_STATUS_ERROR 0x04 #define SRB_STATUS_INVALID_REQUEST 0x06 +#define SRB_STATUS_TIMEOUT 0x09 +#define SRB_STATUS_SELECTION_TIMEOUT 0x0A +#define SRB_STATUS_BUS_RESET 0x0E #define SRB_STATUS_DATA_OVERRUN 0x12 #define SRB_STATUS_INVALID_LUN 0x20 #define SRB_STATUS_INTERNAL_ERROR 0x30 @@ -472,7 +475,7 @@ static void storvsc_device_scan(struct work_struct *work) sdev = scsi_device_lookup(wrk->host, 0, wrk->tgt_id, wrk->lun); if (!sdev) goto done; - scsi_rescan_device(&sdev->sdev_gendev); + scsi_rescan_device(sdev); scsi_device_put(sdev); done: @@ -981,6 +984,10 @@ static void storvsc_handle_error(struct vmscsi_request *vm_srb, case SRB_STATUS_ABORTED: case SRB_STATUS_INVALID_REQUEST: case SRB_STATUS_INTERNAL_ERROR: + case SRB_STATUS_TIMEOUT: + case SRB_STATUS_SELECTION_TIMEOUT: + case SRB_STATUS_BUS_RESET: + case SRB_STATUS_DATA_OVERRUN: if (vm_srb->srb_status & SRB_STATUS_AUTOSENSE_VALID) { /* Check for capacity change */ if ((asc == 0x2a) && (ascq == 0x9)) { diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index d06e933191a2183c41fedee812355aa0414a6e74..afa9d02a33eca3d6094db6001cff2b429662ed70 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -12,7 +12,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index bd5633667d01567cd408475f3f9c3c27a98a197e..9d1bdcdc13312988b2ee052c3736bb548f21ffba 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -325,7 +325,7 @@ static void virtscsi_handle_param_change(struct virtio_scsi *vscsi, /* Handle "Parameters changed", "Mode parameters changed", and "Capacity data has changed". */ if (asc == 0x2a && (ascq == 0x00 || ascq == 0x01 || ascq == 0x09)) - scsi_rescan_device(&sdev->sdev_gendev); + scsi_rescan_device(sdev); scsi_device_put(sdev); } diff --git a/drivers/scsi/xen-scsifront.c b/drivers/scsi/xen-scsifront.c index caae61aa2afe00226deecdcda2d6d3daed20b6b2..9ec55ddc120428ba20567b904c8f86cfee0dab36 100644 --- a/drivers/scsi/xen-scsifront.c +++ b/drivers/scsi/xen-scsifront.c @@ -743,7 +743,7 @@ static int scsifront_sdev_configure(struct scsi_device *sdev) if (info->host_active == STATE_ERROR) return -EIO; - if (info && current == info->curr) { + if (current == info->curr) { err = xenbus_printf(XBT_NIL, info->dev->nodename, info->dev_state_path, "%d", XenbusStateConnected); if (err) { @@ -761,7 +761,7 @@ static void scsifront_sdev_destroy(struct scsi_device *sdev) struct vscsifrnt_info *info = shost_priv(sdev->host); int err; - if (info && current == info->curr) { + if (current == info->curr) { err = xenbus_printf(XBT_NIL, info->dev->nodename, info->dev_state_path, "%d", XenbusStateClosed); if (err) @@ -903,7 +903,7 @@ static int scsifront_probe(struct xenbus_device *dev, xenbus_dev_fatal(dev, err, "fail to allocate scsi host"); return err; } - info = (struct vscsifrnt_info *)host->hostdata; + info = shost_priv(host); dev_set_drvdata(&dev->dev, info); info->dev = dev; diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index 1dcd243df5677aa39dcbc76b876cdbbd435ceb43..ec87d9d878f30de6b2cc0c63d575059360ee8798 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -100,6 +100,7 @@ static void __init imx8mm_soc_uid(void) { void __iomem *ocotp_base; struct device_node *np; + struct clk *clk; u32 offset = of_machine_is_compatible("fsl,imx8mp") ? IMX8MP_OCOTP_UID_OFFSET : 0; @@ -109,11 +110,20 @@ static void __init imx8mm_soc_uid(void) ocotp_base = of_iomap(np, 0); WARN_ON(!ocotp_base); + clk = of_clk_get_by_name(np, NULL); + if (IS_ERR(clk)) { + WARN_ON(IS_ERR(clk)); + return; + } + + clk_prepare_enable(clk); soc_uid = readl_relaxed(ocotp_base + OCOTP_UID_HIGH + offset); soc_uid <<= 32; soc_uid |= readl_relaxed(ocotp_base + OCOTP_UID_LOW + offset); + clk_disable_unprepare(clk); + clk_put(clk); iounmap(ocotp_base); of_node_put(np); } diff --git a/drivers/soc/loongson/Kconfig b/drivers/soc/loongson/Kconfig index 314e13bb3e01c52ce9cc1c7829eb3cc4dde2c17a..368344943a93c5c08ef93a513a6b84eb4acb48c1 100644 --- a/drivers/soc/loongson/Kconfig +++ b/drivers/soc/loongson/Kconfig @@ -20,6 +20,7 @@ config LOONGSON2_GUTS config LOONGSON2_PM bool "Loongson-2 SoC Power Management Controller Driver" depends on LOONGARCH && OF + depends on INPUT=y help The Loongson-2's power management controller was ACPI, supports ACPI S2Idle (Suspend To Idle), ACPI S3 (Suspend To RAM), ACPI S4 (Suspend To diff --git a/drivers/soc/loongson/loongson2_guts.c b/drivers/soc/loongson/loongson2_guts.c index bace4bc8e03b01a372fffd80a37a95a7244205db..9a469779eea75bd77518fe1934252d7a415a9703 100644 --- a/drivers/soc/loongson/loongson2_guts.c +++ b/drivers/soc/loongson/loongson2_guts.c @@ -70,7 +70,7 @@ static const struct loongson2_soc_die_attr *loongson2_soc_die_match( if (matches->svr == (svr & matches->mask)) return matches; matches++; - }; + } return NULL; } @@ -94,7 +94,6 @@ static int loongson2_guts_probe(struct platform_device *pdev) { struct device_node *root, *np = pdev->dev.of_node; struct device *dev = &pdev->dev; - struct resource *res; const struct loongson2_soc_die_attr *soc_die; const char *machine; u32 svr; @@ -106,8 +105,7 @@ static int loongson2_guts_probe(struct platform_device *pdev) guts->little_endian = of_property_read_bool(np, "little-endian"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - guts->regs = ioremap(res->start, res->end - res->start + 1); + guts->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(guts->regs)) return PTR_ERR(guts->regs); diff --git a/drivers/soc/loongson/loongson2_pm.c b/drivers/soc/loongson/loongson2_pm.c index 796add6e8b63d43e86092e12e54ea5dd2c242437..b8e5e1e3528a6ee8252c24c59fc1a1cc8fafae69 100644 --- a/drivers/soc/loongson/loongson2_pm.c +++ b/drivers/soc/loongson/loongson2_pm.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -192,12 +193,16 @@ static int loongson2_pm_probe(struct platform_device *pdev) if (loongson_sysconf.suspend_addr) suspend_set_ops(&loongson2_suspend_ops); + /* Populate children */ + retval = devm_of_platform_populate(dev); + if (retval) + dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n"); + return 0; } static const struct of_device_id loongson2_pm_match[] = { { .compatible = "loongson,ls2k0500-pmc", }, - { .compatible = "loongson,ls2k1000-pmc", }, {}, }; diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index de31589ed05404a83771d020c0ace184c66efc29..5a75ab64d1ed33d7b28bc5e70e7b1f3a9b9a9402 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -334,6 +334,11 @@ if RISCV config ARCH_R9A07G043 bool "RISC-V Platform support for RZ/Five" select ARCH_RZG2L + select AX45MP_L2_CACHE if RISCV_DMA_NONCOHERENT + select DMA_GLOBAL_POOL + select ERRATA_ANDES if RISCV_SBI + select ERRATA_ANDES_CMO if ERRATA_ANDES + help This enables support for the Renesas RZ/Five SoC. diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 1cc2281cb37059544c3b02b3f31ce7862e6ad9c2..1720031f35a358a1defeddc656b6cc20035d38d2 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -13,7 +13,6 @@ #include "sysfs_local.h" static DEFINE_IDA(sdw_bus_ida); -static DEFINE_IDA(sdw_peripheral_ida); static int sdw_get_id(struct sdw_bus *bus) { @@ -194,8 +193,8 @@ static int sdw_delete_slave(struct device *dev, void *data) if (slave->dev_num) { /* clear dev_num if assigned */ clear_bit(slave->dev_num, bus->assigned); - if (bus->dev_num_ida_min) - ida_free(&sdw_peripheral_ida, slave->dev_num); + if (bus->ops && bus->ops->put_device_num) + bus->ops->put_device_num(bus, slave); } list_del_init(&slave->node); mutex_unlock(&bus->bus_lock); @@ -739,16 +738,15 @@ EXPORT_SYMBOL(sdw_compare_devid); /* called with bus_lock held */ static int sdw_get_device_num(struct sdw_slave *slave) { + struct sdw_bus *bus = slave->bus; int bit; - if (slave->bus->dev_num_ida_min) { - bit = ida_alloc_range(&sdw_peripheral_ida, - slave->bus->dev_num_ida_min, SDW_MAX_DEVICES, - GFP_KERNEL); + if (bus->ops && bus->ops->get_device_num) { + bit = bus->ops->get_device_num(bus, slave); if (bit < 0) goto err; } else { - bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES); + bit = find_first_zero_bit(bus->assigned, SDW_MAX_DEVICES); if (bit == SDW_MAX_DEVICES) { bit = -ENODEV; goto err; @@ -759,7 +757,7 @@ static int sdw_get_device_num(struct sdw_slave *slave) * Do not update dev_num in Slave data structure here, * Update once program dev_num is successful */ - set_bit(bit, slave->bus->assigned); + set_bit(bit, bus->assigned); err: return bit; @@ -810,7 +808,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave) slave->dev_num = slave->dev_num_sticky; if (bus->ops && bus->ops->new_peripheral_assigned) - bus->ops->new_peripheral_assigned(bus, dev_num); + bus->ops->new_peripheral_assigned(bus, slave, dev_num); return 0; } diff --git a/drivers/soundwire/intel_ace2x.c b/drivers/soundwire/intel_ace2x.c index 1be0bea5f40fa9b8c39cfc16258988e1fdc42e96..a9d25ae0b73fec3808f22f23b6e28146f592ad21 100644 --- a/drivers/soundwire/intel_ace2x.c +++ b/drivers/soundwire/intel_ace2x.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "cadence_master.h" #include "bus.h" @@ -191,10 +192,292 @@ static bool intel_check_cmdsync_unlocked(struct sdw_intel *sdw) return hdac_bus_eml_sdw_check_cmdsync_unlocked(sdw->link_res->hbus); } +/* DAI callbacks */ +static int intel_params_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + struct snd_pcm_hw_params *hw_params, + int link_id, int alh_stream_id) +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_params_data params_data; + + params_data.substream = substream; + params_data.dai = dai; + params_data.hw_params = hw_params; + params_data.link_id = link_id; + params_data.alh_stream_id = alh_stream_id; + + if (res->ops && res->ops->params_stream && res->dev) + return res->ops->params_stream(res->dev, + ¶ms_data); + return -EIO; +} + +static int intel_free_stream(struct sdw_intel *sdw, + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai, + int link_id) + +{ + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_intel_stream_free_data free_data; + + free_data.substream = substream; + free_data.dai = dai; + free_data.link_id = link_id; + + if (res->ops && res->ops->free_stream && res->dev) + return res->ops->free_stream(res->dev, + &free_data); + + return 0; +} + /* * DAI operations */ +static int intel_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dai_runtime *dai_runtime; + struct sdw_cdns_pdi *pdi; + struct sdw_stream_config sconfig; + struct sdw_port_config *pconfig; + int ch, dir; + int ret; + + dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) + return -EIO; + + ch = params_channels(params); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = SDW_DATA_DIR_RX; + else + dir = SDW_DATA_DIR_TX; + + pdi = sdw_cdns_alloc_pdi(cdns, &cdns->pcm, ch, dir, dai->id); + + if (!pdi) { + ret = -EINVAL; + goto error; + } + + /* the SHIM will be configured in the callback functions */ + + sdw_cdns_config_stream(cdns, ch, dir, pdi); + + /* store pdi and state, may be needed in prepare step */ + dai_runtime->paused = false; + dai_runtime->suspended = false; + dai_runtime->pdi = pdi; + + /* Inform DSP about PDI stream number */ + ret = intel_params_stream(sdw, substream, dai, params, + sdw->instance, + pdi->intel_alh_id); + if (ret) + goto error; + + sconfig.direction = dir; + sconfig.ch_count = ch; + sconfig.frame_rate = params_rate(params); + sconfig.type = dai_runtime->stream_type; + + sconfig.bps = snd_pcm_format_width(params_format(params)); + + /* Port configuration */ + pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL); + if (!pconfig) { + ret = -ENOMEM; + goto error; + } + + pconfig->num = pdi->num; + pconfig->ch_mask = (1 << ch) - 1; + + ret = sdw_stream_add_master(&cdns->bus, &sconfig, + pconfig, 1, dai_runtime->stream); + if (ret) + dev_err(cdns->dev, "add master to stream failed:%d\n", ret); + + kfree(pconfig); +error: + return ret; +} + +static int intel_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dai_runtime *dai_runtime; + int ch, dir; + int ret = 0; + + dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) { + dev_err(dai->dev, "failed to get dai runtime in %s\n", + __func__); + return -EIO; + } + + if (dai_runtime->suspended) { + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_pcm_hw_params *hw_params; + + hw_params = &rtd->dpcm[substream->stream].hw_params; + + dai_runtime->suspended = false; + + /* + * .prepare() is called after system resume, where we + * need to reinitialize the SHIM/ALH/Cadence IP. + * .prepare() is also called to deal with underflows, + * but in those cases we cannot touch ALH/SHIM + * registers + */ + + /* configure stream */ + ch = params_channels(hw_params); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + dir = SDW_DATA_DIR_RX; + else + dir = SDW_DATA_DIR_TX; + + /* the SHIM will be configured in the callback functions */ + + sdw_cdns_config_stream(cdns, ch, dir, dai_runtime->pdi); + + /* Inform DSP about PDI stream number */ + ret = intel_params_stream(sdw, substream, dai, + hw_params, + sdw->instance, + dai_runtime->pdi->intel_alh_id); + } + + return ret; +} + +static int +intel_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_cdns_dai_runtime *dai_runtime; + int ret; + + dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) + return -EIO; + + /* + * The sdw stream state will transition to RELEASED when stream-> + * master_list is empty. So the stream state will transition to + * DEPREPARED for the first cpu-dai and to RELEASED for the last + * cpu-dai. + */ + ret = sdw_stream_remove_master(&cdns->bus, dai_runtime->stream); + if (ret < 0) { + dev_err(dai->dev, "remove master from stream %s failed: %d\n", + dai_runtime->stream->name, ret); + return ret; + } + + ret = intel_free_stream(sdw, substream, dai, sdw->instance); + if (ret < 0) { + dev_err(dai->dev, "intel_free_stream: failed %d\n", ret); + return ret; + } + + dai_runtime->pdi = NULL; + + return 0; +} + +static int intel_pcm_set_sdw_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + return cdns_set_sdw_stream(dai, stream, direction); +} + +static void *intel_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_cdns_dai_runtime *dai_runtime; + + dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) + return ERR_PTR(-EINVAL); + + return dai_runtime->stream; +} + +static int intel_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +{ + struct sdw_cdns *cdns = snd_soc_dai_get_drvdata(dai); + struct sdw_intel *sdw = cdns_to_intel(cdns); + struct sdw_intel_link_res *res = sdw->link_res; + struct sdw_cdns_dai_runtime *dai_runtime; + int ret = 0; + + /* + * The .trigger callback is used to program HDaudio DMA and send required IPC to audio + * firmware. + */ + if (res->ops && res->ops->trigger) { + ret = res->ops->trigger(substream, cmd, dai); + if (ret < 0) + return ret; + } + + dai_runtime = cdns->dai_runtime_array[dai->id]; + if (!dai_runtime) { + dev_err(dai->dev, "failed to get dai runtime in %s\n", + __func__); + return -EIO; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_SUSPEND: + + /* + * The .prepare callback is used to deal with xruns and resume operations. + * In the case of xruns, the DMAs and SHIM registers cannot be touched, + * but for resume operations the DMAs and SHIM registers need to be initialized. + * the .trigger callback is used to track the suspend case only. + */ + + dai_runtime->suspended = true; + + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dai_runtime->paused = true; + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dai_runtime->paused = false; + break; + default: + break; + } + + return ret; +} + static const struct snd_soc_dai_ops intel_pcm_dai_ops = { + .hw_params = intel_hw_params, + .prepare = intel_prepare, + .hw_free = intel_hw_free, + .trigger = intel_trigger, + .set_stream = intel_pcm_set_sdw_stream, + .get_stream = intel_get_sdw_stream, }; static const struct snd_soc_component_driver dai_component = { diff --git a/drivers/soundwire/intel_auxdevice.c b/drivers/soundwire/intel_auxdevice.c index 0daa6ca9a224e1f92249bb4b3bc95e7e2c7d8fa5..7f15e3549e539d053a82b63b8346a362c6d0145c 100644 --- a/drivers/soundwire/intel_auxdevice.c +++ b/drivers/soundwire/intel_auxdevice.c @@ -23,9 +23,6 @@ #include "intel.h" #include "intel_auxdevice.h" -/* IDA min selected to avoid conflicts with HDaudio/iDISP SDI values */ -#define INTEL_DEV_NUM_IDA_MIN 4 - #define INTEL_MASTER_SUSPEND_DELAY_MS 3000 /* @@ -44,6 +41,39 @@ static int md_flags; module_param_named(sdw_md_flags, md_flags, int, 0444); MODULE_PARM_DESC(sdw_md_flags, "SoundWire Intel Master device flags (0x0 all off)"); +struct wake_capable_part { + const u16 mfg_id; + const u16 part_id; +}; + +static struct wake_capable_part wake_capable_list[] = { + {0x025d, 0x5682}, + {0x025d, 0x700}, + {0x025d, 0x711}, + {0x025d, 0x1712}, + {0x025d, 0x1713}, + {0x025d, 0x1716}, + {0x025d, 0x1717}, + {0x025d, 0x712}, + {0x025d, 0x713}, + {0x025d, 0x714}, + {0x025d, 0x715}, + {0x025d, 0x716}, + {0x025d, 0x717}, + {0x025d, 0x722}, +}; + +static bool is_wake_capable(struct sdw_slave *slave) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wake_capable_list); i++) + if (slave->id.part_id == wake_capable_list[i].part_id && + slave->id.mfg_id == wake_capable_list[i].mfg_id) + return true; + return false; +} + static int generic_pre_bank_switch(struct sdw_bus *bus) { struct sdw_cdns *cdns = bus_to_cdns(bus); @@ -60,18 +90,32 @@ static int generic_post_bank_switch(struct sdw_bus *bus) return sdw->link_res->hw_ops->post_bank_switch(sdw); } -static void generic_new_peripheral_assigned(struct sdw_bus *bus, int dev_num) +static void generic_new_peripheral_assigned(struct sdw_bus *bus, + struct sdw_slave *slave, + int dev_num) { struct sdw_cdns *cdns = bus_to_cdns(bus); struct sdw_intel *sdw = cdns_to_intel(cdns); + int dev_num_min; + int dev_num_max; + bool wake_capable = slave->prop.wake_capable || is_wake_capable(slave); + + if (wake_capable) { + dev_num_min = SDW_INTEL_DEV_NUM_IDA_MIN; + dev_num_max = SDW_MAX_DEVICES; + } else { + dev_num_min = 1; + dev_num_max = SDW_INTEL_DEV_NUM_IDA_MIN - 1; + } /* paranoia check, this should never happen */ - if (dev_num < INTEL_DEV_NUM_IDA_MIN || dev_num > SDW_MAX_DEVICES) { - dev_err(bus->dev, "%s: invalid dev_num %d\n", __func__, dev_num); + if (dev_num < dev_num_min || dev_num > dev_num_max) { + dev_err(bus->dev, "%s: invalid dev_num %d, wake supported %d\n", + __func__, dev_num, slave->prop.wake_capable); return; } - if (sdw->link_res->hw_ops->program_sdi) + if (sdw->link_res->hw_ops->program_sdi && wake_capable) sdw->link_res->hw_ops->program_sdi(sdw, dev_num); } @@ -123,6 +167,30 @@ static int intel_prop_read(struct sdw_bus *bus) return 0; } +static DEFINE_IDA(intel_peripheral_ida); + +static int intel_get_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave) +{ + int bit; + + if (slave->prop.wake_capable || is_wake_capable(slave)) + return ida_alloc_range(&intel_peripheral_ida, + SDW_INTEL_DEV_NUM_IDA_MIN, SDW_MAX_DEVICES, + GFP_KERNEL); + + bit = find_first_zero_bit(slave->bus->assigned, SDW_MAX_DEVICES); + if (bit == SDW_MAX_DEVICES) + return -ENODEV; + + return bit; +} + +static void intel_put_device_num_ida(struct sdw_bus *bus, struct sdw_slave *slave) +{ + if (slave->prop.wake_capable || is_wake_capable(slave)) + ida_free(&intel_peripheral_ida, slave->dev_num); +} + static struct sdw_master_ops sdw_intel_ops = { .read_prop = intel_prop_read, .override_adr = sdw_dmi_override_adr, @@ -132,6 +200,8 @@ static struct sdw_master_ops sdw_intel_ops = { .pre_bank_switch = generic_pre_bank_switch, .post_bank_switch = generic_post_bank_switch, .read_ping_status = cdns_read_ping_status, + .get_device_num = intel_get_device_num_ida, + .put_device_num = intel_put_device_num_ida, .new_peripheral_assigned = generic_new_peripheral_assigned, }; @@ -165,7 +235,6 @@ static int intel_link_probe(struct auxiliary_device *auxdev, cdns->msg_count = 0; bus->link_id = auxdev->id; - bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN; bus->clk_stop_timeout = 1; sdw_cdns_probe(cdns); @@ -248,13 +317,6 @@ int intel_link_startup(struct auxiliary_device *auxdev) sdw_intel_debugfs_init(sdw); - /* start bus */ - ret = sdw_intel_start_bus(sdw); - if (ret) { - dev_err(dev, "bus start failed: %d\n", ret); - goto err_power_up; - } - /* Enable runtime PM */ if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) { pm_runtime_set_autosuspend_delay(dev, @@ -264,6 +326,15 @@ int intel_link_startup(struct auxiliary_device *auxdev) pm_runtime_set_active(dev); pm_runtime_enable(dev); + + pm_runtime_resume(bus->dev); + } + + /* start bus */ + ret = sdw_intel_start_bus(sdw); + if (ret) { + dev_err(dev, "bus start failed: %d\n", ret); + goto err_pm_runtime; } clock_stop_quirks = sdw->link_res->clock_stop_quirks; @@ -293,12 +364,18 @@ int intel_link_startup(struct auxiliary_device *auxdev) * with a delay. A more complete solution would require the * definition of Master properties. */ - if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) { + pm_runtime_mark_last_busy(bus->dev); + pm_runtime_mark_last_busy(dev); pm_runtime_idle(dev); + } sdw->startup_done = true; return 0; +err_pm_runtime: + if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME)) + pm_runtime_disable(dev); err_power_up: sdw_intel_link_power_down(sdw); err_init: @@ -552,6 +629,8 @@ static int __maybe_unused intel_resume(struct device *dev) pm_runtime_mark_last_busy(dev); pm_runtime_enable(dev); + pm_runtime_resume(bus->dev); + link_flags = md_flags >> (bus->link_id * 8); if (!(link_flags & SDW_INTEL_MASTER_DISABLE_PM_RUNTIME_IDLE)) @@ -587,6 +666,7 @@ static int __maybe_unused intel_resume(struct device *dev) * counters and delay the pm_runtime suspend by several * seconds, by when all enumeration should be complete. */ + pm_runtime_mark_last_busy(bus->dev); pm_runtime_mark_last_busy(dev); return 0; diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index c029e4d53573eac7a4ea09baa3292924629c8bee..55be9f4b8d59ab0212e6e934e9c50663715f981a 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 453a9b37ce7857cc1a8709251f2591b41ef869d8..d239fc5a49ccca737f81f8756c3e826f7e04dc81 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -256,7 +256,6 @@ static int cs42l43_spi_probe(struct platform_device *pdev) ret = devm_spi_register_controller(priv->dev, priv->ctlr); if (ret) { - pm_runtime_disable(priv->dev); dev_err(priv->dev, "Failed to register SPI controller: %d\n", ret); } diff --git a/drivers/spi/spi-gxp.c b/drivers/spi/spi-gxp.c index fd2fac236bbd887160bcf7e25bc65ded4eb4a947..3aff5a166c946c8d3b670203916441222832b082 100644 --- a/drivers/spi/spi-gxp.c +++ b/drivers/spi/spi-gxp.c @@ -194,7 +194,7 @@ static ssize_t gxp_spi_write(struct gxp_spi_chip *chip, const struct spi_mem_op return ret; } - return write_len; + return 0; } static int do_gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index a8a74c7cb79f85baf691df3bc7994030ed4c17f5..498e35c8db2c1d733cc33a197e5cb04d06fd6c43 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -662,7 +662,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx, if (spi_imx->count >= 512) ctrl |= 0xFFF << MX51_ECSPI_CTRL_BL_OFFSET; else - ctrl |= (spi_imx->count*8 - 1) + ctrl |= (spi_imx->count * spi_imx->bits_per_word - 1) << MX51_ECSPI_CTRL_BL_OFFSET; } diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c index a7381e774b95391b7780a2181a82ca35a73c773c..57d767a68e7b2766dcea5510809cf2f09e0bef63 100644 --- a/drivers/spi/spi-intel-pci.c +++ b/drivers/spi/spi-intel-pci.c @@ -72,6 +72,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x5794), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info }, diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 45a4acc956610a911674f811e2d7bf53492fd7a2..c964f41dcc428c100c1b3e4c56912e0eed379f7a 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -1084,6 +1084,13 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f) fspi_writel(f, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT, base + FSPI_AHBCR); + /* Reset the FLSHxCR1 registers. */ + reg = FSPI_FLSHXCR1_TCSH(0x3) | FSPI_FLSHXCR1_TCSS(0x3); + fspi_writel(f, reg, base + FSPI_FLSHA1CR1); + fspi_writel(f, reg, base + FSPI_FLSHA2CR1); + fspi_writel(f, reg, base + FSPI_FLSHB1CR1); + fspi_writel(f, reg, base + FSPI_FLSHB2CR1); + /* AHB Read - Set lut sequence ID for all CS. */ fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA1CR2); fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA2CR2); diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index b6d66caba4c02eb3e988ad9a5f76317a4244af32..ef665f470c5b5ed3f24efbc5012e04e764d629ae 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -277,6 +277,7 @@ struct stm32_spi_cfg { * @fifo_size: size of the embedded fifo in bytes * @cur_midi: master inter-data idleness in ns * @cur_speed: speed configured in Hz + * @cur_half_period: time of a half bit in us * @cur_bpw: number of bits in a single SPI data frame * @cur_fthlv: fifo threshold level (data frames in a single data packet) * @cur_comm: SPI communication mode @@ -304,6 +305,7 @@ struct stm32_spi { unsigned int cur_midi; unsigned int cur_speed; + unsigned int cur_half_period; unsigned int cur_bpw; unsigned int cur_fthlv; unsigned int cur_comm; @@ -468,6 +470,8 @@ static int stm32_spi_prepare_mbr(struct stm32_spi *spi, u32 speed_hz, spi->cur_speed = spi->clk_rate / (1 << mbrdiv); + spi->cur_half_period = DIV_ROUND_CLOSEST(USEC_PER_SEC, 2 * spi->cur_speed); + return mbrdiv - 1; } @@ -709,6 +713,10 @@ static void stm32h7_spi_disable(struct stm32_spi *spi) return; } + /* Add a delay to make sure that transmission is ended. */ + if (spi->cur_half_period) + udelay(spi->cur_half_period); + if (spi->cur_usedma && spi->dma_tx) dmaengine_terminate_async(spi->dma_tx); if (spi->cur_usedma && spi->dma_rx) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 3f5b1556ece0227c4edfe2ab056d041408f4ff46..fddc633097736a212df994130c514a2bd46f656e 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -106,6 +106,7 @@ struct sun6i_spi { struct reset_control *rstc; struct completion done; + struct completion dma_rx_done; const u8 *tx_buf; u8 *rx_buf; @@ -200,6 +201,13 @@ static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) return SUN6I_MAX_XFER_SIZE - 1; } +static void sun6i_spi_dma_rx_cb(void *param) +{ + struct sun6i_spi *sspi = param; + + complete(&sspi->dma_rx_done); +} + static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, struct spi_transfer *tfr) { @@ -211,7 +219,7 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, struct dma_slave_config rxconf = { .direction = DMA_DEV_TO_MEM, .src_addr = sspi->dma_addr_rx, - .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, .src_maxburst = 8, }; @@ -224,6 +232,8 @@ static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi, DMA_PREP_INTERRUPT); if (!rxdesc) return -EINVAL; + rxdesc->callback_param = sspi; + rxdesc->callback = sun6i_spi_dma_rx_cb; } txdesc = NULL; @@ -279,6 +289,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, return -EINVAL; reinit_completion(&sspi->done); + reinit_completion(&sspi->dma_rx_done); sspi->tx_buf = tfr->tx_buf; sspi->rx_buf = tfr->rx_buf; sspi->len = tfr->len; @@ -479,6 +490,22 @@ static int sun6i_spi_transfer_one(struct spi_master *master, start = jiffies; timeout = wait_for_completion_timeout(&sspi->done, msecs_to_jiffies(tx_time)); + + if (!use_dma) { + sun6i_spi_drain_fifo(sspi); + } else { + if (timeout && rx_len) { + /* + * Even though RX on the peripheral side has finished + * RX DMA might still be in flight + */ + timeout = wait_for_completion_timeout(&sspi->dma_rx_done, + timeout); + if (!timeout) + dev_warn(&master->dev, "RX DMA timeout\n"); + } + } + end = jiffies; if (!timeout) { dev_warn(&master->dev, @@ -506,7 +533,6 @@ static irqreturn_t sun6i_spi_handler(int irq, void *dev_id) /* Transfer complete */ if (status & SUN6I_INT_CTL_TC) { sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC); - sun6i_spi_drain_fifo(sspi); complete(&sspi->done); return IRQ_HANDLED; } @@ -665,6 +691,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) } init_completion(&sspi->done); + init_completion(&sspi->dma_rx_done); sspi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(sspi->rstc)) { diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 94d9a33d9af56e8c76bf7b14937d97bec190a3a9..9a46b2478f4e98bd38c794b17fa7a2bdae4e36f4 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -1340,9 +1340,9 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) return 0; clk_dis_all: - pm_runtime_put_sync(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); clk_disable_unprepare(xqspi->refclk); clk_dis_pclk: clk_disable_unprepare(xqspi->pclk); @@ -1366,11 +1366,15 @@ static void zynqmp_qspi_remove(struct platform_device *pdev) { struct zynqmp_qspi *xqspi = platform_get_drvdata(pdev); + pm_runtime_get_sync(&pdev->dev); + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); + + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); clk_disable_unprepare(xqspi->refclk); clk_disable_unprepare(xqspi->pclk); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_disable(&pdev->dev); } MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match); diff --git a/drivers/staging/greybus/pwm.c b/drivers/staging/greybus/pwm.c index f569d371a007ff73d4cae20c8625a4aad99214f8..57cc1960d0598f5442724a528020cb34487bd00b 100644 --- a/drivers/staging/greybus/pwm.c +++ b/drivers/staging/greybus/pwm.c @@ -266,7 +266,7 @@ static int gb_pwm_probe(struct gbphy_device *gbphy_dev, { struct gb_connection *connection; struct gb_pwm_chip *pwmc; - struct pwm_chip *pwm; + struct pwm_chip *chip; int ret; pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); @@ -294,13 +294,13 @@ static int gb_pwm_probe(struct gbphy_device *gbphy_dev, if (ret) goto exit_connection_disable; - pwm = &pwmc->chip; + chip = &pwmc->chip; - pwm->dev = &gbphy_dev->dev; - pwm->ops = &gb_pwm_ops; - pwm->npwm = pwmc->pwm_max + 1; + chip->dev = &gbphy_dev->dev; + chip->ops = &gb_pwm_ops; + chip->npwm = pwmc->pwm_max + 1; - ret = pwmchip_add(pwm); + ret = pwmchip_add(chip); if (ret) { dev_err(&gbphy_dev->dev, "failed to register PWM: %d\n", ret); diff --git a/drivers/staging/media/atomisp/Kconfig b/drivers/staging/media/atomisp/Kconfig index 5d8917160d41bf8e162492dc1180f4a218055239..75c985da75b5c601c3eab05fc622978bb2671f5d 100644 --- a/drivers/staging/media/atomisp/Kconfig +++ b/drivers/staging/media/atomisp/Kconfig @@ -12,12 +12,12 @@ menuconfig INTEL_ATOMISP config VIDEO_ATOMISP tristate "Intel Atom Image Signal Processor Driver" depends on VIDEO_DEV && INTEL_ATOMISP + depends on IPU_BRIDGE depends on MEDIA_PCI_SUPPORT depends on PMIC_OPREGION depends on I2C select V4L2_FWNODE select IOSF_MBI - select IPU_BRIDGE select VIDEOBUF2_VMALLOC select VIDEO_V4L2_SUBDEV_API help diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c index 9767159aeb9b2f80ec5d307bb7ae74ea3ef5f2be..abf5c72607b64e01885d944d08604280ecca5609 100644 --- a/drivers/staging/media/av7110/sp8870.c +++ b/drivers/staging/media/av7110/sp8870.c @@ -606,4 +606,4 @@ MODULE_DESCRIPTION("Spase SP8870 DVB-T Demodulator driver"); MODULE_AUTHOR("Juergen Peitz"); MODULE_LICENSE("GPL"); -EXPORT_SYMBOL(sp8870_attach); +EXPORT_SYMBOL_GPL(sp8870_attach); diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index e98b3010520e2d0641a8579ae50a794a5425d7fa..94171e62dee9e4a5acb8ce17c35bad712f8dd268 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1455,17 +1455,18 @@ static int __maybe_unused vi_runtime_suspend(struct device *dev) } /* - * Graph Management + * Find the entity matching a given fwnode in an v4l2_async_notifier list */ static struct tegra_vi_graph_entity * -tegra_vi_graph_find_entity(struct tegra_vi_channel *chan, +tegra_vi_graph_find_entity(struct list_head *list, const struct fwnode_handle *fwnode) { struct tegra_vi_graph_entity *entity; struct v4l2_async_connection *asd; - list_for_each_entry(asd, &chan->notifier.done_list, asc_entry) { + list_for_each_entry(asd, list, asc_entry) { entity = to_tegra_vi_graph_entity(asd); + if (entity->asd.match.fwnode == fwnode) return entity; } @@ -1532,7 +1533,8 @@ static int tegra_vi_graph_build(struct tegra_vi_channel *chan, } /* find the remote entity from notifier list */ - ent = tegra_vi_graph_find_entity(chan, link.remote_node); + ent = tegra_vi_graph_find_entity(&chan->notifier.done_list, + link.remote_node); if (!ent) { dev_err(vi->dev, "no entity found for %pOF\n", to_of_node(link.remote_node)); @@ -1664,7 +1666,8 @@ static int tegra_vi_graph_notify_bound(struct v4l2_async_notifier *notifier, * Locate the entity corresponding to the bound subdev and store the * subdev pointer. */ - entity = tegra_vi_graph_find_entity(chan, subdev->fwnode); + entity = tegra_vi_graph_find_entity(&chan->notifier.waiting_list, + subdev->fwnode); if (!entity) { dev_err(vi->dev, "no entity for subdev %s\n", subdev->name); return -EINVAL; @@ -1713,7 +1716,8 @@ static int tegra_vi_graph_parse_one(struct tegra_vi_channel *chan, /* skip entities that are already processed */ if (device_match_fwnode(vi->dev, remote) || - tegra_vi_graph_find_entity(chan, remote)) { + tegra_vi_graph_find_entity(&chan->notifier.waiting_list, + remote)) { fwnode_handle_put(remote); continue; } diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 5d0f51822414e04018bfaaf73787d4d09ae53d70..1cff6052e820219eaab4f4061117154d40596d3a 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -45,9 +45,9 @@ static ssize_t lio_target_np_driver_show(struct config_item *item, char *page, tpg_np_new = iscsit_tpg_locate_child_np(tpg_np, type); if (tpg_np_new) - rb = sprintf(page, "1\n"); + rb = sysfs_emit(page, "1\n"); else - rb = sprintf(page, "0\n"); + rb = sysfs_emit(page, "0\n"); return rb; } @@ -282,7 +282,7 @@ static ssize_t iscsi_nacl_attrib_##name##_show(struct config_item *item,\ { \ struct se_node_acl *se_nacl = attrib_to_nacl(item); \ struct iscsi_node_acl *nacl = to_iscsi_nacl(se_nacl); \ - return sprintf(page, "%u\n", nacl->node_attrib.name); \ + return sysfs_emit(page, "%u\n", nacl->node_attrib.name); \ } \ \ static ssize_t iscsi_nacl_attrib_##name##_store(struct config_item *item,\ @@ -320,7 +320,7 @@ static ssize_t iscsi_nacl_attrib_authentication_show(struct config_item *item, struct se_node_acl *se_nacl = attrib_to_nacl(item); struct iscsi_node_acl *nacl = to_iscsi_nacl(se_nacl); - return sprintf(page, "%d\n", nacl->node_attrib.authentication); + return sysfs_emit(page, "%d\n", nacl->node_attrib.authentication); } static ssize_t iscsi_nacl_attrib_authentication_store(struct config_item *item, @@ -533,102 +533,102 @@ static ssize_t lio_target_nacl_info_show(struct config_item *item, char *page) spin_lock_bh(&se_nacl->nacl_sess_lock); se_sess = se_nacl->nacl_sess; if (!se_sess) { - rb += sprintf(page+rb, "No active iSCSI Session for Initiator" + rb += sysfs_emit_at(page, rb, "No active iSCSI Session for Initiator" " Endpoint: %s\n", se_nacl->initiatorname); } else { sess = se_sess->fabric_sess_ptr; - rb += sprintf(page+rb, "InitiatorName: %s\n", + rb += sysfs_emit_at(page, rb, "InitiatorName: %s\n", sess->sess_ops->InitiatorName); - rb += sprintf(page+rb, "InitiatorAlias: %s\n", + rb += sysfs_emit_at(page, rb, "InitiatorAlias: %s\n", sess->sess_ops->InitiatorAlias); - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "LIO Session ID: %u ISID: 0x%6ph TSIH: %hu ", sess->sid, sess->isid, sess->tsih); - rb += sprintf(page+rb, "SessionType: %s\n", + rb += sysfs_emit_at(page, rb, "SessionType: %s\n", (sess->sess_ops->SessionType) ? "Discovery" : "Normal"); - rb += sprintf(page+rb, "Session State: "); + rb += sysfs_emit_at(page, rb, "Session State: "); switch (sess->session_state) { case TARG_SESS_STATE_FREE: - rb += sprintf(page+rb, "TARG_SESS_FREE\n"); + rb += sysfs_emit_at(page, rb, "TARG_SESS_FREE\n"); break; case TARG_SESS_STATE_ACTIVE: - rb += sprintf(page+rb, "TARG_SESS_STATE_ACTIVE\n"); + rb += sysfs_emit_at(page, rb, "TARG_SESS_STATE_ACTIVE\n"); break; case TARG_SESS_STATE_LOGGED_IN: - rb += sprintf(page+rb, "TARG_SESS_STATE_LOGGED_IN\n"); + rb += sysfs_emit_at(page, rb, "TARG_SESS_STATE_LOGGED_IN\n"); break; case TARG_SESS_STATE_FAILED: - rb += sprintf(page+rb, "TARG_SESS_STATE_FAILED\n"); + rb += sysfs_emit_at(page, rb, "TARG_SESS_STATE_FAILED\n"); break; case TARG_SESS_STATE_IN_CONTINUE: - rb += sprintf(page+rb, "TARG_SESS_STATE_IN_CONTINUE\n"); + rb += sysfs_emit_at(page, rb, "TARG_SESS_STATE_IN_CONTINUE\n"); break; default: - rb += sprintf(page+rb, "ERROR: Unknown Session" + rb += sysfs_emit_at(page, rb, "ERROR: Unknown Session" " State!\n"); break; } - rb += sprintf(page+rb, "---------------------[iSCSI Session" + rb += sysfs_emit_at(page, rb, "---------------------[iSCSI Session" " Values]-----------------------\n"); - rb += sprintf(page+rb, " CmdSN/WR : CmdSN/WC : ExpCmdSN" + rb += sysfs_emit_at(page, rb, " CmdSN/WR : CmdSN/WC : ExpCmdSN" " : MaxCmdSN : ITT : TTT\n"); max_cmd_sn = (u32) atomic_read(&sess->max_cmd_sn); - rb += sprintf(page+rb, " 0x%08x 0x%08x 0x%08x 0x%08x" + rb += sysfs_emit_at(page, rb, " 0x%08x 0x%08x 0x%08x 0x%08x" " 0x%08x 0x%08x\n", sess->cmdsn_window, (max_cmd_sn - sess->exp_cmd_sn) + 1, sess->exp_cmd_sn, max_cmd_sn, sess->init_task_tag, sess->targ_xfer_tag); - rb += sprintf(page+rb, "----------------------[iSCSI" + rb += sysfs_emit_at(page, rb, "----------------------[iSCSI" " Connections]-------------------------\n"); spin_lock(&sess->conn_lock); list_for_each_entry(conn, &sess->sess_conn_list, conn_list) { - rb += sprintf(page+rb, "CID: %hu Connection" + rb += sysfs_emit_at(page, rb, "CID: %hu Connection" " State: ", conn->cid); switch (conn->conn_state) { case TARG_CONN_STATE_FREE: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_FREE\n"); break; case TARG_CONN_STATE_XPT_UP: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_XPT_UP\n"); break; case TARG_CONN_STATE_IN_LOGIN: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_IN_LOGIN\n"); break; case TARG_CONN_STATE_LOGGED_IN: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_LOGGED_IN\n"); break; case TARG_CONN_STATE_IN_LOGOUT: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_IN_LOGOUT\n"); break; case TARG_CONN_STATE_LOGOUT_REQUESTED: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_LOGOUT_REQUESTED\n"); break; case TARG_CONN_STATE_CLEANUP_WAIT: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "TARG_CONN_STATE_CLEANUP_WAIT\n"); break; default: - rb += sprintf(page+rb, + rb += sysfs_emit_at(page, rb, "ERROR: Unknown Connection State!\n"); break; } - rb += sprintf(page+rb, " Address %pISc %s", &conn->login_sockaddr, + rb += sysfs_emit_at(page, rb, " Address %pISc %s", &conn->login_sockaddr, (conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP"); - rb += sprintf(page+rb, " StatSN: 0x%08x\n", + rb += sysfs_emit_at(page, rb, " StatSN: 0x%08x\n", conn->stat_sn); } spin_unlock(&sess->conn_lock); @@ -641,7 +641,7 @@ static ssize_t lio_target_nacl_info_show(struct config_item *item, char *page) static ssize_t lio_target_nacl_cmdsn_depth_show(struct config_item *item, char *page) { - return sprintf(page, "%u\n", acl_to_nacl(item)->queue_depth); + return sysfs_emit(page, "%u\n", acl_to_nacl(item)->queue_depth); } static ssize_t lio_target_nacl_cmdsn_depth_store(struct config_item *item, @@ -750,7 +750,7 @@ static ssize_t iscsi_tpg_attrib_##name##_show(struct config_item *item, \ if (iscsit_get_tpg(tpg) < 0) \ return -EINVAL; \ \ - rb = sprintf(page, "%u\n", tpg->tpg_attrib.name); \ + rb = sysfs_emit(page, "%u\n", tpg->tpg_attrib.name); \ iscsit_put_tpg(tpg); \ return rb; \ } \ @@ -783,7 +783,6 @@ CONFIGFS_ATTR(iscsi_tpg_attrib_, name) DEF_TPG_ATTRIB(authentication); DEF_TPG_ATTRIB(login_timeout); -DEF_TPG_ATTRIB(netif_timeout); DEF_TPG_ATTRIB(generate_node_acls); DEF_TPG_ATTRIB(default_cmdsn_depth); DEF_TPG_ATTRIB(cache_dynamic_acls); @@ -799,7 +798,6 @@ DEF_TPG_ATTRIB(login_keys_workaround); static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_attr_authentication, &iscsi_tpg_attrib_attr_login_timeout, - &iscsi_tpg_attrib_attr_netif_timeout, &iscsi_tpg_attrib_attr_generate_node_acls, &iscsi_tpg_attrib_attr_default_cmdsn_depth, &iscsi_tpg_attrib_attr_cache_dynamic_acls, @@ -1138,7 +1136,7 @@ static void lio_target_tiqn_deltpg(struct se_portal_group *se_tpg) static ssize_t lio_target_wwn_lio_version_show(struct config_item *item, char *page) { - return sprintf(page, "Datera Inc. iSCSI Target "ISCSIT_VERSION"\n"); + return sysfs_emit(page, "Datera Inc. iSCSI Target %s\n", ISCSIT_VERSION); } CONFIGFS_ATTR_RO(lio_target_wwn_, lio_version); @@ -1146,7 +1144,7 @@ CONFIGFS_ATTR_RO(lio_target_wwn_, lio_version); static ssize_t lio_target_wwn_cpus_allowed_list_show( struct config_item *item, char *page) { - return sprintf(page, "%*pbl\n", + return sysfs_emit(page, "%*pbl\n", cpumask_pr_args(iscsit_global->allowed_cpumask)); } @@ -1283,7 +1281,7 @@ static ssize_t iscsi_disc_enforce_discovery_auth_show(struct config_item *item, { struct iscsi_node_auth *discovery_auth = &iscsit_global->discovery_acl.node_auth; - return sprintf(page, "%d\n", discovery_auth->enforce_discovery_auth); + return sysfs_emit(page, "%d\n", discovery_auth->enforce_discovery_auth); } static ssize_t iscsi_disc_enforce_discovery_auth_store(struct config_item *item, diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 3cac1aafef689cf119a4943ec7fc66d922574c54..f7bac98fd4fef96f6383c88f4ac54c22efc5788c 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -211,7 +211,6 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) a->authentication = TA_AUTHENTICATION; a->login_timeout = TA_LOGIN_TIMEOUT; - a->netif_timeout = TA_NETIF_TIMEOUT; a->default_cmdsn_depth = TA_DEFAULT_CMDSN_DEPTH; a->generate_node_acls = TA_GENERATE_NODE_ACLS; a->cache_dynamic_acls = TA_CACHE_DYNAMIC_ACLS; @@ -666,31 +665,6 @@ int iscsit_ta_login_timeout( return 0; } -int iscsit_ta_netif_timeout( - struct iscsi_portal_group *tpg, - u32 netif_timeout) -{ - struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; - - if (netif_timeout > TA_NETIF_TIMEOUT_MAX) { - pr_err("Requested Network Interface Timeout %u larger" - " than maximum %u\n", netif_timeout, - TA_NETIF_TIMEOUT_MAX); - return -EINVAL; - } else if (netif_timeout < TA_NETIF_TIMEOUT_MIN) { - pr_err("Requested Network Interface Timeout %u smaller" - " than minimum %u\n", netif_timeout, - TA_NETIF_TIMEOUT_MIN); - return -EINVAL; - } - - a->netif_timeout = netif_timeout; - pr_debug("Set Network Interface Timeout to %u for" - " Target Portal Group %hu\n", a->netif_timeout, tpg->tpgt); - - return 0; -} - int iscsit_ta_generate_node_acls( struct iscsi_portal_group *tpg, u32 flag) diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index 839e453627769211030b5cbbdcab09e11666efdd..71d067f62177843e4dffe1f05091324f5a1f1529 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -38,7 +38,6 @@ extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *, struct iscsi_tpg_np *); extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32); extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32); -extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32); extern int iscsit_ta_generate_node_acls(struct iscsi_portal_group *, u32); extern int iscsit_ta_default_cmdsn_depth(struct iscsi_portal_group *, u32); extern int iscsit_ta_cache_dynamic_acls(struct iscsi_portal_group *, u32); diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 936e5ff1b209ecb9df704766d214326eee5e811d..d5860c1c1f469e5528dc69202e75697b57a794c2 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1392,16 +1392,16 @@ static ssize_t target_wwn_vendor_id_store(struct config_item *item, /* +2 to allow for a trailing (stripped) '\n' and null-terminator */ unsigned char buf[INQUIRY_VENDOR_LEN + 2]; char *stripped = NULL; - size_t len; + ssize_t len; ssize_t ret; - len = strlcpy(buf, page, sizeof(buf)); - if (len < sizeof(buf)) { + len = strscpy(buf, page, sizeof(buf)); + if (len > 0) { /* Strip any newline added from userspace. */ stripped = strstrip(buf); len = strlen(stripped); } - if (len > INQUIRY_VENDOR_LEN) { + if (len < 0 || len > INQUIRY_VENDOR_LEN) { pr_err("Emulated T10 Vendor Identification exceeds" " INQUIRY_VENDOR_LEN: " __stringify(INQUIRY_VENDOR_LEN) "\n"); @@ -1448,16 +1448,16 @@ static ssize_t target_wwn_product_id_store(struct config_item *item, /* +2 to allow for a trailing (stripped) '\n' and null-terminator */ unsigned char buf[INQUIRY_MODEL_LEN + 2]; char *stripped = NULL; - size_t len; + ssize_t len; ssize_t ret; - len = strlcpy(buf, page, sizeof(buf)); - if (len < sizeof(buf)) { + len = strscpy(buf, page, sizeof(buf)); + if (len > 0) { /* Strip any newline added from userspace. */ stripped = strstrip(buf); len = strlen(stripped); } - if (len > INQUIRY_MODEL_LEN) { + if (len < 0 || len > INQUIRY_MODEL_LEN) { pr_err("Emulated T10 Vendor exceeds INQUIRY_MODEL_LEN: " __stringify(INQUIRY_MODEL_LEN) "\n"); @@ -1504,16 +1504,16 @@ static ssize_t target_wwn_revision_store(struct config_item *item, /* +2 to allow for a trailing (stripped) '\n' and null-terminator */ unsigned char buf[INQUIRY_REVISION_LEN + 2]; char *stripped = NULL; - size_t len; + ssize_t len; ssize_t ret; - len = strlcpy(buf, page, sizeof(buf)); - if (len < sizeof(buf)) { + len = strscpy(buf, page, sizeof(buf)); + if (len > 0) { /* Strip any newline added from userspace. */ stripped = strstrip(buf); len = strlen(stripped); } - if (len > INQUIRY_REVISION_LEN) { + if (len < 0 || len > INQUIRY_REVISION_LEN) { pr_err("Emulated T10 Revision exceeds INQUIRY_REVISION_LEN: " __stringify(INQUIRY_REVISION_LEN) "\n"); diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index b7ac60f4a21945b1545e7d7112f09efe2e906a69..b6523d4b9259e43c674e3796634fa4dc983f766b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -843,7 +843,6 @@ sector_t target_to_linux_sector(struct se_device *dev, sector_t lb) EXPORT_SYMBOL(target_to_linux_sector); struct devices_idr_iter { - struct config_item *prev_item; int (*fn)(struct se_device *dev, void *data); void *data; }; @@ -853,11 +852,9 @@ static int target_devices_idr_iter(int id, void *p, void *data) { struct devices_idr_iter *iter = data; struct se_device *dev = p; + struct config_item *item; int ret; - config_item_put(iter->prev_item); - iter->prev_item = NULL; - /* * We add the device early to the idr, so it can be used * by backend modules during configuration. We do not want @@ -867,12 +864,13 @@ static int target_devices_idr_iter(int id, void *p, void *data) if (!target_dev_configured(dev)) return 0; - iter->prev_item = config_item_get_unless_zero(&dev->dev_group.cg_item); - if (!iter->prev_item) + item = config_item_get_unless_zero(&dev->dev_group.cg_item); + if (!item) return 0; mutex_unlock(&device_mutex); ret = iter->fn(dev, iter->data); + config_item_put(item); mutex_lock(&device_mutex); return ret; @@ -895,7 +893,6 @@ int target_for_each_device(int (*fn)(struct se_device *dev, void *data), mutex_lock(&device_mutex); ret = idr_for_each(&devices_idr, target_devices_idr_iter, &iter); mutex_unlock(&device_mutex); - config_item_put(iter.prev_item); return ret; } diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index a7050f63b7cc19b3a7b966e9fff7e3e9101fb87d..a6a06a5f74834d78058d9a2483525251b0d91a7a 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -739,11 +739,16 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, if (data_direction == DMA_TO_DEVICE) { struct iblock_dev *ib_dev = IBLOCK_DEV(dev); + + /* + * Set bits to indicate WRITE_ODIRECT so we are not throttled + * by WBT. + */ + opf = REQ_OP_WRITE | REQ_SYNC | REQ_IDLE; /* * Force writethrough using REQ_FUA if a volatile write cache * is not enabled, or if initiator set the Force Unit Access bit. */ - opf = REQ_OP_WRITE; miter_dir = SG_MITER_TO_SG; if (bdev_fua(ib_dev->ibd_bd)) { if (cmd->se_cmd_flags & SCF_FUA) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 687adc9e086ca942ba85dd591ab3b7bf76cc9192..0686882bcbda351fbf5c46b6a66cdf4cfe35e9e0 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -264,6 +264,7 @@ void target_free_cmd_counter(struct target_cmd_counter *cmd_cnt) percpu_ref_put(&cmd_cnt->refcnt); percpu_ref_exit(&cmd_cnt->refcnt); + kfree(cmd_cnt); } EXPORT_SYMBOL_GPL(target_free_cmd_counter); diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 72685ee0d53f881cbaa8116fec69f465d81f7ca9..6bb5cae0968865910b00298474f1cd4330fe375b 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -238,8 +238,6 @@ int optee_notif_send(struct optee *optee, u_int key); u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, struct tee_param *param); -int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len); -int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len); void optee_supp_init(struct optee_supp *supp); void optee_supp_uninit(struct optee_supp *supp); void optee_supp_release(struct optee_supp *supp); diff --git a/drivers/tee/tee_private.h b/drivers/tee/tee_private.h index 409cadcc1cff7b41a0f7e6c7142f8d8793276585..754e11dcb240898d33e0af4af698ee81c3ff6615 100644 --- a/drivers/tee/tee_private.h +++ b/drivers/tee/tee_private.h @@ -47,8 +47,6 @@ struct tee_device { struct tee_shm_pool *pool; }; -int tee_shm_init(void); - int tee_shm_get_fd(struct tee_shm *shm); bool tee_device_get(struct tee_device *teedev); diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 19a4b33cb564767b1c8a5ca114af64c7574d4b55..c81a00fbca7db936285d48f10a3bd2c437a1bc24 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -510,4 +510,16 @@ config KHADAS_MCU_FAN_THERMAL If you say yes here you get support for the FAN controlled by the Microcontroller found on the Khadas VIM boards. +config LOONGSON2_THERMAL + tristate "Loongson-2 SoC series thermal driver" + depends on LOONGARCH || COMPILE_TEST + depends on OF + help + Support for Thermal driver found on Loongson-2 SoC series platforms. + The thermal driver realizes get_temp and set_trips function, which + are used to obtain the temperature of the current node and set the + temperature range to trigger the interrupt. When the input temperature + is higher than the high temperature threshold or lower than the low + temperature threshold, the interrupt will occur. + endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 058664bc3ec097afac5257ae3f485672c63df9e0..c934cab309ae0cbf5e7e94b6e2dc250bb21adbef 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o +obj-$(CONFIG_LOONGSON2_THERMAL) += loongson2_thermal.o diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index 9f6dc4fc9112d028ae00fdd6393e0021a0ab1062..f00765bfc22e822e5cde477338f2e0438e284008 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -876,8 +876,9 @@ static int armada_thermal_probe(struct platform_device *pdev) /* Wait the sensors to be valid */ armada_wait_sensor_validity(priv); - tz = thermal_zone_device_register(priv->zone_name, 0, 0, priv, - &legacy_ops, NULL, 0, 0); + tz = thermal_tripless_zone_device_register(priv->zone_name, + priv, &legacy_ops, + NULL); if (IS_ERR(tz)) { dev_err(&pdev->dev, "Failed to register thermal zone device\n"); diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index 0b73abdaa792a68013f4c6e4b5a72a110c4f9afa..9674e5ffcfa274556fcb690fbe7d8dfd3c720718 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -334,7 +334,6 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) return PTR_ERR(priv->tmon_base); priv->dev = &pdev->dev; - platform_set_drvdata(pdev, priv); of_ops = priv->temp_params->of_ops; thermal = devm_thermal_of_zone_register(&pdev->dev, 0, priv, diff --git a/drivers/thermal/broadcom/sr-thermal.c b/drivers/thermal/broadcom/sr-thermal.c index 7479158900226f169520b0a1880cfc793f7f10ba..9a29dfd4c7fe5f546d003800c33bf700f694544f 100644 --- a/drivers/thermal/broadcom/sr-thermal.c +++ b/drivers/thermal/broadcom/sr-thermal.c @@ -91,7 +91,6 @@ static int sr_thermal_probe(struct platform_device *pdev) dev_dbg(dev, "thermal sensor %d registered\n", i); } - platform_set_drvdata(pdev, sr_thermal); return 0; } diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index fca5c2c93bf97720617c3f14e30b0c6645f88d09..576f88b6a1b3524c4089440169801fbe5ede59eb 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -229,7 +229,7 @@ MODULE_DEVICE_TABLE(of, db8500_thermal_match); static struct platform_driver db8500_thermal_driver = { .driver = { .name = "db8500-thermal", - .of_match_table = of_match_ptr(db8500_thermal_match), + .of_match_table = db8500_thermal_match, }, .probe = db8500_thermal_probe, .suspend = db8500_thermal_suspend, diff --git a/drivers/thermal/dove_thermal.c b/drivers/thermal/dove_thermal.c index 9954040d1d2cca5dd589cc99d41977c6a2646b9b..7a18cb960bee0ad3dc98c9d24647ba356a6ca33c 100644 --- a/drivers/thermal/dove_thermal.c +++ b/drivers/thermal/dove_thermal.c @@ -139,8 +139,8 @@ static int dove_thermal_probe(struct platform_device *pdev) return ret; } - thermal = thermal_zone_device_register("dove_thermal", 0, 0, - priv, &ops, NULL, 0, 0); + thermal = thermal_tripless_zone_device_register("dove_thermal", priv, + &ops, NULL); if (IS_ERR(thermal)) { dev_err(&pdev->dev, "Failed to register thermal zone device\n"); diff --git a/drivers/thermal/imx8mm_thermal.c b/drivers/thermal/imx8mm_thermal.c index e89b11b3f2b935ed80252fbf955b4219afbb158b..14111ccf6e4c26b7ea38cd55977278374a18f39f 100644 --- a/drivers/thermal/imx8mm_thermal.c +++ b/drivers/thermal/imx8mm_thermal.c @@ -178,10 +178,8 @@ static int imx8mm_tmu_probe_set_calib_v1(struct platform_device *pdev, int ret; ret = nvmem_cell_read_u32(&pdev->dev, "calib", &ana0); - if (ret) { - dev_warn(dev, "Failed to read OCOTP nvmem cell (%d).\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to read OCOTP nvmem cell\n"); writel(FIELD_PREP(TASR_BUF_VREF_MASK, FIELD_GET(ANA0_BUF_VREF_MASK, ana0)) | diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index ddd600820f683e25e4345e04c02f09dd60648b6f..ffc2871a021cf63c274ddeb593351f1de0312c8d 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -609,9 +609,9 @@ static int int3400_thermal_probe(struct platform_device *pdev) evaluate_odvp(priv); - priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0, - priv, &int3400_thermal_ops, - &int3400_thermal_params, 0, 0); + priv->thermal = thermal_tripless_zone_device_register("INT3400 Thermal", priv, + &int3400_thermal_ops, + &int3400_thermal_params); if (IS_ERR(priv->thermal)) { result = PTR_ERR(priv->thermal); goto free_art_trt; diff --git a/drivers/thermal/k3_bandgap.c b/drivers/thermal/k3_bandgap.c index 68f59b3735d3bf29ec8d00094ccb584b4b861176..4a918c1e92f9ba31aec160c95c25f47c8b6ff91c 100644 --- a/drivers/thermal/k3_bandgap.c +++ b/drivers/thermal/k3_bandgap.c @@ -225,7 +225,6 @@ static int k3_bandgap_probe(struct platform_device *pdev) devm_thermal_add_hwmon_sysfs(dev, data[id].tzd); } - platform_set_drvdata(pdev, bgp); return 0; diff --git a/drivers/thermal/k3_j72xx_bandgap.c b/drivers/thermal/k3_j72xx_bandgap.c index a5a0fc9b9356f0fd3530d8eb32070a7739887721..2fc799b07b909460b47adbe2ebb7fef5e19c6fa9 100644 --- a/drivers/thermal/k3_j72xx_bandgap.c +++ b/drivers/thermal/k3_j72xx_bandgap.c @@ -502,8 +502,6 @@ static int k3_j72xx_bandgap_probe(struct platform_device *pdev) writel(K3_VTM_ANYMAXT_OUTRG_ALERT_EN, data[0].bgp->cfg2_base + K3_VTM_MISC_CTRL_OFFSET); - platform_set_drvdata(pdev, bgp); - print_look_up_table(dev, ref_table); /* * Now that the derived_table has the appropriate look up values diff --git a/drivers/thermal/kirkwood_thermal.c b/drivers/thermal/kirkwood_thermal.c index 668747bd86ef67581053b826e3c153223b50e08e..acb10d24256d2a6304c49f0c398b51de3d4a301d 100644 --- a/drivers/thermal/kirkwood_thermal.c +++ b/drivers/thermal/kirkwood_thermal.c @@ -71,8 +71,8 @@ static int kirkwood_thermal_probe(struct platform_device *pdev) if (IS_ERR(priv->sensor)) return PTR_ERR(priv->sensor); - thermal = thermal_zone_device_register("kirkwood_thermal", 0, 0, - priv, &ops, NULL, 0, 0); + thermal = thermal_tripless_zone_device_register("kirkwood_thermal", + priv, &ops, NULL); if (IS_ERR(thermal)) { dev_err(&pdev->dev, "Failed to register thermal zone device\n"); diff --git a/drivers/thermal/loongson2_thermal.c b/drivers/thermal/loongson2_thermal.c new file mode 100644 index 0000000000000000000000000000000000000000..133098dc085471d6e0ef35bc3df196933704f32a --- /dev/null +++ b/drivers/thermal/loongson2_thermal.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Author: zhanghongchen + * Yinbo Zhu + * Copyright (C) 2022-2023 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "thermal_hwmon.h" + +#define LOONGSON2_MAX_SENSOR_SEL_NUM 3 + +#define LOONGSON2_THSENS_CTRL_HI_REG 0x0 +#define LOONGSON2_THSENS_CTRL_LOW_REG 0x8 +#define LOONGSON2_THSENS_STATUS_REG 0x10 +#define LOONGSON2_THSENS_OUT_REG 0x14 + +#define LOONGSON2_THSENS_INT_LO BIT(0) +#define LOONGSON2_THSENS_INT_HIGH BIT(1) +#define LOONGSON2_THSENS_OUT_MASK 0xFF + +struct loongson2_thermal_chip_data { + unsigned int thermal_sensor_sel; +}; + +struct loongson2_thermal_data { + void __iomem *regs; + const struct loongson2_thermal_chip_data *chip_data; +}; + +static int loongson2_thermal_set(struct loongson2_thermal_data *data, + int low, int high, bool enable) +{ + u64 reg_ctrl = 0; + int reg_off = data->chip_data->thermal_sensor_sel * 2; + + low = clamp(-40, low, high); + high = clamp(125, low, high); + + low += HECTO; + high += HECTO; + + reg_ctrl = low; + reg_ctrl |= enable ? 0x100 : 0; + writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_LOW_REG + reg_off); + + reg_ctrl = high; + reg_ctrl |= enable ? 0x100 : 0; + writew(reg_ctrl, data->regs + LOONGSON2_THSENS_CTRL_HI_REG + reg_off); + + return 0; +} + +static int loongson2_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + u32 reg_val; + struct loongson2_thermal_data *data = thermal_zone_device_priv(tz); + + reg_val = readl(data->regs + LOONGSON2_THSENS_OUT_REG); + *temp = ((reg_val & LOONGSON2_THSENS_OUT_MASK) - HECTO) * KILO; + + return 0; +} + +static irqreturn_t loongson2_thermal_irq_thread(int irq, void *dev) +{ + struct thermal_zone_device *tzd = dev; + struct loongson2_thermal_data *data = thermal_zone_device_priv(tzd); + + writeb(LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH, data->regs + + LOONGSON2_THSENS_STATUS_REG); + + thermal_zone_device_update(tzd, THERMAL_EVENT_UNSPECIFIED); + + return IRQ_HANDLED; +} + +static int loongson2_thermal_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct loongson2_thermal_data *data = thermal_zone_device_priv(tz); + + return loongson2_thermal_set(data, low/MILLI, high/MILLI, true); +} + +static const struct thermal_zone_device_ops loongson2_of_thermal_ops = { + .get_temp = loongson2_thermal_get_temp, + .set_trips = loongson2_thermal_set_trips, +}; + +static int loongson2_thermal_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_thermal_data *data; + struct thermal_zone_device *tzd; + int ret, irq, i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->chip_data = device_get_match_data(dev); + + data->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->regs)) + return PTR_ERR(data->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + writeb(LOONGSON2_THSENS_INT_LO | LOONGSON2_THSENS_INT_HIGH, data->regs + + LOONGSON2_THSENS_STATUS_REG); + + loongson2_thermal_set(data, 0, 0, false); + + for (i = 0; i <= LOONGSON2_MAX_SENSOR_SEL_NUM; i++) { + tzd = devm_thermal_of_zone_register(dev, i, data, + &loongson2_of_thermal_ops); + + if (!IS_ERR(tzd)) + break; + + if (PTR_ERR(tzd) != ENODEV) + continue; + + return dev_err_probe(dev, PTR_ERR(tzd), "failed to register"); + } + + ret = devm_request_threaded_irq(dev, irq, NULL, loongson2_thermal_irq_thread, + IRQF_ONESHOT, "loongson2_thermal", tzd); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to request alarm irq\n"); + + devm_thermal_add_hwmon_sysfs(dev, tzd); + + return 0; +} + +static const struct loongson2_thermal_chip_data loongson2_thermal_ls2k1000_data = { + .thermal_sensor_sel = 0, +}; + +static const struct of_device_id of_loongson2_thermal_match[] = { + { + .compatible = "loongson,ls2k1000-thermal", + .data = &loongson2_thermal_ls2k1000_data, + }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_loongson2_thermal_match); + +static struct platform_driver loongson2_thermal_driver = { + .driver = { + .name = "loongson2_thermal", + .of_match_table = of_loongson2_thermal_match, + }, + .probe = loongson2_thermal_probe, +}; +module_platform_driver(loongson2_thermal_driver); + +MODULE_DESCRIPTION("Loongson2 thermal driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c index 61c7622d9945dbdc2901e87ab0115fe204e3d881..919b6ee208d81a00ce5c4e1646e555296203c1c6 100644 --- a/drivers/thermal/max77620_thermal.c +++ b/drivers/thermal/max77620_thermal.c @@ -139,8 +139,6 @@ static int max77620_thermal_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, mtherm); - return 0; } diff --git a/drivers/thermal/mediatek/auxadc_thermal.c b/drivers/thermal/mediatek/auxadc_thermal.c index c537aed7101778b6a43cbbd57d6ba8aebf54ac00..843214d30bd8b2e80037b28bfcb04169bb8e85aa 100644 --- a/drivers/thermal/mediatek/auxadc_thermal.c +++ b/drivers/thermal/mediatek/auxadc_thermal.c @@ -1282,8 +1282,6 @@ static int mtk_thermal_probe(struct platform_device *pdev) mtk_thermal_init_bank(mt, i, apmixed_phys_base, auxadc_phys_base, ctrl_id); - platform_set_drvdata(pdev, mt); - tzdev = devm_thermal_of_zone_register(&pdev->dev, 0, mt, &mtk_thermal_ops); if (IS_ERR(tzdev)) diff --git a/drivers/thermal/mediatek/lvts_thermal.c b/drivers/thermal/mediatek/lvts_thermal.c index 054c965ae5e11540e51aba26d4959e35ceab9e9d..effd9b00a424bb23c7024c0c13563ce6c7691ab6 100644 --- a/drivers/thermal/mediatek/lvts_thermal.c +++ b/drivers/thermal/mediatek/lvts_thermal.c @@ -58,14 +58,19 @@ #define LVTS_PROTTC(__base) (__base + 0x00CC) #define LVTS_CLKEN(__base) (__base + 0x00E4) -#define LVTS_PERIOD_UNIT ((118 * 1000) / (256 * 38)) -#define LVTS_GROUP_INTERVAL 1 -#define LVTS_FILTER_INTERVAL 1 -#define LVTS_SENSOR_INTERVAL 1 -#define LVTS_HW_FILTER 0x2 +#define LVTS_PERIOD_UNIT 0 +#define LVTS_GROUP_INTERVAL 0 +#define LVTS_FILTER_INTERVAL 0 +#define LVTS_SENSOR_INTERVAL 0 +#define LVTS_HW_FILTER 0x0 #define LVTS_TSSEL_CONF 0x13121110 #define LVTS_CALSCALE_CONF 0x300 -#define LVTS_MONINT_CONF 0x9FBF7BDE +#define LVTS_MONINT_CONF 0x8300318C + +#define LVTS_MONINT_OFFSET_SENSOR0 0xC +#define LVTS_MONINT_OFFSET_SENSOR1 0x180 +#define LVTS_MONINT_OFFSET_SENSOR2 0x3000 +#define LVTS_MONINT_OFFSET_SENSOR3 0x3000000 #define LVTS_INT_SENSOR0 0x0009001F #define LVTS_INT_SENSOR1 0x001203E0 @@ -81,8 +86,13 @@ #define LVTS_MSR_IMMEDIATE_MODE 0 #define LVTS_MSR_FILTERED_MODE 1 +#define LVTS_MSR_READ_TIMEOUT_US 400 +#define LVTS_MSR_READ_WAIT_US (LVTS_MSR_READ_TIMEOUT_US / 2) + #define LVTS_HW_SHUTDOWN_MT8195 105000 +#define LVTS_MINIMUM_THRESHOLD 20000 + static int golden_temp = LVTS_GOLDEN_TEMP_DEFAULT; static int coeff_b = LVTS_COEFF_B; @@ -110,6 +120,8 @@ struct lvts_sensor { void __iomem *base; int id; int dt_id; + int low_thresh; + int high_thresh; }; struct lvts_ctrl { @@ -119,6 +131,8 @@ struct lvts_ctrl { int num_lvts_sensor; int mode; void __iomem *base; + int low_thresh; + int high_thresh; }; struct lvts_domain { @@ -190,7 +204,7 @@ static int lvts_debugfs_init(struct device *dev, struct lvts_domain *lvts_td) int i; lvts_td->dom_dentry = debugfs_create_dir(dev_name(dev), NULL); - if (!lvts_td->dom_dentry) + if (IS_ERR(lvts_td->dom_dentry)) return 0; for (i = 0; i < lvts_td->num_lvts_ctrl; i++) { @@ -257,6 +271,7 @@ static int lvts_get_temp(struct thermal_zone_device *tz, int *temp) struct lvts_sensor *lvts_sensor = thermal_zone_device_priv(tz); void __iomem *msr = lvts_sensor->msr; u32 value; + int rc; /* * Measurement registers: @@ -269,7 +284,8 @@ static int lvts_get_temp(struct thermal_zone_device *tz, int *temp) * 16 : Valid temperature * 15-0 : Raw temperature */ - value = readl(msr); + rc = readl_poll_timeout(msr, value, value & BIT(16), + LVTS_MSR_READ_WAIT_US, LVTS_MSR_READ_TIMEOUT_US); /* * As the thermal zone temperature will read before the @@ -282,7 +298,7 @@ static int lvts_get_temp(struct thermal_zone_device *tz, int *temp) * functionning temperature and directly jump to a system * shutdown. */ - if (!(value & BIT(16))) + if (rc) return -EAGAIN; *temp = lvts_raw_to_temp(value & 0xFFFF); @@ -290,32 +306,84 @@ static int lvts_get_temp(struct thermal_zone_device *tz, int *temp) return 0; } +static void lvts_update_irq_mask(struct lvts_ctrl *lvts_ctrl) +{ + u32 masks[] = { + LVTS_MONINT_OFFSET_SENSOR0, + LVTS_MONINT_OFFSET_SENSOR1, + LVTS_MONINT_OFFSET_SENSOR2, + LVTS_MONINT_OFFSET_SENSOR3, + }; + u32 value = 0; + int i; + + value = readl(LVTS_MONINT(lvts_ctrl->base)); + + for (i = 0; i < ARRAY_SIZE(masks); i++) { + if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh + && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) + value |= masks[i]; + else + value &= ~masks[i]; + } + + writel(value, LVTS_MONINT(lvts_ctrl->base)); +} + +static bool lvts_should_update_thresh(struct lvts_ctrl *lvts_ctrl, int high) +{ + int i; + + if (high > lvts_ctrl->high_thresh) + return true; + + for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) + if (lvts_ctrl->sensors[i].high_thresh == lvts_ctrl->high_thresh + && lvts_ctrl->sensors[i].low_thresh == lvts_ctrl->low_thresh) + return false; + + return true; +} + static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high) { struct lvts_sensor *lvts_sensor = thermal_zone_device_priv(tz); + struct lvts_ctrl *lvts_ctrl = container_of(lvts_sensor, struct lvts_ctrl, sensors[lvts_sensor->id]); void __iomem *base = lvts_sensor->base; - u32 raw_low = lvts_temp_to_raw(low); + u32 raw_low = lvts_temp_to_raw(low != -INT_MAX ? low : LVTS_MINIMUM_THRESHOLD); u32 raw_high = lvts_temp_to_raw(high); + bool should_update_thresh; + + lvts_sensor->low_thresh = low; + lvts_sensor->high_thresh = high; + + should_update_thresh = lvts_should_update_thresh(lvts_ctrl, high); + if (should_update_thresh) { + lvts_ctrl->high_thresh = high; + lvts_ctrl->low_thresh = low; + } + lvts_update_irq_mask(lvts_ctrl); + + if (!should_update_thresh) + return 0; /* - * Hot to normal temperature threshold + * Low offset temperature threshold * - * LVTS_H2NTHRE + * LVTS_OFFSETL * * Bits: * * 14-0 : Raw temperature for threshold */ - if (low != -INT_MAX) { - pr_debug("%s: Setting low limit temperature interrupt: %d\n", - thermal_zone_device_type(tz), low); - writel(raw_low, LVTS_H2NTHRE(base)); - } + pr_debug("%s: Setting low limit temperature interrupt: %d\n", + thermal_zone_device_type(tz), low); + writel(raw_low, LVTS_OFFSETL(base)); /* - * Hot temperature threshold + * High offset temperature threshold * - * LVTS_HTHRE + * LVTS_OFFSETH * * Bits: * @@ -323,7 +391,7 @@ static int lvts_set_trips(struct thermal_zone_device *tz, int low, int high) */ pr_debug("%s: Setting high limit temperature interrupt: %d\n", thermal_zone_device_type(tz), high); - writel(raw_high, LVTS_HTHRE(base)); + writel(raw_high, LVTS_OFFSETH(base)); return 0; } @@ -451,7 +519,7 @@ static irqreturn_t lvts_irq_handler(int irq, void *data) for (i = 0; i < lvts_td->num_lvts_ctrl; i++) { - aux = lvts_ctrl_irq_handler(lvts_td->lvts_ctrl); + aux = lvts_ctrl_irq_handler(&lvts_td->lvts_ctrl[i]); if (aux != IRQ_HANDLED) continue; @@ -521,6 +589,9 @@ static int lvts_sensor_init(struct device *dev, struct lvts_ctrl *lvts_ctrl, */ lvts_sensor[i].msr = lvts_ctrl_data->mode == LVTS_MSR_IMMEDIATE_MODE ? imm_regs[i] : msr_regs[i]; + + lvts_sensor[i].low_thresh = INT_MIN; + lvts_sensor[i].high_thresh = INT_MIN; }; lvts_ctrl->num_lvts_sensor = lvts_ctrl_data->num_lvts_sensor; @@ -688,6 +759,9 @@ static int lvts_ctrl_init(struct device *dev, struct lvts_domain *lvts_td, */ lvts_ctrl[i].hw_tshut_raw_temp = lvts_temp_to_raw(lvts_data->lvts_ctrl[i].hw_tshut_temp); + + lvts_ctrl[i].low_thresh = INT_MIN; + lvts_ctrl[i].high_thresh = INT_MIN; } /* @@ -896,24 +970,6 @@ static int lvts_ctrl_configure(struct device *dev, struct lvts_ctrl *lvts_ctrl) LVTS_HW_FILTER << 3 | LVTS_HW_FILTER; writel(value, LVTS_MSRCTL0(lvts_ctrl->base)); - /* - * LVTS_MSRCTL1 : Measurement control - * - * Bits: - * - * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3 - * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2 - * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1 - * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0 - * - * That configuration will ignore the filtering and the delays - * introduced below in MONCTL1 and MONCTL2 - */ - if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) { - value = BIT(9) | BIT(6) | BIT(5) | BIT(4); - writel(value, LVTS_MSRCTL1(lvts_ctrl->base)); - } - /* * LVTS_MONCTL1 : Period unit and group interval configuration * @@ -979,6 +1035,15 @@ static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl) struct thermal_zone_device *tz; u32 sensor_map = 0; int i; + /* + * Bitmaps to enable each sensor on immediate and filtered modes, as + * described in MSRCTL1 and MONCTL0 registers below, respectively. + */ + u32 sensor_imm_bitmap[] = { BIT(4), BIT(5), BIT(6), BIT(9) }; + u32 sensor_filt_bitmap[] = { BIT(0), BIT(1), BIT(2), BIT(3) }; + + u32 *sensor_bitmap = lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE ? + sensor_imm_bitmap : sensor_filt_bitmap; for (i = 0; i < lvts_ctrl->num_lvts_sensor; i++) { @@ -1016,20 +1081,38 @@ static int lvts_ctrl_start(struct device *dev, struct lvts_ctrl *lvts_ctrl) * map, so we can enable the temperature monitoring in * the hardware thermal controller. */ - sensor_map |= BIT(i); + sensor_map |= sensor_bitmap[i]; } /* - * Bits: - * 9: Single point access flow - * 0-3: Enable sensing point 0-3 - * * The initialization of the thermal zones give us * which sensor point to enable. If any thermal zone * was not described in the device tree, it won't be * enabled here in the sensor map. */ - writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base)); + if (lvts_ctrl->mode == LVTS_MSR_IMMEDIATE_MODE) { + /* + * LVTS_MSRCTL1 : Measurement control + * + * Bits: + * + * 9: Ignore MSRCTL0 config and do immediate measurement on sensor3 + * 6: Ignore MSRCTL0 config and do immediate measurement on sensor2 + * 5: Ignore MSRCTL0 config and do immediate measurement on sensor1 + * 4: Ignore MSRCTL0 config and do immediate measurement on sensor0 + * + * That configuration will ignore the filtering and the delays + * introduced in MONCTL1 and MONCTL2 + */ + writel(sensor_map, LVTS_MSRCTL1(lvts_ctrl->base)); + } else { + /* + * Bits: + * 9: Single point access flow + * 0-3: Enable sensing point 0-3 + */ + writel(sensor_map | BIT(9), LVTS_MONCTL0(lvts_ctrl->base)); + } return 0; } @@ -1138,7 +1221,7 @@ static int lvts_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return dev_err_probe(dev, irq, "No irq resource\n"); + return irq; ret = lvts_domain_init(dev, lvts_td, lvts_data); if (ret) diff --git a/drivers/thermal/qcom/tsens-v0_1.c b/drivers/thermal/qcom/tsens-v0_1.c index a941b4241b0ab96aa7e0f70f8d7104dbd5e9e944..87c09f62ee816daca236b48ca8fe1063e81857a2 100644 --- a/drivers/thermal/qcom/tsens-v0_1.c +++ b/drivers/thermal/qcom/tsens-v0_1.c @@ -23,7 +23,7 @@ #define BIT_APPEND 0x3 -struct tsens_legacy_calibration_format tsens_8916_nvmem = { +static struct tsens_legacy_calibration_format tsens_8916_nvmem = { .base_len = 7, .base_shift = 3, .sp_len = 5, @@ -39,7 +39,7 @@ struct tsens_legacy_calibration_format tsens_8916_nvmem = { }, }; -struct tsens_legacy_calibration_format tsens_8974_nvmem = { +static struct tsens_legacy_calibration_format tsens_8974_nvmem = { .base_len = 8, .base_shift = 2, .sp_len = 6, @@ -61,7 +61,7 @@ struct tsens_legacy_calibration_format tsens_8974_nvmem = { }, }; -struct tsens_legacy_calibration_format tsens_8974_backup_nvmem = { +static struct tsens_legacy_calibration_format tsens_8974_backup_nvmem = { .base_len = 8, .base_shift = 2, .sp_len = 6, diff --git a/drivers/thermal/qcom/tsens-v1.c b/drivers/thermal/qcom/tsens-v1.c index 51322430f1fe11ff4408c88d9a1e24efe39a1c65..dc1c4ae2d8b01b42a0edbb7f12a5780b25d0c8ac 100644 --- a/drivers/thermal/qcom/tsens-v1.c +++ b/drivers/thermal/qcom/tsens-v1.c @@ -21,7 +21,7 @@ #define TM_HIGH_LOW_INT_STATUS_OFF 0x0088 #define TM_HIGH_LOW_Sn_INT_THRESHOLD_OFF 0x0090 -struct tsens_legacy_calibration_format tsens_qcs404_nvmem = { +static struct tsens_legacy_calibration_format tsens_qcs404_nvmem = { .base_len = 8, .base_shift = 2, .sp_len = 6, diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 58f4d8f7a3fd60999a161e516c7919dc905d9ddb..e5bc2c82010f928b769d47dd4e7549e5d76a2701 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -887,7 +887,7 @@ static int exynos_map_dt_data(struct platform_device *pdev) return -EADDRNOTAVAIL; } - data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev); + data->soc = (uintptr_t)of_device_get_match_data(&pdev->dev); switch (data->soc) { case SOC_ARCH_EXYNOS4210: diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 6e78616a576ead65b1044e438b2f3b359da57cb2..96d99289799a164b0562b2fb61781c62a54ac96e 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -122,8 +122,8 @@ static int spear_thermal_probe(struct platform_device *pdev) stdev->flags = val; writel_relaxed(stdev->flags, stdev->thermal_base); - spear_thermal = thermal_zone_device_register("spear_thermal", 0, 0, - stdev, &ops, NULL, 0, 0); + spear_thermal = thermal_tripless_zone_device_register("spear_thermal", + stdev, &ops, NULL); if (IS_ERR(spear_thermal)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); ret = PTR_ERR(spear_thermal); diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c index cca16d632d9fc9565dea90f9094486202f13b193..f989b55a8aa8e35a07ff646f44cfc19721b0daa8 100644 --- a/drivers/thermal/sun8i_thermal.c +++ b/drivers/thermal/sun8i_thermal.c @@ -56,8 +56,6 @@ #define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12) #define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x) -/* millidegree celsius */ - struct tsensor { struct ths_device *tmdev; struct thermal_zone_device *tzd; @@ -286,7 +284,7 @@ static int sun8i_ths_calibrate(struct ths_device *tmdev) size_t callen; int ret = 0; - calcell = devm_nvmem_cell_get(dev, "calibration"); + calcell = nvmem_cell_get(dev, "calibration"); if (IS_ERR(calcell)) { if (PTR_ERR(calcell) == -EPROBE_DEFER) return -EPROBE_DEFER; @@ -316,6 +314,8 @@ static int sun8i_ths_calibrate(struct ths_device *tmdev) kfree(caldata); out: + if (!IS_ERR(calcell)) + nvmem_cell_put(calcell); return ret; } @@ -489,8 +489,6 @@ static int sun8i_ths_probe(struct platform_device *pdev) if (!tmdev->chip) return -EINVAL; - platform_set_drvdata(pdev, tmdev); - ret = sun8i_ths_resource_init(tmdev); if (ret) return ret; diff --git a/drivers/thermal/tegra/tegra-bpmp-thermal.c b/drivers/thermal/tegra/tegra-bpmp-thermal.c index a2879d624945d367fdbb5ee586cadcc2f3535abc..4ffc3bb3bf35ce911158d401f85f6786ec0dfd4f 100644 --- a/drivers/thermal/tegra/tegra-bpmp-thermal.c +++ b/drivers/thermal/tegra/tegra-bpmp-thermal.c @@ -167,19 +167,69 @@ static int tegra_bpmp_thermal_get_num_zones(struct tegra_bpmp *bpmp, return 0; } +static int tegra_bpmp_thermal_trips_supported(struct tegra_bpmp *bpmp, bool *supported) +{ + struct mrq_thermal_host_to_bpmp_request req; + union mrq_thermal_bpmp_to_host_response reply; + struct tegra_bpmp_message msg; + int err; + + memset(&req, 0, sizeof(req)); + req.type = CMD_THERMAL_QUERY_ABI; + req.query_abi.type = CMD_THERMAL_SET_TRIP; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_THERMAL; + msg.tx.data = &req; + msg.tx.size = sizeof(req); + msg.rx.data = &reply; + msg.rx.size = sizeof(reply); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err) + return err; + + if (msg.rx.ret == 0) { + *supported = true; + return 0; + } else if (msg.rx.ret == -BPMP_ENODEV) { + *supported = false; + return 0; + } else { + return -EINVAL; + } +} + static const struct thermal_zone_device_ops tegra_bpmp_of_thermal_ops = { .get_temp = tegra_bpmp_thermal_get_temp, .set_trips = tegra_bpmp_thermal_set_trips, }; +static const struct thermal_zone_device_ops tegra_bpmp_of_thermal_ops_notrips = { + .get_temp = tegra_bpmp_thermal_get_temp, +}; + static int tegra_bpmp_thermal_probe(struct platform_device *pdev) { struct tegra_bpmp *bpmp = dev_get_drvdata(pdev->dev.parent); + const struct thermal_zone_device_ops *thermal_ops; struct tegra_bpmp_thermal *tegra; struct thermal_zone_device *tzd; unsigned int i, max_num_zones; + bool supported; int err; + err = tegra_bpmp_thermal_trips_supported(bpmp, &supported); + if (err) { + dev_err(&pdev->dev, "failed to determine if trip points are supported\n"); + return err; + } + + if (supported) + thermal_ops = &tegra_bpmp_of_thermal_ops; + else + thermal_ops = &tegra_bpmp_of_thermal_ops_notrips; + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); if (!tegra) return -ENOMEM; @@ -222,7 +272,7 @@ static int tegra_bpmp_thermal_probe(struct platform_device *pdev) } tzd = devm_thermal_of_zone_register( - &pdev->dev, i, zone, &tegra_bpmp_of_thermal_ops); + &pdev->dev, i, zone, thermal_ops); if (IS_ERR(tzd)) { if (PTR_ERR(tzd) == -EPROBE_DEFER) return -EPROBE_DEFER; diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index f4f1a04f8c0f39f90c0253bf366008007e228070..1717e4a19dcbf3c856175a23f8a6736a162b1cf8 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -142,7 +142,6 @@ static int gadc_thermal_probe(struct platform_device *pdev) return ret; gti->dev = &pdev->dev; - platform_set_drvdata(pdev, gti); gti->tz_dev = devm_thermal_of_zone_register(&pdev->dev, 0, gti, &gadc_thermal_ops); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index a59700593d326d3e04a3b1e5b158a32b098ee802..58533ea75cd92563a19acd07e37f9095031eb9a7 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -348,12 +348,14 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip_id) struct thermal_trip trip; /* Ignore disabled trip points */ - if (test_bit(trip_id, &tz->trips_disabled) || - trip.temperature == THERMAL_TEMP_INVALID) + if (test_bit(trip_id, &tz->trips_disabled)) return; __thermal_zone_get_trip(tz, trip_id, &trip); + if (trip.temperature == THERMAL_TEMP_INVALID) + return; + if (tz->last_temperature != THERMAL_TEMP_INVALID) { if (tz->last_temperature < trip.temperature && tz->temperature >= trip.temperature) @@ -1266,7 +1268,7 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t return ERR_PTR(-EINVAL); } - if (num_trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp) && !trips) + if (num_trips > 0 && !trips) return ERR_PTR(-EINVAL); if (!thermal_class) @@ -1389,16 +1391,16 @@ thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *t } EXPORT_SYMBOL_GPL(thermal_zone_device_register_with_trips); -struct thermal_zone_device *thermal_zone_device_register(const char *type, int ntrips, int mask, - void *devdata, struct thermal_zone_device_ops *ops, - const struct thermal_zone_params *tzp, int passive_delay, - int polling_delay) +struct thermal_zone_device *thermal_tripless_zone_device_register( + const char *type, + void *devdata, + struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp) { - return thermal_zone_device_register_with_trips(type, NULL, ntrips, mask, - devdata, ops, tzp, - passive_delay, polling_delay); + return thermal_zone_device_register_with_trips(type, NULL, 0, 0, devdata, + ops, tzp, 0, 0); } -EXPORT_SYMBOL_GPL(thermal_zone_device_register); +EXPORT_SYMBOL_GPL(thermal_tripless_zone_device_register); void *thermal_zone_device_priv(struct thermal_zone_device *tzd) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 04513f9fbfa1803b93c669a3abd86e5d4ce4cb5c..de884bea28b66cd104565f275a5842e615a3341a 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -70,7 +70,7 @@ static inline bool cdev_is_power_actor(struct thermal_cooling_device *cdev) void thermal_cdev_update(struct thermal_cooling_device *); void __thermal_cdev_update(struct thermal_cooling_device *cdev); -int get_tz_trend(struct thermal_zone_device *tz, int trip); +int get_tz_trend(struct thermal_zone_device *tz, int trip_index); struct thermal_instance * get_thermal_instance(struct thermal_zone_device *tz, diff --git a/drivers/thermal/thermal_helpers.c b/drivers/thermal/thermal_helpers.c index cfba0965a22da0ba638a90f9075bae2915ee5c1f..4d66372c962952bea2167904b1200f6d39721838 100644 --- a/drivers/thermal/thermal_helpers.c +++ b/drivers/thermal/thermal_helpers.c @@ -22,8 +22,9 @@ #include "thermal_core.h" #include "thermal_trace.h" -int get_tz_trend(struct thermal_zone_device *tz, int trip) +int get_tz_trend(struct thermal_zone_device *tz, int trip_index) { + struct thermal_trip *trip = tz->trips ? &tz->trips[trip_index] : NULL; enum thermal_trend trend; if (tz->emul_temperature || !tz->ops->get_trend || diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 4ca905723429c7653e8add3f389602c12b80b439..1e0655b63259fe391f8a43a3a71f01172b5f74b8 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -37,8 +37,10 @@ static int of_find_trip_id(struct device_node *np, struct device_node *trip) */ for_each_child_of_node(trips, t) { - if (t == trip) + if (t == trip) { + of_node_put(t); goto out; + } i++; } @@ -401,8 +403,10 @@ static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz, for_each_child_of_node(cm_np, child) { ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action); - if (ret) + if (ret) { + of_node_put(child); break; + } } of_node_put(cm_np); diff --git a/drivers/thermal/thermal_sysfs.c b/drivers/thermal/thermal_sysfs.c index 6c20c9f90a05acb777b13019fa341fcb80e69320..4e6a97db894e9cfd19c0015a755c8638a3d1b3c6 100644 --- a/drivers/thermal/thermal_sysfs.c +++ b/drivers/thermal/thermal_sysfs.c @@ -185,9 +185,6 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, if (sscanf(attr->attr.name, "trip_point_%d_hyst", &trip_id) != 1) return -EINVAL; - if (kstrtoint(buf, 10, &trip.hysteresis)) - return -EINVAL; - mutex_lock(&tz->lock); if (!device_is_registered(dev)) { @@ -198,7 +195,11 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr, ret = __thermal_zone_get_trip(tz, trip_id, &trip); if (ret) goto unlock; - + + ret = kstrtoint(buf, 10, &trip.hysteresis); + if (ret) + goto unlock; + ret = thermal_zone_set_trip(tz, trip_id, &trip); unlock: mutex_unlock(&tz->lock); diff --git a/drivers/thermal/thermal_trip.c b/drivers/thermal/thermal_trip.c index 53115cfdfd42a4318f52d648913c1043aa8d4c5c..024e2e365a26b60d8ef73c3bffa2053e90f818fb 100644 --- a/drivers/thermal/thermal_trip.c +++ b/drivers/thermal/thermal_trip.c @@ -101,29 +101,11 @@ void __thermal_zone_set_trips(struct thermal_zone_device *tz) int __thermal_zone_get_trip(struct thermal_zone_device *tz, int trip_id, struct thermal_trip *trip) { - int ret; - - if (!tz || trip_id < 0 || trip_id >= tz->num_trips || !trip) + if (!tz || !tz->trips || trip_id < 0 || trip_id >= tz->num_trips || !trip) return -EINVAL; - if (tz->trips) { - *trip = tz->trips[trip_id]; - return 0; - } - - if (tz->ops->get_trip_hyst) { - ret = tz->ops->get_trip_hyst(tz, trip_id, &trip->hysteresis); - if (ret) - return ret; - } else { - trip->hysteresis = 0; - } - - ret = tz->ops->get_trip_temp(tz, trip_id, &trip->temperature); - if (ret) - return ret; - - return tz->ops->get_trip_type(tz, trip_id, &trip->type); + *trip = tz->trips[trip_id]; + return 0; } EXPORT_SYMBOL_GPL(__thermal_zone_get_trip); diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index a1c9a153018320788f4b39855e1f038aa23e46ae..0c2eb9c6e58b6a3d0b8935a19c80099cb64f0c8d 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -314,7 +314,7 @@ int ti_bandgap_adc_to_mcelsius(struct ti_bandgap *bgp, int adc_val, int *t) */ static inline int ti_bandgap_validate(struct ti_bandgap *bgp, int id) { - if (!bgp || IS_ERR(bgp)) { + if (IS_ERR_OR_NULL(bgp)) { pr_err("%s: invalid bandgap pointer\n", __func__); return -EINVAL; } diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index d414a4b7a94a454a88df5207fd07bc9653c11926..0cf0826b805a998abd0d4e2cfa6938de8a46c4f0 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -109,7 +109,9 @@ static inline int __ti_thermal_get_temp(struct thermal_zone_device *tz, int *tem return ret; } -static int __ti_thermal_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) +static int __ti_thermal_get_trend(struct thermal_zone_device *tz, + const struct thermal_trip *trip, + enum thermal_trend *trend) { struct ti_thermal_data *data = thermal_zone_device_priv(tz); struct ti_bandgap *bgp; diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index b3550ff9c4942778d9d4afaa4cbcef6f692d6eb1..1f3aba607cd51dcc389d1c9ad723643e476e7d13 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -3097,10 +3097,8 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc) gsm->has_devices = false; } for (i = NUM_DLCI - 1; i >= 0; i--) - if (gsm->dlci[i]) { + if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); - gsm->dlci[i] = NULL; - } mutex_unlock(&gsm->mutex); /* Now wipe the queues */ tty_ldisc_flush(gsm->tty); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index fb891b67968f0fb61914293b8aeea0b00f68427b..141627370aabc334936aa560f4881ec258005fbc 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1936,7 +1936,10 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) skip_rx = true; if (status & (UART_LSR_DR | UART_LSR_BI) && !skip_rx) { - if (irqd_is_wakeup_set(irq_get_irq_data(port->irq))) + struct irq_data *d; + + d = irq_get_irq_data(port->irq); + if (d && irqd_is_wakeup_set(d)) pm_wakeup_event(tport->tty->dev, 0); if (!up->dma || handle_rx_dma(up, iir)) status = serial8250_rx_chars(up, status); diff --git a/drivers/ufs/core/Kconfig b/drivers/ufs/core/Kconfig index e11978171403acd1d7225d48efb4251db8595ffb..817208ee64ecffd8997f727d079e45b803e9f095 100644 --- a/drivers/ufs/core/Kconfig +++ b/drivers/ufs/core/Kconfig @@ -35,14 +35,6 @@ config SCSI_UFS_CRYPTO capabilities of the UFS device (if present) to perform crypto operations on data being transferred to/from the device. -config SCSI_UFS_HPB - bool "Support UFS Host Performance Booster" - help - The UFS HPB feature improves random read performance. It caches - L2P (logical to physical) map of UFS to host DRAM. The driver uses HPB - read command by piggybacking physical page number for bypassing FTL (flash - translation layer)'s L2P address translation. - config SCSI_UFS_FAULT_INJECTION bool "UFS Fault Injection Support" depends on FAULT_INJECTION diff --git a/drivers/ufs/core/Makefile b/drivers/ufs/core/Makefile index 4d02e0f2de10a2fa356ca59edd5c79b199536dc4..cf820fa09a0483a57409d5ff7f43dfff96cdb538 100644 --- a/drivers/ufs/core/Makefile +++ b/drivers/ufs/core/Makefile @@ -5,6 +5,5 @@ ufshcd-core-y += ufshcd.o ufs-sysfs.o ufs-mcq.o ufshcd-core-$(CONFIG_DEBUG_FS) += ufs-debugfs.o ufshcd-core-$(CONFIG_SCSI_UFS_BSG) += ufs_bsg.o ufshcd-core-$(CONFIG_SCSI_UFS_CRYPTO) += ufshcd-crypto.o -ufshcd-core-$(CONFIG_SCSI_UFS_HPB) += ufshpb.o ufshcd-core-$(CONFIG_SCSI_UFS_FAULT_INJECTION) += ufs-fault-injection.o ufshcd-core-$(CONFIG_SCSI_UFS_HWMON) += ufs-hwmon.o diff --git a/drivers/ufs/core/ufs-hwmon.c b/drivers/ufs/core/ufs-hwmon.c index 101d7082446fc1828ab7afa4e001c732ebcfe32e..34194064367ff200db8864268d8b55a1f6fc99f2 100644 --- a/drivers/ufs/core/ufs-hwmon.c +++ b/drivers/ufs/core/ufs-hwmon.c @@ -127,7 +127,8 @@ static int ufs_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 return err; } -static umode_t ufs_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, +static umode_t ufs_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, u32 attr, int channel) { if (type != hwmon_temp) diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 386674ead7f0d43965a7378848b839ee33509b48..2ba8ec254dceeec5bacf2b027bb1b06ed9f54c56 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -97,6 +97,7 @@ void ufshcd_mcq_config_mac(struct ufs_hba *hba, u32 max_active_cmds) val |= FIELD_PREP(MCQ_CFG_MAC_MASK, max_active_cmds); ufshcd_writel(hba, val, REG_UFS_MCQ_CFG); } +EXPORT_SYMBOL_GPL(ufshcd_mcq_config_mac); /** * ufshcd_mcq_req_to_hwq - find the hardware queue on which the @@ -104,7 +105,7 @@ void ufshcd_mcq_config_mac(struct ufs_hba *hba, u32 max_active_cmds) * @hba: per adapter instance * @req: pointer to the request to be issued * - * Returns the hardware queue instance on which the request would + * Return: the hardware queue instance on which the request would * be queued. */ struct ufs_hw_queue *ufshcd_mcq_req_to_hwq(struct ufs_hba *hba, @@ -120,7 +121,7 @@ struct ufs_hw_queue *ufshcd_mcq_req_to_hwq(struct ufs_hba *hba, * ufshcd_mcq_decide_queue_depth - decide the queue depth * @hba: per adapter instance * - * Returns queue-depth on success, non-zero on error + * Return: queue-depth on success, non-zero on error * * MAC - Max. Active Command of the Host Controller (HC) * HC wouldn't send more than this commands to the device. @@ -245,6 +246,7 @@ u32 ufshcd_mcq_read_cqis(struct ufs_hba *hba, int i) { return readl(mcq_opr_base(hba, OPR_CQIS, i) + REG_CQIS); } +EXPORT_SYMBOL_GPL(ufshcd_mcq_read_cqis); void ufshcd_mcq_write_cqis(struct ufs_hba *hba, u32 val, int i) { @@ -388,6 +390,7 @@ void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba) MCQ_CFG_n(REG_SQATTR, i)); } } +EXPORT_SYMBOL_GPL(ufshcd_mcq_make_queues_operational); void ufshcd_mcq_enable_esi(struct ufs_hba *hba) { @@ -487,10 +490,10 @@ static int ufshcd_mcq_sq_start(struct ufs_hba *hba, struct ufs_hw_queue *hwq) /** * ufshcd_mcq_sq_cleanup - Clean up submission queue resources * associated with the pending command. - * @hba - per adapter instance. - * @task_tag - The command's task tag. + * @hba: per adapter instance. + * @task_tag: The command's task tag. * - * Returns 0 for success; error code otherwise. + * Return: 0 for success; error code otherwise. */ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag) { @@ -551,16 +554,11 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag) * Write the sqe's Command Type to 0xF. The host controller will not * fetch any sqe with Command Type = 0xF. * - * @utrd - UTP Transfer Request Descriptor to be nullified. + * @utrd: UTP Transfer Request Descriptor to be nullified. */ static void ufshcd_mcq_nullify_sqe(struct utp_transfer_req_desc *utrd) { - u32 dword_0; - - dword_0 = le32_to_cpu(utrd->header.dword_0); - dword_0 &= ~UPIU_COMMAND_TYPE_MASK; - dword_0 |= FIELD_PREP(UPIU_COMMAND_TYPE_MASK, 0xF); - utrd->header.dword_0 = cpu_to_le32(dword_0); + utrd->header.command_type = 0xf; } /** @@ -568,11 +566,11 @@ static void ufshcd_mcq_nullify_sqe(struct utp_transfer_req_desc *utrd) * If the command is in the submission queue and not issued to the device yet, * nullify the sqe so the host controller will skip fetching the sqe. * - * @hba - per adapter instance. - * @hwq - Hardware Queue to be searched. - * @task_tag - The command's task tag. + * @hba: per adapter instance. + * @hwq: Hardware Queue to be searched. + * @task_tag: The command's task tag. * - * Returns true if the SQE containing the command is present in the SQ + * Return: true if the SQE containing the command is present in the SQ * (not fetched by the controller); returns false if the SQE is not in the SQ. */ static bool ufshcd_mcq_sqe_search(struct ufs_hba *hba, @@ -621,9 +619,9 @@ static bool ufshcd_mcq_sqe_search(struct ufs_hba *hba, /** * ufshcd_mcq_abort - Abort the command in MCQ. - * @cmd - The command to be aborted. + * @cmd: The command to be aborted. * - * Returns SUCCESS or FAILED error codes + * Return: SUCCESS or FAILED error codes */ int ufshcd_mcq_abort(struct scsi_cmnd *cmd) { diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index 6c72075750dd0463aa056a6b84e156fd5893c10c..c95906443d5f9d5548ec6c03ec5b051fbbf6531e 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -718,8 +718,6 @@ UFS_DEVICE_DESC_PARAM(device_version, _DEV_VER, 2); UFS_DEVICE_DESC_PARAM(number_of_secure_wpa, _NUM_SEC_WPA, 1); UFS_DEVICE_DESC_PARAM(psa_max_data_size, _PSA_MAX_DATA, 4); UFS_DEVICE_DESC_PARAM(psa_state_timeout, _PSA_TMT, 1); -UFS_DEVICE_DESC_PARAM(hpb_version, _HPB_VER, 2); -UFS_DEVICE_DESC_PARAM(hpb_control, _HPB_CONTROL, 1); UFS_DEVICE_DESC_PARAM(ext_feature_sup, _EXT_UFS_FEATURE_SUP, 4); UFS_DEVICE_DESC_PARAM(wb_presv_us_en, _WB_PRESRV_USRSPC_EN, 1); UFS_DEVICE_DESC_PARAM(wb_type, _WB_TYPE, 1); @@ -752,8 +750,6 @@ static struct attribute *ufs_sysfs_device_descriptor[] = { &dev_attr_number_of_secure_wpa.attr, &dev_attr_psa_max_data_size.attr, &dev_attr_psa_state_timeout.attr, - &dev_attr_hpb_version.attr, - &dev_attr_hpb_control.attr, &dev_attr_ext_feature_sup.attr, &dev_attr_wb_presv_us_en.attr, &dev_attr_wb_type.attr, @@ -827,10 +823,6 @@ UFS_GEOMETRY_DESC_PARAM(enh4_memory_max_alloc_units, _ENM4_MAX_NUM_UNITS, 4); UFS_GEOMETRY_DESC_PARAM(enh4_memory_capacity_adjustment_factor, _ENM4_CAP_ADJ_FCTR, 2); -UFS_GEOMETRY_DESC_PARAM(hpb_region_size, _HPB_REGION_SIZE, 1); -UFS_GEOMETRY_DESC_PARAM(hpb_number_lu, _HPB_NUMBER_LU, 1); -UFS_GEOMETRY_DESC_PARAM(hpb_subregion_size, _HPB_SUBREGION_SIZE, 1); -UFS_GEOMETRY_DESC_PARAM(hpb_max_active_regions, _HPB_MAX_ACTIVE_REGS, 2); UFS_GEOMETRY_DESC_PARAM(wb_max_alloc_units, _WB_MAX_ALLOC_UNITS, 4); UFS_GEOMETRY_DESC_PARAM(wb_max_wb_luns, _WB_MAX_WB_LUNS, 1); UFS_GEOMETRY_DESC_PARAM(wb_buff_cap_adj, _WB_BUFF_CAP_ADJ, 1); @@ -868,10 +860,6 @@ static struct attribute *ufs_sysfs_geometry_descriptor[] = { &dev_attr_enh3_memory_capacity_adjustment_factor.attr, &dev_attr_enh4_memory_max_alloc_units.attr, &dev_attr_enh4_memory_capacity_adjustment_factor.attr, - &dev_attr_hpb_region_size.attr, - &dev_attr_hpb_number_lu.attr, - &dev_attr_hpb_subregion_size.attr, - &dev_attr_hpb_max_active_regions.attr, &dev_attr_wb_max_alloc_units.attr, &dev_attr_wb_max_wb_luns.attr, &dev_attr_wb_buff_cap_adj.attr, @@ -1132,7 +1120,6 @@ UFS_FLAG(disable_fw_update, _PERMANENTLY_DISABLE_FW_UPDATE); UFS_FLAG(wb_enable, _WB_EN); UFS_FLAG(wb_flush_en, _WB_BUFF_FLUSH_EN); UFS_FLAG(wb_flush_during_h8, _WB_BUFF_FLUSH_DURING_HIBERN8); -UFS_FLAG(hpb_enable, _HPB_EN); static struct attribute *ufs_sysfs_device_flags[] = { &dev_attr_device_init.attr, @@ -1146,7 +1133,6 @@ static struct attribute *ufs_sysfs_device_flags[] = { &dev_attr_wb_enable.attr, &dev_attr_wb_flush_en.attr, &dev_attr_wb_flush_during_h8.attr, - &dev_attr_hpb_enable.attr, NULL, }; @@ -1193,7 +1179,6 @@ out: \ static DEVICE_ATTR_RO(_name) UFS_ATTRIBUTE(boot_lun_enabled, _BOOT_LU_EN); -UFS_ATTRIBUTE(max_data_size_hpb_single_cmd, _MAX_HPB_SINGLE_CMD); UFS_ATTRIBUTE(current_power_mode, _POWER_MODE); UFS_ATTRIBUTE(active_icc_level, _ACTIVE_ICC_LVL); UFS_ATTRIBUTE(ooo_data_enabled, _OOO_DATA_EN); @@ -1217,7 +1202,6 @@ UFS_ATTRIBUTE(wb_cur_buf, _CURR_WB_BUFF_SIZE); static struct attribute *ufs_sysfs_attributes[] = { &dev_attr_boot_lun_enabled.attr, - &dev_attr_max_data_size_hpb_single_cmd.attr, &dev_attr_current_power_mode.attr, &dev_attr_active_icc_level.attr, &dev_attr_ooo_data_enabled.attr, @@ -1291,9 +1275,6 @@ UFS_UNIT_DESC_PARAM(provisioning_type, _PROVISIONING_TYPE, 1); UFS_UNIT_DESC_PARAM(physical_memory_resourse_count, _PHY_MEM_RSRC_CNT, 8); UFS_UNIT_DESC_PARAM(context_capabilities, _CTX_CAPABILITIES, 2); UFS_UNIT_DESC_PARAM(large_unit_granularity, _LARGE_UNIT_SIZE_M1, 1); -UFS_UNIT_DESC_PARAM(hpb_lu_max_active_regions, _HPB_LU_MAX_ACTIVE_RGNS, 2); -UFS_UNIT_DESC_PARAM(hpb_pinned_region_start_offset, _HPB_PIN_RGN_START_OFF, 2); -UFS_UNIT_DESC_PARAM(hpb_number_pinned_regions, _HPB_NUM_PIN_RGNS, 2); UFS_UNIT_DESC_PARAM(wb_buf_alloc_units, _WB_BUF_ALLOC_UNITS, 4); static struct attribute *ufs_sysfs_unit_descriptor[] = { @@ -1311,9 +1292,6 @@ static struct attribute *ufs_sysfs_unit_descriptor[] = { &dev_attr_physical_memory_resourse_count.attr, &dev_attr_context_capabilities.attr, &dev_attr_large_unit_granularity.attr, - &dev_attr_hpb_lu_max_active_regions.attr, - &dev_attr_hpb_pinned_region_start_offset.attr, - &dev_attr_hpb_number_pinned_regions.attr, &dev_attr_wb_buf_alloc_units.attr, NULL, }; diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index 0d38e7fa34cc8af94dce82182f84e5034873a9cd..374e5aae4e7e86b0b80b75defa85dfea2e60fedd 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -76,8 +76,7 @@ static int ufs_bsg_exec_advanced_rpmb_req(struct ufs_hba *hba, struct bsg_job *j int ret; int data_len; - if (hba->ufs_version < ufshci_version(4, 0) || !hba->dev_info.b_advanced_rpmb_en || - !(hba->capabilities & MASK_EHSLUTRD_SUPPORTED)) + if (hba->ufs_version < ufshci_version(4, 0) || !hba->dev_info.b_advanced_rpmb_en) return -EINVAL; if (rpmb_request->ehs_req.length != 2 || rpmb_request->ehs_req.ehs_type != 1) @@ -232,6 +231,8 @@ static inline void ufs_bsg_node_release(struct device *dev) * @hba: per adapter object * * Called during initial loading of the driver, and before scsi_scan_host. + * + * Returns: 0 (success). */ int ufs_bsg_probe(struct ufs_hba *hba) { diff --git a/drivers/ufs/core/ufshcd-crypto.h b/drivers/ufs/core/ufshcd-crypto.h index 504cc841540b21893fb89e233d4fd9268f771179..be8596f20ba2f2cc32b701bf1f255df11e473a7e 100644 --- a/drivers/ufs/core/ufshcd-crypto.h +++ b/drivers/ufs/core/ufshcd-crypto.h @@ -26,15 +26,15 @@ static inline void ufshcd_prepare_lrbp_crypto(struct request *rq, } static inline void -ufshcd_prepare_req_desc_hdr_crypto(struct ufshcd_lrb *lrbp, u32 *dword_0, - u32 *dword_1, u32 *dword_3) +ufshcd_prepare_req_desc_hdr_crypto(struct ufshcd_lrb *lrbp, + struct request_desc_header *h) { - if (lrbp->crypto_key_slot >= 0) { - *dword_0 |= UTP_REQ_DESC_CRYPTO_ENABLE_CMD; - *dword_0 |= lrbp->crypto_key_slot; - *dword_1 = lower_32_bits(lrbp->data_unit_num); - *dword_3 = upper_32_bits(lrbp->data_unit_num); - } + if (lrbp->crypto_key_slot < 0) + return; + h->enable_crypto = 1; + h->cci = lrbp->crypto_key_slot; + h->dunl = cpu_to_le32(lower_32_bits(lrbp->data_unit_num)); + h->dunu = cpu_to_le32(upper_32_bits(lrbp->data_unit_num)); } bool ufshcd_crypto_enable(struct ufs_hba *hba); @@ -51,8 +51,8 @@ static inline void ufshcd_prepare_lrbp_crypto(struct request *rq, struct ufshcd_lrb *lrbp) { } static inline void -ufshcd_prepare_req_desc_hdr_crypto(struct ufshcd_lrb *lrbp, u32 *dword_0, - u32 *dword_1, u32 *dword_3) { } +ufshcd_prepare_req_desc_hdr_crypto(struct ufshcd_lrb *lrbp, + struct request_desc_header *h) { } static inline bool ufshcd_crypto_enable(struct ufs_hba *hba) { diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index 0f3bd943b58b1f2141f960164e055b6041f89a53..f42d99ce5bf1ee3fc7d5d604a0b589a6a9f59825 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -93,7 +93,7 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd); int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, struct utp_upiu_req *req_upiu, struct utp_upiu_req *rsp_upiu, - int msgcode, + enum upiu_request_transaction msgcode, u8 *desc_buff, int *buff_len, enum query_opcode desc_op); @@ -294,7 +294,7 @@ extern const struct ufs_pm_lvl_states ufs_pm_lvl_states[]; * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN * @scsi_lun: scsi LUN id * - * Returns UPIU LUN id + * Return: UPIU LUN id */ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) { diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 1294467757964e52127bde42f53774487f1dd4c2..c2df07545f966bb566846341bdafabb6ead242ad 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,6 @@ #include "ufs-fault-injection.h" #include "ufs_bsg.h" #include "ufshcd-crypto.h" -#include "ufshpb.h" #include #define CREATE_TRACE_POINTS @@ -238,8 +238,7 @@ static const struct ufs_dev_quirk ufs_fixups[] = { /* UFS cards deviations table */ { .wmanufacturerid = UFS_VENDOR_MICRON, .model = UFS_ANY_MODEL, - .quirk = UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM | - UFS_DEVICE_QUIRK_SWAP_L2P_ENTRY_FOR_HPB_READ }, + .quirk = UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM }, { .wmanufacturerid = UFS_VENDOR_SAMSUNG, .model = UFS_ANY_MODEL, .quirk = UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM | @@ -703,8 +702,7 @@ EXPORT_SYMBOL_GPL(ufshcd_delay_us); * @interval_us: polling interval in microseconds * @timeout_ms: timeout in milliseconds * - * Return: - * -ETIMEDOUT on error, zero on success. + * Return: -ETIMEDOUT on error, zero on success. */ static int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, u32 val, unsigned long interval_us, @@ -732,7 +730,7 @@ static int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask, * ufshcd_get_intr_mask - Get the interrupt bit mask * @hba: Pointer to adapter instance * - * Returns interrupt bit mask per version + * Return: interrupt bit mask per version */ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba) { @@ -748,7 +746,7 @@ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba) * ufshcd_get_ufs_version - Get the UFS version supported by the HBA * @hba: Pointer to adapter instance * - * Returns UFSHCI version supported by the controller + * Return: UFSHCI version supported by the controller */ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) { @@ -775,7 +773,7 @@ static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) * the host controller * @hba: pointer to adapter instance * - * Returns true if device present, false if no device detected + * Return: true if device present, false if no device detected */ static inline bool ufshcd_is_device_present(struct ufs_hba *hba) { @@ -788,7 +786,8 @@ static inline bool ufshcd_is_device_present(struct ufs_hba *hba) * @cqe: pointer to the completion queue entry * * This function is used to get the OCS field from UTRD - * Returns the OCS field in the UTRD + * + * Return: the OCS field in the UTRD. */ static enum utp_ocs ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp, struct cq_entry *cqe) @@ -796,7 +795,7 @@ static enum utp_ocs ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp, if (cqe) return le32_to_cpu(cqe->status) & MASK_OCS; - return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS; + return lrbp->utr_descriptor_ptr->header.ocs & MASK_OCS; } /** @@ -841,7 +840,7 @@ static inline void ufshcd_utmrl_clear(struct ufs_hba *hba, u32 pos) * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY * @reg: Register value of host controller status * - * Returns integer, 0 on Success and positive value if failed + * Return: 0 on success; a positive value if failed. */ static inline int ufshcd_get_lists_status(u32 reg) { @@ -853,7 +852,8 @@ static inline int ufshcd_get_lists_status(u32 reg) * @hba: Pointer to adapter instance * * This function gets the result of UIC command completion - * Returns 0 on success, non zero value on error + * + * Return: 0 on success; non-zero value on error. */ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba) { @@ -866,7 +866,8 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba) * @hba: Pointer to adapter instance * * This function gets UIC command argument3 - * Returns 0 on success, non zero value on error + * + * Return: 0 on success; non-zero value on error. */ static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba) { @@ -876,38 +877,13 @@ static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba) /** * ufshcd_get_req_rsp - returns the TR response transaction type * @ucd_rsp_ptr: pointer to response UPIU - */ -static inline int -ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr) -{ - return be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24; -} - -/** - * ufshcd_get_rsp_upiu_result - Get the result from response UPIU - * @ucd_rsp_ptr: pointer to response UPIU - * - * This function gets the response status and scsi_status from response UPIU - * Returns the response result code. - */ -static inline int -ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) -{ - return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT; -} - -/* - * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length - * from response UPIU - * @ucd_rsp_ptr: pointer to response UPIU * - * Return the data segment length. + * Return: UPIU type. */ -static inline unsigned int -ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr) +static inline enum upiu_response_transaction +ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr) { - return be32_to_cpu(ucd_rsp_ptr->header.dword_2) & - MASK_RSP_UPIU_DATA_SEG_LEN; + return ucd_rsp_ptr->header.transaction_code; } /** @@ -917,12 +893,11 @@ ufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr) * The function checks if the device raised an exception event indicated in * the Device Information field of response UPIU. * - * Returns true if exception is raised, false otherwise. + * Return: true if exception is raised, false otherwise. */ static inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr) { - return be32_to_cpu(ucd_rsp_ptr->header.dword_2) & - MASK_RSP_EXCEPTION_EVENT; + return ucd_rsp_ptr->header.device_information & 1; } /** @@ -993,12 +968,13 @@ static inline void ufshcd_hba_start(struct ufs_hba *hba) * ufshcd_is_hba_active - Get controller state * @hba: per adapter instance * - * Returns true if and only if the controller is active. + * Return: true if and only if the controller is active. */ -static inline bool ufshcd_is_hba_active(struct ufs_hba *hba) +bool ufshcd_is_hba_active(struct ufs_hba *hba) { return ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & CONTROLLER_ENABLE; } +EXPORT_SYMBOL_GPL(ufshcd_is_hba_active); u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba) { @@ -1029,8 +1005,7 @@ static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba) * @hba: per adapter instance * @scale_up: If True, set max possible frequency othewise set low frequency * - * Returns 0 if successful - * Returns < 0 for any other errors + * Return: 0 if successful; < 0 upon failure. */ static int ufshcd_set_clk_freq(struct ufs_hba *hba, bool scale_up) { @@ -1092,8 +1067,7 @@ static int ufshcd_set_clk_freq(struct ufs_hba *hba, bool scale_up) * @hba: per adapter instance * @scale_up: True if scaling up and false if scaling down * - * Returns 0 if successful - * Returns < 0 for any other errors + * Return: 0 if successful; < 0 upon failure. */ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) { @@ -1124,7 +1098,7 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) * @hba: per adapter instance * @scale_up: True if scaling up and false if scaling down * - * Returns true if scaling is required, false otherwise. + * Return: true if scaling is required, false otherwise. */ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba, bool scale_up) @@ -1241,9 +1215,8 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, * @hba: per adapter instance * @scale_up: True for scaling up gear and false for scaling down * - * Returns 0 for success, - * Returns -EBUSY if scaling can't happen at this time - * Returns non-zero for any other errors + * Return: 0 for success; -EBUSY if scaling can't happen at this time; + * non-zero for any other errors. */ static int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up) { @@ -1333,9 +1306,8 @@ static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err, bool sc * @hba: per adapter instance * @scale_up: True for scaling up and false for scalin down * - * Returns 0 for success, - * Returns -EBUSY if scaling can't happen at this time - * Returns non-zero for any other errors + * Return: 0 for success; -EBUSY if scaling can't happen at this time; non-zero + * for any other errors. */ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up) { @@ -2225,10 +2197,11 @@ void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag, static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp) { u8 *const sense_buffer = lrbp->cmd->sense_buffer; + u16 resp_len; int len; - if (sense_buffer && - ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) { + resp_len = be16_to_cpu(lrbp->ucd_rsp_ptr->header.data_segment_length); + if (sense_buffer && resp_len) { int len_to_copy; len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len); @@ -2244,6 +2217,8 @@ static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp) * descriptor * @hba: per adapter instance * @lrbp: pointer to local reference block + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) @@ -2261,8 +2236,8 @@ int ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) u16 buf_len; /* data segment length */ - resp_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) & - MASK_QUERY_DATA_SEG_LEN; + resp_len = be16_to_cpu(lrbp->ucd_rsp_ptr->header + .data_segment_length); buf_len = be16_to_cpu( hba->dev_cmd.query.request.upiu_req.length); if (likely(buf_len >= resp_len)) { @@ -2320,11 +2295,16 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba) * ufshcd_ready_for_uic_cmd - Check if controller is ready * to accept UIC commands * @hba: per adapter instance - * Return true on success, else false + * + * Return: true on success, else false. */ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba) { - return ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY; + u32 val; + int ret = read_poll_timeout(ufshcd_readl, val, val & UIC_COMMAND_READY, + 500, UIC_CMD_TIMEOUT * 1000, false, hba, + REG_CONTROLLER_STATUS); + return ret == 0 ? true : false; } /** @@ -2332,7 +2312,8 @@ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba) * @hba: Pointer to adapter instance * * This function gets the UPMCRS field of HCS register - * Returns value of UPMCRS field + * + * Return: value of UPMCRS field. */ static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) { @@ -2370,7 +2351,7 @@ ufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) * @hba: per adapter instance * @uic_cmd: UIC command * - * Returns 0 only if success. + * Return: 0 only if success. */ static int ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) @@ -2409,14 +2390,13 @@ ufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) * @uic_cmd: UIC command * @completion: initialize the completion only if this is set to true * - * Returns 0 only if success. + * Return: 0 only if success. */ static int __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, bool completion) { lockdep_assert_held(&hba->uic_cmd_mutex); - lockdep_assert_held(hba->host->host_lock); if (!ufshcd_ready_for_uic_cmd(hba)) { dev_err(hba->dev, @@ -2438,12 +2418,11 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, * @hba: per adapter instance * @uic_cmd: UIC command * - * Returns 0 only if success. + * Return: 0 only if success. */ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; - unsigned long flags; if (hba->quirks & UFSHCD_QUIRK_BROKEN_UIC_CMD) return 0; @@ -2452,9 +2431,7 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) mutex_lock(&hba->uic_cmd_mutex); ufshcd_add_delay_before_dme_cmd(hba); - spin_lock_irqsave(hba->host->host_lock, flags); ret = __ufshcd_send_uic_cmd(hba, uic_cmd, true); - spin_unlock_irqrestore(hba->host->host_lock, flags); if (!ret) ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); @@ -2515,7 +2492,7 @@ static void ufshcd_sgl_to_prdt(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, int * @hba: per adapter instance * @lrbp: pointer to local reference block * - * Returns 0 in case of success, non-zero value in case of failure + * Return: 0 in case of success, non-zero value in case of failure. */ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { @@ -2584,10 +2561,10 @@ static void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp, u8 *upiu_flags, enum dma_data_direction cmd_dir, int ehs_length) { struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr; - u32 data_direction; - u32 dword_0; - u32 dword_1 = 0; - u32 dword_3 = 0; + struct request_desc_header *h = &req_desc->header; + enum utp_data_direction data_direction; + + *h = (typeof(*h)){ }; if (cmd_dir == DMA_FROM_DEVICE) { data_direction = UTP_DEVICE_TO_HOST; @@ -2600,25 +2577,22 @@ static void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp, u8 *upiu_flags, *upiu_flags = UPIU_CMD_FLAGS_NONE; } - dword_0 = data_direction | (lrbp->command_type << UPIU_COMMAND_TYPE_OFFSET) | - ehs_length << 8; + h->command_type = lrbp->command_type; + h->data_direction = data_direction; + h->ehs_length = ehs_length; + if (lrbp->intr_cmd) - dword_0 |= UTP_REQ_DESC_INT_CMD; + h->interrupt = 1; /* Prepare crypto related dwords */ - ufshcd_prepare_req_desc_hdr_crypto(lrbp, &dword_0, &dword_1, &dword_3); + ufshcd_prepare_req_desc_hdr_crypto(lrbp, h); - /* Transfer request descriptor header fields */ - req_desc->header.dword_0 = cpu_to_le32(dword_0); - req_desc->header.dword_1 = cpu_to_le32(dword_1); /* * assigning invalid value for command status. Controller * updates OCS on command completion, with the command * status */ - req_desc->header.dword_2 = - cpu_to_le32(OCS_INVALID_COMMAND_STATUS); - req_desc->header.dword_3 = cpu_to_le32(dword_3); + h->ocs = OCS_INVALID_COMMAND_STATUS; req_desc->prd_table_length = 0; } @@ -2636,15 +2610,13 @@ void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u8 upiu_flags) struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr; unsigned short cdb_len; - /* command descriptor fields */ - ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD( - UPIU_TRANSACTION_COMMAND, upiu_flags, - lrbp->lun, lrbp->task_tag); - ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD( - UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0); - - /* Total EHS length and Data segment length will be zero */ - ucd_req_ptr->header.dword_2 = 0; + ucd_req_ptr->header = (struct utp_upiu_header){ + .transaction_code = UPIU_TRANSACTION_COMMAND, + .flags = upiu_flags, + .lun = lrbp->lun, + .task_tag = lrbp->task_tag, + .command_set_type = UPIU_COMMAND_SET_TYPE_SCSI, + }; ucd_req_ptr->sc.exp_data_transfer_len = cpu_to_be32(cmd->sdb.length); @@ -2669,18 +2641,19 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, u16 len = be16_to_cpu(query->request.upiu_req.length); /* Query request header */ - ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD( - UPIU_TRANSACTION_QUERY_REQ, upiu_flags, - lrbp->lun, lrbp->task_tag); - ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD( - 0, query->request.query_func, 0, 0); - - /* Data segment length only need for WRITE_DESC */ - if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC) - ucd_req_ptr->header.dword_2 = - UPIU_HEADER_DWORD(0, 0, (len >> 8), (u8)len); - else - ucd_req_ptr->header.dword_2 = 0; + ucd_req_ptr->header = (struct utp_upiu_header){ + .transaction_code = UPIU_TRANSACTION_QUERY_REQ, + .flags = upiu_flags, + .lun = lrbp->lun, + .task_tag = lrbp->task_tag, + .query_function = query->request.query_func, + /* Data segment length only need for WRITE_DESC */ + .data_segment_length = + query->request.upiu_req.opcode == + UPIU_QUERY_OPCODE_WRITE_DESC ? + cpu_to_be16(len) : + 0, + }; /* Copy the Query Request buffer as is */ memcpy(&ucd_req_ptr->qr, &query->request.upiu_req, @@ -2699,13 +2672,10 @@ static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp) memset(ucd_req_ptr, 0, sizeof(struct utp_upiu_req)); - /* command descriptor fields */ - ucd_req_ptr->header.dword_0 = - UPIU_HEADER_DWORD( - UPIU_TRANSACTION_NOP_OUT, 0, 0, lrbp->task_tag); - /* clear rest of the fields of basic header */ - ucd_req_ptr->header.dword_1 = 0; - ucd_req_ptr->header.dword_2 = 0; + ucd_req_ptr->header = (struct utp_upiu_header){ + .transaction_code = UPIU_TRANSACTION_NOP_OUT, + .task_tag = lrbp->task_tag, + }; memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); } @@ -2715,6 +2685,8 @@ static inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp) * for Device Management Purposes * @hba: per adapter instance * @lrbp: pointer to local reference block + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_compose_devman_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) @@ -2743,6 +2715,8 @@ static int ufshcd_compose_devman_upiu(struct ufs_hba *hba, * for SCSI Purposes * @hba: per adapter instance * @lrbp: pointer to local reference block + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { @@ -2768,7 +2742,7 @@ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID * @upiu_wlun_id: UPIU W-LUN id * - * Returns SCSI W-LUN id + * Return: SCSI W-LUN id. */ static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id) { @@ -2839,7 +2813,7 @@ static void ufshcd_init_lrb(struct ufs_hba *hba, struct ufshcd_lrb *lrb, int i) * @host: SCSI host pointer * @cmd: command from SCSI Midlayer * - * Returns 0 for success, non-zero in case of failure + * Return: 0 for success, non-zero in case of failure. */ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { @@ -2908,8 +2882,6 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) lrbp->req_abort_skip = false; - ufshpb_prep(hba, lrbp); - ufshcd_comp_scsi_upiu(hba, lrbp); err = ufshcd_map_sg(hba, lrbp); @@ -2952,7 +2924,7 @@ static int ufshcd_compose_dev_cmd(struct ufs_hba *hba, * Check with the block layer if the command is inflight * @cmd: command to check. * - * Returns true if command is inflight; false if not. + * Return: true if command is inflight; false if not. */ bool ufshcd_cmd_inflight(struct scsi_cmnd *cmd) { @@ -3007,26 +2979,17 @@ static int ufshcd_clear_cmd(struct ufs_hba *hba, u32 task_tag) mask, ~mask, 1000, 1000); } -static int -ufshcd_check_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) -{ - struct ufs_query_res *query_res = &hba->dev_cmd.query.response; - - /* Get the UPIU response */ - query_res->response = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr) >> - UPIU_RSP_CODE_OFFSET; - return query_res->response; -} - /** * ufshcd_dev_cmd_completion() - handles device management command responses * @hba: per adapter instance * @lrbp: pointer to local reference block + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { - int resp; + enum upiu_response_transaction resp; int err = 0; hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0); @@ -3040,11 +3003,13 @@ ufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) __func__, resp); } break; - case UPIU_TRANSACTION_QUERY_RSP: - err = ufshcd_check_query_response(hba, lrbp); - if (!err) + case UPIU_TRANSACTION_QUERY_RSP: { + u8 response = lrbp->ucd_rsp_ptr->header.response; + + if (response == 0) err = ufshcd_copy_query_response(hba, lrbp); break; + } case UPIU_TRANSACTION_REJECT_UPIU: /* TODO: handle Reject UPIU Response */ err = -EPERM; @@ -3159,6 +3124,8 @@ static int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba, * @cmd_type: specifies the type (NOP, Query...) * @timeout: timeout in milliseconds * + * Return: 0 upon success; < 0 upon failure. + * * NOTE: Since there is only one available tag for device management commands, * it is expected you hold the hba->dev_cmd.lock mutex. */ @@ -3250,7 +3217,7 @@ static int ufshcd_query_flag_retry(struct ufs_hba *hba, * @index: flag index to access * @flag_res: the flag value after the query request completes * - * Returns 0 for success, non-zero in case of failure + * Return: 0 for success, non-zero in case of failure. */ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res) @@ -3319,7 +3286,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, * @selector: selector field * @attr_val: the attribute value after the query request completes * - * Returns 0 for success, non-zero in case of failure + * Return: 0 for success, non-zero in case of failure. */ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) @@ -3384,7 +3351,7 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, * @attr_val: the attribute value after the query request * completes * - * Returns 0 for success, non-zero in case of failure + * Return: 0 for success, non-zero in case of failure. */ int ufshcd_query_attr_retry(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, @@ -3482,9 +3449,10 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba, * @desc_buf: the buffer that contains the descriptor * @buf_len: length parameter passed to the device * - * Returns 0 for success, non-zero in case of failure. * The buf_len parameter will contain, on return, the length parameter * received on the response. + * + * Return: 0 for success, non-zero in case of failure. */ int ufshcd_query_descriptor_retry(struct ufs_hba *hba, enum query_opcode opcode, @@ -3514,7 +3482,7 @@ int ufshcd_query_descriptor_retry(struct ufs_hba *hba, * @param_read_buf: pointer to buffer where parameter would be read * @param_size: sizeof(param_read_buf) * - * Return 0 in case of success, non-zero otherwise + * Return: 0 in case of success, non-zero otherwise. */ int ufshcd_read_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, @@ -3694,7 +3662,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, * @param_read_buf: pointer to buffer where parameter would be read * @param_size: sizeof(param_read_buf) * - * Return 0 in case of success, non-zero otherwise + * Return: 0 in case of success, non-zero otherwise. */ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba, int lun, @@ -3749,7 +3717,7 @@ static int ufshcd_get_ref_clk_gating_wait(struct ufs_hba *hba) * (UTMRDL) * 4. Allocate memory for local reference block(lrb). * - * Returns 0 for success, non-zero in case of failure + * Return: 0 for success, non-zero in case of failure. */ static int ufshcd_memory_alloc(struct ufs_hba *hba) { @@ -3896,7 +3864,7 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) * Once the Unipro links are up, the device connected to the controller * is detected. * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_dme_link_startup(struct ufs_hba *hba) { @@ -3918,7 +3886,7 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) * DME_RESET command is issued in order to reset UniPro stack. * This function now deals with cold reset. * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_dme_reset(struct ufs_hba *hba) { @@ -3957,7 +3925,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_configure_adapt); * * DME_ENABLE command is issued in order to enable UniPro stack. * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_dme_enable(struct ufs_hba *hba) { @@ -4013,7 +3981,7 @@ static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba) * @mib_val: setting value as uic command argument3 * @peer: indicate whether peer or local * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer) @@ -4057,7 +4025,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_set_attr); * @mib_val: the value of the attribute as returned by the UIC command * @peer: indicate whether peer or local * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer) @@ -4138,7 +4106,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_get_attr); * addition to normal UIC command completion Status (UCCS). This function only * returns after the relevant status bits indicate the completion. * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) { @@ -4166,8 +4134,8 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) wmb(); reenable_intr = true; } - ret = __ufshcd_send_uic_cmd(hba, cmd, false); spin_unlock_irqrestore(hba->host->host_lock, flags); + ret = __ufshcd_send_uic_cmd(hba, cmd, false); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", @@ -4228,7 +4196,7 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) * @hba: per adapter instance * @mode: powr mode value * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) { @@ -4380,8 +4348,8 @@ static void ufshcd_init_pwr_info(struct ufs_hba *hba) { hba->pwr_info.gear_rx = UFS_PWM_G1; hba->pwr_info.gear_tx = UFS_PWM_G1; - hba->pwr_info.lane_rx = 1; - hba->pwr_info.lane_tx = 1; + hba->pwr_info.lane_rx = UFS_LANE_1; + hba->pwr_info.lane_tx = UFS_LANE_1; hba->pwr_info.pwr_rx = SLOWAUTO_MODE; hba->pwr_info.pwr_tx = SLOWAUTO_MODE; hba->pwr_info.hs_rate = 0; @@ -4390,6 +4358,8 @@ static void ufshcd_init_pwr_info(struct ufs_hba *hba) /** * ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device * @hba: per-adapter instance + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) { @@ -4547,6 +4517,8 @@ static int ufshcd_change_power_mode(struct ufs_hba *hba, * ufshcd_config_pwr_mode - configure a new power mode * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration + * + * Return: 0 upon success; < 0 upon failure. */ int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) @@ -4571,6 +4543,8 @@ EXPORT_SYMBOL_GPL(ufshcd_config_pwr_mode); * @hba: per-adapter instance * * Set fDeviceInit flag and poll until device toggles it. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_complete_dev_init(struct ufs_hba *hba) { @@ -4621,7 +4595,7 @@ static int ufshcd_complete_dev_init(struct ufs_hba *hba) * 3. Program UTRL and UTMRL base address * 4. Configure run-stop-registers * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_make_hba_operational(struct ufs_hba *hba) { @@ -4702,7 +4676,7 @@ EXPORT_SYMBOL_GPL(ufshcd_hba_stop); * sequence kicks off. When controller is ready it will set * the Host Controller Enable bit to 1. * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_hba_execute_hce(struct ufs_hba *hba) { @@ -4847,7 +4821,7 @@ EXPORT_SYMBOL_GPL(ufshcd_update_evt_hist); * ufshcd_link_startup - Initialize unipro link startup * @hba: per adapter instance * - * Returns 0 for success, non-zero in case of failure + * Return: 0 for success, non-zero in case of failure. */ static int ufshcd_link_startup(struct ufs_hba *hba) { @@ -4942,6 +4916,8 @@ static int ufshcd_link_startup(struct ufs_hba *hba) * If the UTP layer at the device side is not initialized, it may * not respond with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT * and we retry sending NOP OUT for %NOP_OUT_RETRIES iterations. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_verify_dev_init(struct ufs_hba *hba) { @@ -5066,7 +5042,7 @@ static void ufshcd_lu_init(struct ufs_hba *hba, struct scsi_device *sdev) * ufshcd_slave_alloc - handle initial SCSI device configurations * @sdev: pointer to SCSI device * - * Returns success + * Return: success. */ static int ufshcd_slave_alloc(struct scsi_device *sdev) { @@ -5102,43 +5078,25 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev) * @depth: required depth to set * * Change queue depth and make sure the max. limits are not crossed. + * + * Return: new queue depth. */ static int ufshcd_change_queue_depth(struct scsi_device *sdev, int depth) { return scsi_change_queue_depth(sdev, min(depth, sdev->host->can_queue)); } -static void ufshcd_hpb_destroy(struct ufs_hba *hba, struct scsi_device *sdev) -{ - /* skip well-known LU */ - if ((sdev->lun >= UFS_UPIU_MAX_UNIT_NUM_ID) || - !(hba->dev_info.hpb_enabled) || !ufshpb_is_allowed(hba)) - return; - - ufshpb_destroy_lu(hba, sdev); -} - -static void ufshcd_hpb_configure(struct ufs_hba *hba, struct scsi_device *sdev) -{ - /* skip well-known LU */ - if ((sdev->lun >= UFS_UPIU_MAX_UNIT_NUM_ID) || - !(hba->dev_info.hpb_enabled) || !ufshpb_is_allowed(hba)) - return; - - ufshpb_init_hpb_lu(hba, sdev); -} - /** * ufshcd_slave_configure - adjust SCSI device configurations * @sdev: pointer to SCSI device + * + * Return: 0 (success). */ static int ufshcd_slave_configure(struct scsi_device *sdev) { struct ufs_hba *hba = shost_priv(sdev->host); struct request_queue *q = sdev->request_queue; - ufshcd_hpb_configure(hba, sdev); - blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1); if (hba->quirks & UFSHCD_QUIRK_4KB_DMA_ALIGNMENT) blk_queue_update_dma_alignment(q, SZ_4K - 1); @@ -5173,8 +5131,6 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) hba = shost_priv(sdev->host); - ufshcd_hpb_destroy(hba, sdev); - /* Drop the reference as it won't be needed anymore */ if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) { spin_lock_irqsave(hba->host->host_lock, flags); @@ -5208,7 +5164,7 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev) * @lrbp: pointer to local reference block of completed command * @scsi_status: SCSI command status * - * Returns value base on SCSI command status + * Return: value base on SCSI command status. */ static inline int ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status) @@ -5242,7 +5198,7 @@ ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status) * @lrbp: pointer to local reference block of completed command * @cqe: pointer to the completion queue entry * - * Returns result of the command to notify SCSI midlayer + * Return: result of the command to notify SCSI midlayer. */ static inline int ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, @@ -5251,36 +5207,37 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, int result = 0; int scsi_status; enum utp_ocs ocs; + u8 upiu_flags; + u32 resid; - scsi_set_resid(lrbp->cmd, - be32_to_cpu(lrbp->ucd_rsp_ptr->sr.residual_transfer_count)); + upiu_flags = lrbp->ucd_rsp_ptr->header.flags; + resid = be32_to_cpu(lrbp->ucd_rsp_ptr->sr.residual_transfer_count); + /* + * Test !overflow instead of underflow to support UFS devices that do + * not set either flag. + */ + if (resid && !(upiu_flags & UPIU_RSP_FLAG_OVERFLOW)) + scsi_set_resid(lrbp->cmd, resid); /* overall command status of utrd */ ocs = ufshcd_get_tr_ocs(lrbp, cqe); if (hba->quirks & UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR) { - if (be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_1) & - MASK_RSP_UPIU_RESULT) + if (lrbp->ucd_rsp_ptr->header.response || + lrbp->ucd_rsp_ptr->header.status) ocs = OCS_SUCCESS; } switch (ocs) { case OCS_SUCCESS: - result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr); hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0); - switch (result) { + switch (ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr)) { case UPIU_TRANSACTION_RESPONSE: - /* - * get the response UPIU result to extract - * the SCSI command status - */ - result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr); - /* * get the result based on SCSI status response * to notify the SCSI midlayer of the command status */ - scsi_status = result & MASK_SCSI_STATUS; + scsi_status = lrbp->ucd_rsp_ptr->header.status; result = ufshcd_scsi_cmd_status(lrbp, scsi_status); /* @@ -5300,9 +5257,6 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, ufshcd_is_exception_event(lrbp->ucd_rsp_ptr)) /* Flushed in suspend */ schedule_work(&hba->eeh_work); - - if (scsi_status == SAM_STAT_GOOD) - ufshpb_rsp_upiu(hba, lrbp); break; case UPIU_TRANSACTION_REJECT_UPIU: /* TODO: handle Reject UPIU Response */ @@ -5372,7 +5326,7 @@ static bool ufshcd_is_auto_hibern8_error(struct ufs_hba *hba, * @hba: per adapter instance * @intr_status: interrupt status generated by the controller * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -5448,8 +5402,7 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag, if (hba->dev_cmd.complete) { if (cqe) { ocs = le32_to_cpu(cqe->status) & MASK_OCS; - lrbp->utr_descriptor_ptr->header.dword_2 = - cpu_to_le32(ocs); + lrbp->utr_descriptor_ptr->header.ocs = ocs; } complete(hba->dev_cmd.complete); ufshcd_clk_scaling_update_busy(hba); @@ -5492,7 +5445,7 @@ static void ufshcd_clear_polled(struct ufs_hba *hba, } /* - * Returns > 0 if one or more commands have been completed or 0 if no + * Return: > 0 if one or more commands have been completed or 0 if no * requests have been completed. */ static int ufshcd_poll(struct Scsi_Host *shost, unsigned int queue_num) @@ -5582,7 +5535,7 @@ static void ufshcd_mcq_compl_pending_transfer(struct ufs_hba *hba, * ufshcd_transfer_req_compl - handle SCSI and query command completion * @hba: per adapter instance * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -5659,7 +5612,7 @@ int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, * Disables exception event in the device so that the EVENT_ALERT * bit is not set. * - * Returns zero on success, non-zero error value on failure. + * Return: zero on success, non-zero error value on failure. */ static inline int ufshcd_disable_ee(struct ufs_hba *hba, u16 mask) { @@ -5674,7 +5627,7 @@ static inline int ufshcd_disable_ee(struct ufs_hba *hba, u16 mask) * Enable corresponding exception event in the device to allow * device to alert host in critical scenarios. * - * Returns zero on success, non-zero error value on failure. + * Return: zero on success, non-zero error value on failure. */ static inline int ufshcd_enable_ee(struct ufs_hba *hba, u16 mask) { @@ -5690,7 +5643,7 @@ static inline int ufshcd_enable_ee(struct ufs_hba *hba, u16 mask) * as the device is allowed to manage its own way of handling background * operations. * - * Returns zero on success, non-zero on failure. + * Return: zero on success, non-zero on failure. */ static int ufshcd_enable_auto_bkops(struct ufs_hba *hba) { @@ -5729,7 +5682,7 @@ static int ufshcd_enable_auto_bkops(struct ufs_hba *hba) * host is idle so that BKOPS are managed effectively without any negative * impacts. * - * Returns zero on success, non-zero on failure. + * Return: zero on success, non-zero on failure. */ static int ufshcd_disable_auto_bkops(struct ufs_hba *hba) { @@ -5805,7 +5758,7 @@ static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status) * bkops_status is greater than or equal to "status" argument passed to * this function, disable otherwise. * - * Returns 0 for success, non-zero in case of failure. + * Return: 0 for success, non-zero in case of failure. * * NOTE: Caller of this function can check the "hba->auto_bkops_enabled" flag * to know whether auto bkops is enabled or disabled after this function @@ -5846,6 +5799,8 @@ static int ufshcd_bkops_ctrl(struct ufs_hba *hba, * * If BKOPs is enabled, this function returns 0, 1 if the bkops in not enabled * and negative error value for any other failure. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_urgent_bkops(struct ufs_hba *hba) { @@ -6157,7 +6112,7 @@ static void ufshcd_complete_requests(struct ufs_hba *hba, bool force_compl) * to recover from the DL NAC errors or not. * @hba: per-adapter instance * - * Returns true if error handling is required, false otherwise + * Return: true if error handling is required, false otherwise. */ static bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba) { @@ -6384,54 +6339,48 @@ static bool ufshcd_is_pwr_mode_restore_needed(struct ufs_hba *hba) return false; } +static bool ufshcd_abort_one(struct request *rq, void *priv) +{ + int *ret = priv; + u32 tag = rq->tag; + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); + struct scsi_device *sdev = cmd->device; + struct Scsi_Host *shost = sdev->host; + struct ufs_hba *hba = shost_priv(shost); + + *ret = ufshcd_try_to_abort_task(hba, tag); + dev_err(hba->dev, "Aborting tag %d / CDB %#02x %s\n", tag, + hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1, + *ret ? "failed" : "succeeded"); + return *ret == 0; +} + +/** + * ufshcd_abort_all - Abort all pending commands. + * @hba: Host bus adapter pointer. + * + * Return: true if and only if the host controller needs to be reset. + */ static bool ufshcd_abort_all(struct ufs_hba *hba) { - bool needs_reset = false; - int tag, ret; + int tag, ret = 0; - if (is_mcq_enabled(hba)) { - struct ufshcd_lrb *lrbp; - int tag; + blk_mq_tagset_busy_iter(&hba->host->tag_set, ufshcd_abort_one, &ret); + if (ret) + goto out; - for (tag = 0; tag < hba->nutrs; tag++) { - lrbp = &hba->lrb[tag]; - if (!ufshcd_cmd_inflight(lrbp->cmd)) - continue; - ret = ufshcd_try_to_abort_task(hba, tag); - dev_err(hba->dev, "Aborting tag %d / CDB %#02x %s\n", tag, - hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1, - ret ? "failed" : "succeeded"); - if (ret) { - needs_reset = true; - goto out; - } - } - } else { - /* Clear pending transfer requests */ - for_each_set_bit(tag, &hba->outstanding_reqs, hba->nutrs) { - ret = ufshcd_try_to_abort_task(hba, tag); - dev_err(hba->dev, "Aborting tag %d / CDB %#02x %s\n", tag, - hba->lrb[tag].cmd ? hba->lrb[tag].cmd->cmnd[0] : -1, - ret ? "failed" : "succeeded"); - if (ret) { - needs_reset = true; - goto out; - } - } - } /* Clear pending task management requests */ for_each_set_bit(tag, &hba->outstanding_tasks, hba->nutmrs) { - if (ufshcd_clear_tm_cmd(hba, tag)) { - needs_reset = true; + ret = ufshcd_clear_tm_cmd(hba, tag); + if (ret) goto out; - } } out: /* Complete the requests that are cleared by s/w */ ufshcd_complete_requests(hba, false); - return needs_reset; + return ret != 0; } /** @@ -6618,7 +6567,7 @@ static void ufshcd_err_handler(struct work_struct *work) * ufshcd_update_uic_error - check and set fatal UIC error flags. * @hba: per-adapter instance * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -6711,7 +6660,7 @@ static irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba) * @hba: per-adapter instance * @intr_status: interrupt status generated by the controller * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -6787,7 +6736,7 @@ static irqreturn_t ufshcd_check_errors(struct ufs_hba *hba, u32 intr_status) * ufshcd_tmc_handler - handle task management function completion * @hba: per adapter instance * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -6816,7 +6765,7 @@ static irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba) * ufshcd_handle_mcq_cq_events - handle MCQ completion queue events * @hba: per adapter instance * - * Returns IRQ_HANDLED if interrupt is handled + * Return: IRQ_HANDLED if interrupt is handled. */ static irqreturn_t ufshcd_handle_mcq_cq_events(struct ufs_hba *hba) { @@ -6851,7 +6800,7 @@ static irqreturn_t ufshcd_handle_mcq_cq_events(struct ufs_hba *hba) * @hba: per adapter instance * @intr_status: contains interrupts generated by the controller * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -6882,7 +6831,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) * @irq: irq number * @__hba: pointer to adapter instance * - * Returns + * Return: * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ @@ -6978,7 +6927,7 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba, WARN_ONCE(task_tag < 0 || task_tag >= hba->nutmrs, "Invalid tag %d\n", task_tag); hba->tmf_rqs[req->tag] = req; - treq->upiu_req.req_header.dword_0 |= cpu_to_be32(task_tag); + treq->upiu_req.req_header.task_tag = task_tag; memcpy(hba->utmrdl_base_addr + task_tag, treq, sizeof(*treq)); ufshcd_vops_setup_task_mgmt(hba, task_tag, tm_function); @@ -7031,23 +6980,23 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba, * @tm_function: task management function opcode * @tm_response: task management service response return value * - * Returns non-zero value on error, zero on success. + * Return: non-zero value on error, zero on success. */ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, u8 tm_function, u8 *tm_response) { - struct utp_task_req_desc treq = { { 0 }, }; + struct utp_task_req_desc treq = { }; enum utp_ocs ocs_value; int err; /* Configure task request descriptor */ - treq.header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD); - treq.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS); + treq.header.interrupt = 1; + treq.header.ocs = OCS_INVALID_COMMAND_STATUS; /* Configure task request UPIU */ - treq.upiu_req.req_header.dword_0 = cpu_to_be32(lun_id << 8) | - cpu_to_be32(UPIU_TRANSACTION_TASK_REQ << 24); - treq.upiu_req.req_header.dword_1 = cpu_to_be32(tm_function << 16); + treq.upiu_req.req_header.transaction_code = UPIU_TRANSACTION_TASK_REQ; + treq.upiu_req.req_header.lun = lun_id; + treq.upiu_req.req_header.tm_function = tm_function; /* * The host shall provide the same value for LUN field in the basic @@ -7060,7 +7009,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, if (err == -ETIMEDOUT) return err; - ocs_value = le32_to_cpu(treq.header.dword_2) & MASK_OCS; + ocs_value = treq.header.ocs & MASK_OCS; if (ocs_value != OCS_SUCCESS) dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", __func__, ocs_value); @@ -7086,6 +7035,8 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id, * * Since there is only one available tag for device management commands, * the caller is expected to hold the hba->dev_cmd.lock mutex. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba, struct utp_upiu_req *req_upiu, @@ -7119,7 +7070,7 @@ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba, lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE; /* update the task tag in the request upiu */ - req_upiu->header.dword_0 |= cpu_to_be32(tag); + req_upiu->header.task_tag = tag; ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE, 0); @@ -7152,8 +7103,8 @@ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba, memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu)); if (desc_buff && desc_op == UPIU_QUERY_OPCODE_READ_DESC) { u8 *descp = (u8 *)lrbp->ucd_rsp_ptr + sizeof(*rsp_upiu); - u16 resp_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) & - MASK_QUERY_DATA_SEG_LEN; + u16 resp_len = be16_to_cpu(lrbp->ucd_rsp_ptr->header + .data_segment_length); if (*buff_len >= resp_len) { memcpy(desc_buff, descp, resp_len); @@ -7187,19 +7138,21 @@ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba, * Management requests. * It is up to the caller to fill the upiu conent properly, as it will * be copied without any further input validations. + * + * Return: 0 upon success; < 0 upon failure. */ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, struct utp_upiu_req *req_upiu, struct utp_upiu_req *rsp_upiu, - int msgcode, + enum upiu_request_transaction msgcode, u8 *desc_buff, int *buff_len, enum query_opcode desc_op) { int err; enum dev_cmd_type cmd_type = DEV_CMD_TYPE_QUERY; - struct utp_task_req_desc treq = { { 0 }, }; + struct utp_task_req_desc treq = { }; enum utp_ocs ocs_value; - u8 tm_f = be32_to_cpu(req_upiu->header.dword_1) >> 16 & MASK_TM_FUNC; + u8 tm_f = req_upiu->header.tm_function; switch (msgcode) { case UPIU_TRANSACTION_NOP_OUT: @@ -7216,8 +7169,8 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, break; case UPIU_TRANSACTION_TASK_REQ: - treq.header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD); - treq.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS); + treq.header.interrupt = 1; + treq.header.ocs = OCS_INVALID_COMMAND_STATUS; memcpy(&treq.upiu_req, req_upiu, sizeof(*req_upiu)); @@ -7225,7 +7178,7 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, if (err == -ETIMEDOUT) break; - ocs_value = le32_to_cpu(treq.header.dword_2) & MASK_OCS; + ocs_value = treq.header.ocs & MASK_OCS; if (ocs_value != OCS_SUCCESS) { dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", __func__, ocs_value); @@ -7255,7 +7208,7 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, * @sg_list: Pointer to SG list when DATA IN/OUT UPIU is required in ARPMB operation * @dir: DMA direction * - * Returns zero on success, non-zero on failure + * Return: zero on success, non-zero on failure. */ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *req_upiu, struct utp_upiu_req *rsp_upiu, struct ufs_ehs *req_ehs, @@ -7288,10 +7241,18 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r /* Advanced RPMB starts from UFS 4.0, so its command type is UTP_CMD_TYPE_UFS_STORAGE */ lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE; - ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, dir, 2); + /* + * According to UFSHCI 4.0 specification page 24, if EHSLUTRDS is 0, host controller takes + * EHS length from CMD UPIU, and SW driver use EHS Length field in CMD UPIU. if it is 1, + * HW controller takes EHS length from UTRD. + */ + if (hba->capabilities & MASK_EHSLUTRD_SUPPORTED) + ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, dir, 2); + else + ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, dir, 0); - /* update the task tag and LUN in the request upiu */ - req_upiu->header.dword_0 |= cpu_to_be32(upiu_flags << 16 | UFS_UPIU_RPMB_WLUN << 8 | tag); + /* update the task tag */ + req_upiu->header.task_tag = tag; /* copy the UPIU(contains CDB) request as it is */ memcpy(lrbp->ucd_req_ptr, req_upiu, sizeof(*lrbp->ucd_req_ptr)); @@ -7313,9 +7274,10 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r /* Just copy the upiu response as it is */ memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu)); /* Get the response UPIU result */ - result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr); + result = (lrbp->ucd_rsp_ptr->header.response << 8) | + lrbp->ucd_rsp_ptr->header.status; - ehs_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) >> 24; + ehs_len = lrbp->ucd_rsp_ptr->header.ehs_length; /* * Since the bLength in EHS indicates the total size of the EHS Header and EHS Data * in 32 Byte units, the value of the bLength Request/Response for Advanced RPMB @@ -7341,7 +7303,7 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r * ufshcd_eh_device_reset_handler() - Reset a single logical unit. * @cmd: SCSI command pointer * - * Returns SUCCESS/FAILED + * Return: SUCCESS or FAILED. */ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) { @@ -7436,7 +7398,7 @@ static void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap) * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is * really issued and then try to abort it. * - * Returns zero on success, non-zero on failure + * Return: zero on success, non-zero on failure. */ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) { @@ -7524,7 +7486,7 @@ int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag) * ufshcd_abort - scsi host template eh_abort_handler callback * @cmd: SCSI command pointer * - * Returns SUCCESS/FAILED + * Return: SUCCESS or FAILED. */ static int ufshcd_abort(struct scsi_cmnd *cmd) { @@ -7649,7 +7611,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) * local and remote (device) Uni-Pro stack and the attributes * are reset to default state. * - * Returns zero on success, non-zero on failure + * Return: zero on success, non-zero on failure. */ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) { @@ -7659,7 +7621,6 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) * Stop the host controller and complete the requests * cleared by h/w */ - ufshpb_toggle_state(hba, HPB_PRESENT, HPB_RESET); ufshcd_hba_stop(hba); hba->silence_err_logs = true; ufshcd_complete_requests(hba, true); @@ -7687,7 +7648,7 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba) * Reset and recover device, host and re-establish link. This * is helpful to recover the communication in fatal error conditions. * - * Returns zero on success, non-zero on failure + * Return: zero on success, non-zero on failure. */ static int ufshcd_reset_and_restore(struct ufs_hba *hba) { @@ -7745,7 +7706,7 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba) * ufshcd_eh_host_reset_handler - host reset handler registered to scsi layer * @cmd: SCSI command pointer * - * Returns SUCCESS/FAILED + * Return: SUCCESS or FAILED. */ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) { @@ -7777,7 +7738,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) * @start_scan: row at the desc table to start scan from * @buff: power descriptor buffer * - * Returns calculated max ICC level for specific regulator + * Return: calculated max ICC level for specific regulator. */ static u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, const char *buff) @@ -7823,7 +7784,7 @@ static u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, * @hba: per-adapter instance * @desc_buf: power descriptor buffer to extract ICC levels from. * - * Returns calculated ICC level + * Return: calculated ICC level. */ static u32 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba, const u8 *desc_buf) @@ -7932,7 +7893,7 @@ static inline void ufshcd_blk_pm_runtime_init(struct scsi_device *sdev) * This function adds scsi device instances for each of all well known LUs * (except "REPORT LUNS" LU). * - * Returns zero on success (all required W-LUs are added successfully), + * Return: zero on success (all required W-LUs are added successfully), * non-zero error value on failure (if failed to add any of the required W-LU). */ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba) @@ -8122,7 +8083,6 @@ static int ufs_get_device_desc(struct ufs_hba *hba) { int err; u8 model_index; - u8 b_ufs_feature_sup; u8 *desc_buf; struct ufs_dev_info *dev_info = &hba->dev_info; @@ -8151,26 +8111,9 @@ static int ufs_get_device_desc(struct ufs_hba *hba) dev_info->wspecversion = desc_buf[DEVICE_DESC_PARAM_SPEC_VER] << 8 | desc_buf[DEVICE_DESC_PARAM_SPEC_VER + 1]; dev_info->bqueuedepth = desc_buf[DEVICE_DESC_PARAM_Q_DPTH]; - b_ufs_feature_sup = desc_buf[DEVICE_DESC_PARAM_UFS_FEAT]; model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - if (dev_info->wspecversion >= UFS_DEV_HPB_SUPPORT_VERSION && - (b_ufs_feature_sup & UFS_DEV_HPB_SUPPORT)) { - bool hpb_en = false; - - ufshpb_get_dev_info(hba, desc_buf); - - if (!ufshpb_is_legacy(hba)) - err = ufshcd_query_flag_retry(hba, - UPIU_QUERY_OPCODE_READ_FLAG, - QUERY_FLAG_IDN_HPB_EN, 0, - &hpb_en); - - if (ufshpb_is_legacy(hba) || (!err && hpb_en)) - dev_info->hpb_enabled = true; - } - err = ufshcd_read_string_desc(hba, model_index, &dev_info->model, SD_ASCII_STD); if (err < 0) { @@ -8219,7 +8162,7 @@ static void ufs_put_device_desc(struct ufs_hba *hba) * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce * the hibern8 exit latency. * - * Returns zero on success, non-zero error value on failure. + * Return: zero on success, non-zero error value on failure. */ static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba) { @@ -8254,7 +8197,7 @@ static int ufshcd_tune_pa_tactivate(struct ufs_hba *hba) * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY. * This optimal value can help reduce the hibern8 exit latency. * - * Returns zero on success, non-zero error value on failure. + * Return: zero on success, non-zero error value on failure. */ static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba) { @@ -8296,7 +8239,7 @@ static int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba) * PA_TACTIVATE, we need to enable UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE quirk * for such devices. * - * Returns zero on success, non-zero error value on failure. + * Return: zero on success, non-zero error value on failure. */ static int ufshcd_quirk_tune_host_pa_tactivate(struct ufs_hba *hba) { @@ -8405,10 +8348,6 @@ static int ufshcd_device_geo_params_init(struct ufs_hba *hba) else if (desc_buf[GEOMETRY_DESC_PARAM_MAX_NUM_LUN] == 0) hba->dev_info.max_lu_supported = 8; - if (desc_buf[QUERY_DESC_LENGTH_OFFSET] >= - GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS) - ufshpb_get_geo_info(hba, desc_buf); - out: kfree(desc_buf); return err; @@ -8558,6 +8497,8 @@ static void ufshcd_set_timestamp_attr(struct ufs_hba *hba) /** * ufshcd_add_lus - probe and add UFS logical units * @hba: per-adapter instance + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_add_lus(struct ufs_hba *hba) { @@ -8584,7 +8525,6 @@ static int ufshcd_add_lus(struct ufs_hba *hba) } ufs_bsg_probe(hba); - ufshpb_init(hba); scsi_scan_host(hba->host); pm_runtime_put_sync(hba->dev); @@ -8770,6 +8710,8 @@ static int ufshcd_device_init(struct ufs_hba *hba, bool init_dev_params) * @init_dev_params: whether or not to call ufshcd_device_params_init(). * * Execute link-startup and verify device initialization + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) { @@ -8818,7 +8760,6 @@ static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params) /* Enable Auto-Hibernate if configured */ ufshcd_auto_hibern8_enable(hba); - ufshpb_toggle_state(hba, HPB_RESET, HPB_PRESENT); out: spin_lock_irqsave(hba->host->host_lock, flags); if (ret) @@ -8888,10 +8829,6 @@ static enum scsi_timeout_action ufshcd_eh_timed_out(struct scsi_cmnd *scmd) static const struct attribute_group *ufshcd_driver_groups[] = { &ufs_sysfs_unit_descriptor_group, &ufs_sysfs_lun_attributes_group, -#ifdef CONFIG_SCSI_UFS_HPB - &ufs_sysfs_hpb_stat_group, - &ufs_sysfs_hpb_param_group, -#endif NULL, }; @@ -9235,8 +9172,9 @@ static int ufshcd_variant_hba_init(struct ufs_hba *hba) err = ufshcd_vops_init(hba); if (err) - dev_err(hba->dev, "%s: variant %s init failed err %d\n", - __func__, ufshcd_get_var_name(hba), err); + dev_err_probe(hba->dev, err, + "%s: variant %s init failed with err %d\n", + __func__, ufshcd_get_var_name(hba), err); out: return err; } @@ -9345,8 +9283,8 @@ static int ufshcd_execute_start_stop(struct scsi_device *sdev, * @hba: per adapter instance * @pwr_mode: device power mode to set * - * Returns 0 if requested power mode is set successfully - * Returns < 0 if failed to set the requested power mode + * Return: 0 if requested power mode is set successfully; + * < 0 if failed to set the requested power mode. */ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, enum ufs_dev_pwr_mode pwr_mode) @@ -9576,8 +9514,6 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) req_link_state = UIC_LINK_OFF_STATE; } - ufshpb_suspend(hba); - /* * If we can't transition into any of the low power modes * just gate the clocks. @@ -9731,7 +9667,6 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ufshcd_update_evt_hist(hba, UFS_EVT_WL_SUSP_ERR, (u32)ret); hba->clk_gating.is_suspended = false; ufshcd_release(hba); - ufshpb_resume(hba); } hba->pm_op_in_progress = false; return ret; @@ -9812,7 +9747,6 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) /* Enable Auto-Hibernate if configured */ ufshcd_auto_hibern8_enable(hba); - ufshpb_resume(hba); goto out; set_old_link_state: @@ -9934,6 +9868,8 @@ static int ufshcd_wl_resume(struct device *dev) * * This function will put disable irqs, turn off clocks * and set vreg and hba-vreg in lpm mode. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_suspend(struct ufs_hba *hba) { @@ -9971,7 +9907,7 @@ static int ufshcd_suspend(struct ufs_hba *hba) * This function basically turns on the regulators, clocks and * irqs of the hba. * - * Returns 0 for success and non-zero for failure + * Return: 0 for success and non-zero for failure. */ static int ufshcd_resume(struct ufs_hba *hba) { @@ -10012,7 +9948,7 @@ static int ufshcd_resume(struct ufs_hba *hba) * Executed before putting the system into a sleep state in which the contents * of main memory are preserved. * - * Returns 0 for success and non-zero for failure + * Return: 0 for success and non-zero for failure. */ int ufshcd_system_suspend(struct device *dev) { @@ -10039,7 +9975,7 @@ EXPORT_SYMBOL(ufshcd_system_suspend); * Executed after waking the system up from a sleep state in which the contents * of main memory were preserved. * - * Returns 0 for success and non-zero for failure + * Return: 0 for success and non-zero for failure. */ int ufshcd_system_resume(struct device *dev) { @@ -10069,7 +10005,7 @@ EXPORT_SYMBOL(ufshcd_system_resume); * * Check the description of ufshcd_suspend() function for more details. * - * Returns 0 for success and non-zero for failure + * Return: 0 for success and non-zero for failure. */ int ufshcd_runtime_suspend(struct device *dev) { @@ -10095,6 +10031,8 @@ EXPORT_SYMBOL(ufshcd_runtime_suspend); * * 1. Turn on all the controller related clocks * 2. Turn ON VCC rail + * + * Return: 0 upon success; < 0 upon failure. */ int ufshcd_runtime_resume(struct device *dev) { @@ -10152,7 +10090,6 @@ void ufshcd_remove(struct ufs_hba *hba) ufshcd_rpm_get_sync(hba); ufs_hwmon_remove(hba); ufs_bsg_remove(hba); - ufshpb_remove(hba); ufs_sysfs_remove_nodes(hba->dev); blk_mq_destroy_queue(hba->tmf_queue); blk_put_queue(hba->tmf_queue); @@ -10230,7 +10167,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dealloc_host); * addressing capability * @hba: per adapter instance * - * Returns 0 for success, non-zero for failure + * Return: 0 for success, non-zero for failure. */ static int ufshcd_set_dma_mask(struct ufs_hba *hba) { @@ -10245,7 +10182,8 @@ static int ufshcd_set_dma_mask(struct ufs_hba *hba) * ufshcd_alloc_host - allocate Host Bus Adapter (HBA) * @dev: pointer to device handle * @hba_handle: driver private handle - * Returns 0 on success, non-zero value on failure + * + * Return: 0 on success, non-zero value on failure. */ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) { @@ -10301,7 +10239,8 @@ static const struct blk_mq_ops ufshcd_tmf_ops = { * @hba: per-adapter instance * @mmio_base: base register address * @irq: Interrupt line of device - * Returns 0 on success, non-zero value on failure + * + * Return: 0 on success, non-zero value on failure. */ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) { @@ -10632,6 +10571,53 @@ static const struct dev_pm_ops ufshcd_wl_pm_ops = { SET_RUNTIME_PM_OPS(ufshcd_wl_runtime_suspend, ufshcd_wl_runtime_resume, NULL) }; +static void ufshcd_check_header_layout(void) +{ + /* + * gcc compilers before version 10 cannot do constant-folding for + * sub-byte bitfields. Hence skip the layout checks for gcc 9 and + * before. + */ + if (IS_ENABLED(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 100000) + return; + + BUILD_BUG_ON(((u8 *)&(struct request_desc_header){ + .cci = 3})[0] != 3); + + BUILD_BUG_ON(((u8 *)&(struct request_desc_header){ + .ehs_length = 2})[1] != 2); + + BUILD_BUG_ON(((u8 *)&(struct request_desc_header){ + .enable_crypto = 1})[2] + != 0x80); + + BUILD_BUG_ON((((u8 *)&(struct request_desc_header){ + .command_type = 5, + .data_direction = 3, + .interrupt = 1, + })[3]) != ((5 << 4) | (3 << 1) | 1)); + + BUILD_BUG_ON(((__le32 *)&(struct request_desc_header){ + .dunl = cpu_to_le32(0xdeadbeef)})[1] != + cpu_to_le32(0xdeadbeef)); + + BUILD_BUG_ON(((u8 *)&(struct request_desc_header){ + .ocs = 4})[8] != 4); + + BUILD_BUG_ON(((u8 *)&(struct request_desc_header){ + .cds = 5})[9] != 5); + + BUILD_BUG_ON(((__le32 *)&(struct request_desc_header){ + .dunu = cpu_to_le32(0xbadcafe)})[3] != + cpu_to_le32(0xbadcafe)); + + BUILD_BUG_ON(((u8 *)&(struct utp_upiu_header){ + .iid = 0xf })[4] != 0xf0); + + BUILD_BUG_ON(((u8 *)&(struct utp_upiu_header){ + .command_set_type = 0xf })[4] != 0xf); +} + /* * ufs_dev_wlun_template - describes ufs device wlun * ufs-device wlun - used to send pm commands @@ -10657,6 +10643,8 @@ static int __init ufshcd_core_init(void) { int ret; + ufshcd_check_header_layout(); + ufs_debugfs_init(); ret = scsi_register_driver(&ufs_dev_wlun_template.gendrv); diff --git a/drivers/ufs/core/ufshpb.c b/drivers/ufs/core/ufshpb.c deleted file mode 100644 index 255f8b38d0c2d29f2ad016e10229e1039de6e21c..0000000000000000000000000000000000000000 --- a/drivers/ufs/core/ufshpb.c +++ /dev/null @@ -1,2668 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Universal Flash Storage Host Performance Booster - * - * Copyright (C) 2017-2021 Samsung Electronics Co., Ltd. - * - * Authors: - * Yongmyung Lee - * Jinyoung Choi - */ - -#include -#include -#include -#include -#include - -#include "ufshcd-priv.h" -#include "ufshpb.h" -#include "../../scsi/sd.h" - -#define ACTIVATION_THRESHOLD 8 /* 8 IOs */ -#define READ_TO_MS 1000 -#define READ_TO_EXPIRIES 100 -#define POLLING_INTERVAL_MS 200 -#define THROTTLE_MAP_REQ_DEFAULT 1 - -/* memory management */ -static struct kmem_cache *ufshpb_mctx_cache; -static mempool_t *ufshpb_mctx_pool; -static mempool_t *ufshpb_page_pool; -/* A cache size of 2MB can cache ppn in the 1GB range. */ -static unsigned int ufshpb_host_map_kbytes = SZ_2K; -static int tot_active_srgn_pages; - -static struct workqueue_struct *ufshpb_wq; - -static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx, - int srgn_idx); - -bool ufshpb_is_allowed(struct ufs_hba *hba) -{ - return !(hba->ufshpb_dev.hpb_disabled); -} - -/* HPB version 1.0 is called as legacy version. */ -bool ufshpb_is_legacy(struct ufs_hba *hba) -{ - return hba->ufshpb_dev.is_legacy; -} - -static struct ufshpb_lu *ufshpb_get_hpb_data(struct scsi_device *sdev) -{ - return sdev->hostdata; -} - -static int ufshpb_get_state(struct ufshpb_lu *hpb) -{ - return atomic_read(&hpb->hpb_state); -} - -static void ufshpb_set_state(struct ufshpb_lu *hpb, int state) -{ - atomic_set(&hpb->hpb_state, state); -} - -static int ufshpb_is_valid_srgn(struct ufshpb_region *rgn, - struct ufshpb_subregion *srgn) -{ - return rgn->rgn_state != HPB_RGN_INACTIVE && - srgn->srgn_state == HPB_SRGN_VALID; -} - -static bool ufshpb_is_read_cmd(struct scsi_cmnd *cmd) -{ - return req_op(scsi_cmd_to_rq(cmd)) == REQ_OP_READ; -} - -static bool ufshpb_is_write_or_discard(struct scsi_cmnd *cmd) -{ - return op_is_write(req_op(scsi_cmd_to_rq(cmd))) || - op_is_discard(req_op(scsi_cmd_to_rq(cmd))); -} - -static bool ufshpb_is_supported_chunk(struct ufshpb_lu *hpb, int transfer_len) -{ - return transfer_len <= hpb->pre_req_max_tr_len; -} - -static bool ufshpb_is_general_lun(int lun) -{ - return lun < UFS_UPIU_MAX_UNIT_NUM_ID; -} - -static bool ufshpb_is_pinned_region(struct ufshpb_lu *hpb, int rgn_idx) -{ - return hpb->lu_pinned_end != PINNED_NOT_SET && - rgn_idx >= hpb->lu_pinned_start && rgn_idx <= hpb->lu_pinned_end; -} - -static void ufshpb_kick_map_work(struct ufshpb_lu *hpb) -{ - bool ret = false; - unsigned long flags; - - if (ufshpb_get_state(hpb) != HPB_PRESENT) - return; - - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - if (!list_empty(&hpb->lh_inact_rgn) || !list_empty(&hpb->lh_act_srgn)) - ret = true; - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - - if (ret) - queue_work(ufshpb_wq, &hpb->map_work); -} - -static bool ufshpb_is_hpb_rsp_valid(struct ufs_hba *hba, - struct ufshcd_lrb *lrbp, - struct utp_hpb_rsp *rsp_field) -{ - /* Check HPB_UPDATE_ALERT */ - if (!(lrbp->ucd_rsp_ptr->header.dword_2 & - UPIU_HEADER_DWORD(0, 2, 0, 0))) - return false; - - if (be16_to_cpu(rsp_field->sense_data_len) != DEV_SENSE_SEG_LEN || - rsp_field->desc_type != DEV_DES_TYPE || - rsp_field->additional_len != DEV_ADDITIONAL_LEN || - rsp_field->active_rgn_cnt > MAX_ACTIVE_NUM || - rsp_field->inactive_rgn_cnt > MAX_INACTIVE_NUM || - rsp_field->hpb_op == HPB_RSP_NONE || - (rsp_field->hpb_op == HPB_RSP_REQ_REGION_UPDATE && - !rsp_field->active_rgn_cnt && !rsp_field->inactive_rgn_cnt)) - return false; - - if (!ufshpb_is_general_lun(rsp_field->lun)) { - dev_warn(hba->dev, "ufshpb: lun(%d) not supported\n", - lrbp->lun); - return false; - } - - return true; -} - -static void ufshpb_iterate_rgn(struct ufshpb_lu *hpb, int rgn_idx, int srgn_idx, - int srgn_offset, int cnt, bool set_dirty) -{ - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn, *prev_srgn = NULL; - int set_bit_len; - int bitmap_len; - unsigned long flags; - -next_srgn: - rgn = hpb->rgn_tbl + rgn_idx; - srgn = rgn->srgn_tbl + srgn_idx; - - if (likely(!srgn->is_last)) - bitmap_len = hpb->entries_per_srgn; - else - bitmap_len = hpb->last_srgn_entries; - - if ((srgn_offset + cnt) > bitmap_len) - set_bit_len = bitmap_len - srgn_offset; - else - set_bit_len = cnt; - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - if (rgn->rgn_state != HPB_RGN_INACTIVE) { - if (set_dirty) { - if (srgn->srgn_state == HPB_SRGN_VALID) - bitmap_set(srgn->mctx->ppn_dirty, srgn_offset, - set_bit_len); - } else if (hpb->is_hcm) { - /* rewind the read timer for lru regions */ - rgn->read_timeout = ktime_add_ms(ktime_get(), - rgn->hpb->params.read_timeout_ms); - rgn->read_timeout_expiries = - rgn->hpb->params.read_timeout_expiries; - } - } - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - - if (hpb->is_hcm && prev_srgn != srgn) { - bool activate = false; - - spin_lock(&rgn->rgn_lock); - if (set_dirty) { - rgn->reads -= srgn->reads; - srgn->reads = 0; - set_bit(RGN_FLAG_DIRTY, &rgn->rgn_flags); - } else { - srgn->reads++; - rgn->reads++; - if (srgn->reads == hpb->params.activation_thld) - activate = true; - } - spin_unlock(&rgn->rgn_lock); - - if (activate || - test_and_clear_bit(RGN_FLAG_UPDATE, &rgn->rgn_flags)) { - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - ufshpb_update_active_info(hpb, rgn_idx, srgn_idx); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, - "activate region %d-%d\n", rgn_idx, srgn_idx); - } - - prev_srgn = srgn; - } - - srgn_offset = 0; - if (++srgn_idx == hpb->srgns_per_rgn) { - srgn_idx = 0; - rgn_idx++; - } - - cnt -= set_bit_len; - if (cnt > 0) - goto next_srgn; -} - -static bool ufshpb_test_ppn_dirty(struct ufshpb_lu *hpb, int rgn_idx, - int srgn_idx, int srgn_offset, int cnt) -{ - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - int bitmap_len; - int bit_len; - -next_srgn: - rgn = hpb->rgn_tbl + rgn_idx; - srgn = rgn->srgn_tbl + srgn_idx; - - if (!ufshpb_is_valid_srgn(rgn, srgn)) - return true; - - /* - * If the region state is active, mctx must be allocated. - * In this case, check whether the region is evicted or - * mctx allocation fail. - */ - if (unlikely(!srgn->mctx)) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "no mctx in region %d subregion %d.\n", - srgn->rgn_idx, srgn->srgn_idx); - return true; - } - - if (likely(!srgn->is_last)) - bitmap_len = hpb->entries_per_srgn; - else - bitmap_len = hpb->last_srgn_entries; - - if ((srgn_offset + cnt) > bitmap_len) - bit_len = bitmap_len - srgn_offset; - else - bit_len = cnt; - - if (find_next_bit(srgn->mctx->ppn_dirty, bit_len + srgn_offset, - srgn_offset) < bit_len + srgn_offset) - return true; - - srgn_offset = 0; - if (++srgn_idx == hpb->srgns_per_rgn) { - srgn_idx = 0; - rgn_idx++; - } - - cnt -= bit_len; - if (cnt > 0) - goto next_srgn; - - return false; -} - -static inline bool is_rgn_dirty(struct ufshpb_region *rgn) -{ - return test_bit(RGN_FLAG_DIRTY, &rgn->rgn_flags); -} - -static int ufshpb_fill_ppn_from_page(struct ufshpb_lu *hpb, - struct ufshpb_map_ctx *mctx, int pos, - int len, __be64 *ppn_buf) -{ - struct page *page; - int index, offset; - int copied; - - index = pos / (PAGE_SIZE / HPB_ENTRY_SIZE); - offset = pos % (PAGE_SIZE / HPB_ENTRY_SIZE); - - if ((offset + len) <= (PAGE_SIZE / HPB_ENTRY_SIZE)) - copied = len; - else - copied = (PAGE_SIZE / HPB_ENTRY_SIZE) - offset; - - page = mctx->m_page[index]; - if (unlikely(!page)) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "error. cannot find page in mctx\n"); - return -ENOMEM; - } - - memcpy(ppn_buf, page_address(page) + (offset * HPB_ENTRY_SIZE), - copied * HPB_ENTRY_SIZE); - - return copied; -} - -static void -ufshpb_get_pos_from_lpn(struct ufshpb_lu *hpb, unsigned long lpn, int *rgn_idx, - int *srgn_idx, int *offset) -{ - int rgn_offset; - - *rgn_idx = lpn >> hpb->entries_per_rgn_shift; - rgn_offset = lpn & hpb->entries_per_rgn_mask; - *srgn_idx = rgn_offset >> hpb->entries_per_srgn_shift; - *offset = rgn_offset & hpb->entries_per_srgn_mask; -} - -static void -ufshpb_set_hpb_read_to_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp, - __be64 ppn, u8 transfer_len) -{ - unsigned char *cdb = lrbp->cmd->cmnd; - __be64 ppn_tmp = ppn; - cdb[0] = UFSHPB_READ; - - if (hba->dev_quirks & UFS_DEVICE_QUIRK_SWAP_L2P_ENTRY_FOR_HPB_READ) - ppn_tmp = (__force __be64)swab64((__force u64)ppn); - - /* ppn value is stored as big-endian in the host memory */ - memcpy(&cdb[6], &ppn_tmp, sizeof(__be64)); - cdb[14] = transfer_len; - cdb[15] = 0; - - lrbp->cmd->cmd_len = UFS_CDB_SIZE; -} - -/* - * This function will set up HPB read command using host-side L2P map data. - */ -int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) -{ - struct ufshpb_lu *hpb; - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - struct scsi_cmnd *cmd = lrbp->cmd; - u32 lpn; - __be64 ppn; - unsigned long flags; - int transfer_len, rgn_idx, srgn_idx, srgn_offset; - int err = 0; - - hpb = ufshpb_get_hpb_data(cmd->device); - if (!hpb) - return -ENODEV; - - if (ufshpb_get_state(hpb) == HPB_INIT) - return -ENODEV; - - if (ufshpb_get_state(hpb) != HPB_PRESENT) { - dev_notice(&hpb->sdev_ufs_lu->sdev_dev, - "%s: ufshpb state is not PRESENT", __func__); - return -ENODEV; - } - - if (blk_rq_is_passthrough(scsi_cmd_to_rq(cmd)) || - (!ufshpb_is_write_or_discard(cmd) && - !ufshpb_is_read_cmd(cmd))) - return 0; - - transfer_len = sectors_to_logical(cmd->device, - blk_rq_sectors(scsi_cmd_to_rq(cmd))); - if (unlikely(!transfer_len)) - return 0; - - lpn = sectors_to_logical(cmd->device, blk_rq_pos(scsi_cmd_to_rq(cmd))); - ufshpb_get_pos_from_lpn(hpb, lpn, &rgn_idx, &srgn_idx, &srgn_offset); - rgn = hpb->rgn_tbl + rgn_idx; - srgn = rgn->srgn_tbl + srgn_idx; - - /* If command type is WRITE or DISCARD, set bitmap as dirty */ - if (ufshpb_is_write_or_discard(cmd)) { - ufshpb_iterate_rgn(hpb, rgn_idx, srgn_idx, srgn_offset, - transfer_len, true); - return 0; - } - - if (!ufshpb_is_supported_chunk(hpb, transfer_len)) - return 0; - - if (hpb->is_hcm) { - /* - * in host control mode, reads are the main source for - * activation trials. - */ - ufshpb_iterate_rgn(hpb, rgn_idx, srgn_idx, srgn_offset, - transfer_len, false); - - /* keep those counters normalized */ - if (rgn->reads > hpb->entries_per_srgn) - schedule_work(&hpb->ufshpb_normalization_work); - } - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - if (ufshpb_test_ppn_dirty(hpb, rgn_idx, srgn_idx, srgn_offset, - transfer_len)) { - hpb->stats.miss_cnt++; - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - return 0; - } - - err = ufshpb_fill_ppn_from_page(hpb, srgn->mctx, srgn_offset, 1, &ppn); - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - if (unlikely(err < 0)) { - /* - * In this case, the region state is active, - * but the ppn table is not allocated. - * Make sure that ppn table must be allocated on - * active state. - */ - dev_err(hba->dev, "get ppn failed. err %d\n", err); - return err; - } - - ufshpb_set_hpb_read_to_upiu(hba, lrbp, ppn, transfer_len); - - hpb->stats.hit_cnt++; - return 0; -} - -static struct ufshpb_req *ufshpb_get_req(struct ufshpb_lu *hpb, int rgn_idx, - enum req_op op, bool atomic) -{ - struct ufshpb_req *rq; - struct request *req; - int retries = HPB_MAP_REQ_RETRIES; - - rq = kmem_cache_alloc(hpb->map_req_cache, GFP_KERNEL); - if (!rq) - return NULL; - -retry: - req = blk_mq_alloc_request(hpb->sdev_ufs_lu->request_queue, op, - BLK_MQ_REQ_NOWAIT); - - if (!atomic && (PTR_ERR(req) == -EWOULDBLOCK) && (--retries > 0)) { - usleep_range(3000, 3100); - goto retry; - } - - if (IS_ERR(req)) - goto free_rq; - - rq->hpb = hpb; - rq->req = req; - rq->rb.rgn_idx = rgn_idx; - - return rq; - -free_rq: - kmem_cache_free(hpb->map_req_cache, rq); - return NULL; -} - -static void ufshpb_put_req(struct ufshpb_lu *hpb, struct ufshpb_req *rq) -{ - blk_mq_free_request(rq->req); - kmem_cache_free(hpb->map_req_cache, rq); -} - -static struct ufshpb_req *ufshpb_get_map_req(struct ufshpb_lu *hpb, - struct ufshpb_subregion *srgn) -{ - struct ufshpb_req *map_req; - struct bio *bio; - unsigned long flags; - - if (hpb->is_hcm && - hpb->num_inflight_map_req >= hpb->params.inflight_map_req) { - dev_info(&hpb->sdev_ufs_lu->sdev_dev, - "map_req throttle. inflight %d throttle %d", - hpb->num_inflight_map_req, - hpb->params.inflight_map_req); - return NULL; - } - - map_req = ufshpb_get_req(hpb, srgn->rgn_idx, REQ_OP_DRV_IN, false); - if (!map_req) - return NULL; - - bio = bio_alloc(NULL, hpb->pages_per_srgn, 0, GFP_KERNEL); - if (!bio) { - ufshpb_put_req(hpb, map_req); - return NULL; - } - - map_req->bio = bio; - - map_req->rb.srgn_idx = srgn->srgn_idx; - map_req->rb.mctx = srgn->mctx; - - spin_lock_irqsave(&hpb->param_lock, flags); - hpb->num_inflight_map_req++; - spin_unlock_irqrestore(&hpb->param_lock, flags); - - return map_req; -} - -static void ufshpb_put_map_req(struct ufshpb_lu *hpb, - struct ufshpb_req *map_req) -{ - unsigned long flags; - - bio_put(map_req->bio); - ufshpb_put_req(hpb, map_req); - - spin_lock_irqsave(&hpb->param_lock, flags); - hpb->num_inflight_map_req--; - spin_unlock_irqrestore(&hpb->param_lock, flags); -} - -static int ufshpb_clear_dirty_bitmap(struct ufshpb_lu *hpb, - struct ufshpb_subregion *srgn) -{ - struct ufshpb_region *rgn; - u32 num_entries = hpb->entries_per_srgn; - - if (!srgn->mctx) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "no mctx in region %d subregion %d.\n", - srgn->rgn_idx, srgn->srgn_idx); - return -1; - } - - if (unlikely(srgn->is_last)) - num_entries = hpb->last_srgn_entries; - - bitmap_zero(srgn->mctx->ppn_dirty, num_entries); - - rgn = hpb->rgn_tbl + srgn->rgn_idx; - clear_bit(RGN_FLAG_DIRTY, &rgn->rgn_flags); - - return 0; -} - -static void ufshpb_update_active_info(struct ufshpb_lu *hpb, int rgn_idx, - int srgn_idx) -{ - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - - rgn = hpb->rgn_tbl + rgn_idx; - srgn = rgn->srgn_tbl + srgn_idx; - - list_del_init(&rgn->list_inact_rgn); - - if (list_empty(&srgn->list_act_srgn)) - list_add_tail(&srgn->list_act_srgn, &hpb->lh_act_srgn); - - hpb->stats.rcmd_active_cnt++; -} - -static void ufshpb_update_inactive_info(struct ufshpb_lu *hpb, int rgn_idx) -{ - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - int srgn_idx; - - rgn = hpb->rgn_tbl + rgn_idx; - - for_each_sub_region(rgn, srgn_idx, srgn) - list_del_init(&srgn->list_act_srgn); - - if (list_empty(&rgn->list_inact_rgn)) - list_add_tail(&rgn->list_inact_rgn, &hpb->lh_inact_rgn); - - hpb->stats.rcmd_inactive_cnt++; -} - -static void ufshpb_activate_subregion(struct ufshpb_lu *hpb, - struct ufshpb_subregion *srgn) -{ - struct ufshpb_region *rgn; - - /* - * If there is no mctx in subregion - * after I/O progress for HPB_READ_BUFFER, the region to which the - * subregion belongs was evicted. - * Make sure the region must not evict in I/O progress - */ - if (!srgn->mctx) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "no mctx in region %d subregion %d.\n", - srgn->rgn_idx, srgn->srgn_idx); - srgn->srgn_state = HPB_SRGN_INVALID; - return; - } - - rgn = hpb->rgn_tbl + srgn->rgn_idx; - - if (unlikely(rgn->rgn_state == HPB_RGN_INACTIVE)) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "region %d subregion %d evicted\n", - srgn->rgn_idx, srgn->srgn_idx); - srgn->srgn_state = HPB_SRGN_INVALID; - return; - } - srgn->srgn_state = HPB_SRGN_VALID; -} - -static enum rq_end_io_ret ufshpb_umap_req_compl_fn(struct request *req, - blk_status_t error) -{ - struct ufshpb_req *umap_req = req->end_io_data; - - ufshpb_put_req(umap_req->hpb, umap_req); - return RQ_END_IO_NONE; -} - -static enum rq_end_io_ret ufshpb_map_req_compl_fn(struct request *req, - blk_status_t error) -{ - struct ufshpb_req *map_req = req->end_io_data; - struct ufshpb_lu *hpb = map_req->hpb; - struct ufshpb_subregion *srgn; - unsigned long flags; - - srgn = hpb->rgn_tbl[map_req->rb.rgn_idx].srgn_tbl + - map_req->rb.srgn_idx; - - ufshpb_clear_dirty_bitmap(hpb, srgn); - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - ufshpb_activate_subregion(hpb, srgn); - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - - ufshpb_put_map_req(map_req->hpb, map_req); - return RQ_END_IO_NONE; -} - -static void ufshpb_set_unmap_cmd(unsigned char *cdb, struct ufshpb_region *rgn) -{ - cdb[0] = UFSHPB_WRITE_BUFFER; - cdb[1] = rgn ? UFSHPB_WRITE_BUFFER_INACT_SINGLE_ID : - UFSHPB_WRITE_BUFFER_INACT_ALL_ID; - if (rgn) - put_unaligned_be16(rgn->rgn_idx, &cdb[2]); - cdb[9] = 0x00; -} - -static void ufshpb_set_read_buf_cmd(unsigned char *cdb, int rgn_idx, - int srgn_idx, int srgn_mem_size) -{ - cdb[0] = UFSHPB_READ_BUFFER; - cdb[1] = UFSHPB_READ_BUFFER_ID; - - put_unaligned_be16(rgn_idx, &cdb[2]); - put_unaligned_be16(srgn_idx, &cdb[4]); - put_unaligned_be24(srgn_mem_size, &cdb[6]); - - cdb[9] = 0x00; -} - -static void ufshpb_execute_umap_req(struct ufshpb_lu *hpb, - struct ufshpb_req *umap_req, - struct ufshpb_region *rgn) -{ - struct request *req = umap_req->req; - struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req); - - req->timeout = 0; - req->end_io_data = umap_req; - req->end_io = ufshpb_umap_req_compl_fn; - - ufshpb_set_unmap_cmd(scmd->cmnd, rgn); - scmd->cmd_len = HPB_WRITE_BUFFER_CMD_LENGTH; - - blk_execute_rq_nowait(req, true); - - hpb->stats.umap_req_cnt++; -} - -static int ufshpb_execute_map_req(struct ufshpb_lu *hpb, - struct ufshpb_req *map_req, bool last) -{ - struct request_queue *q; - struct request *req; - struct scsi_cmnd *scmd; - int mem_size = hpb->srgn_mem_size; - int ret = 0; - int i; - - q = hpb->sdev_ufs_lu->request_queue; - for (i = 0; i < hpb->pages_per_srgn; i++) { - ret = bio_add_pc_page(q, map_req->bio, map_req->rb.mctx->m_page[i], - PAGE_SIZE, 0); - if (ret != PAGE_SIZE) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "bio_add_pc_page fail %d - %d\n", - map_req->rb.rgn_idx, map_req->rb.srgn_idx); - return ret; - } - } - - req = map_req->req; - - blk_rq_append_bio(req, map_req->bio); - - req->end_io_data = map_req; - req->end_io = ufshpb_map_req_compl_fn; - - if (unlikely(last)) - mem_size = hpb->last_srgn_entries * HPB_ENTRY_SIZE; - - scmd = blk_mq_rq_to_pdu(req); - ufshpb_set_read_buf_cmd(scmd->cmnd, map_req->rb.rgn_idx, - map_req->rb.srgn_idx, mem_size); - scmd->cmd_len = HPB_READ_BUFFER_CMD_LENGTH; - - blk_execute_rq_nowait(req, true); - - hpb->stats.map_req_cnt++; - return 0; -} - -static struct ufshpb_map_ctx *ufshpb_get_map_ctx(struct ufshpb_lu *hpb, - bool last) -{ - struct ufshpb_map_ctx *mctx; - u32 num_entries = hpb->entries_per_srgn; - int i, j; - - mctx = mempool_alloc(ufshpb_mctx_pool, GFP_KERNEL); - if (!mctx) - return NULL; - - mctx->m_page = kmem_cache_alloc(hpb->m_page_cache, GFP_KERNEL); - if (!mctx->m_page) - goto release_mctx; - - if (unlikely(last)) - num_entries = hpb->last_srgn_entries; - - mctx->ppn_dirty = bitmap_zalloc(num_entries, GFP_KERNEL); - if (!mctx->ppn_dirty) - goto release_m_page; - - for (i = 0; i < hpb->pages_per_srgn; i++) { - mctx->m_page[i] = mempool_alloc(ufshpb_page_pool, GFP_KERNEL); - if (!mctx->m_page[i]) { - for (j = 0; j < i; j++) - mempool_free(mctx->m_page[j], ufshpb_page_pool); - goto release_ppn_dirty; - } - clear_page(page_address(mctx->m_page[i])); - } - - return mctx; - -release_ppn_dirty: - bitmap_free(mctx->ppn_dirty); -release_m_page: - kmem_cache_free(hpb->m_page_cache, mctx->m_page); -release_mctx: - mempool_free(mctx, ufshpb_mctx_pool); - return NULL; -} - -static void ufshpb_put_map_ctx(struct ufshpb_lu *hpb, - struct ufshpb_map_ctx *mctx) -{ - int i; - - for (i = 0; i < hpb->pages_per_srgn; i++) - mempool_free(mctx->m_page[i], ufshpb_page_pool); - - bitmap_free(mctx->ppn_dirty); - kmem_cache_free(hpb->m_page_cache, mctx->m_page); - mempool_free(mctx, ufshpb_mctx_pool); -} - -static int ufshpb_check_srgns_issue_state(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn) -{ - struct ufshpb_subregion *srgn; - int srgn_idx; - - for_each_sub_region(rgn, srgn_idx, srgn) - if (srgn->srgn_state == HPB_SRGN_ISSUED) - return -EPERM; - - return 0; -} - -static void ufshpb_read_to_handler(struct work_struct *work) -{ - struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu, - ufshpb_read_to_work.work); - struct victim_select_info *lru_info = &hpb->lru_info; - struct ufshpb_region *rgn, *next_rgn; - unsigned long flags; - unsigned int poll; - LIST_HEAD(expired_list); - - if (test_and_set_bit(TIMEOUT_WORK_RUNNING, &hpb->work_data_bits)) - return; - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - - list_for_each_entry_safe(rgn, next_rgn, &lru_info->lh_lru_rgn, - list_lru_rgn) { - bool timedout = ktime_after(ktime_get(), rgn->read_timeout); - - if (timedout) { - rgn->read_timeout_expiries--; - if (is_rgn_dirty(rgn) || - rgn->read_timeout_expiries == 0) - list_add(&rgn->list_expired_rgn, &expired_list); - else - rgn->read_timeout = ktime_add_ms(ktime_get(), - hpb->params.read_timeout_ms); - } - } - - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - - list_for_each_entry_safe(rgn, next_rgn, &expired_list, - list_expired_rgn) { - list_del_init(&rgn->list_expired_rgn); - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - ufshpb_update_inactive_info(hpb, rgn->rgn_idx); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - } - - ufshpb_kick_map_work(hpb); - - clear_bit(TIMEOUT_WORK_RUNNING, &hpb->work_data_bits); - - poll = hpb->params.timeout_polling_interval_ms; - schedule_delayed_work(&hpb->ufshpb_read_to_work, - msecs_to_jiffies(poll)); -} - -static void ufshpb_add_lru_info(struct victim_select_info *lru_info, - struct ufshpb_region *rgn) -{ - rgn->rgn_state = HPB_RGN_ACTIVE; - list_add_tail(&rgn->list_lru_rgn, &lru_info->lh_lru_rgn); - atomic_inc(&lru_info->active_cnt); - if (rgn->hpb->is_hcm) { - rgn->read_timeout = - ktime_add_ms(ktime_get(), - rgn->hpb->params.read_timeout_ms); - rgn->read_timeout_expiries = - rgn->hpb->params.read_timeout_expiries; - } -} - -static void ufshpb_hit_lru_info(struct victim_select_info *lru_info, - struct ufshpb_region *rgn) -{ - list_move_tail(&rgn->list_lru_rgn, &lru_info->lh_lru_rgn); -} - -static struct ufshpb_region *ufshpb_victim_lru_info(struct ufshpb_lu *hpb) -{ - struct victim_select_info *lru_info = &hpb->lru_info; - struct ufshpb_region *rgn, *victim_rgn = NULL; - - list_for_each_entry(rgn, &lru_info->lh_lru_rgn, list_lru_rgn) { - if (ufshpb_check_srgns_issue_state(hpb, rgn)) - continue; - - /* - * in host control mode, verify that the exiting region - * has fewer reads - */ - if (hpb->is_hcm && - rgn->reads > hpb->params.eviction_thld_exit) - continue; - - victim_rgn = rgn; - break; - } - - if (!victim_rgn) - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "%s: no region allocated\n", - __func__); - - return victim_rgn; -} - -static void ufshpb_cleanup_lru_info(struct victim_select_info *lru_info, - struct ufshpb_region *rgn) -{ - list_del_init(&rgn->list_lru_rgn); - rgn->rgn_state = HPB_RGN_INACTIVE; - atomic_dec(&lru_info->active_cnt); -} - -static void ufshpb_purge_active_subregion(struct ufshpb_lu *hpb, - struct ufshpb_subregion *srgn) -{ - if (srgn->srgn_state != HPB_SRGN_UNUSED) { - ufshpb_put_map_ctx(hpb, srgn->mctx); - srgn->srgn_state = HPB_SRGN_UNUSED; - srgn->mctx = NULL; - } -} - -static int ufshpb_issue_umap_req(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn, - bool atomic) -{ - struct ufshpb_req *umap_req; - int rgn_idx = rgn ? rgn->rgn_idx : 0; - - umap_req = ufshpb_get_req(hpb, rgn_idx, REQ_OP_DRV_OUT, atomic); - if (!umap_req) - return -ENOMEM; - - ufshpb_execute_umap_req(hpb, umap_req, rgn); - - return 0; -} - -static int ufshpb_issue_umap_single_req(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn) -{ - return ufshpb_issue_umap_req(hpb, rgn, true); -} - -static void __ufshpb_evict_region(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn) -{ - struct victim_select_info *lru_info; - struct ufshpb_subregion *srgn; - int srgn_idx; - - lru_info = &hpb->lru_info; - - dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, "evict region %d\n", rgn->rgn_idx); - - ufshpb_cleanup_lru_info(lru_info, rgn); - - for_each_sub_region(rgn, srgn_idx, srgn) - ufshpb_purge_active_subregion(hpb, srgn); -} - -static int ufshpb_evict_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - if (rgn->rgn_state == HPB_RGN_PINNED) { - dev_warn(&hpb->sdev_ufs_lu->sdev_dev, - "pinned region cannot drop-out. region %d\n", - rgn->rgn_idx); - goto out; - } - - if (!list_empty(&rgn->list_lru_rgn)) { - if (ufshpb_check_srgns_issue_state(hpb, rgn)) { - ret = -EBUSY; - goto out; - } - - if (hpb->is_hcm) { - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - ret = ufshpb_issue_umap_single_req(hpb, rgn); - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - if (ret) - goto out; - } - - __ufshpb_evict_region(hpb, rgn); - } -out: - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - return ret; -} - -static int ufshpb_issue_map_req(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn, - struct ufshpb_subregion *srgn) -{ - struct ufshpb_req *map_req; - unsigned long flags; - int ret; - int err = -EAGAIN; - bool alloc_required = false; - enum HPB_SRGN_STATE state = HPB_SRGN_INVALID; - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - - if (ufshpb_get_state(hpb) != HPB_PRESENT) { - dev_notice(&hpb->sdev_ufs_lu->sdev_dev, - "%s: ufshpb state is not PRESENT\n", __func__); - goto unlock_out; - } - - if ((rgn->rgn_state == HPB_RGN_INACTIVE) && - (srgn->srgn_state == HPB_SRGN_INVALID)) { - err = 0; - goto unlock_out; - } - - if (srgn->srgn_state == HPB_SRGN_UNUSED) - alloc_required = true; - - /* - * If the subregion is already ISSUED state, - * a specific event (e.g., GC or wear-leveling, etc.) occurs in - * the device and HPB response for map loading is received. - * In this case, after finishing the HPB_READ_BUFFER, - * the next HPB_READ_BUFFER is performed again to obtain the latest - * map data. - */ - if (srgn->srgn_state == HPB_SRGN_ISSUED) - goto unlock_out; - - srgn->srgn_state = HPB_SRGN_ISSUED; - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - - if (alloc_required) { - srgn->mctx = ufshpb_get_map_ctx(hpb, srgn->is_last); - if (!srgn->mctx) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "get map_ctx failed. region %d - %d\n", - rgn->rgn_idx, srgn->srgn_idx); - state = HPB_SRGN_UNUSED; - goto change_srgn_state; - } - } - - map_req = ufshpb_get_map_req(hpb, srgn); - if (!map_req) - goto change_srgn_state; - - - ret = ufshpb_execute_map_req(hpb, map_req, srgn->is_last); - if (ret) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "%s: issue map_req failed: %d, region %d - %d\n", - __func__, ret, srgn->rgn_idx, srgn->srgn_idx); - goto free_map_req; - } - return 0; - -free_map_req: - ufshpb_put_map_req(hpb, map_req); -change_srgn_state: - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - srgn->srgn_state = state; -unlock_out: - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - return err; -} - -static int ufshpb_add_region(struct ufshpb_lu *hpb, struct ufshpb_region *rgn) -{ - struct ufshpb_region *victim_rgn = NULL; - struct victim_select_info *lru_info = &hpb->lru_info; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - /* - * If region belongs to lru_list, just move the region - * to the front of lru list because the state of the region - * is already active-state. - */ - if (!list_empty(&rgn->list_lru_rgn)) { - ufshpb_hit_lru_info(lru_info, rgn); - goto out; - } - - if (rgn->rgn_state == HPB_RGN_INACTIVE) { - if (atomic_read(&lru_info->active_cnt) == - lru_info->max_lru_active_cnt) { - /* - * If the maximum number of active regions - * is exceeded, evict the least recently used region. - * This case may occur when the device responds - * to the eviction information late. - * It is okay to evict the least recently used region, - * because the device could detect this region - * by not issuing HPB_READ - * - * in host control mode, verify that the entering - * region has enough reads - */ - if (hpb->is_hcm && - rgn->reads < hpb->params.eviction_thld_enter) { - ret = -EACCES; - goto out; - } - - victim_rgn = ufshpb_victim_lru_info(hpb); - if (!victim_rgn) { - dev_warn(&hpb->sdev_ufs_lu->sdev_dev, - "cannot get victim region %s\n", - hpb->is_hcm ? "" : "error"); - ret = -ENOMEM; - goto out; - } - - dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, - "LRU full (%d), choose victim %d\n", - atomic_read(&lru_info->active_cnt), - victim_rgn->rgn_idx); - - if (hpb->is_hcm) { - spin_unlock_irqrestore(&hpb->rgn_state_lock, - flags); - ret = ufshpb_issue_umap_single_req(hpb, - victim_rgn); - spin_lock_irqsave(&hpb->rgn_state_lock, - flags); - if (ret) - goto out; - } - - __ufshpb_evict_region(hpb, victim_rgn); - } - - /* - * When a region is added to lru_info list_head, - * it is guaranteed that the subregion has been - * assigned all mctx. If failed, try to receive mctx again - * without being added to lru_info list_head - */ - ufshpb_add_lru_info(lru_info, rgn); - } -out: - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); - return ret; -} -/** - *ufshpb_submit_region_inactive() - submit a region to be inactivated later - *@hpb: per-LU HPB instance - *@region_index: the index associated with the region that will be inactivated later - */ -static void ufshpb_submit_region_inactive(struct ufshpb_lu *hpb, int region_index) -{ - int subregion_index; - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - - /* - * Remove this region from active region list and add it to inactive list - */ - spin_lock(&hpb->rsp_list_lock); - ufshpb_update_inactive_info(hpb, region_index); - spin_unlock(&hpb->rsp_list_lock); - - rgn = hpb->rgn_tbl + region_index; - - /* - * Set subregion state to be HPB_SRGN_INVALID, there will no HPB read on this subregion - */ - spin_lock(&hpb->rgn_state_lock); - if (rgn->rgn_state != HPB_RGN_INACTIVE) { - for (subregion_index = 0; subregion_index < rgn->srgn_cnt; subregion_index++) { - srgn = rgn->srgn_tbl + subregion_index; - if (srgn->srgn_state == HPB_SRGN_VALID) - srgn->srgn_state = HPB_SRGN_INVALID; - } - } - spin_unlock(&hpb->rgn_state_lock); -} - -static void ufshpb_rsp_req_region_update(struct ufshpb_lu *hpb, - struct utp_hpb_rsp *rsp_field) -{ - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - int i, rgn_i, srgn_i; - - BUILD_BUG_ON(sizeof(struct ufshpb_active_field) != HPB_ACT_FIELD_SIZE); - /* - * If the active region and the inactive region are the same, - * we will inactivate this region. - * The device could check this (region inactivated) and - * will response the proper active region information - */ - for (i = 0; i < rsp_field->active_rgn_cnt; i++) { - rgn_i = - be16_to_cpu(rsp_field->hpb_active_field[i].active_rgn); - srgn_i = - be16_to_cpu(rsp_field->hpb_active_field[i].active_srgn); - - rgn = hpb->rgn_tbl + rgn_i; - if (hpb->is_hcm && - (rgn->rgn_state != HPB_RGN_ACTIVE || is_rgn_dirty(rgn))) { - /* - * in host control mode, subregion activation - * recommendations are only allowed to active regions. - * Also, ignore recommendations for dirty regions - the - * host will make decisions concerning those by himself - */ - continue; - } - - dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, - "activate(%d) region %d - %d\n", i, rgn_i, srgn_i); - - spin_lock(&hpb->rsp_list_lock); - ufshpb_update_active_info(hpb, rgn_i, srgn_i); - spin_unlock(&hpb->rsp_list_lock); - - srgn = rgn->srgn_tbl + srgn_i; - - /* blocking HPB_READ */ - spin_lock(&hpb->rgn_state_lock); - if (srgn->srgn_state == HPB_SRGN_VALID) - srgn->srgn_state = HPB_SRGN_INVALID; - spin_unlock(&hpb->rgn_state_lock); - } - - if (hpb->is_hcm) { - /* - * in host control mode the device is not allowed to inactivate - * regions - */ - goto out; - } - - for (i = 0; i < rsp_field->inactive_rgn_cnt; i++) { - rgn_i = be16_to_cpu(rsp_field->hpb_inactive_field[i]); - dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, "inactivate(%d) region %d\n", i, rgn_i); - ufshpb_submit_region_inactive(hpb, rgn_i); - } - -out: - dev_dbg(&hpb->sdev_ufs_lu->sdev_dev, "Noti: #ACT %u #INACT %u\n", - rsp_field->active_rgn_cnt, rsp_field->inactive_rgn_cnt); - - if (ufshpb_get_state(hpb) == HPB_PRESENT) - queue_work(ufshpb_wq, &hpb->map_work); -} - -/* - * Set the flags of all active regions to RGN_FLAG_UPDATE to let host side reload L2P entries later - */ -static void ufshpb_set_regions_update(struct ufshpb_lu *hpb) -{ - struct victim_select_info *lru_info = &hpb->lru_info; - struct ufshpb_region *rgn; - unsigned long flags; - - spin_lock_irqsave(&hpb->rgn_state_lock, flags); - - list_for_each_entry(rgn, &lru_info->lh_lru_rgn, list_lru_rgn) - set_bit(RGN_FLAG_UPDATE, &rgn->rgn_flags); - - spin_unlock_irqrestore(&hpb->rgn_state_lock, flags); -} - -static void ufshpb_dev_reset_handler(struct ufs_hba *hba) -{ - struct scsi_device *sdev; - struct ufshpb_lu *hpb; - - __shost_for_each_device(sdev, hba->host) { - hpb = ufshpb_get_hpb_data(sdev); - if (!hpb) - continue; - - if (hpb->is_hcm) { - /* - * For the HPB host control mode, in case device powered up and lost HPB - * information, we will set the region flag to be RGN_FLAG_UPDATE, it will - * let host reload its L2P entries(reactivate region in the UFS device). - */ - ufshpb_set_regions_update(hpb); - } else { - /* - * For the HPB device control mode, if host side receives 02h:HPB Operation - * in UPIU response, which means device recommends the host side should - * inactivate all active regions. Here we add all active regions to inactive - * list, they will be inactivated later in ufshpb_map_work_handler(). - */ - struct victim_select_info *lru_info = &hpb->lru_info; - struct ufshpb_region *rgn; - - list_for_each_entry(rgn, &lru_info->lh_lru_rgn, list_lru_rgn) - ufshpb_submit_region_inactive(hpb, rgn->rgn_idx); - - if (ufshpb_get_state(hpb) == HPB_PRESENT) - queue_work(ufshpb_wq, &hpb->map_work); - } - } -} - -/* - * This function will parse recommended active subregion information in sense - * data field of response UPIU with SAM_STAT_GOOD state. - */ -void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) -{ - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(lrbp->cmd->device); - struct utp_hpb_rsp *rsp_field = &lrbp->ucd_rsp_ptr->hr; - int data_seg_len; - - data_seg_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) - & MASK_RSP_UPIU_DATA_SEG_LEN; - - /* If data segment length is zero, rsp_field is not valid */ - if (!data_seg_len) - return; - - if (unlikely(lrbp->lun != rsp_field->lun)) { - struct scsi_device *sdev; - bool found = false; - - __shost_for_each_device(sdev, hba->host) { - hpb = ufshpb_get_hpb_data(sdev); - - if (!hpb) - continue; - - if (rsp_field->lun == hpb->lun) { - found = true; - break; - } - } - - if (!found) - return; - } - - if (!hpb) - return; - - if (ufshpb_get_state(hpb) == HPB_INIT) - return; - - if ((ufshpb_get_state(hpb) != HPB_PRESENT) && - (ufshpb_get_state(hpb) != HPB_SUSPEND)) { - dev_notice(&hpb->sdev_ufs_lu->sdev_dev, - "%s: ufshpb state is not PRESENT/SUSPEND\n", - __func__); - return; - } - - BUILD_BUG_ON(sizeof(struct utp_hpb_rsp) != UTP_HPB_RSP_SIZE); - - if (!ufshpb_is_hpb_rsp_valid(hba, lrbp, rsp_field)) - return; - - hpb->stats.rcmd_noti_cnt++; - - switch (rsp_field->hpb_op) { - case HPB_RSP_REQ_REGION_UPDATE: - if (data_seg_len != DEV_DATA_SEG_LEN) - dev_warn(&hpb->sdev_ufs_lu->sdev_dev, - "%s: data seg length is not same.\n", - __func__); - ufshpb_rsp_req_region_update(hpb, rsp_field); - break; - case HPB_RSP_DEV_RESET: - dev_warn(&hpb->sdev_ufs_lu->sdev_dev, - "UFS device lost HPB information during PM.\n"); - ufshpb_dev_reset_handler(hba); - - break; - default: - dev_notice(&hpb->sdev_ufs_lu->sdev_dev, - "hpb_op is not available: %d\n", - rsp_field->hpb_op); - break; - } -} - -static void ufshpb_add_active_list(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn, - struct ufshpb_subregion *srgn) -{ - if (!list_empty(&rgn->list_inact_rgn)) - return; - - if (!list_empty(&srgn->list_act_srgn)) { - list_move(&srgn->list_act_srgn, &hpb->lh_act_srgn); - return; - } - - list_add(&srgn->list_act_srgn, &hpb->lh_act_srgn); -} - -static void ufshpb_add_pending_evict_list(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn, - struct list_head *pending_list) -{ - struct ufshpb_subregion *srgn; - int srgn_idx; - - if (!list_empty(&rgn->list_inact_rgn)) - return; - - for_each_sub_region(rgn, srgn_idx, srgn) - if (!list_empty(&srgn->list_act_srgn)) - return; - - list_add_tail(&rgn->list_inact_rgn, pending_list); -} - -static void ufshpb_run_active_subregion_list(struct ufshpb_lu *hpb) -{ - struct ufshpb_region *rgn; - struct ufshpb_subregion *srgn; - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - while ((srgn = list_first_entry_or_null(&hpb->lh_act_srgn, - struct ufshpb_subregion, - list_act_srgn))) { - if (ufshpb_get_state(hpb) == HPB_SUSPEND) - break; - - list_del_init(&srgn->list_act_srgn); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - - rgn = hpb->rgn_tbl + srgn->rgn_idx; - ret = ufshpb_add_region(hpb, rgn); - if (ret) - goto active_failed; - - ret = ufshpb_issue_map_req(hpb, rgn, srgn); - if (ret) { - dev_err(&hpb->sdev_ufs_lu->sdev_dev, - "issue map_req failed. ret %d, region %d - %d\n", - ret, rgn->rgn_idx, srgn->srgn_idx); - goto active_failed; - } - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - } - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - return; - -active_failed: - dev_err(&hpb->sdev_ufs_lu->sdev_dev, "failed to activate region %d - %d, will retry\n", - rgn->rgn_idx, srgn->srgn_idx); - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - ufshpb_add_active_list(hpb, rgn, srgn); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); -} - -static void ufshpb_run_inactive_region_list(struct ufshpb_lu *hpb) -{ - struct ufshpb_region *rgn; - unsigned long flags; - int ret; - LIST_HEAD(pending_list); - - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - while ((rgn = list_first_entry_or_null(&hpb->lh_inact_rgn, - struct ufshpb_region, - list_inact_rgn))) { - if (ufshpb_get_state(hpb) == HPB_SUSPEND) - break; - - list_del_init(&rgn->list_inact_rgn); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - - ret = ufshpb_evict_region(hpb, rgn); - if (ret) { - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - ufshpb_add_pending_evict_list(hpb, rgn, &pending_list); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); - } - - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - } - - list_splice(&pending_list, &hpb->lh_inact_rgn); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); -} - -static void ufshpb_normalization_work_handler(struct work_struct *work) -{ - struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu, - ufshpb_normalization_work); - int rgn_idx; - u8 factor = hpb->params.normalization_factor; - - for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) { - struct ufshpb_region *rgn = hpb->rgn_tbl + rgn_idx; - int srgn_idx; - - spin_lock(&rgn->rgn_lock); - rgn->reads = 0; - for (srgn_idx = 0; srgn_idx < hpb->srgns_per_rgn; srgn_idx++) { - struct ufshpb_subregion *srgn = rgn->srgn_tbl + srgn_idx; - - srgn->reads >>= factor; - rgn->reads += srgn->reads; - } - spin_unlock(&rgn->rgn_lock); - - if (rgn->rgn_state != HPB_RGN_ACTIVE || rgn->reads) - continue; - - /* if region is active but has no reads - inactivate it */ - spin_lock(&hpb->rsp_list_lock); - ufshpb_update_inactive_info(hpb, rgn->rgn_idx); - spin_unlock(&hpb->rsp_list_lock); - } -} - -static void ufshpb_map_work_handler(struct work_struct *work) -{ - struct ufshpb_lu *hpb = container_of(work, struct ufshpb_lu, map_work); - - if (ufshpb_get_state(hpb) != HPB_PRESENT) { - dev_notice(&hpb->sdev_ufs_lu->sdev_dev, - "%s: ufshpb state is not PRESENT\n", __func__); - return; - } - - ufshpb_run_inactive_region_list(hpb); - ufshpb_run_active_subregion_list(hpb); -} - -/* - * this function doesn't need to hold lock due to be called in init. - * (rgn_state_lock, rsp_list_lock, etc..) - */ -static int ufshpb_init_pinned_active_region(struct ufs_hba *hba, - struct ufshpb_lu *hpb, - struct ufshpb_region *rgn) -{ - struct ufshpb_subregion *srgn; - int srgn_idx, i; - int err = 0; - - for_each_sub_region(rgn, srgn_idx, srgn) { - srgn->mctx = ufshpb_get_map_ctx(hpb, srgn->is_last); - srgn->srgn_state = HPB_SRGN_INVALID; - if (!srgn->mctx) { - err = -ENOMEM; - dev_err(hba->dev, - "alloc mctx for pinned region failed\n"); - goto release; - } - - list_add_tail(&srgn->list_act_srgn, &hpb->lh_act_srgn); - } - - rgn->rgn_state = HPB_RGN_PINNED; - return 0; - -release: - for (i = 0; i < srgn_idx; i++) { - srgn = rgn->srgn_tbl + i; - ufshpb_put_map_ctx(hpb, srgn->mctx); - } - return err; -} - -static void ufshpb_init_subregion_tbl(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn, bool last) -{ - int srgn_idx; - struct ufshpb_subregion *srgn; - - for_each_sub_region(rgn, srgn_idx, srgn) { - INIT_LIST_HEAD(&srgn->list_act_srgn); - - srgn->rgn_idx = rgn->rgn_idx; - srgn->srgn_idx = srgn_idx; - srgn->srgn_state = HPB_SRGN_UNUSED; - } - - if (unlikely(last && hpb->last_srgn_entries)) - srgn->is_last = true; -} - -static int ufshpb_alloc_subregion_tbl(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn, int srgn_cnt) -{ - rgn->srgn_tbl = kvcalloc(srgn_cnt, sizeof(struct ufshpb_subregion), - GFP_KERNEL); - if (!rgn->srgn_tbl) - return -ENOMEM; - - rgn->srgn_cnt = srgn_cnt; - return 0; -} - -static void ufshpb_lu_parameter_init(struct ufs_hba *hba, - struct ufshpb_lu *hpb, - struct ufshpb_dev_info *hpb_dev_info, - struct ufshpb_lu_info *hpb_lu_info) -{ - u32 entries_per_rgn; - u64 rgn_mem_size, tmp; - - if (ufshpb_is_legacy(hba)) - hpb->pre_req_max_tr_len = HPB_LEGACY_CHUNK_HIGH; - else - hpb->pre_req_max_tr_len = hpb_dev_info->max_hpb_single_cmd; - - hpb->lu_pinned_start = hpb_lu_info->pinned_start; - hpb->lu_pinned_end = hpb_lu_info->num_pinned ? - (hpb_lu_info->pinned_start + hpb_lu_info->num_pinned - 1) - : PINNED_NOT_SET; - hpb->lru_info.max_lru_active_cnt = - hpb_lu_info->max_active_rgns - hpb_lu_info->num_pinned; - - rgn_mem_size = (1ULL << hpb_dev_info->rgn_size) * HPB_RGN_SIZE_UNIT - * HPB_ENTRY_SIZE; - do_div(rgn_mem_size, HPB_ENTRY_BLOCK_SIZE); - hpb->srgn_mem_size = (1ULL << hpb_dev_info->srgn_size) - * HPB_RGN_SIZE_UNIT / HPB_ENTRY_BLOCK_SIZE * HPB_ENTRY_SIZE; - - tmp = rgn_mem_size; - do_div(tmp, HPB_ENTRY_SIZE); - entries_per_rgn = (u32)tmp; - hpb->entries_per_rgn_shift = ilog2(entries_per_rgn); - hpb->entries_per_rgn_mask = entries_per_rgn - 1; - - hpb->entries_per_srgn = hpb->srgn_mem_size / HPB_ENTRY_SIZE; - hpb->entries_per_srgn_shift = ilog2(hpb->entries_per_srgn); - hpb->entries_per_srgn_mask = hpb->entries_per_srgn - 1; - - tmp = rgn_mem_size; - do_div(tmp, hpb->srgn_mem_size); - hpb->srgns_per_rgn = (int)tmp; - - hpb->rgns_per_lu = DIV_ROUND_UP(hpb_lu_info->num_blocks, - entries_per_rgn); - hpb->srgns_per_lu = DIV_ROUND_UP(hpb_lu_info->num_blocks, - (hpb->srgn_mem_size / HPB_ENTRY_SIZE)); - hpb->last_srgn_entries = hpb_lu_info->num_blocks - % (hpb->srgn_mem_size / HPB_ENTRY_SIZE); - - hpb->pages_per_srgn = DIV_ROUND_UP(hpb->srgn_mem_size, PAGE_SIZE); - - if (hpb_dev_info->control_mode == HPB_HOST_CONTROL) - hpb->is_hcm = true; -} - -static int ufshpb_alloc_region_tbl(struct ufs_hba *hba, struct ufshpb_lu *hpb) -{ - struct ufshpb_region *rgn_table, *rgn; - int rgn_idx, i; - int ret = 0; - - rgn_table = kvcalloc(hpb->rgns_per_lu, sizeof(struct ufshpb_region), - GFP_KERNEL); - if (!rgn_table) - return -ENOMEM; - - for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) { - int srgn_cnt = hpb->srgns_per_rgn; - bool last_srgn = false; - - rgn = rgn_table + rgn_idx; - rgn->rgn_idx = rgn_idx; - - spin_lock_init(&rgn->rgn_lock); - - INIT_LIST_HEAD(&rgn->list_inact_rgn); - INIT_LIST_HEAD(&rgn->list_lru_rgn); - INIT_LIST_HEAD(&rgn->list_expired_rgn); - - if (rgn_idx == hpb->rgns_per_lu - 1) { - srgn_cnt = ((hpb->srgns_per_lu - 1) % - hpb->srgns_per_rgn) + 1; - last_srgn = true; - } - - ret = ufshpb_alloc_subregion_tbl(hpb, rgn, srgn_cnt); - if (ret) - goto release_srgn_table; - ufshpb_init_subregion_tbl(hpb, rgn, last_srgn); - - if (ufshpb_is_pinned_region(hpb, rgn_idx)) { - ret = ufshpb_init_pinned_active_region(hba, hpb, rgn); - if (ret) - goto release_srgn_table; - } else { - rgn->rgn_state = HPB_RGN_INACTIVE; - } - - rgn->rgn_flags = 0; - rgn->hpb = hpb; - } - - hpb->rgn_tbl = rgn_table; - - return 0; - -release_srgn_table: - for (i = 0; i <= rgn_idx; i++) - kvfree(rgn_table[i].srgn_tbl); - - kvfree(rgn_table); - return ret; -} - -static void ufshpb_destroy_subregion_tbl(struct ufshpb_lu *hpb, - struct ufshpb_region *rgn) -{ - int srgn_idx; - struct ufshpb_subregion *srgn; - - for_each_sub_region(rgn, srgn_idx, srgn) - if (srgn->srgn_state != HPB_SRGN_UNUSED) { - srgn->srgn_state = HPB_SRGN_UNUSED; - ufshpb_put_map_ctx(hpb, srgn->mctx); - } -} - -static void ufshpb_destroy_region_tbl(struct ufshpb_lu *hpb) -{ - int rgn_idx; - - for (rgn_idx = 0; rgn_idx < hpb->rgns_per_lu; rgn_idx++) { - struct ufshpb_region *rgn; - - rgn = hpb->rgn_tbl + rgn_idx; - if (rgn->rgn_state != HPB_RGN_INACTIVE) { - rgn->rgn_state = HPB_RGN_INACTIVE; - - ufshpb_destroy_subregion_tbl(hpb, rgn); - } - - kvfree(rgn->srgn_tbl); - } - - kvfree(hpb->rgn_tbl); -} - -/* SYSFS functions */ -#define ufshpb_sysfs_attr_show_func(__name) \ -static ssize_t __name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct scsi_device *sdev = to_scsi_device(dev); \ - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); \ - \ - if (!hpb) \ - return -ENODEV; \ - \ - return sysfs_emit(buf, "%llu\n", hpb->stats.__name); \ -} \ -\ -static DEVICE_ATTR_RO(__name) - -ufshpb_sysfs_attr_show_func(hit_cnt); -ufshpb_sysfs_attr_show_func(miss_cnt); -ufshpb_sysfs_attr_show_func(rcmd_noti_cnt); -ufshpb_sysfs_attr_show_func(rcmd_active_cnt); -ufshpb_sysfs_attr_show_func(rcmd_inactive_cnt); -ufshpb_sysfs_attr_show_func(map_req_cnt); -ufshpb_sysfs_attr_show_func(umap_req_cnt); - -static struct attribute *hpb_dev_stat_attrs[] = { - &dev_attr_hit_cnt.attr, - &dev_attr_miss_cnt.attr, - &dev_attr_rcmd_noti_cnt.attr, - &dev_attr_rcmd_active_cnt.attr, - &dev_attr_rcmd_inactive_cnt.attr, - &dev_attr_map_req_cnt.attr, - &dev_attr_umap_req_cnt.attr, - NULL, -}; - -struct attribute_group ufs_sysfs_hpb_stat_group = { - .name = "hpb_stats", - .attrs = hpb_dev_stat_attrs, -}; - -/* SYSFS functions */ -#define ufshpb_sysfs_param_show_func(__name) \ -static ssize_t __name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct scsi_device *sdev = to_scsi_device(dev); \ - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); \ - \ - if (!hpb) \ - return -ENODEV; \ - \ - return sysfs_emit(buf, "%d\n", hpb->params.__name); \ -} - -ufshpb_sysfs_param_show_func(requeue_timeout_ms); -static ssize_t -requeue_timeout_ms_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val < 0) - return -EINVAL; - - hpb->params.requeue_timeout_ms = val; - - return count; -} -static DEVICE_ATTR_RW(requeue_timeout_ms); - -ufshpb_sysfs_param_show_func(activation_thld); -static ssize_t -activation_thld_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val <= 0) - return -EINVAL; - - hpb->params.activation_thld = val; - - return count; -} -static DEVICE_ATTR_RW(activation_thld); - -ufshpb_sysfs_param_show_func(normalization_factor); -static ssize_t -normalization_factor_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val <= 0 || val > ilog2(hpb->entries_per_srgn)) - return -EINVAL; - - hpb->params.normalization_factor = val; - - return count; -} -static DEVICE_ATTR_RW(normalization_factor); - -ufshpb_sysfs_param_show_func(eviction_thld_enter); -static ssize_t -eviction_thld_enter_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val <= hpb->params.eviction_thld_exit) - return -EINVAL; - - hpb->params.eviction_thld_enter = val; - - return count; -} -static DEVICE_ATTR_RW(eviction_thld_enter); - -ufshpb_sysfs_param_show_func(eviction_thld_exit); -static ssize_t -eviction_thld_exit_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val <= hpb->params.activation_thld) - return -EINVAL; - - hpb->params.eviction_thld_exit = val; - - return count; -} -static DEVICE_ATTR_RW(eviction_thld_exit); - -ufshpb_sysfs_param_show_func(read_timeout_ms); -static ssize_t -read_timeout_ms_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - /* read_timeout >> timeout_polling_interval */ - if (val < hpb->params.timeout_polling_interval_ms * 2) - return -EINVAL; - - hpb->params.read_timeout_ms = val; - - return count; -} -static DEVICE_ATTR_RW(read_timeout_ms); - -ufshpb_sysfs_param_show_func(read_timeout_expiries); -static ssize_t -read_timeout_expiries_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val <= 0) - return -EINVAL; - - hpb->params.read_timeout_expiries = val; - - return count; -} -static DEVICE_ATTR_RW(read_timeout_expiries); - -ufshpb_sysfs_param_show_func(timeout_polling_interval_ms); -static ssize_t -timeout_polling_interval_ms_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - /* timeout_polling_interval << read_timeout */ - if (val <= 0 || val > hpb->params.read_timeout_ms / 2) - return -EINVAL; - - hpb->params.timeout_polling_interval_ms = val; - - return count; -} -static DEVICE_ATTR_RW(timeout_polling_interval_ms); - -ufshpb_sysfs_param_show_func(inflight_map_req); -static ssize_t inflight_map_req_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct scsi_device *sdev = to_scsi_device(dev); - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - int val; - - if (!hpb) - return -ENODEV; - - if (!hpb->is_hcm) - return -EOPNOTSUPP; - - if (kstrtouint(buf, 0, &val)) - return -EINVAL; - - if (val <= 0 || val > hpb->sdev_ufs_lu->queue_depth - 1) - return -EINVAL; - - hpb->params.inflight_map_req = val; - - return count; -} -static DEVICE_ATTR_RW(inflight_map_req); - -static void ufshpb_hcm_param_init(struct ufshpb_lu *hpb) -{ - hpb->params.activation_thld = ACTIVATION_THRESHOLD; - hpb->params.normalization_factor = 1; - hpb->params.eviction_thld_enter = (ACTIVATION_THRESHOLD << 5); - hpb->params.eviction_thld_exit = (ACTIVATION_THRESHOLD << 4); - hpb->params.read_timeout_ms = READ_TO_MS; - hpb->params.read_timeout_expiries = READ_TO_EXPIRIES; - hpb->params.timeout_polling_interval_ms = POLLING_INTERVAL_MS; - hpb->params.inflight_map_req = THROTTLE_MAP_REQ_DEFAULT; -} - -static struct attribute *hpb_dev_param_attrs[] = { - &dev_attr_requeue_timeout_ms.attr, - &dev_attr_activation_thld.attr, - &dev_attr_normalization_factor.attr, - &dev_attr_eviction_thld_enter.attr, - &dev_attr_eviction_thld_exit.attr, - &dev_attr_read_timeout_ms.attr, - &dev_attr_read_timeout_expiries.attr, - &dev_attr_timeout_polling_interval_ms.attr, - &dev_attr_inflight_map_req.attr, - NULL, -}; - -struct attribute_group ufs_sysfs_hpb_param_group = { - .name = "hpb_params", - .attrs = hpb_dev_param_attrs, -}; - -static int ufshpb_pre_req_mempool_init(struct ufshpb_lu *hpb) -{ - struct ufshpb_req *pre_req = NULL, *t; - int qd = hpb->sdev_ufs_lu->queue_depth / 2; - int i; - - INIT_LIST_HEAD(&hpb->lh_pre_req_free); - - hpb->pre_req = kcalloc(qd, sizeof(struct ufshpb_req), GFP_KERNEL); - hpb->throttle_pre_req = qd; - hpb->num_inflight_pre_req = 0; - - if (!hpb->pre_req) - goto release_mem; - - for (i = 0; i < qd; i++) { - pre_req = hpb->pre_req + i; - INIT_LIST_HEAD(&pre_req->list_req); - pre_req->req = NULL; - - pre_req->bio = bio_alloc(NULL, 1, 0, GFP_KERNEL); - if (!pre_req->bio) - goto release_mem; - - pre_req->wb.m_page = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (!pre_req->wb.m_page) { - bio_put(pre_req->bio); - goto release_mem; - } - - list_add_tail(&pre_req->list_req, &hpb->lh_pre_req_free); - } - - return 0; -release_mem: - list_for_each_entry_safe(pre_req, t, &hpb->lh_pre_req_free, list_req) { - list_del_init(&pre_req->list_req); - bio_put(pre_req->bio); - __free_page(pre_req->wb.m_page); - } - - kfree(hpb->pre_req); - return -ENOMEM; -} - -static void ufshpb_pre_req_mempool_destroy(struct ufshpb_lu *hpb) -{ - struct ufshpb_req *pre_req = NULL; - int i; - - for (i = 0; i < hpb->throttle_pre_req; i++) { - pre_req = hpb->pre_req + i; - bio_put(hpb->pre_req[i].bio); - if (!pre_req->wb.m_page) - __free_page(hpb->pre_req[i].wb.m_page); - list_del_init(&pre_req->list_req); - } - - kfree(hpb->pre_req); -} - -static void ufshpb_stat_init(struct ufshpb_lu *hpb) -{ - hpb->stats.hit_cnt = 0; - hpb->stats.miss_cnt = 0; - hpb->stats.rcmd_noti_cnt = 0; - hpb->stats.rcmd_active_cnt = 0; - hpb->stats.rcmd_inactive_cnt = 0; - hpb->stats.map_req_cnt = 0; - hpb->stats.umap_req_cnt = 0; -} - -static void ufshpb_param_init(struct ufshpb_lu *hpb) -{ - hpb->params.requeue_timeout_ms = HPB_REQUEUE_TIME_MS; - if (hpb->is_hcm) - ufshpb_hcm_param_init(hpb); -} - -static int ufshpb_lu_hpb_init(struct ufs_hba *hba, struct ufshpb_lu *hpb) -{ - int ret; - - spin_lock_init(&hpb->rgn_state_lock); - spin_lock_init(&hpb->rsp_list_lock); - spin_lock_init(&hpb->param_lock); - - INIT_LIST_HEAD(&hpb->lru_info.lh_lru_rgn); - INIT_LIST_HEAD(&hpb->lh_act_srgn); - INIT_LIST_HEAD(&hpb->lh_inact_rgn); - INIT_LIST_HEAD(&hpb->list_hpb_lu); - - INIT_WORK(&hpb->map_work, ufshpb_map_work_handler); - if (hpb->is_hcm) { - INIT_WORK(&hpb->ufshpb_normalization_work, - ufshpb_normalization_work_handler); - INIT_DELAYED_WORK(&hpb->ufshpb_read_to_work, - ufshpb_read_to_handler); - } - - hpb->map_req_cache = kmem_cache_create("ufshpb_req_cache", - sizeof(struct ufshpb_req), 0, 0, NULL); - if (!hpb->map_req_cache) { - dev_err(hba->dev, "ufshpb(%d) ufshpb_req_cache create fail", - hpb->lun); - return -ENOMEM; - } - - hpb->m_page_cache = kmem_cache_create("ufshpb_m_page_cache", - sizeof(struct page *) * hpb->pages_per_srgn, - 0, 0, NULL); - if (!hpb->m_page_cache) { - dev_err(hba->dev, "ufshpb(%d) ufshpb_m_page_cache create fail", - hpb->lun); - ret = -ENOMEM; - goto release_req_cache; - } - - ret = ufshpb_pre_req_mempool_init(hpb); - if (ret) { - dev_err(hba->dev, "ufshpb(%d) pre_req_mempool init fail", - hpb->lun); - goto release_m_page_cache; - } - - ret = ufshpb_alloc_region_tbl(hba, hpb); - if (ret) - goto release_pre_req_mempool; - - ufshpb_stat_init(hpb); - ufshpb_param_init(hpb); - - if (hpb->is_hcm) { - unsigned int poll; - - poll = hpb->params.timeout_polling_interval_ms; - schedule_delayed_work(&hpb->ufshpb_read_to_work, - msecs_to_jiffies(poll)); - } - - return 0; - -release_pre_req_mempool: - ufshpb_pre_req_mempool_destroy(hpb); -release_m_page_cache: - kmem_cache_destroy(hpb->m_page_cache); -release_req_cache: - kmem_cache_destroy(hpb->map_req_cache); - return ret; -} - -static struct ufshpb_lu * -ufshpb_alloc_hpb_lu(struct ufs_hba *hba, struct scsi_device *sdev, - struct ufshpb_dev_info *hpb_dev_info, - struct ufshpb_lu_info *hpb_lu_info) -{ - struct ufshpb_lu *hpb; - int ret; - - hpb = kzalloc(sizeof(struct ufshpb_lu), GFP_KERNEL); - if (!hpb) - return NULL; - - hpb->lun = sdev->lun; - hpb->sdev_ufs_lu = sdev; - - ufshpb_lu_parameter_init(hba, hpb, hpb_dev_info, hpb_lu_info); - - ret = ufshpb_lu_hpb_init(hba, hpb); - if (ret) { - dev_err(hba->dev, "hpb lu init failed. ret %d", ret); - goto release_hpb; - } - - sdev->hostdata = hpb; - return hpb; - -release_hpb: - kfree(hpb); - return NULL; -} - -static void ufshpb_discard_rsp_lists(struct ufshpb_lu *hpb) -{ - struct ufshpb_region *rgn, *next_rgn; - struct ufshpb_subregion *srgn, *next_srgn; - unsigned long flags; - - /* - * If the device reset occurred, the remaining HPB region information - * may be stale. Therefore, by discarding the lists of HPB response - * that remained after reset, we prevent unnecessary work. - */ - spin_lock_irqsave(&hpb->rsp_list_lock, flags); - list_for_each_entry_safe(rgn, next_rgn, &hpb->lh_inact_rgn, - list_inact_rgn) - list_del_init(&rgn->list_inact_rgn); - - list_for_each_entry_safe(srgn, next_srgn, &hpb->lh_act_srgn, - list_act_srgn) - list_del_init(&srgn->list_act_srgn); - spin_unlock_irqrestore(&hpb->rsp_list_lock, flags); -} - -static void ufshpb_cancel_jobs(struct ufshpb_lu *hpb) -{ - if (hpb->is_hcm) { - cancel_delayed_work_sync(&hpb->ufshpb_read_to_work); - cancel_work_sync(&hpb->ufshpb_normalization_work); - } - cancel_work_sync(&hpb->map_work); -} - -static bool ufshpb_check_hpb_reset_query(struct ufs_hba *hba) -{ - int err = 0; - bool flag_res = true; - int try; - - /* wait for the device to complete HPB reset query */ - for (try = 0; try < HPB_RESET_REQ_RETRIES; try++) { - dev_dbg(hba->dev, - "%s: start flag reset polling %d times\n", - __func__, try); - - /* Poll fHpbReset flag to be cleared */ - err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, - QUERY_FLAG_IDN_HPB_RESET, 0, &flag_res); - - if (err) { - dev_err(hba->dev, - "%s: reading fHpbReset flag failed with error %d\n", - __func__, err); - return flag_res; - } - - if (!flag_res) - goto out; - - usleep_range(1000, 1100); - } - if (flag_res) { - dev_err(hba->dev, - "%s: fHpbReset was not cleared by the device\n", - __func__); - } -out: - return flag_res; -} - -/** - * ufshpb_toggle_state - switch HPB state of all LUs - * @hba: per-adapter instance - * @src: expected current HPB state - * @dest: target HPB state to switch to - */ -void ufshpb_toggle_state(struct ufs_hba *hba, enum UFSHPB_STATE src, enum UFSHPB_STATE dest) -{ - struct ufshpb_lu *hpb; - struct scsi_device *sdev; - - shost_for_each_device(sdev, hba->host) { - hpb = ufshpb_get_hpb_data(sdev); - - if (!hpb || ufshpb_get_state(hpb) != src) - continue; - ufshpb_set_state(hpb, dest); - - if (dest == HPB_RESET) { - ufshpb_cancel_jobs(hpb); - ufshpb_discard_rsp_lists(hpb); - } - } -} - -void ufshpb_suspend(struct ufs_hba *hba) -{ - struct ufshpb_lu *hpb; - struct scsi_device *sdev; - - shost_for_each_device(sdev, hba->host) { - hpb = ufshpb_get_hpb_data(sdev); - if (!hpb || ufshpb_get_state(hpb) != HPB_PRESENT) - continue; - - ufshpb_set_state(hpb, HPB_SUSPEND); - ufshpb_cancel_jobs(hpb); - } -} - -void ufshpb_resume(struct ufs_hba *hba) -{ - struct ufshpb_lu *hpb; - struct scsi_device *sdev; - - shost_for_each_device(sdev, hba->host) { - hpb = ufshpb_get_hpb_data(sdev); - if (!hpb || ufshpb_get_state(hpb) != HPB_SUSPEND) - continue; - - ufshpb_set_state(hpb, HPB_PRESENT); - ufshpb_kick_map_work(hpb); - if (hpb->is_hcm) { - unsigned int poll = hpb->params.timeout_polling_interval_ms; - - schedule_delayed_work(&hpb->ufshpb_read_to_work, msecs_to_jiffies(poll)); - } - } -} - -static int ufshpb_get_lu_info(struct ufs_hba *hba, int lun, - struct ufshpb_lu_info *hpb_lu_info) -{ - u16 max_active_rgns; - u8 lu_enable; - int size = QUERY_DESC_MAX_SIZE; - int ret; - char desc_buf[QUERY_DESC_MAX_SIZE]; - - ufshcd_rpm_get_sync(hba); - ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC, - QUERY_DESC_IDN_UNIT, lun, 0, - desc_buf, &size); - ufshcd_rpm_put_sync(hba); - - if (ret) { - dev_err(hba->dev, - "%s: idn: %d lun: %d query request failed", - __func__, QUERY_DESC_IDN_UNIT, lun); - return ret; - } - - lu_enable = desc_buf[UNIT_DESC_PARAM_LU_ENABLE]; - if (lu_enable != LU_ENABLED_HPB_FUNC) - return -ENODEV; - - max_active_rgns = get_unaligned_be16( - desc_buf + UNIT_DESC_PARAM_HPB_LU_MAX_ACTIVE_RGNS); - if (!max_active_rgns) { - dev_err(hba->dev, - "lun %d wrong number of max active regions\n", lun); - return -ENODEV; - } - - hpb_lu_info->num_blocks = get_unaligned_be64( - desc_buf + UNIT_DESC_PARAM_LOGICAL_BLK_COUNT); - hpb_lu_info->pinned_start = get_unaligned_be16( - desc_buf + UNIT_DESC_PARAM_HPB_PIN_RGN_START_OFF); - hpb_lu_info->num_pinned = get_unaligned_be16( - desc_buf + UNIT_DESC_PARAM_HPB_NUM_PIN_RGNS); - hpb_lu_info->max_active_rgns = max_active_rgns; - - return 0; -} - -void ufshpb_destroy_lu(struct ufs_hba *hba, struct scsi_device *sdev) -{ - struct ufshpb_lu *hpb = ufshpb_get_hpb_data(sdev); - - if (!hpb) - return; - - ufshpb_set_state(hpb, HPB_FAILED); - - sdev = hpb->sdev_ufs_lu; - sdev->hostdata = NULL; - - ufshpb_cancel_jobs(hpb); - - ufshpb_pre_req_mempool_destroy(hpb); - ufshpb_destroy_region_tbl(hpb); - - kmem_cache_destroy(hpb->map_req_cache); - kmem_cache_destroy(hpb->m_page_cache); - - list_del_init(&hpb->list_hpb_lu); - - kfree(hpb); -} - -static void ufshpb_hpb_lu_prepared(struct ufs_hba *hba) -{ - int pool_size; - struct ufshpb_lu *hpb; - struct scsi_device *sdev; - bool init_success; - - if (tot_active_srgn_pages == 0) { - ufshpb_remove(hba); - return; - } - - init_success = !ufshpb_check_hpb_reset_query(hba); - - pool_size = PAGE_ALIGN(ufshpb_host_map_kbytes * SZ_1K) / PAGE_SIZE; - if (pool_size > tot_active_srgn_pages) { - mempool_resize(ufshpb_mctx_pool, tot_active_srgn_pages); - mempool_resize(ufshpb_page_pool, tot_active_srgn_pages); - } - - shost_for_each_device(sdev, hba->host) { - hpb = ufshpb_get_hpb_data(sdev); - if (!hpb) - continue; - - if (init_success) { - ufshpb_set_state(hpb, HPB_PRESENT); - if ((hpb->lu_pinned_end - hpb->lu_pinned_start) > 0) - queue_work(ufshpb_wq, &hpb->map_work); - } else { - dev_err(hba->dev, "destroy HPB lu %d\n", hpb->lun); - ufshpb_destroy_lu(hba, sdev); - } - } - - if (!init_success) - ufshpb_remove(hba); -} - -void ufshpb_init_hpb_lu(struct ufs_hba *hba, struct scsi_device *sdev) -{ - struct ufshpb_lu *hpb; - int ret; - struct ufshpb_lu_info hpb_lu_info = { 0 }; - int lun = sdev->lun; - - if (lun >= hba->dev_info.max_lu_supported) - goto out; - - ret = ufshpb_get_lu_info(hba, lun, &hpb_lu_info); - if (ret) - goto out; - - hpb = ufshpb_alloc_hpb_lu(hba, sdev, &hba->ufshpb_dev, - &hpb_lu_info); - if (!hpb) - goto out; - - tot_active_srgn_pages += hpb_lu_info.max_active_rgns * - hpb->srgns_per_rgn * hpb->pages_per_srgn; - -out: - /* All LUs are initialized */ - if (atomic_dec_and_test(&hba->ufshpb_dev.slave_conf_cnt)) - ufshpb_hpb_lu_prepared(hba); -} - -static int ufshpb_init_mem_wq(struct ufs_hba *hba) -{ - int ret; - unsigned int pool_size; - - ufshpb_mctx_cache = kmem_cache_create("ufshpb_mctx_cache", - sizeof(struct ufshpb_map_ctx), - 0, 0, NULL); - if (!ufshpb_mctx_cache) { - dev_err(hba->dev, "ufshpb: cannot init mctx cache\n"); - return -ENOMEM; - } - - pool_size = PAGE_ALIGN(ufshpb_host_map_kbytes * SZ_1K) / PAGE_SIZE; - dev_info(hba->dev, "%s:%d ufshpb_host_map_kbytes %u pool_size %u\n", - __func__, __LINE__, ufshpb_host_map_kbytes, pool_size); - - ufshpb_mctx_pool = mempool_create_slab_pool(pool_size, - ufshpb_mctx_cache); - if (!ufshpb_mctx_pool) { - dev_err(hba->dev, "ufshpb: cannot init mctx pool\n"); - ret = -ENOMEM; - goto release_mctx_cache; - } - - ufshpb_page_pool = mempool_create_page_pool(pool_size, 0); - if (!ufshpb_page_pool) { - dev_err(hba->dev, "ufshpb: cannot init page pool\n"); - ret = -ENOMEM; - goto release_mctx_pool; - } - - ufshpb_wq = alloc_workqueue("ufshpb-wq", - WQ_UNBOUND | WQ_MEM_RECLAIM, 0); - if (!ufshpb_wq) { - dev_err(hba->dev, "ufshpb: alloc workqueue failed\n"); - ret = -ENOMEM; - goto release_page_pool; - } - - return 0; - -release_page_pool: - mempool_destroy(ufshpb_page_pool); -release_mctx_pool: - mempool_destroy(ufshpb_mctx_pool); -release_mctx_cache: - kmem_cache_destroy(ufshpb_mctx_cache); - return ret; -} - -void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf) -{ - struct ufshpb_dev_info *hpb_info = &hba->ufshpb_dev; - int max_active_rgns = 0; - int hpb_num_lu; - - hpb_num_lu = geo_buf[GEOMETRY_DESC_PARAM_HPB_NUMBER_LU]; - if (hpb_num_lu == 0) { - dev_err(hba->dev, "No HPB LU supported\n"); - hpb_info->hpb_disabled = true; - return; - } - - hpb_info->rgn_size = geo_buf[GEOMETRY_DESC_PARAM_HPB_REGION_SIZE]; - hpb_info->srgn_size = geo_buf[GEOMETRY_DESC_PARAM_HPB_SUBREGION_SIZE]; - max_active_rgns = get_unaligned_be16(geo_buf + - GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS); - - if (hpb_info->rgn_size == 0 || hpb_info->srgn_size == 0 || - max_active_rgns == 0) { - dev_err(hba->dev, "No HPB supported device\n"); - hpb_info->hpb_disabled = true; - return; - } -} - -void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) -{ - struct ufshpb_dev_info *hpb_dev_info = &hba->ufshpb_dev; - int version, ret; - int max_single_cmd; - - hpb_dev_info->control_mode = desc_buf[DEVICE_DESC_PARAM_HPB_CONTROL]; - - version = get_unaligned_be16(desc_buf + DEVICE_DESC_PARAM_HPB_VER); - if ((version != HPB_SUPPORT_VERSION) && - (version != HPB_SUPPORT_LEGACY_VERSION)) { - dev_err(hba->dev, "%s: HPB %x version is not supported.\n", - __func__, version); - hpb_dev_info->hpb_disabled = true; - return; - } - - if (version == HPB_SUPPORT_LEGACY_VERSION) - hpb_dev_info->is_legacy = true; - - /* - * Get the number of user logical unit to check whether all - * scsi_device finish initialization - */ - hpb_dev_info->num_lu = desc_buf[DEVICE_DESC_PARAM_NUM_LU]; - - if (hpb_dev_info->is_legacy) - return; - - ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR, - QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD, 0, 0, &max_single_cmd); - - if (ret) - hpb_dev_info->max_hpb_single_cmd = HPB_LEGACY_CHUNK_HIGH; - else - hpb_dev_info->max_hpb_single_cmd = min(max_single_cmd + 1, HPB_MULTI_CHUNK_HIGH); -} - -void ufshpb_init(struct ufs_hba *hba) -{ - struct ufshpb_dev_info *hpb_dev_info = &hba->ufshpb_dev; - int try; - int ret; - - if (!ufshpb_is_allowed(hba) || !hba->dev_info.hpb_enabled) - return; - - if (ufshpb_init_mem_wq(hba)) { - hpb_dev_info->hpb_disabled = true; - return; - } - - atomic_set(&hpb_dev_info->slave_conf_cnt, hpb_dev_info->num_lu); - tot_active_srgn_pages = 0; - /* issue HPB reset query */ - for (try = 0; try < HPB_RESET_REQ_RETRIES; try++) { - ret = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_SET_FLAG, - QUERY_FLAG_IDN_HPB_RESET, 0, NULL); - if (!ret) - break; - } -} - -void ufshpb_remove(struct ufs_hba *hba) -{ - mempool_destroy(ufshpb_page_pool); - mempool_destroy(ufshpb_mctx_pool); - kmem_cache_destroy(ufshpb_mctx_cache); - - destroy_workqueue(ufshpb_wq); -} - -module_param(ufshpb_host_map_kbytes, uint, 0644); -MODULE_PARM_DESC(ufshpb_host_map_kbytes, - "ufshpb host mapping memory kilo-bytes for ufshpb memory-pool"); diff --git a/drivers/ufs/core/ufshpb.h b/drivers/ufs/core/ufshpb.h deleted file mode 100644 index b428bbdd27992db14011a986ed98118a5d5bd1de..0000000000000000000000000000000000000000 --- a/drivers/ufs/core/ufshpb.h +++ /dev/null @@ -1,318 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Universal Flash Storage Host Performance Booster - * - * Copyright (C) 2017-2021 Samsung Electronics Co., Ltd. - * - * Authors: - * Yongmyung Lee - * Jinyoung Choi - */ - -#ifndef _UFSHPB_H_ -#define _UFSHPB_H_ - -/* hpb response UPIU macro */ -#define HPB_RSP_NONE 0x0 -#define HPB_RSP_REQ_REGION_UPDATE 0x1 -#define HPB_RSP_DEV_RESET 0x2 -#define MAX_ACTIVE_NUM 2 -#define MAX_INACTIVE_NUM 2 -#define DEV_DATA_SEG_LEN 0x14 -#define DEV_SENSE_SEG_LEN 0x12 -#define DEV_DES_TYPE 0x80 -#define DEV_ADDITIONAL_LEN 0x10 - -/* hpb map & entries macro */ -#define HPB_RGN_SIZE_UNIT 512 -#define HPB_ENTRY_BLOCK_SIZE SZ_4K -#define HPB_ENTRY_SIZE 0x8 -#define PINNED_NOT_SET U32_MAX - -/* hpb support chunk size */ -#define HPB_LEGACY_CHUNK_HIGH 1 -#define HPB_MULTI_CHUNK_HIGH 255 - -/* hpb vender defined opcode */ -#define UFSHPB_READ 0xF8 -#define UFSHPB_READ_BUFFER 0xF9 -#define UFSHPB_READ_BUFFER_ID 0x01 -#define UFSHPB_WRITE_BUFFER 0xFA -#define UFSHPB_WRITE_BUFFER_INACT_SINGLE_ID 0x01 -#define UFSHPB_WRITE_BUFFER_PREFETCH_ID 0x02 -#define UFSHPB_WRITE_BUFFER_INACT_ALL_ID 0x03 -#define HPB_WRITE_BUFFER_CMD_LENGTH 10 -#define MAX_HPB_READ_ID 0x7F -#define HPB_READ_BUFFER_CMD_LENGTH 10 -#define LU_ENABLED_HPB_FUNC 0x02 - -#define HPB_RESET_REQ_RETRIES 10 -#define HPB_MAP_REQ_RETRIES 5 -#define HPB_REQUEUE_TIME_MS 0 - -#define HPB_SUPPORT_VERSION 0x200 -#define HPB_SUPPORT_LEGACY_VERSION 0x100 - -enum UFSHPB_MODE { - HPB_HOST_CONTROL, - HPB_DEVICE_CONTROL, -}; - -enum UFSHPB_STATE { - HPB_INIT, - HPB_PRESENT, - HPB_SUSPEND, - HPB_FAILED, - HPB_RESET, -}; - -enum HPB_RGN_STATE { - HPB_RGN_INACTIVE, - HPB_RGN_ACTIVE, - /* pinned regions are always active */ - HPB_RGN_PINNED, -}; - -enum HPB_SRGN_STATE { - HPB_SRGN_UNUSED, - HPB_SRGN_INVALID, - HPB_SRGN_VALID, - HPB_SRGN_ISSUED, -}; - -/** - * struct ufshpb_lu_info - UFSHPB logical unit related info - * @num_blocks: the number of logical block - * @pinned_start: the start region number of pinned region - * @num_pinned: the number of pinned regions - * @max_active_rgns: maximum number of active regions - */ -struct ufshpb_lu_info { - int num_blocks; - int pinned_start; - int num_pinned; - int max_active_rgns; -}; - -struct ufshpb_map_ctx { - struct page **m_page; - unsigned long *ppn_dirty; -}; - -struct ufshpb_subregion { - struct ufshpb_map_ctx *mctx; - enum HPB_SRGN_STATE srgn_state; - int rgn_idx; - int srgn_idx; - bool is_last; - - /* subregion reads - for host mode */ - unsigned int reads; - - /* below information is used by rsp_list */ - struct list_head list_act_srgn; -}; - -struct ufshpb_region { - struct ufshpb_lu *hpb; - struct ufshpb_subregion *srgn_tbl; - enum HPB_RGN_STATE rgn_state; - int rgn_idx; - int srgn_cnt; - - /* below information is used by rsp_list */ - struct list_head list_inact_rgn; - - /* below information is used by lru */ - struct list_head list_lru_rgn; - unsigned long rgn_flags; -#define RGN_FLAG_DIRTY 0 -#define RGN_FLAG_UPDATE 1 - - /* region reads - for host mode */ - spinlock_t rgn_lock; - unsigned int reads; - /* region "cold" timer - for host mode */ - ktime_t read_timeout; - unsigned int read_timeout_expiries; - struct list_head list_expired_rgn; -}; - -#define for_each_sub_region(rgn, i, srgn) \ - for ((i) = 0; \ - ((i) < (rgn)->srgn_cnt) && ((srgn) = &(rgn)->srgn_tbl[i]); \ - (i)++) - -/** - * struct ufshpb_req - HPB related request structure (write/read buffer) - * @req: block layer request structure - * @bio: bio for this request - * @hpb: ufshpb_lu structure that related to - * @list_req: ufshpb_req mempool list - * @sense: store its sense data - * @mctx: L2P map information - * @rgn_idx: target region index - * @srgn_idx: target sub-region index - * @lun: target logical unit number - * @m_page: L2P map information data for pre-request - * @len: length of host-side cached L2P map in m_page - * @lpn: start LPN of L2P map in m_page - */ -struct ufshpb_req { - struct request *req; - struct bio *bio; - struct ufshpb_lu *hpb; - struct list_head list_req; - union { - struct { - struct ufshpb_map_ctx *mctx; - unsigned int rgn_idx; - unsigned int srgn_idx; - unsigned int lun; - } rb; - struct { - struct page *m_page; - unsigned int len; - unsigned long lpn; - } wb; - }; -}; - -struct victim_select_info { - struct list_head lh_lru_rgn; /* LRU list of regions */ - int max_lru_active_cnt; /* supported hpb #region - pinned #region */ - atomic_t active_cnt; -}; - -/** - * ufshpb_params - ufs hpb parameters - * @requeue_timeout_ms - requeue threshold of wb command (0x2) - * @activation_thld - min reads [IOs] to activate/update a region - * @normalization_factor - shift right the region's reads - * @eviction_thld_enter - min reads [IOs] for the entering region in eviction - * @eviction_thld_exit - max reads [IOs] for the exiting region in eviction - * @read_timeout_ms - timeout [ms] from the last read IO to the region - * @read_timeout_expiries - amount of allowable timeout expireis - * @timeout_polling_interval_ms - frequency in which timeouts are checked - * @inflight_map_req - number of inflight map requests - */ -struct ufshpb_params { - unsigned int requeue_timeout_ms; - unsigned int activation_thld; - unsigned int normalization_factor; - unsigned int eviction_thld_enter; - unsigned int eviction_thld_exit; - unsigned int read_timeout_ms; - unsigned int read_timeout_expiries; - unsigned int timeout_polling_interval_ms; - unsigned int inflight_map_req; -}; - -struct ufshpb_stats { - u64 hit_cnt; - u64 miss_cnt; - u64 rcmd_noti_cnt; - u64 rcmd_active_cnt; - u64 rcmd_inactive_cnt; - u64 map_req_cnt; - u64 pre_req_cnt; - u64 umap_req_cnt; -}; - -struct ufshpb_lu { - int lun; - struct scsi_device *sdev_ufs_lu; - - spinlock_t rgn_state_lock; /* for protect rgn/srgn state */ - struct ufshpb_region *rgn_tbl; - - atomic_t hpb_state; - - spinlock_t rsp_list_lock; - struct list_head lh_act_srgn; /* hold rsp_list_lock */ - struct list_head lh_inact_rgn; /* hold rsp_list_lock */ - - /* pre request information */ - struct ufshpb_req *pre_req; - int num_inflight_pre_req; - int throttle_pre_req; - int num_inflight_map_req; /* hold param_lock */ - spinlock_t param_lock; - - struct list_head lh_pre_req_free; - int pre_req_max_tr_len; - - /* cached L2P map management worker */ - struct work_struct map_work; - - /* for selecting victim */ - struct victim_select_info lru_info; - struct work_struct ufshpb_normalization_work; - struct delayed_work ufshpb_read_to_work; - unsigned long work_data_bits; -#define TIMEOUT_WORK_RUNNING 0 - - /* pinned region information */ - u32 lu_pinned_start; - u32 lu_pinned_end; - - /* HPB related configuration */ - u32 rgns_per_lu; - u32 srgns_per_lu; - u32 last_srgn_entries; - int srgns_per_rgn; - u32 srgn_mem_size; - u32 entries_per_rgn_mask; - u32 entries_per_rgn_shift; - u32 entries_per_srgn; - u32 entries_per_srgn_mask; - u32 entries_per_srgn_shift; - u32 pages_per_srgn; - - bool is_hcm; - - struct ufshpb_stats stats; - struct ufshpb_params params; - - struct kmem_cache *map_req_cache; - struct kmem_cache *m_page_cache; - - struct list_head list_hpb_lu; -}; - -struct ufs_hba; -struct ufshcd_lrb; - -#ifndef CONFIG_SCSI_UFS_HPB -static int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { return 0; } -static void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) {} -static void ufshpb_resume(struct ufs_hba *hba) {} -static void ufshpb_suspend(struct ufs_hba *hba) {} -static void ufshpb_toggle_state(struct ufs_hba *hba, enum UFSHPB_STATE src, enum UFSHPB_STATE dest) {} -static void ufshpb_init(struct ufs_hba *hba) {} -static void ufshpb_init_hpb_lu(struct ufs_hba *hba, struct scsi_device *sdev) {} -static void ufshpb_destroy_lu(struct ufs_hba *hba, struct scsi_device *sdev) {} -static void ufshpb_remove(struct ufs_hba *hba) {} -static bool ufshpb_is_allowed(struct ufs_hba *hba) { return false; } -static void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf) {} -static void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf) {} -static bool ufshpb_is_legacy(struct ufs_hba *hba) { return false; } -#else -int ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp); -void ufshpb_rsp_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp); -void ufshpb_resume(struct ufs_hba *hba); -void ufshpb_suspend(struct ufs_hba *hba); -void ufshpb_toggle_state(struct ufs_hba *hba, enum UFSHPB_STATE src, enum UFSHPB_STATE dest); -void ufshpb_init(struct ufs_hba *hba); -void ufshpb_init_hpb_lu(struct ufs_hba *hba, struct scsi_device *sdev); -void ufshpb_destroy_lu(struct ufs_hba *hba, struct scsi_device *sdev); -void ufshpb_remove(struct ufs_hba *hba); -bool ufshpb_is_allowed(struct ufs_hba *hba); -void ufshpb_get_geo_info(struct ufs_hba *hba, u8 *geo_buf); -void ufshpb_get_dev_info(struct ufs_hba *hba, u8 *desc_buf); -bool ufshpb_is_legacy(struct ufs_hba *hba); -extern struct attribute_group ufs_sysfs_hpb_stat_group; -extern struct attribute_group ufs_sysfs_hpb_param_group; -#endif - -#endif /* End of Header */ diff --git a/drivers/ufs/host/cdns-pltfrm.c b/drivers/ufs/host/cdns-pltfrm.c index 26761425a76cae0160424332cc5e621fe64efe70..2491e7e870283d29bae9bc1abaf6829bf4212fc3 100644 --- a/drivers/ufs/host/cdns-pltfrm.c +++ b/drivers/ufs/host/cdns-pltfrm.c @@ -101,11 +101,10 @@ static void cdns_ufs_set_l4_attr(struct ufs_hba *hba) } /** - * cdns_ufs_set_hclkdiv() - * Sets HCLKDIV register value based on the core_clk + * cdns_ufs_set_hclkdiv() - set HCLKDIV register value based on the core_clk. * @hba: host controller instance * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_set_hclkdiv(struct ufs_hba *hba) { @@ -143,12 +142,11 @@ static int cdns_ufs_set_hclkdiv(struct ufs_hba *hba) } /** - * cdns_ufs_hce_enable_notify() - * Called before and after HCE enable bit is set. + * cdns_ufs_hce_enable_notify() - set HCLKDIV register * @hba: host controller instance * @status: notify stage (pre, post change) * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_hce_enable_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) @@ -160,12 +158,10 @@ static int cdns_ufs_hce_enable_notify(struct ufs_hba *hba, } /** - * cdns_ufs_hibern8_notify() - * Called around hibern8 enter/exit. + * cdns_ufs_hibern8_notify() - save and restore L4 attributes. * @hba: host controller instance * @cmd: UIC Command * @status: notify stage (pre, post change) - * */ static void cdns_ufs_hibern8_notify(struct ufs_hba *hba, enum uic_cmd_dme cmd, enum ufs_notify_change_status status) @@ -177,12 +173,11 @@ static void cdns_ufs_hibern8_notify(struct ufs_hba *hba, enum uic_cmd_dme cmd, } /** - * cdns_ufs_link_startup_notify() - * Called before and after Link startup is carried out. + * cdns_ufs_link_startup_notify() - handle link startup. * @hba: host controller instance * @status: notify stage (pre, post change) * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) @@ -212,7 +207,7 @@ static int cdns_ufs_link_startup_notify(struct ufs_hba *hba, * cdns_ufs_init - performs additional ufs initialization * @hba: host controller instance * - * Returns status of initialization + * Return: status of initialization. */ static int cdns_ufs_init(struct ufs_hba *hba) { @@ -235,7 +230,7 @@ static int cdns_ufs_init(struct ufs_hba *hba) * cdns_ufs_m31_16nm_phy_initialization - performs m31 phy initialization * @hba: host controller instance * - * Always returns 0 + * Return: 0 (success). */ static int cdns_ufs_m31_16nm_phy_initialization(struct ufs_hba *hba) { @@ -284,7 +279,7 @@ MODULE_DEVICE_TABLE(of, cdns_ufs_of_match); * cdns_ufs_pltfrm_probe - probe routine of the driver * @pdev: pointer to platform device handle * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev) { @@ -308,7 +303,7 @@ static int cdns_ufs_pltfrm_probe(struct platform_device *pdev) * cdns_ufs_pltfrm_remove - removes the ufs driver * @pdev: pointer to platform device handle * - * Always returns 0 + * Return: 0 (success). */ static int cdns_ufs_pltfrm_remove(struct platform_device *pdev) { diff --git a/drivers/ufs/host/tc-dwc-g210-pci.c b/drivers/ufs/host/tc-dwc-g210-pci.c index f96fe58558417e35810c8ffb8acd1a851932d4cf..876781fd6861ee60fd24ac0d97f8b7aa69320f02 100644 --- a/drivers/ufs/host/tc-dwc-g210-pci.c +++ b/drivers/ufs/host/tc-dwc-g210-pci.c @@ -51,7 +51,7 @@ static void tc_dwc_g210_pci_remove(struct pci_dev *pdev) * @pdev: pointer to PCI device handle * @id: PCI device id * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int tc_dwc_g210_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/ufs/host/tc-dwc-g210.c b/drivers/ufs/host/tc-dwc-g210.c index deb93dbd83a433d26d7cf86b62d69bdb93eddc3e..0ac53cc8465ee4708a2b21540433a8ea7d34b7c2 100644 --- a/drivers/ufs/host/tc-dwc-g210.c +++ b/drivers/ufs/host/tc-dwc-g210.c @@ -17,11 +17,10 @@ #include "tc-dwc-g210.h" /** - * tc_dwc_g210_setup_40bit_rmmi() - * This function configures Synopsys TC specific atributes (40-bit RMMI) + * tc_dwc_g210_setup_40bit_rmmi() - configure 40-bit RMMI. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_40bit_rmmi(struct ufs_hba *hba) { @@ -81,11 +80,10 @@ static int tc_dwc_g210_setup_40bit_rmmi(struct ufs_hba *hba) } /** - * tc_dwc_g210_setup_20bit_rmmi_lane0() - * This function configures Synopsys TC 20-bit RMMI Lane 0 + * tc_dwc_g210_setup_20bit_rmmi_lane0() - configure 20-bit RMMI Lane 0. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_20bit_rmmi_lane0(struct ufs_hba *hba) { @@ -134,11 +132,10 @@ static int tc_dwc_g210_setup_20bit_rmmi_lane0(struct ufs_hba *hba) } /** - * tc_dwc_g210_setup_20bit_rmmi_lane1() - * This function configures Synopsys TC 20-bit RMMI Lane 1 + * tc_dwc_g210_setup_20bit_rmmi_lane1() - configure 20-bit RMMI Lane 1. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_20bit_rmmi_lane1(struct ufs_hba *hba) { @@ -211,11 +208,10 @@ static int tc_dwc_g210_setup_20bit_rmmi_lane1(struct ufs_hba *hba) } /** - * tc_dwc_g210_setup_20bit_rmmi() - * This function configures Synopsys TC specific atributes (20-bit RMMI) + * tc_dwc_g210_setup_20bit_rmmi() - configure 20-bit RMMI. * @hba: Pointer to drivers structure * - * Returns 0 on success or non-zero value on failure + * Return: 0 on success or non-zero value on failure. */ static int tc_dwc_g210_setup_20bit_rmmi(struct ufs_hba *hba) { @@ -251,12 +247,10 @@ static int tc_dwc_g210_setup_20bit_rmmi(struct ufs_hba *hba) } /** - * tc_dwc_g210_config_40_bit() - * This function configures Local (host) Synopsys 40-bit TC specific attributes - * + * tc_dwc_g210_config_40_bit() - configure 40-bit TC specific attributes. * @hba: Pointer to drivers structure * - * Returns 0 on success non-zero value on failure + * Return: 0 on success non-zero value on failure. */ int tc_dwc_g210_config_40_bit(struct ufs_hba *hba) { @@ -283,12 +277,10 @@ int tc_dwc_g210_config_40_bit(struct ufs_hba *hba) EXPORT_SYMBOL(tc_dwc_g210_config_40_bit); /** - * tc_dwc_g210_config_20_bit() - * This function configures Local (host) Synopsys 20-bit TC specific attributes - * + * tc_dwc_g210_config_20_bit() - configure 20-bit TC specific attributes. * @hba: Pointer to drivers structure * - * Returns 0 on success non-zero value on failure + * Return: 0 on success non-zero value on failure. */ int tc_dwc_g210_config_20_bit(struct ufs_hba *hba) { diff --git a/drivers/ufs/host/ti-j721e-ufs.c b/drivers/ufs/host/ti-j721e-ufs.c index 122d650d08102f8ca65ee9b04e072120b0705023..117eb7da92acd1ec579d98f36f91a236eeec402d 100644 --- a/drivers/ufs/host/ti-j721e-ufs.c +++ b/drivers/ufs/host/ti-j721e-ufs.c @@ -81,6 +81,8 @@ static const struct of_device_id ti_j721e_ufs_of_match[] = { { }, }; +MODULE_DEVICE_TABLE(of, ti_j721e_ufs_of_match); + static struct platform_driver ti_j721e_ufs_driver = { .probe = ti_j721e_ufs_probe, .remove = ti_j721e_ufs_remove, diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index e68b05976f9ebd7bbee8b29f96c309dd2ddbf80c..2383ecd88f1cbacba1e112c3b018b5c09f6c0b5f 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -27,8 +28,14 @@ #include #include "ufs-mediatek.h" +static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq); + #define CREATE_TRACE_POINTS #include "ufs-mediatek-trace.h" +#undef CREATE_TRACE_POINTS + +#define MAX_SUPP_MAC 64 +#define MCQ_QUEUE_OFFSET(c) ((((c) >> 16) & 0xFF) * 0x200) static const struct ufs_dev_quirk ufs_mtk_dev_fixups[] = { { .wmanufacturerid = UFS_ANY_VENDOR, @@ -659,7 +666,7 @@ static void ufs_mtk_pwr_ctrl(struct ufs_hba *hba, bool on) * @on: If true, enable clocks else disable them. * @status: PRE_CHANGE or POST_CHANGE notify * - * Returns 0 on success, non-zero on failure. + * Return: 0 on success, non-zero on failure. */ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, enum ufs_notify_change_status status) @@ -840,6 +847,37 @@ static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba) } } +static void ufs_mtk_init_mcq_irq(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct platform_device *pdev; + int i; + int irq; + + host->mcq_nr_intr = UFSHCD_MAX_Q_NR; + pdev = container_of(hba->dev, struct platform_device, dev); + + for (i = 0; i < host->mcq_nr_intr; i++) { + /* irq index 0 is legacy irq, sq/cq irq start from index 1 */ + irq = platform_get_irq(pdev, i + 1); + if (irq < 0) { + host->mcq_intr_info[i].irq = MTK_MCQ_INVALID_IRQ; + goto failed; + } + host->mcq_intr_info[i].hba = hba; + host->mcq_intr_info[i].irq = irq; + dev_info(hba->dev, "get platform mcq irq: %d, %d\n", i, irq); + } + + return; +failed: + /* invalidate irq info */ + for (i = 0; i < host->mcq_nr_intr; i++) + host->mcq_intr_info[i].irq = MTK_MCQ_INVALID_IRQ; + + host->mcq_nr_intr = 0; +} + /** * ufs_mtk_init - find other essential mmio bases * @hba: host controller instance @@ -847,7 +885,7 @@ static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba) * Binds PHY with controller and powers up PHY enabling clocks * and regulators. * - * Returns -EPROBE_DEFER if binding fails, returns negative error + * Return: -EPROBE_DEFER if binding fails, returns negative error * on phy power up failure and returns zero on success. */ static int ufs_mtk_init(struct ufs_hba *hba) @@ -876,6 +914,8 @@ static int ufs_mtk_init(struct ufs_hba *hba) /* Initialize host capability */ ufs_mtk_init_host_caps(hba); + ufs_mtk_init_mcq_irq(hba); + err = ufs_mtk_bind_mphy(hba); if (err) goto out_variant_clear; @@ -1173,7 +1213,17 @@ static int ufs_mtk_link_set_hpm(struct ufs_hba *hba) else return err; - err = ufshcd_make_hba_operational(hba); + if (!hba->mcq_enabled) { + err = ufshcd_make_hba_operational(hba); + } else { + ufs_mtk_config_mcq(hba, false); + ufshcd_mcq_make_queues_operational(hba); + ufshcd_mcq_config_mac(hba, hba->nutrs); + /* Enable MCQ mode */ + ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_MEM_CFG) | 0x1, + REG_UFS_MEM_CFG); + } + if (err) return err; @@ -1497,6 +1547,121 @@ static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up, return 0; } +static int ufs_mtk_get_hba_mac(struct ufs_hba *hba) +{ + return MAX_SUPP_MAC; +} + +static int ufs_mtk_op_runtime_config(struct ufs_hba *hba) +{ + struct ufshcd_mcq_opr_info_t *opr; + int i; + + hba->mcq_opr[OPR_SQD].offset = REG_UFS_MTK_SQD; + hba->mcq_opr[OPR_SQIS].offset = REG_UFS_MTK_SQIS; + hba->mcq_opr[OPR_CQD].offset = REG_UFS_MTK_CQD; + hba->mcq_opr[OPR_CQIS].offset = REG_UFS_MTK_CQIS; + + for (i = 0; i < OPR_MAX; i++) { + opr = &hba->mcq_opr[i]; + opr->stride = REG_UFS_MCQ_STRIDE; + opr->base = hba->mmio_base + opr->offset; + } + + return 0; +} + +static int ufs_mtk_mcq_config_resource(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + /* fail mcq initialization if interrupt is not filled properly */ + if (!host->mcq_nr_intr) { + dev_info(hba->dev, "IRQs not ready. MCQ disabled."); + return -EINVAL; + } + + hba->mcq_base = hba->mmio_base + MCQ_QUEUE_OFFSET(hba->mcq_capabilities); + return 0; +} + +static irqreturn_t ufs_mtk_mcq_intr(int irq, void *__intr_info) +{ + struct ufs_mtk_mcq_intr_info *mcq_intr_info = __intr_info; + struct ufs_hba *hba = mcq_intr_info->hba; + struct ufs_hw_queue *hwq; + u32 events; + int qid = mcq_intr_info->qid; + + hwq = &hba->uhq[qid]; + + events = ufshcd_mcq_read_cqis(hba, qid); + if (events) + ufshcd_mcq_write_cqis(hba, events, qid); + + if (events & UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS) + ufshcd_mcq_poll_cqe_lock(hba, hwq); + + return IRQ_HANDLED; +} + +static int ufs_mtk_config_mcq_irq(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + u32 irq, i; + int ret; + + for (i = 0; i < host->mcq_nr_intr; i++) { + irq = host->mcq_intr_info[i].irq; + if (irq == MTK_MCQ_INVALID_IRQ) { + dev_err(hba->dev, "invalid irq. %d\n", i); + return -ENOPARAM; + } + + host->mcq_intr_info[i].qid = i; + ret = devm_request_irq(hba->dev, irq, ufs_mtk_mcq_intr, 0, UFSHCD, + &host->mcq_intr_info[i]); + + dev_dbg(hba->dev, "request irq %d intr %s\n", irq, ret ? "failed" : ""); + + if (ret) { + dev_err(hba->dev, "Cannot request irq %d\n", ret); + return ret; + } + } + + return 0; +} + +static int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + int ret = 0; + + if (!host->mcq_set_intr) { + /* Disable irq option register */ + ufshcd_rmwl(hba, MCQ_INTR_EN_MSK, 0, REG_UFS_MMIO_OPT_CTRL_0); + + if (irq) { + ret = ufs_mtk_config_mcq_irq(hba); + if (ret) + return ret; + } + + host->mcq_set_intr = true; + } + + ufshcd_rmwl(hba, MCQ_AH8, MCQ_AH8, REG_UFS_MMIO_OPT_CTRL_0); + ufshcd_rmwl(hba, MCQ_INTR_EN_MSK, MCQ_MULTI_INTR_EN, REG_UFS_MMIO_OPT_CTRL_0); + + return 0; +} + +static int ufs_mtk_config_esi(struct ufs_hba *hba) +{ + return ufs_mtk_config_mcq(hba, true); +} + /* * struct ufs_hba_mtk_vops - UFS MTK specific variant operations * @@ -1520,13 +1685,18 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = { .event_notify = ufs_mtk_event_notify, .config_scaling_param = ufs_mtk_config_scaling_param, .clk_scale_notify = ufs_mtk_clk_scale_notify, + /* mcq vops */ + .get_hba_mac = ufs_mtk_get_hba_mac, + .op_runtime_config = ufs_mtk_op_runtime_config, + .mcq_config_resource = ufs_mtk_mcq_config_resource, + .config_esi = ufs_mtk_config_esi, }; /** * ufs_mtk_probe - probe routine of the driver * @pdev: pointer to Platform device handle * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int ufs_mtk_probe(struct platform_device *pdev) { @@ -1566,7 +1736,7 @@ static int ufs_mtk_probe(struct platform_device *pdev) out: if (err) - dev_info(dev, "probe failed %d\n", err); + dev_err(dev, "probe failed %d\n", err); of_node_put(reset_node); return err; diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 2fc6d7b87694ecc06b45728629ba3a4db4b50fad..f76e80d91729c80d0837c5608a8041acea1696a8 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -10,11 +10,27 @@ #include #include +/* + * MCQ define and struct + */ +#define UFSHCD_MAX_Q_NR 8 +#define MTK_MCQ_INVALID_IRQ 0xFFFF + +/* REG_UFS_MMIO_OPT_CTRL_0 160h */ +#define EHS_EN BIT(0) +#define PFM_IMPV BIT(1) +#define MCQ_MULTI_INTR_EN BIT(2) +#define MCQ_CMB_INTR_EN BIT(3) +#define MCQ_AH8 BIT(4) + +#define MCQ_INTR_EN_MSK (MCQ_MULTI_INTR_EN | MCQ_CMB_INTR_EN) + /* * Vendor specific UFSHCI Registers */ #define REG_UFS_XOUFS_CTRL 0x140 #define REG_UFS_REFCLK_CTRL 0x144 +#define REG_UFS_MMIO_OPT_CTRL_0 0x160 #define REG_UFS_EXTREG 0x2100 #define REG_UFS_MPHYCTRL 0x2200 #define REG_UFS_MTK_IP_VER 0x2240 @@ -26,6 +42,13 @@ #define REG_UFS_DEBUG_SEL_B2 0x22D8 #define REG_UFS_DEBUG_SEL_B3 0x22DC +#define REG_UFS_MTK_SQD 0x2800 +#define REG_UFS_MTK_SQIS 0x2814 +#define REG_UFS_MTK_CQD 0x281C +#define REG_UFS_MTK_CQIS 0x2824 + +#define REG_UFS_MCQ_STRIDE 0x30 + /* * Ref-clk control * @@ -136,6 +159,12 @@ struct ufs_mtk_hw_ver { u8 major; }; +struct ufs_mtk_mcq_intr_info { + struct ufs_hba *hba; + u32 irq; + u8 qid; +}; + struct ufs_mtk_host { struct phy *mphy; struct pm_qos_request pm_qos_req; @@ -155,6 +184,10 @@ struct ufs_mtk_host { u16 ref_clk_ungating_wait_us; u16 ref_clk_gating_wait_us; u32 ip_ver; + + bool mcq_set_intr; + int mcq_nr_intr; + struct ufs_mtk_mcq_intr_info mcq_intr_info[UFSHCD_MAX_Q_NR]; }; /* diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index c1557d21b027e0045fa00d78afacfddb45cce2b8..d1149b1c3ed50e4220227ddde7e0f9bc13e883f6 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,49 @@ enum { TSTBUS_MAX, }; +#define QCOM_UFS_MAX_GEAR 4 +#define QCOM_UFS_MAX_LANE 2 + +enum { + MODE_MIN, + MODE_PWM, + MODE_HS_RA, + MODE_HS_RB, + MODE_MAX, +}; + +static const struct __ufs_qcom_bw_table { + u32 mem_bw; + u32 cfg_bw; +} ufs_qcom_bw_table[MODE_MAX + 1][QCOM_UFS_MAX_GEAR + 1][QCOM_UFS_MAX_LANE + 1] = { + [MODE_MIN][0][0] = { 0, 0 }, /* Bandwidth values in KB/s */ + [MODE_PWM][UFS_PWM_G1][UFS_LANE_1] = { 922, 1000 }, + [MODE_PWM][UFS_PWM_G2][UFS_LANE_1] = { 1844, 1000 }, + [MODE_PWM][UFS_PWM_G3][UFS_LANE_1] = { 3688, 1000 }, + [MODE_PWM][UFS_PWM_G4][UFS_LANE_1] = { 7376, 1000 }, + [MODE_PWM][UFS_PWM_G1][UFS_LANE_2] = { 1844, 1000 }, + [MODE_PWM][UFS_PWM_G2][UFS_LANE_2] = { 3688, 1000 }, + [MODE_PWM][UFS_PWM_G3][UFS_LANE_2] = { 7376, 1000 }, + [MODE_PWM][UFS_PWM_G4][UFS_LANE_2] = { 14752, 1000 }, + [MODE_HS_RA][UFS_HS_G1][UFS_LANE_1] = { 127796, 1000 }, + [MODE_HS_RA][UFS_HS_G2][UFS_LANE_1] = { 255591, 1000 }, + [MODE_HS_RA][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, + [MODE_HS_RA][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, + [MODE_HS_RA][UFS_HS_G1][UFS_LANE_2] = { 255591, 1000 }, + [MODE_HS_RA][UFS_HS_G2][UFS_LANE_2] = { 511181, 1000 }, + [MODE_HS_RA][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, + [MODE_HS_RA][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, + [MODE_HS_RB][UFS_HS_G1][UFS_LANE_1] = { 149422, 1000 }, + [MODE_HS_RB][UFS_HS_G2][UFS_LANE_1] = { 298189, 1000 }, + [MODE_HS_RB][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, + [MODE_HS_RB][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, + [MODE_HS_RB][UFS_HS_G1][UFS_LANE_2] = { 298189, 1000 }, + [MODE_HS_RB][UFS_HS_G2][UFS_LANE_2] = { 596378, 1000 }, + [MODE_HS_RB][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, + [MODE_HS_RB][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, + [MODE_MAX][0][0] = { 7643136, 307200 }, +}; + static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); @@ -485,7 +529,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, } /* - * Returns zero for success and non-zero in case of a failure + * Return: zero for success and non-zero in case of a failure. */ static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate, bool update_link_startup_timer) @@ -789,6 +833,51 @@ static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable) } } +static int ufs_qcom_icc_set_bw(struct ufs_qcom_host *host, u32 mem_bw, u32 cfg_bw) +{ + struct device *dev = host->hba->dev; + int ret; + + ret = icc_set_bw(host->icc_ddr, 0, mem_bw); + if (ret < 0) { + dev_err(dev, "failed to set bandwidth request: %d\n", ret); + return ret; + } + + ret = icc_set_bw(host->icc_cpu, 0, cfg_bw); + if (ret < 0) { + dev_err(dev, "failed to set bandwidth request: %d\n", ret); + return ret; + } + + return 0; +} + +static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *host) +{ + struct ufs_pa_layer_attr *p = &host->dev_req_params; + int gear = max_t(u32, p->gear_rx, p->gear_tx); + int lane = max_t(u32, p->lane_rx, p->lane_tx); + + if (ufshcd_is_hs_mode(p)) { + if (p->hs_rate == PA_HS_MODE_B) + return ufs_qcom_bw_table[MODE_HS_RB][gear][lane]; + else + return ufs_qcom_bw_table[MODE_HS_RA][gear][lane]; + } else { + return ufs_qcom_bw_table[MODE_PWM][gear][lane]; + } +} + +static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host) +{ + struct __ufs_qcom_bw_table bw_table; + + bw_table = ufs_qcom_get_bw_table(host); + + return ufs_qcom_icc_set_bw(host, bw_table.mem_bw, bw_table.cfg_bw); +} + static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_change_status status, struct ufs_pa_layer_attr *dev_max_params, @@ -852,6 +941,8 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); + ufs_qcom_icc_update_bw(host); + /* disable the device ref clock if entered PWM mode */ if (ufshcd_is_hs_mode(&hba->pwr_info) && !ufshcd_is_hs_mode(dev_req_params)) @@ -964,7 +1055,7 @@ static void ufs_qcom_set_caps(struct ufs_hba *hba) * @on: If true, enable clocks else disable them. * @status: PRE_CHANGE or POST_CHANGE notify * - * Returns 0 on success, non-zero on failure. + * Return: 0 on success, non-zero on failure. */ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, enum ufs_notify_change_status status) @@ -981,7 +1072,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, switch (status) { case PRE_CHANGE: - if (!on) { + if (on) { + ufs_qcom_icc_update_bw(host); + } else { if (!ufs_qcom_is_link_active(hba)) { /* disable device ref_clk */ ufs_qcom_dev_ref_clk_ctrl(host, false); @@ -993,6 +1086,9 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, /* enable the device ref clock for HS mode*/ if (ufshcd_is_hs_mode(&hba->pwr_info)) ufs_qcom_dev_ref_clk_ctrl(host, true); + } else { + ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].mem_bw, + ufs_qcom_bw_table[MODE_MIN][0][0].cfg_bw); } break; } @@ -1031,6 +1127,34 @@ static const struct reset_control_ops ufs_qcom_reset_ops = { .deassert = ufs_qcom_reset_deassert, }; +static int ufs_qcom_icc_init(struct ufs_qcom_host *host) +{ + struct device *dev = host->hba->dev; + int ret; + + host->icc_ddr = devm_of_icc_get(dev, "ufs-ddr"); + if (IS_ERR(host->icc_ddr)) + return dev_err_probe(dev, PTR_ERR(host->icc_ddr), + "failed to acquire interconnect path\n"); + + host->icc_cpu = devm_of_icc_get(dev, "cpu-ufs"); + if (IS_ERR(host->icc_cpu)) + return dev_err_probe(dev, PTR_ERR(host->icc_cpu), + "failed to acquire interconnect path\n"); + + /* + * Set Maximum bandwidth vote before initializing the UFS controller and + * device. Ideally, a minimal interconnect vote would suffice for the + * initialization, but a max vote would allow faster initialization. + */ + ret = ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MAX][0][0].mem_bw, + ufs_qcom_bw_table[MODE_MAX][0][0].cfg_bw); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to set bandwidth request\n"); + + return 0; +} + /** * ufs_qcom_init - bind phy with controller * @hba: host controller instance @@ -1038,7 +1162,7 @@ static const struct reset_control_ops ufs_qcom_reset_ops = { * Binds PHY with controller and powers up PHY enabling clocks * and regulators. * - * Returns -EPROBE_DEFER if binding fails, returns negative error + * Return: -EPROBE_DEFER if binding fails, returns negative error * on phy power up failure and returns zero on success. */ static int ufs_qcom_init(struct ufs_hba *hba) @@ -1085,6 +1209,10 @@ static int ufs_qcom_init(struct ufs_hba *hba) } } + err = ufs_qcom_icc_init(host); + if (err) + goto out_variant_clear; + host->device_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(host->device_reset)) { @@ -1254,6 +1382,10 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params; int err = 0; + /* check the host controller state before sending hibern8 cmd */ + if (!ufshcd_is_hba_active(hba)) + return 0; + if (status == PRE_CHANGE) { err = ufshcd_uic_hibern8_enter(hba); if (err) @@ -1282,6 +1414,7 @@ static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, dev_req_params->pwr_rx, dev_req_params->hs_rate, false); + ufs_qcom_icc_update_bw(host); ufshcd_uic_hibern8_exit(hba); } @@ -1483,6 +1616,7 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, struct devfreq_simple_ondemand_data *d) { p->polling_ms = 60; + p->timer = DEVFREQ_TIMER_DELAYED; d->upthreshold = 70; d->downdifferential = 5; } @@ -1643,11 +1777,12 @@ static void ufs_qcom_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) ufshcd_mcq_config_esi(hba, msg); } -static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *__hba) +static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *data) { - struct ufs_hba *hba = __hba; - struct ufs_qcom_host *host = ufshcd_get_variant(hba); - u32 id = irq - host->esi_base; + struct msi_desc *desc = data; + struct device *dev = msi_desc_to_dev(desc); + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 id = desc->msi_index; struct ufs_hw_queue *hwq = &hba->uhq[id]; ufshcd_mcq_write_cqis(hba, 0x1, id); @@ -1665,8 +1800,6 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) if (host->esi_enabled) return 0; - else if (host->esi_base < 0) - return -EINVAL; /* * 1. We only handle CQs as of now. @@ -1675,16 +1808,16 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL]; ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs, ufs_qcom_write_msi_msg); - if (ret) + if (ret) { + dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret); goto out; + } + msi_lock_descs(hba->dev); msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) { - if (!desc->msi_index) - host->esi_base = desc->irq; - ret = devm_request_irq(hba->dev, desc->irq, ufs_qcom_mcq_esi_handler, - IRQF_SHARED, "qcom-mcq-esi", hba); + IRQF_SHARED, "qcom-mcq-esi", desc); if (ret) { dev_err(hba->dev, "%s: Fail to request IRQ for %d, err = %d\n", __func__, desc->irq, ret); @@ -1692,14 +1825,17 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) break; } } + msi_unlock_descs(hba->dev); if (ret) { /* Rewind */ + msi_lock_descs(hba->dev); msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) { if (desc == failed_desc) break; devm_free_irq(hba->dev, desc->irq, hba); } + msi_unlock_descs(hba->dev); platform_msi_domain_free_irqs(hba->dev); } else { if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 && @@ -1712,12 +1848,8 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) } out: - if (ret) { - host->esi_base = -1; - dev_warn(hba->dev, "Failed to request Platform MSI %d\n", ret); - } else { + if (!ret) host->esi_enabled = true; - } return ret; } @@ -1757,7 +1889,7 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { * ufs_qcom_probe - probe routine of the driver * @pdev: pointer to Platform device handle * - * Return zero for success and non-zero for failure + * Return: zero for success and non-zero for failure. */ static int ufs_qcom_probe(struct platform_device *pdev) { diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h index 6289ad5a42d0b799eb08663b4f4d6df49b851bff..d6f8e74bd5381c8b7473179fda57b79c7bab607e 100644 --- a/drivers/ufs/host/ufs-qcom.h +++ b/drivers/ufs/host/ufs-qcom.h @@ -206,6 +206,9 @@ struct ufs_qcom_host { struct clk *tx_l1_sync_clk; bool is_lane_clks_enabled; + struct icc_path *icc_ddr; + struct icc_path *icc_cpu; + #ifdef CONFIG_SCSI_UFS_CRYPTO struct qcom_ice *ice; #endif @@ -226,7 +229,6 @@ struct ufs_qcom_host { u32 hs_gear; - int esi_base; bool esi_enabled; }; diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c index ab0652d8705ac913a18186a411ad432a83ab3a9c..cc94970b86c9308fbad8eabd59f54b54107fc76d 100644 --- a/drivers/ufs/host/ufs-renesas.c +++ b/drivers/ufs/host/ufs-renesas.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c index e28a67e1e3145c926f9c4824ef2be5ebd1ecd215..21b1cf912dcc6522f68fa57d7d7913daf17f9186 100644 --- a/drivers/ufs/host/ufshcd-dwc.c +++ b/drivers/ufs/host/ufshcd-dwc.c @@ -34,9 +34,7 @@ int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba, EXPORT_SYMBOL(ufshcd_dwc_dme_set_attrs); /** - * ufshcd_dwc_program_clk_div() - * This function programs the clk divider value. This value is needed to - * provide 1 microsecond tick to unipro layer. + * ufshcd_dwc_program_clk_div() - program clock divider. * @hba: Private Structure pointer * @divider_val: clock divider value to be programmed * @@ -47,11 +45,10 @@ static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val) } /** - * ufshcd_dwc_link_is_up() - * Check if link is up + * ufshcd_dwc_link_is_up() - check if link is up. * @hba: private structure pointer * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) { @@ -68,7 +65,9 @@ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) } /** - * ufshcd_dwc_connection_setup() + * ufshcd_dwc_connection_setup() - configure unipro attributes. + * @hba: pointer to drivers private data + * * This function configures both the local side (host) and the peer side * (device) unipro attributes to establish the connection to application/ * cport. @@ -76,9 +75,7 @@ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba) * have this connection setup on reset. But invoking this function does no * harm and should be fine even working with any ufs device. * - * @hba: pointer to drivers private data - * - * Returns 0 on success non-zero value on failure + * Return: 0 on success non-zero value on failure. */ static int ufshcd_dwc_connection_setup(struct ufs_hba *hba) { @@ -107,12 +104,11 @@ static int ufshcd_dwc_connection_setup(struct ufs_hba *hba) } /** - * ufshcd_dwc_link_startup_notify() - * UFS Host DWC specific link startup sequence + * ufshcd_dwc_link_startup_notify() - program clock divider. * @hba: private structure pointer * @status: Callback notify status * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba, enum ufs_notify_change_status status) diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index cf3987773051f0890945c9d9a10904a1389d037b..248a49e5e7f3570fd2e4d15add6805b062607efc 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -524,7 +524,7 @@ static void ufshcd_pci_remove(struct pci_dev *pdev) * @pdev: pointer to PCI device handle * @id: PCI device id * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ static int ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -590,6 +590,7 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = { }; static const struct pci_device_id ufshcd_pci_tbl[] = { + { PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops }, { PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops }, diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c index 0b7430033047d4ce3afc5b0ccb4fabb627668108..797a4dfe45d908552835f0705449fc7e20466fe1 100644 --- a/drivers/ufs/host/ufshcd-pltfrm.c +++ b/drivers/ufs/host/ufshcd-pltfrm.c @@ -166,6 +166,8 @@ EXPORT_SYMBOL_GPL(ufshcd_populate_vreg); * If any of the supplies are not defined it is assumed that they are always-on * and hence return zero. If the property is defined but parsing is failed * then return corresponding error. + * + * Return: 0 upon success; < 0 upon failure. */ static int ufshcd_parse_regulator_info(struct ufs_hba *hba) { @@ -212,7 +214,7 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) * @dev_max: pointer to device attributes * @agreed_pwr: returned agreed attributes * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *pltfrm_param, const struct ufs_pa_layer_attr *dev_max, @@ -305,8 +307,8 @@ EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param); void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param) { *dev_param = (struct ufs_dev_params){ - .tx_lanes = 2, - .rx_lanes = 2, + .tx_lanes = UFS_LANE_2, + .rx_lanes = UFS_LANE_2, .hs_rx_gear = UFS_HS_G3, .hs_tx_gear = UFS_HS_G3, .pwm_rx_gear = UFS_PWM_G4, @@ -326,7 +328,7 @@ EXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param); * @pdev: pointer to Platform device handle * @vops: pointer to variant ops * - * Returns 0 on success, non-zero value on failure + * Return: 0 on success, non-zero value on failure. */ int ufshcd_pltfrm_init(struct platform_device *pdev, const struct ufs_hba_variant_ops *vops) @@ -373,7 +375,8 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, err = ufshcd_init(hba, mmio_base, irq); if (err) { - dev_err(dev, "Initialization failed\n"); + dev_err_probe(dev, err, "Initialization failed with error %d\n", + err); goto dealloc_host; } diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index ff01f2c17452d80c8192b7a7c6bf63470f742870..6010135e1acce6b10221596e1d71dc74ae781986 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include /* * USB Control Register diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 0c7bf88d4a7fbec2697aabf5e6d3023290d7ffa3..f67733cecfdf5d2b19a1dc8cfb40f78b901bbd11 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -84,6 +84,9 @@ void ucsi_debugfs_register(struct ucsi *ucsi) void ucsi_debugfs_unregister(struct ucsi *ucsi) { + if (IS_ERR_OR_NULL(ucsi) || !ucsi->debugfs) + return; + debugfs_remove_recursive(ucsi->debugfs->dentry); kfree(ucsi->debugfs); } diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index b53420e874acb331d4a9f9bc2bcb2bfb60312fd2..ca56242972b3af646b6808b33d7f7c4947c45b6c 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -100,9 +100,6 @@ struct mlx5_vdpa_dev { bool suspended; }; -int mlx5_vdpa_alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid); -int mlx5_vdpa_dealloc_pd(struct mlx5_vdpa_dev *dev, u32 pdn, u16 uid); -int mlx5_vdpa_get_null_mkey(struct mlx5_vdpa_dev *dev, u32 *null_mkey); int mlx5_vdpa_create_tis(struct mlx5_vdpa_dev *mvdev, void *in, u32 *tisn); void mlx5_vdpa_destroy_tis(struct mlx5_vdpa_dev *mvdev, u32 tisn); int mlx5_vdpa_create_rqt(struct mlx5_vdpa_dev *mvdev, void *in, int inlen, u32 *rqtn); diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index d343af4fa60ee17007ad8f840b73011aed21f085..76d41058add9a8a5f45edb405ed822efba6de67f 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "vdpa_sim.h" @@ -410,6 +411,11 @@ static u64 vdpasim_get_device_features(struct vdpa_device *vdpa) return vdpasim->dev_attr.supported_features; } +static u64 vdpasim_get_backend_features(const struct vdpa_device *vdpa) +{ + return BIT_ULL(VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK); +} + static int vdpasim_set_driver_features(struct vdpa_device *vdpa, u64 features) { struct vdpasim *vdpasim = vdpa_to_sim(vdpa); @@ -733,6 +739,7 @@ static const struct vdpa_config_ops vdpasim_config_ops = { .get_vq_align = vdpasim_get_vq_align, .get_vq_group = vdpasim_get_vq_group, .get_device_features = vdpasim_get_device_features, + .get_backend_features = vdpasim_get_backend_features, .set_driver_features = vdpasim_set_driver_features, .get_driver_features = vdpasim_get_driver_features, .set_config_cb = vdpasim_set_config_cb, @@ -770,6 +777,7 @@ static const struct vdpa_config_ops vdpasim_batch_config_ops = { .get_vq_align = vdpasim_get_vq_align, .get_vq_group = vdpasim_get_vq_group, .get_device_features = vdpasim_get_device_features, + .get_backend_features = vdpasim_get_backend_features, .set_driver_features = vdpasim_set_driver_features, .get_driver_features = vdpasim_get_driver_features, .set_config_cb = vdpasim_set_config_cb, diff --git a/drivers/vfio/mdev/mdev_sysfs.c b/drivers/vfio/mdev/mdev_sysfs.c index e4490639d3833e68a4d0b8c9deb50004dfa84290..9d2738e10c0b9d4d2b1018e4c948bf427d0315a9 100644 --- a/drivers/vfio/mdev/mdev_sysfs.c +++ b/drivers/vfio/mdev/mdev_sysfs.c @@ -233,7 +233,8 @@ int parent_create_sysfs_files(struct mdev_parent *parent) out_err: while (--i >= 0) mdev_type_remove(parent->types[i]); - return 0; + kset_unregister(parent->mdev_types_kset); + return ret; } static ssize_t remove_store(struct device *dev, struct device_attribute *attr, diff --git a/drivers/vfio/pci/pds/Kconfig b/drivers/vfio/pci/pds/Kconfig index 407b3fd3273381ed5173f7e2ca2b83fe8800dc3b..6eceef7b028aae9b8b7a8cb49614e88525f4bade 100644 --- a/drivers/vfio/pci/pds/Kconfig +++ b/drivers/vfio/pci/pds/Kconfig @@ -3,7 +3,7 @@ config PDS_VFIO_PCI tristate "VFIO support for PDS PCI devices" - depends on PDS_CORE + depends on PDS_CORE && PCI_IOV select VFIO_PCI_CORE help This provides generic PCI support for PDS devices using the VFIO diff --git a/drivers/vfio/pci/pds/vfio_dev.c b/drivers/vfio/pci/pds/vfio_dev.c index b46174f5eb0955047081eb800c66ddfc188275e8..649b18ee394bb7932b8a24763a73a010c2d901ec 100644 --- a/drivers/vfio/pci/pds/vfio_dev.c +++ b/drivers/vfio/pci/pds/vfio_dev.c @@ -162,7 +162,7 @@ static int pds_vfio_init_device(struct vfio_device *vdev) pci_id = PCI_DEVID(pdev->bus->number, pdev->devfn); dev_dbg(&pdev->dev, "%s: PF %#04x VF %#04x vf_id %d domain %d pds_vfio %p\n", - __func__, pci_dev_id(pdev->physfn), pci_id, vf_id, + __func__, pci_dev_id(pci_physfn(pdev)), pci_id, vf_id, pci_domain_nr(pdev->bus), pds_vfio); return 0; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index b43e8680eee8dc4a3cef2d612279a98f3096b484..78379ffd23363d99cd4c95daf048d1955fc185f3 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -403,6 +403,17 @@ static long vhost_vdpa_get_features(struct vhost_vdpa *v, u64 __user *featurep) return 0; } +static u64 vhost_vdpa_get_backend_features(const struct vhost_vdpa *v) +{ + struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; + + if (!ops->get_backend_features) + return 0; + else + return ops->get_backend_features(vdpa); +} + static long vhost_vdpa_set_features(struct vhost_vdpa *v, u64 __user *featurep) { struct vdpa_device *vdpa = v->vdpa; @@ -680,7 +691,8 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, return -EFAULT; if (features & ~(VHOST_VDPA_BACKEND_FEATURES | BIT_ULL(VHOST_BACKEND_F_SUSPEND) | - BIT_ULL(VHOST_BACKEND_F_RESUME))) + BIT_ULL(VHOST_BACKEND_F_RESUME) | + BIT_ULL(VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK))) return -EOPNOTSUPP; if ((features & BIT_ULL(VHOST_BACKEND_F_SUSPEND)) && !vhost_vdpa_can_suspend(v)) @@ -741,6 +753,7 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep, features |= BIT_ULL(VHOST_BACKEND_F_SUSPEND); if (vhost_vdpa_can_resume(v)) features |= BIT_ULL(VHOST_BACKEND_F_RESUME); + features |= vhost_vdpa_get_backend_features(v); if (copy_to_user(featurep, &features, sizeof(features))) r = -EFAULT; break; diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c index 955d938eb6633aa840f693f0aa23a1d118515fdc..7b8fd977f71ccf5ad32c026fda42ad44606cfef3 100644 --- a/drivers/vhost/vringh.c +++ b/drivers/vhost/vringh.c @@ -123,8 +123,18 @@ static inline ssize_t vringh_iov_xfer(struct vringh *vrh, done += partlen; len -= partlen; ptr += partlen; + iov->consumed += partlen; + iov->iov[iov->i].iov_len -= partlen; + iov->iov[iov->i].iov_base += partlen; - vringh_kiov_advance(iov, partlen); + if (!iov->iov[iov->i].iov_len) { + /* Fix up old iov element then increment. */ + iov->iov[iov->i].iov_len = iov->consumed; + iov->iov[iov->i].iov_base -= iov->consumed; + + iov->consumed = 0; + iov->i++; + } } return done; } diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index d3bea42407f1593b0b8b855a5fb7ed32b83a5685..d28c30b2a35d23b4d66cc97258f709b4f2b22aa1 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -87,8 +87,7 @@ static int gpio_backlight_probe(struct platform_device *pdev) /* Not booted with device tree or no phandle link to the node */ bl->props.power = def_value ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; - else if (gpiod_get_direction(gbl->gpiod) == 0 && - gpiod_get_value_cansleep(gbl->gpiod) == 0) + else if (gpiod_get_value_cansleep(gbl->gpiod) == 0) bl->props.power = FB_BLANK_POWERDOWN; else bl->props.power = FB_BLANK_UNBLANK; diff --git a/drivers/video/backlight/led_bl.c b/drivers/video/backlight/led_bl.c index 3259292fda766a109160521616a4070cb3cecd3d..032f8bddf8721e6521106a0f89ec05cdc732ccbd 100644 --- a/drivers/video/backlight/led_bl.c +++ b/drivers/video/backlight/led_bl.c @@ -243,7 +243,7 @@ MODULE_DEVICE_TABLE(of, led_bl_of_match); static struct platform_driver led_bl_driver = { .driver = { .name = "led-backlight", - .of_match_table = of_match_ptr(led_bl_of_match), + .of_match_table = led_bl_of_match, }, .probe = led_bl_probe, .remove_new = led_bl_remove, diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c index 1c9e921bca14aa4e501ad3b8b71edbca55096543..da1f124db69c0239b7e293f6a8d9309d46155548 100644 --- a/drivers/video/backlight/lp855x_bl.c +++ b/drivers/video/backlight/lp855x_bl.c @@ -71,6 +71,7 @@ struct lp855x { struct device *dev; struct lp855x_platform_data *pdata; struct pwm_device *pwm; + bool needs_pwm_init; struct regulator *supply; /* regulator for VDD input */ struct regulator *enable; /* regulator for EN/VDDIO input */ }; @@ -216,16 +217,24 @@ static int lp855x_configure(struct lp855x *lp) return ret; } -static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) +static int lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br) { struct pwm_state state; - pwm_get_state(lp->pwm, &state); + if (lp->needs_pwm_init) { + pwm_init_state(lp->pwm, &state); + /* Legacy platform data compatibility */ + if (lp->pdata->period_ns > 0) + state.period = lp->pdata->period_ns; + lp->needs_pwm_init = false; + } else { + pwm_get_state(lp->pwm, &state); + } state.duty_cycle = div_u64(br * state.period, max_br); state.enabled = state.duty_cycle; - pwm_apply_state(lp->pwm, &state); + return pwm_apply_state(lp->pwm, &state); } static int lp855x_bl_update_status(struct backlight_device *bl) @@ -237,11 +246,12 @@ static int lp855x_bl_update_status(struct backlight_device *bl) brightness = 0; if (lp->mode == PWM_BASED) - lp855x_pwm_ctrl(lp, brightness, bl->props.max_brightness); + return lp855x_pwm_ctrl(lp, brightness, + bl->props.max_brightness); else if (lp->mode == REGISTER_BASED) - lp855x_write_byte(lp, lp->cfg->reg_brightness, (u8)brightness); - - return 0; + return lp855x_write_byte(lp, lp->cfg->reg_brightness, + (u8)brightness); + return -EINVAL; } static const struct backlight_ops lp855x_bl_ops = { @@ -387,7 +397,6 @@ static int lp855x_probe(struct i2c_client *cl) const struct i2c_device_id *id = i2c_client_get_device_id(cl); const struct acpi_device_id *acpi_id = NULL; struct device *dev = &cl->dev; - struct pwm_state pwmstate; struct lp855x *lp; int ret; @@ -470,15 +479,11 @@ static int lp855x_probe(struct i2c_client *cl) else return dev_err_probe(dev, ret, "getting PWM\n"); + lp->needs_pwm_init = false; lp->mode = REGISTER_BASED; dev_dbg(dev, "mode: register based\n"); } else { - pwm_init_state(lp->pwm, &pwmstate); - /* Legacy platform data compatibility */ - if (lp->pdata->period_ns > 0) - pwmstate.period = lp->pdata->period_ns; - pwm_apply_state(lp->pwm, &pwmstate); - + lp->needs_pwm_init = true; lp->mode = PWM_BASED; dev_dbg(dev, "mode: PWM based\n"); } diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c index c6996aa288e62b69c282cb41b12ed6015be4f095..10129095a4c1781115798c436d0bbd41556be31d 100644 --- a/drivers/video/backlight/qcom-wled.c +++ b/drivers/video/backlight/qcom-wled.c @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include /* From DT binding */ diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 1b5a319971ed07f148e54d65de16f267a720252f..30577b1d3de590f123aa6af2c58bae1c6fea08de 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -73,6 +73,7 @@ config DUMMY_CONSOLE_ROWS config FRAMEBUFFER_CONSOLE bool "Framebuffer Console support" depends on FB_CORE && !UML + default DRM_FBDEV_EMULATION select VT_HW_CONSOLE_BINDING select CRC32 select FONT_SUPPORT diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index eac0ba39581e3c5bfc7d6d345aee31a17a7e6730..c29754b65c0ec923387cfb8c1b7e0f47557d33cd 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1762,7 +1762,7 @@ config FB_COBALT config FB_SH7760 bool "SH7760/SH7763/SH7720/SH7721 LCDC support" - depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763 \ + depends on FB=y && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763 \ || CPU_SUBTYPE_SH7720 || CPU_SUBTYPE_SH7721) select FB_IOMEM_HELPERS help diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig index baf7e852c75b60972ab24ddc10ac773b1a1dbf96..5ac1b063753110d51e5f6b8348e5edf23338e6db 100644 --- a/drivers/video/fbdev/core/Kconfig +++ b/drivers/video/fbdev/core/Kconfig @@ -28,7 +28,7 @@ config FIRMWARE_EDID config FB_DEVICE bool "Provide legacy /dev/fb* device" depends on FB_CORE - default y + default FB help Say Y here if you want the legacy /dev/fb* device file and interfaces within sysfs anc procfs. It is only required if you diff --git a/drivers/video/fbdev/g364fb.c b/drivers/video/fbdev/g364fb.c index 7a1013b22fa7172ef3c42371aa08f8dfe961306a..ee6fe51e0a6b12c0eadcc607860dfb3e355fef7c 100644 --- a/drivers/video/fbdev/g364fb.c +++ b/drivers/video/fbdev/g364fb.c @@ -112,7 +112,7 @@ static int g364fb_blank(int blank, struct fb_info *info); static const struct fb_ops g364fb_ops = { .owner = THIS_MODULE, - FB_DEFAULT_IOMEM_HELPERS, + FB_DEFAULT_IOMEM_OPS, .fb_setcolreg = g364fb_setcolreg, .fb_pan_display = g364fb_pan_display, .fb_blank = g364fb_blank, diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index c5310eaf8b46819ffd19a4e379eb3fbc9221e4db..51d8f3299c105594487d504115351fa716b7cc01 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -172,6 +172,14 @@ struct vring_virtqueue { /* Host publishes avail event idx */ bool event; + /* Do DMA mapping by driver */ + bool premapped; + + /* Do unmap or not for desc. Just when premapped is False and + * use_dma_api is true, this is true. + */ + bool do_unmap; + /* Head of free buffer list. */ unsigned int free_head; /* Number we've added since last sync. */ @@ -355,10 +363,14 @@ static struct device *vring_dma_dev(const struct vring_virtqueue *vq) } /* Map one sg entry. */ -static dma_addr_t vring_map_one_sg(const struct vring_virtqueue *vq, - struct scatterlist *sg, - enum dma_data_direction direction) +static int vring_map_one_sg(const struct vring_virtqueue *vq, struct scatterlist *sg, + enum dma_data_direction direction, dma_addr_t *addr) { + if (vq->premapped) { + *addr = sg_dma_address(sg); + return 0; + } + if (!vq->use_dma_api) { /* * If DMA is not used, KMSAN doesn't know that the scatterlist @@ -366,7 +378,8 @@ static dma_addr_t vring_map_one_sg(const struct vring_virtqueue *vq, * depending on the direction. */ kmsan_handle_dma(sg_page(sg), sg->offset, sg->length, direction); - return (dma_addr_t)sg_phys(sg); + *addr = (dma_addr_t)sg_phys(sg); + return 0; } /* @@ -374,9 +387,14 @@ static dma_addr_t vring_map_one_sg(const struct vring_virtqueue *vq, * the way it expects (we don't guarantee that the scatterlist * will exist for the lifetime of the mapping). */ - return dma_map_page(vring_dma_dev(vq), + *addr = dma_map_page(vring_dma_dev(vq), sg_page(sg), sg->offset, sg->length, direction); + + if (dma_mapping_error(vring_dma_dev(vq), *addr)) + return -ENOMEM; + + return 0; } static dma_addr_t vring_map_single(const struct vring_virtqueue *vq, @@ -427,7 +445,7 @@ static void vring_unmap_one_split_indirect(const struct vring_virtqueue *vq, { u16 flags; - if (!vq->use_dma_api) + if (!vq->do_unmap) return; flags = virtio16_to_cpu(vq->vq.vdev, desc->flags); @@ -445,18 +463,21 @@ static unsigned int vring_unmap_one_split(const struct vring_virtqueue *vq, struct vring_desc_extra *extra = vq->split.desc_extra; u16 flags; - if (!vq->use_dma_api) - goto out; - flags = extra[i].flags; if (flags & VRING_DESC_F_INDIRECT) { + if (!vq->use_dma_api) + goto out; + dma_unmap_single(vring_dma_dev(vq), extra[i].addr, extra[i].len, (flags & VRING_DESC_F_WRITE) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } else { + if (!vq->do_unmap) + goto out; + dma_unmap_page(vring_dma_dev(vq), extra[i].addr, extra[i].len, @@ -588,8 +609,9 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, for (n = 0; n < out_sgs; n++) { for (sg = sgs[n]; sg; sg = sg_next(sg)) { - dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_TO_DEVICE); - if (vring_mapping_error(vq, addr)) + dma_addr_t addr; + + if (vring_map_one_sg(vq, sg, DMA_TO_DEVICE, &addr)) goto unmap_release; prev = i; @@ -603,8 +625,9 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, } for (; n < (out_sgs + in_sgs); n++) { for (sg = sgs[n]; sg; sg = sg_next(sg)) { - dma_addr_t addr = vring_map_one_sg(vq, sg, DMA_FROM_DEVICE); - if (vring_mapping_error(vq, addr)) + dma_addr_t addr; + + if (vring_map_one_sg(vq, sg, DMA_FROM_DEVICE, &addr)) goto unmap_release; prev = i; @@ -620,7 +643,7 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, } /* Last one doesn't continue. */ desc[prev].flags &= cpu_to_virtio16(_vq->vdev, ~VRING_DESC_F_NEXT); - if (!indirect && vq->use_dma_api) + if (!indirect && vq->do_unmap) vq->split.desc_extra[prev & (vq->split.vring.num - 1)].flags &= ~VRING_DESC_F_NEXT; @@ -629,8 +652,12 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, dma_addr_t addr = vring_map_single( vq, desc, total_sg * sizeof(struct vring_desc), DMA_TO_DEVICE); - if (vring_mapping_error(vq, addr)) + if (vring_mapping_error(vq, addr)) { + if (vq->premapped) + goto free_indirect; + goto unmap_release; + } virtqueue_add_desc_split(_vq, vq->split.vring.desc, head, addr, @@ -696,6 +723,7 @@ static inline int virtqueue_add_split(struct virtqueue *_vq, i = vring_unmap_one_split(vq, i); } +free_indirect: if (indirect) kfree(desc); @@ -774,8 +802,10 @@ static void detach_buf_split(struct vring_virtqueue *vq, unsigned int head, VRING_DESC_F_INDIRECT)); BUG_ON(len == 0 || len % sizeof(struct vring_desc)); - for (j = 0; j < len / sizeof(struct vring_desc); j++) - vring_unmap_one_split_indirect(vq, &indir_desc[j]); + if (vq->do_unmap) { + for (j = 0; j < len / sizeof(struct vring_desc); j++) + vring_unmap_one_split_indirect(vq, &indir_desc[j]); + } kfree(indir_desc); vq->split.desc_state[head].indir_desc = NULL; @@ -1195,17 +1225,20 @@ static void vring_unmap_extra_packed(const struct vring_virtqueue *vq, { u16 flags; - if (!vq->use_dma_api) - return; - flags = extra->flags; if (flags & VRING_DESC_F_INDIRECT) { + if (!vq->use_dma_api) + return; + dma_unmap_single(vring_dma_dev(vq), extra->addr, extra->len, (flags & VRING_DESC_F_WRITE) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } else { + if (!vq->do_unmap) + return; + dma_unmap_page(vring_dma_dev(vq), extra->addr, extra->len, (flags & VRING_DESC_F_WRITE) ? @@ -1218,7 +1251,7 @@ static void vring_unmap_desc_packed(const struct vring_virtqueue *vq, { u16 flags; - if (!vq->use_dma_api) + if (!vq->do_unmap) return; flags = le16_to_cpu(desc->flags); @@ -1279,9 +1312,8 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq, for (n = 0; n < out_sgs + in_sgs; n++) { for (sg = sgs[n]; sg; sg = sg_next(sg)) { - addr = vring_map_one_sg(vq, sg, n < out_sgs ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (vring_mapping_error(vq, addr)) + if (vring_map_one_sg(vq, sg, n < out_sgs ? + DMA_TO_DEVICE : DMA_FROM_DEVICE, &addr)) goto unmap_release; desc[i].flags = cpu_to_le16(n < out_sgs ? @@ -1296,15 +1328,19 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq, addr = vring_map_single(vq, desc, total_sg * sizeof(struct vring_packed_desc), DMA_TO_DEVICE); - if (vring_mapping_error(vq, addr)) + if (vring_mapping_error(vq, addr)) { + if (vq->premapped) + goto free_desc; + goto unmap_release; + } vq->packed.vring.desc[head].addr = cpu_to_le64(addr); vq->packed.vring.desc[head].len = cpu_to_le32(total_sg * sizeof(struct vring_packed_desc)); vq->packed.vring.desc[head].id = cpu_to_le16(id); - if (vq->use_dma_api) { + if (vq->do_unmap) { vq->packed.desc_extra[id].addr = addr; vq->packed.desc_extra[id].len = total_sg * sizeof(struct vring_packed_desc); @@ -1355,6 +1391,7 @@ static int virtqueue_add_indirect_packed(struct vring_virtqueue *vq, for (i = 0; i < err_idx; i++) vring_unmap_desc_packed(vq, &desc[i]); +free_desc: kfree(desc); END_USE(vq); @@ -1426,9 +1463,10 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, c = 0; for (n = 0; n < out_sgs + in_sgs; n++) { for (sg = sgs[n]; sg; sg = sg_next(sg)) { - dma_addr_t addr = vring_map_one_sg(vq, sg, n < out_sgs ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (vring_mapping_error(vq, addr)) + dma_addr_t addr; + + if (vring_map_one_sg(vq, sg, n < out_sgs ? + DMA_TO_DEVICE : DMA_FROM_DEVICE, &addr)) goto unmap_release; flags = cpu_to_le16(vq->packed.avail_used_flags | @@ -1443,7 +1481,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, desc[i].len = cpu_to_le32(sg->length); desc[i].id = cpu_to_le16(id); - if (unlikely(vq->use_dma_api)) { + if (unlikely(vq->do_unmap)) { vq->packed.desc_extra[curr].addr = addr; vq->packed.desc_extra[curr].len = sg->length; vq->packed.desc_extra[curr].flags = @@ -1461,7 +1499,7 @@ static inline int virtqueue_add_packed(struct virtqueue *_vq, } } - if (i < head) + if (i <= head) vq->packed.avail_wrap_counter ^= 1; /* We're using some buffers from the free list. */ @@ -1577,7 +1615,7 @@ static void detach_buf_packed(struct vring_virtqueue *vq, vq->free_head = id; vq->vq.num_free += state->num; - if (unlikely(vq->use_dma_api)) { + if (unlikely(vq->do_unmap)) { curr = id; for (i = 0; i < state->num; i++) { vring_unmap_extra_packed(vq, @@ -1594,7 +1632,7 @@ static void detach_buf_packed(struct vring_virtqueue *vq, if (!desc) return; - if (vq->use_dma_api) { + if (vq->do_unmap) { len = vq->packed.desc_extra[id].len; for (i = 0; i < len / sizeof(struct vring_packed_desc); i++) @@ -2052,6 +2090,8 @@ static struct virtqueue *vring_create_virtqueue_packed( vq->packed_ring = true; vq->dma_dev = dma_dev; vq->use_dma_api = vring_use_dma_api(vdev); + vq->premapped = false; + vq->do_unmap = vq->use_dma_api; vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && !context; @@ -2112,6 +2152,43 @@ static int virtqueue_resize_packed(struct virtqueue *_vq, u32 num) return -ENOMEM; } +static int virtqueue_disable_and_recycle(struct virtqueue *_vq, + void (*recycle)(struct virtqueue *vq, void *buf)) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = vq->vq.vdev; + void *buf; + int err; + + if (!vq->we_own_ring) + return -EPERM; + + if (!vdev->config->disable_vq_and_reset) + return -ENOENT; + + if (!vdev->config->enable_vq_after_reset) + return -ENOENT; + + err = vdev->config->disable_vq_and_reset(_vq); + if (err) + return err; + + while ((buf = virtqueue_detach_unused_buf(_vq)) != NULL) + recycle(_vq, buf); + + return 0; +} + +static int virtqueue_enable_after_reset(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + struct virtio_device *vdev = vq->vq.vdev; + + if (vdev->config->enable_vq_after_reset(_vq)) + return -EBUSY; + + return 0; +} /* * Generic functions and exported symbols. @@ -2237,6 +2314,23 @@ int virtqueue_add_inbuf_ctx(struct virtqueue *vq, } EXPORT_SYMBOL_GPL(virtqueue_add_inbuf_ctx); +/** + * virtqueue_dma_dev - get the dma dev + * @_vq: the struct virtqueue we're talking about. + * + * Returns the dma dev. That can been used for dma api. + */ +struct device *virtqueue_dma_dev(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + if (vq->use_dma_api) + return vring_dma_dev(vq); + else + return NULL; +} +EXPORT_SYMBOL_GPL(virtqueue_dma_dev); + /** * virtqueue_kick_prepare - first half of split virtqueue_kick call. * @_vq: the struct virtqueue @@ -2541,6 +2635,8 @@ static struct virtqueue *__vring_new_virtqueue(unsigned int index, #endif vq->dma_dev = dma_dev; vq->use_dma_api = vring_use_dma_api(vdev); + vq->premapped = false; + vq->do_unmap = vq->use_dma_api; vq->indirect = virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC) && !context; @@ -2619,7 +2715,7 @@ EXPORT_SYMBOL_GPL(vring_create_virtqueue_dma); * virtqueue_resize - resize the vring of vq * @_vq: the struct virtqueue we're talking about. * @num: new ring num - * @recycle: callback for recycle the useless buffer + * @recycle: callback to recycle unused buffers * * When it is really necessary to create a new vring, it will set the current vq * into the reset state. Then call the passed callback to recycle the buffer @@ -2643,13 +2739,8 @@ int virtqueue_resize(struct virtqueue *_vq, u32 num, void (*recycle)(struct virtqueue *vq, void *buf)) { struct vring_virtqueue *vq = to_vvq(_vq); - struct virtio_device *vdev = vq->vq.vdev; - void *buf; int err; - if (!vq->we_own_ring) - return -EPERM; - if (num > vq->vq.num_max) return -E2BIG; @@ -2659,31 +2750,101 @@ int virtqueue_resize(struct virtqueue *_vq, u32 num, if ((vq->packed_ring ? vq->packed.vring.num : vq->split.vring.num) == num) return 0; - if (!vdev->config->disable_vq_and_reset) - return -ENOENT; - - if (!vdev->config->enable_vq_after_reset) - return -ENOENT; - - err = vdev->config->disable_vq_and_reset(_vq); + err = virtqueue_disable_and_recycle(_vq, recycle); if (err) return err; - while ((buf = virtqueue_detach_unused_buf(_vq)) != NULL) - recycle(_vq, buf); - if (vq->packed_ring) err = virtqueue_resize_packed(_vq, num); else err = virtqueue_resize_split(_vq, num); - if (vdev->config->enable_vq_after_reset(_vq)) - return -EBUSY; - - return err; + return virtqueue_enable_after_reset(_vq); } EXPORT_SYMBOL_GPL(virtqueue_resize); +/** + * virtqueue_set_dma_premapped - set the vring premapped mode + * @_vq: the struct virtqueue we're talking about. + * + * Enable the premapped mode of the vq. + * + * The vring in premapped mode does not do dma internally, so the driver must + * do dma mapping in advance. The driver must pass the dma_address through + * dma_address of scatterlist. When the driver got a used buffer from + * the vring, it has to unmap the dma address. + * + * This function must be called immediately after creating the vq, or after vq + * reset, and before adding any buffers to it. + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error. + * 0: success. + * -EINVAL: vring does not use the dma api, so we can not enable premapped mode. + */ +int virtqueue_set_dma_premapped(struct virtqueue *_vq) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + u32 num; + + START_USE(vq); + + num = vq->packed_ring ? vq->packed.vring.num : vq->split.vring.num; + + if (num != vq->vq.num_free) { + END_USE(vq); + return -EINVAL; + } + + if (!vq->use_dma_api) { + END_USE(vq); + return -EINVAL; + } + + vq->premapped = true; + vq->do_unmap = false; + + END_USE(vq); + + return 0; +} +EXPORT_SYMBOL_GPL(virtqueue_set_dma_premapped); + +/** + * virtqueue_reset - detach and recycle all unused buffers + * @_vq: the struct virtqueue we're talking about. + * @recycle: callback to recycle unused buffers + * + * Caller must ensure we don't call this with other virtqueue operations + * at the same time (except where noted). + * + * Returns zero or a negative error. + * 0: success. + * -EBUSY: Failed to sync with device, vq may not work properly + * -ENOENT: Transport or device not supported + * -EPERM: Operation not permitted + */ +int virtqueue_reset(struct virtqueue *_vq, + void (*recycle)(struct virtqueue *vq, void *buf)) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + int err; + + err = virtqueue_disable_and_recycle(_vq, recycle); + if (err) + return err; + + if (vq->packed_ring) + virtqueue_reinit_packed(vq); + else + virtqueue_reinit_split(vq); + + return virtqueue_enable_after_reset(_vq); +} +EXPORT_SYMBOL_GPL(virtqueue_reset); + /* Only available for split ring */ struct virtqueue *vring_new_virtqueue(unsigned int index, unsigned int num, @@ -2945,4 +3106,149 @@ const struct vring *virtqueue_get_vring(const struct virtqueue *vq) } EXPORT_SYMBOL_GPL(virtqueue_get_vring); +/** + * virtqueue_dma_map_single_attrs - map DMA for _vq + * @_vq: the struct virtqueue we're talking about. + * @ptr: the pointer of the buffer to do dma + * @size: the size of the buffer to do dma + * @dir: DMA direction + * @attrs: DMA Attrs + * + * The caller calls this to do dma mapping in advance. The DMA address can be + * passed to this _vq when it is in pre-mapped mode. + * + * return DMA address. Caller should check that by virtqueue_dma_mapping_error(). + */ +dma_addr_t virtqueue_dma_map_single_attrs(struct virtqueue *_vq, void *ptr, + size_t size, + enum dma_data_direction dir, + unsigned long attrs) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + if (!vq->use_dma_api) + return (dma_addr_t)virt_to_phys(ptr); + + return dma_map_single_attrs(vring_dma_dev(vq), ptr, size, dir, attrs); +} +EXPORT_SYMBOL_GPL(virtqueue_dma_map_single_attrs); + +/** + * virtqueue_dma_unmap_single_attrs - unmap DMA for _vq + * @_vq: the struct virtqueue we're talking about. + * @addr: the dma address to unmap + * @size: the size of the buffer + * @dir: DMA direction + * @attrs: DMA Attrs + * + * Unmap the address that is mapped by the virtqueue_dma_map_* APIs. + * + */ +void virtqueue_dma_unmap_single_attrs(struct virtqueue *_vq, dma_addr_t addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + if (!vq->use_dma_api) + return; + + dma_unmap_single_attrs(vring_dma_dev(vq), addr, size, dir, attrs); +} +EXPORT_SYMBOL_GPL(virtqueue_dma_unmap_single_attrs); + +/** + * virtqueue_dma_mapping_error - check dma address + * @_vq: the struct virtqueue we're talking about. + * @addr: DMA address + * + * Returns 0 means dma valid. Other means invalid dma address. + */ +int virtqueue_dma_mapping_error(struct virtqueue *_vq, dma_addr_t addr) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + if (!vq->use_dma_api) + return 0; + + return dma_mapping_error(vring_dma_dev(vq), addr); +} +EXPORT_SYMBOL_GPL(virtqueue_dma_mapping_error); + +/** + * virtqueue_dma_need_sync - check a dma address needs sync + * @_vq: the struct virtqueue we're talking about. + * @addr: DMA address + * + * Check if the dma address mapped by the virtqueue_dma_map_* APIs needs to be + * synchronized + * + * return bool + */ +bool virtqueue_dma_need_sync(struct virtqueue *_vq, dma_addr_t addr) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + + if (!vq->use_dma_api) + return false; + + return dma_need_sync(vring_dma_dev(vq), addr); +} +EXPORT_SYMBOL_GPL(virtqueue_dma_need_sync); + +/** + * virtqueue_dma_sync_single_range_for_cpu - dma sync for cpu + * @_vq: the struct virtqueue we're talking about. + * @addr: DMA address + * @offset: DMA address offset + * @size: buf size for sync + * @dir: DMA direction + * + * Before calling this function, use virtqueue_dma_need_sync() to confirm that + * the DMA address really needs to be synchronized + * + */ +void virtqueue_dma_sync_single_range_for_cpu(struct virtqueue *_vq, + dma_addr_t addr, + unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + struct device *dev = vring_dma_dev(vq); + + if (!vq->use_dma_api) + return; + + dma_sync_single_range_for_cpu(dev, addr, offset, size, + DMA_BIDIRECTIONAL); +} +EXPORT_SYMBOL_GPL(virtqueue_dma_sync_single_range_for_cpu); + +/** + * virtqueue_dma_sync_single_range_for_device - dma sync for device + * @_vq: the struct virtqueue we're talking about. + * @addr: DMA address + * @offset: DMA address offset + * @size: buf size for sync + * @dir: DMA direction + * + * Before calling this function, use virtqueue_dma_need_sync() to confirm that + * the DMA address really needs to be synchronized + */ +void virtqueue_dma_sync_single_range_for_device(struct virtqueue *_vq, + dma_addr_t addr, + unsigned long offset, size_t size, + enum dma_data_direction dir) +{ + struct vring_virtqueue *vq = to_vvq(_vq); + struct device *dev = vring_dma_dev(vq); + + if (!vq->use_dma_api) + return; + + dma_sync_single_range_for_device(dev, addr, offset, size, + DMA_BIDIRECTIONAL); +} +EXPORT_SYMBOL_GPL(virtqueue_dma_sync_single_range_for_device); + MODULE_LICENSE("GPL"); diff --git a/drivers/virtio/virtio_vdpa.c b/drivers/virtio/virtio_vdpa.c index 961161da59000d301deef6db6207e63a34df669a..06ce6d8c2e00473bea0df046f296b3ad17838f65 100644 --- a/drivers/virtio/virtio_vdpa.c +++ b/drivers/virtio/virtio_vdpa.c @@ -366,11 +366,14 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct irq_affinity default_affd = { 0 }; struct cpumask *masks; struct vdpa_callback cb; + bool has_affinity = desc && ops->set_vq_affinity; int i, err, queue_idx = 0; - masks = create_affinity_masks(nvqs, desc ? desc : &default_affd); - if (!masks) - return -ENOMEM; + if (has_affinity) { + masks = create_affinity_masks(nvqs, desc ? desc : &default_affd); + if (!masks) + return -ENOMEM; + } for (i = 0; i < nvqs; ++i) { if (!names[i]) { @@ -386,20 +389,22 @@ static int virtio_vdpa_find_vqs(struct virtio_device *vdev, unsigned int nvqs, goto err_setup_vq; } - if (ops->set_vq_affinity) + if (has_affinity) ops->set_vq_affinity(vdpa, i, &masks[i]); } cb.callback = virtio_vdpa_config_cb; cb.private = vd_dev; ops->set_config_cb(vdpa, &cb); - kfree(masks); + if (has_affinity) + kfree(masks); return 0; err_setup_vq: virtio_vdpa_del_vqs(vdev); - kfree(masks); + if (has_affinity) + kfree(masks); return err; } diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index c1de8a92e144bd0336c28f3e7b2f6b6b21098571..b2d76c1784bd8a27f004ce20d21cfb754311f6ad 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -551,7 +551,7 @@ static struct i2c_driver ds2482_driver = { .driver = { .name = "ds2482", }, - .probe_new = ds2482_probe, + .probe = ds2482_probe, .remove = ds2482_remove, .id_table = ds2482_id, }; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0cbfb496b9c8e546b247c3552aba1550a6728065..751458959411ccee91ff62a2ba6b65378a88d569 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -307,7 +307,7 @@ config XILINX_WATCHDOG config XILINX_WINDOW_WATCHDOG tristate "Xilinx window watchdog timer" depends on HAS_IOMEM - depends on ARM64 + depends on ARM64 || COMPILE_TEST select WATCHDOG_CORE help Window watchdog driver for the versal_wwdt IP core. @@ -343,7 +343,7 @@ config RAVE_SP_WATCHDOG config MLX_WDT tristate "Mellanox Watchdog" - depends on MELLANOX_PLATFORM + depends on MELLANOX_PLATFORM || COMPILE_TEST select WATCHDOG_CORE select REGMAP help @@ -493,7 +493,7 @@ config FTWDT010_WATCHDOG config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" - depends on ARCH_IXP4XX + depends on ARCH_IXP4XX || (ARM && COMPILE_TEST) select WATCHDOG_CORE help Say Y here if to include support for the watchdog timer @@ -529,7 +529,7 @@ config S3C2410_WATCHDOG config SA1100_WATCHDOG tristate "SA1100/PXA2xx watchdog" - depends on ARCH_SA1100 || ARCH_PXA + depends on ARCH_SA1100 || ARCH_PXA || COMPILE_TEST help Watchdog timer embedded into SA11x0 and PXA2xx chips. This will reboot your system when timeout is reached. @@ -720,7 +720,7 @@ config IMX2_WDT config IMX_SC_WDT tristate "IMX SC Watchdog" depends on HAVE_ARM_SMCCC - depends on IMX_SCU + depends on IMX_SCU || COMPILE_TEST select WATCHDOG_CORE help This is the driver for the system controller watchdog @@ -931,7 +931,7 @@ config ASPEED_WATCHDOG config STM32_WATCHDOG tristate "STM32 Independent WatchDoG (IWDG) support" - depends on ARCH_STM32 + depends on ARCH_STM32 || COMPILE_TEST select WATCHDOG_CORE default y help @@ -1065,7 +1065,7 @@ config ACQUIRE_WDT config ADVANTECH_WDT tristate "Advantech SBC Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help If you are configuring a Linux kernel for the Advantech single-board computer, say `Y' here to support its built-in watchdog timer @@ -1074,14 +1074,16 @@ config ADVANTECH_WDT config ADVANTECH_EC_WDT tristate "Advantech Embedded Controller Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST + select ISA_BUS_API + select WATCHDOG_CORE help This driver supports Advantech products with ITE based Embedded Controller. It does not support Advantech products with other ECs or without EC. config ALIM1535_WDT tristate "ALi M1535 PMU Watchdog Timer" - depends on X86 && PCI + depends on (X86 || COMPILE_TEST) && PCI help This is the driver for the hardware watchdog on the ALi M1535 PMU. @@ -1105,7 +1107,7 @@ config ALIM7101_WDT config EBC_C384_WDT tristate "WinSystems EBC-C384 Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST select ISA_BUS_API select WATCHDOG_CORE help @@ -1115,7 +1117,7 @@ config EBC_C384_WDT config EXAR_WDT tristate "Exar Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST select WATCHDOG_CORE help Enables watchdog timer support for the watchdog timer present @@ -1126,7 +1128,7 @@ config EXAR_WDT config F71808E_WDT tristate "Fintek F718xx, F818xx Super I/O Watchdog" - depends on X86 + depends on X86 || COMPILE_TEST select WATCHDOG_CORE help This is the driver for the hardware watchdog on the Fintek F71808E, @@ -1138,7 +1140,7 @@ config F71808E_WDT config SP5100_TCO tristate "AMD/ATI SP5100 TCO Timer/Watchdog" - depends on X86 && PCI + depends on (X86 || COMPILE_TEST) && PCI select WATCHDOG_CORE help Hardware watchdog driver for the AMD/ATI SP5100 chipset. The TCO @@ -1177,7 +1179,7 @@ config SC520_WDT config SBC_FITPC2_WATCHDOG tristate "Compulab SBC-FITPC2 watchdog" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the built-in watchdog timer on the fit-PC2, fit-PC2i, CM-iAM single-board computers made by Compulab. @@ -1202,7 +1204,7 @@ config SBC_FITPC2_WATCHDOG config EUROTECH_WDT tristate "Eurotech CPU-1220/1410 Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help Enable support for the watchdog timer on the Eurotech CPU-1220 and CPU-1410 cards. These are PC/104 SBCs. Spec sheets and product @@ -1210,7 +1212,7 @@ config EUROTECH_WDT config IB700_WDT tristate "IB700 SBC Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the hardware watchdog on the IB700 Single Board Computer produced by TMC Technology (www.tmc-uk.com). This @@ -1227,7 +1229,7 @@ config IB700_WDT config IBMASR tristate "IBM Automatic Server Restart" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the IBM Automatic Server Restart watchdog timer built-in into some eServer xSeries machines. @@ -1237,7 +1239,7 @@ config IBMASR config WAFER_WDT tristate "ICP Single Board Computer Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is a driver for the hardware watchdog on the ICP Single Board Computer. This driver is working on (at least) the following @@ -1259,7 +1261,7 @@ config I6300ESB_WDT config IE6XX_WDT tristate "Intel Atom E6xx Watchdog" - depends on X86 && PCI + depends on (X86 || COMPILE_TEST) && PCI select WATCHDOG_CORE select MFD_CORE select LPC_SCH @@ -1319,7 +1321,7 @@ config ITCO_VENDOR_SUPPORT config IT8712F_WDT tristate "IT8712F (Smart Guardian) Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the built-in watchdog timer on the IT8712F Super I/0 chipset used on many motherboards. @@ -1332,7 +1334,7 @@ config IT8712F_WDT config IT87_WDT tristate "IT87 Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST select WATCHDOG_CORE help This is the driver for the hardware watchdog on the ITE IT8607, @@ -1350,7 +1352,7 @@ config IT87_WDT config HP_WATCHDOG tristate "HP ProLiant iLO2+ Hardware Watchdog Timer" select WATCHDOG_CORE - depends on (ARM64 || X86) && PCI + depends on (ARM64 || X86 || COMPILE_TEST) && PCI help A software monitoring watchdog and NMI handling driver. This driver will detect lockups and provide a stack trace. This is a driver that @@ -1380,7 +1382,7 @@ config KEMPLD_WDT config SC1200_WDT tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" - depends on X86 + depends on X86 || COMPILE_TEST help This is a driver for National Semiconductor PC87307/PC97307 hardware watchdog cards as found on the SC1200. This watchdog is mainly used @@ -1403,7 +1405,7 @@ config SCx200_WDT config PC87413_WDT tristate "NS PC87413 watchdog" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the hardware watchdog on the PC87413 chipset This watchdog simply watches your kernel to make sure it doesn't @@ -1417,7 +1419,7 @@ config PC87413_WDT config NV_TCO tristate "nVidia TCO Timer/Watchdog" - depends on X86 && PCI + depends on (X86 || COMPILE_TEST) && PCI help Hardware driver for the TCO timer built into the nVidia Hub family (such as the MCP51). The TCO (Total Cost of Ownership) timer is a @@ -1446,7 +1448,7 @@ config RDC321X_WDT config 60XX_WDT tristate "SBC-60XX Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This driver can be used with the watchdog timer found on some single board computers, namely the 6010 PII based computer. @@ -1486,7 +1488,7 @@ config SBC7240_WDT config CPU5_WDT tristate "SMA CPU5 Watchdog" - depends on X86 + depends on X86 || COMPILE_TEST help TBD. To compile this driver as a module, choose M here: the @@ -1494,7 +1496,7 @@ config CPU5_WDT config SMSC_SCH311X_WDT tristate "SMSC SCH311X Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the hardware watchdog timer on the SMSC SCH3112, SCH3114 and SCH3116 Super IO chipset @@ -1506,7 +1508,7 @@ config SMSC_SCH311X_WDT config SMSC37B787_WDT tristate "Winbond SMsC37B787 Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the hardware watchdog component on the Winbond SMsC37B787 chipset as used on the NetRunner Mainboard @@ -1526,7 +1528,7 @@ config SMSC37B787_WDT config TQMX86_WDT tristate "TQ-Systems TQMX86 Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST select WATCHDOG_CORE help This is the driver for the hardware watchdog timer in the TQMX86 IO @@ -1539,7 +1541,7 @@ config TQMX86_WDT config VIA_WDT tristate "VIA Watchdog Timer" - depends on X86 && PCI + depends on (X86 || COMPILE_TEST) && PCI select WATCHDOG_CORE help This is the driver for the hardware watchdog timer on VIA @@ -1552,7 +1554,7 @@ config VIA_WDT config W83627HF_WDT tristate "Watchdog timer for W83627HF/W83627DHG and compatibles" - depends on X86 + depends on X86 || COMPILE_TEST select WATCHDOG_CORE help This is the driver for the hardware watchdog on the following @@ -1582,7 +1584,7 @@ config W83627HF_WDT config W83877F_WDT tristate "W83877F (EMACS) Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the hardware watchdog on the W83877F chipset as used in EMACS PC-104 motherboards (and likely others). This @@ -1597,7 +1599,7 @@ config W83877F_WDT config W83977F_WDT tristate "W83977F (PCM-5335) Watchdog Timer" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the hardware watchdog on the W83977F I/O chip as used in AAEON's PCM-5335 SBC (and likely others). This @@ -1610,7 +1612,7 @@ config W83977F_WDT config MACHZ_WDT tristate "ZF MachZ Watchdog" - depends on X86 + depends on X86 || COMPILE_TEST help If you are using a ZF Micro MachZ processor, say Y here, otherwise N. This is the driver for the watchdog timer built-in on that @@ -1623,7 +1625,7 @@ config MACHZ_WDT config SBC_EPX_C3_WATCHDOG tristate "Winsystems SBC EPX-C3 watchdog" - depends on X86 + depends on X86 || COMPILE_TEST help This is the driver for the built-in watchdog timer on the EPX-C3 Single-board computer made by Winsystems, Inc. @@ -1739,7 +1741,7 @@ config INDYDOG config JZ4740_WDT tristate "Ingenic jz4740 SoC hardware watchdog" - depends on MIPS + depends on MIPS || COMPILE_TEST depends on COMMON_CLK select WATCHDOG_CORE select MFD_SYSCON @@ -1798,6 +1800,19 @@ config OCTEON_WDT from the first interrupt, it is then only poked when the device is written. +config MARVELL_GTI_WDT + tristate "Marvell GTI Watchdog driver" + depends on ARCH_THUNDER || (COMPILE_TEST && 64BIT) + default y + select WATCHDOG_CORE + help + Marvell GTI hardware supports watchdog timer. First timeout + works as watchdog pretimeout and installed interrupt handler + will be called on first timeout. Hardware can generate interrupt + to SCP on second timeout but it is not enabled, so second + timeout is ignored. If device poke does not happen then system + will reboot on third timeout. + config BCM2835_WDT tristate "Broadcom BCM2835 hardware watchdog" depends on ARCH_BCM2835 || (OF && COMPILE_TEST) @@ -1823,7 +1838,7 @@ config BCM_KONA_WDT config BCM_KONA_WDT_DEBUG bool "DEBUGFS support for BCM Kona Watchdog" - depends on BCM_KONA_WDT + depends on BCM_KONA_WDT || COMPILE_TEST help If enabled, adds /sys/kernel/debug/bcm_kona_wdt/info which provides access to the driver's internal data structures as well as watchdog @@ -1864,7 +1879,7 @@ config LANTIQ_WDT config LOONGSON1_WDT tristate "Loongson1 SoC hardware watchdog" - depends on MACH_LOONGSON32 + depends on MACH_LOONGSON32 || COMPILE_TEST select WATCHDOG_CORE help Hardware driver for the Loongson1 SoC Watchdog Timer. @@ -1878,7 +1893,7 @@ config RALINK_WDT config GXP_WATCHDOG tristate "HPE GXP watchdog support" - depends on ARCH_HPE_GXP + depends on ARCH_HPE_GXP || COMPILE_TEST select WATCHDOG_CORE help Say Y here to include support for the watchdog timer diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 3633f5b9823627aefcf9e8a7d211a5fd22e5ee00..7eab9de311cb93a3579615ed91dbe2eecaa816dc 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -98,6 +98,7 @@ obj-$(CONFIG_VISCONTI_WATCHDOG) += visconti_wdt.o obj-$(CONFIG_MSC313E_WATCHDOG) += msc313e_wdt.o obj-$(CONFIG_APPLE_WATCHDOG) += apple_wdt.o obj-$(CONFIG_SUNPLUS_WATCHDOG) += sunplus_wdt.o +obj-$(CONFIG_MARVELL_GTI_WDT) += marvell_gti_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/watchdog/armada_37xx_wdt.c b/drivers/watchdog/armada_37xx_wdt.c index e58652939f8a99085ca536ab61a8c662bdbfe1ec..8133a5d05647c2aff136c854eed5a78447b00c26 100644 --- a/drivers/watchdog/armada_37xx_wdt.c +++ b/drivers/watchdog/armada_37xx_wdt.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index d20ec27ba35420686a8c4c9e48231f2c0609ca06..558015f08c7a87909b489d79d34a5f63f7d7a47e 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -26,8 +27,6 @@ #include #include #include -#include -#include #define WDT_DEFAULT_TIME 5 /* seconds */ #define WDT_MAX_TIME 256 /* seconds */ diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 47250f9b68c7d8efe443f6abd170d92ebbf76138..901b94d456dbd2d75a926ee824244c3c187d4772 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/watchdog/ftwdt010_wdt.c b/drivers/watchdog/ftwdt010_wdt.c index 442c5bf63ff4d95723998c854b57609d04b3a558..28f5af752c105929bf04532610c3d1404bd2ef1d 100644 --- a/drivers/watchdog/ftwdt010_wdt.c +++ b/drivers/watchdog/ftwdt010_wdt.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -221,20 +221,18 @@ static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = { ftwdt010_wdt_resume) }; -#ifdef CONFIG_OF static const struct of_device_id ftwdt010_wdt_match[] = { { .compatible = "faraday,ftwdt010" }, { .compatible = "cortina,gemini-watchdog" }, {}, }; MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match); -#endif static struct platform_driver ftwdt010_wdt_driver = { .probe = ftwdt010_wdt_probe, .driver = { .name = "ftwdt010-wdt", - .of_match_table = of_match_ptr(ftwdt010_wdt_match), + .of_match_table = ftwdt010_wdt_match, .pm = &ftwdt010_wdt_dev_pm_ops, }, }; diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 97afc907f6596426aaa17a2a9d142064f5085a5a..6a1db1c783fa7fd7f601fce6215cd08e04ec18b6 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 6fcc3596103ccc572841f54c2d2fb65b2b3fbe88..42e8ffae18dd2febf8f3b6d53ee951f3b5d60455 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -26,8 +26,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -375,7 +374,7 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) */ imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); imx2_wdt_ping(wdog); - dev_crit(&pdev->dev, "Device shutdown: Expect reboot!\n"); + dev_crit(&pdev->dev, "Device shutdown.\n"); } } diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 7ca486794ba7fede52e01a0f7f4181a46bd4061e..c703586c6e5f086bd3f577ad77b80bbb052f30b9 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 9b2173f765c8cf94ccb2d29261332149f57b8375..fb7fae750181bf3738299408241ee0db2f5f8e27 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -203,3 +203,4 @@ module_platform_driver(mid_wdt_driver); MODULE_AUTHOR("David Cohen "); MODULE_DESCRIPTION("Watchdog Driver for Intel MID platform"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:intel_mid_wdt"); diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c index 6fab504af88b520ed3ee6e84c819e632b65b3256..a273b97ebcb4bdcc34fa4245b30dcc82cfc481ea 100644 --- a/drivers/watchdog/lantiq_wdt.c +++ b/drivers/watchdog/lantiq_wdt.c @@ -9,7 +9,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/drivers/watchdog/loongson1_wdt.c b/drivers/watchdog/loongson1_wdt.c index 4ac7810a314d77615333cb354b57d0ef33aa4c10..0587ff44d3a12bebea3b2e6805a09c4653a82262 100644 --- a/drivers/watchdog/loongson1_wdt.c +++ b/drivers/watchdog/loongson1_wdt.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include diff --git a/drivers/watchdog/marvell_gti_wdt.c b/drivers/watchdog/marvell_gti_wdt.c new file mode 100644 index 0000000000000000000000000000000000000000..d7eb8286e11eca75006f5468a1f9b45c1214db67 --- /dev/null +++ b/drivers/watchdog/marvell_gti_wdt.c @@ -0,0 +1,340 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell GTI Watchdog driver + * + * Copyright (C) 2023 Marvell. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Hardware supports following mode of operation: + * 1) Interrupt Only: + * This will generate the interrupt to arm core whenever timeout happens. + * + * 2) Interrupt + del3t (Interrupt to firmware (SCP processor)). + * This will generate interrupt to arm core on 1st timeout happens + * This will generate interrupt to SCP processor on 2nd timeout happens + * + * 3) Interrupt + Interrupt to SCP processor (called delt3t) + reboot. + * This will generate interrupt to arm core on 1st timeout happens + * Will generate interrupt to SCP processor on 2nd timeout happens, + * if interrupt is configured. + * Reboot on 3rd timeout. + * + * Driver will use hardware in mode-3 above so that system can reboot in case + * a hardware hang. Also h/w is configured not to generate SCP interrupt, so + * effectively 2nd timeout is ignored within hardware. + * + * First timeout is effectively watchdog pretimeout. + */ + +/* GTI CWD Watchdog (GTI_CWD_WDOG) Register */ +#define GTI_CWD_WDOG(reg_offset) (0x8 * (reg_offset)) +#define GTI_CWD_WDOG_MODE_INT_DEL3T_RST 0x3 +#define GTI_CWD_WDOG_MODE_MASK GENMASK_ULL(1, 0) +#define GTI_CWD_WDOG_LEN_SHIFT 4 +#define GTI_CWD_WDOG_LEN_MASK GENMASK_ULL(19, 4) +#define GTI_CWD_WDOG_CNT_SHIFT 20 +#define GTI_CWD_WDOG_CNT_MASK GENMASK_ULL(43, 20) + +/* GTI CWD Watchdog Interrupt (GTI_CWD_INT) Register */ +#define GTI_CWD_INT 0x200 +#define GTI_CWD_INT_PENDING_STATUS(bit) BIT_ULL(bit) + +/* GTI CWD Watchdog Interrupt Enable Clear (GTI_CWD_INT_ENA_CLR) Register */ +#define GTI_CWD_INT_ENA_CLR 0x210 +#define GTI_CWD_INT_ENA_CLR_VAL(bit) BIT_ULL(bit) + +/* GTI CWD Watchdog Interrupt Enable Set (GTI_CWD_INT_ENA_SET) Register */ +#define GTI_CWD_INT_ENA_SET 0x218 +#define GTI_CWD_INT_ENA_SET_VAL(bit) BIT_ULL(bit) + +/* GTI CWD Watchdog Poke (GTI_CWD_POKE) Registers */ +#define GTI_CWD_POKE(reg_offset) (0x10000 + 0x8 * (reg_offset)) +#define GTI_CWD_POKE_VAL 1 + +struct gti_match_data { + u32 gti_num_timers; +}; + +static const struct gti_match_data match_data_octeontx2 = { + .gti_num_timers = 54, +}; + +static const struct gti_match_data match_data_cn10k = { + .gti_num_timers = 64, +}; + +struct gti_wdt_priv { + struct watchdog_device wdev; + void __iomem *base; + u32 clock_freq; + struct clk *sclk; + /* wdt_timer_idx used for timer to be used for system watchdog */ + u32 wdt_timer_idx; + const struct gti_match_data *data; +}; + +static irqreturn_t gti_wdt_interrupt(int irq, void *data) +{ + struct watchdog_device *wdev = data; + struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); + + /* Clear Interrupt Pending Status */ + writeq(GTI_CWD_INT_PENDING_STATUS(priv->wdt_timer_idx), + priv->base + GTI_CWD_INT); + + watchdog_notify_pretimeout(wdev); + + return IRQ_HANDLED; +} + +static int gti_wdt_ping(struct watchdog_device *wdev) +{ + struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); + + writeq(GTI_CWD_POKE_VAL, + priv->base + GTI_CWD_POKE(priv->wdt_timer_idx)); + + return 0; +} + +static int gti_wdt_start(struct watchdog_device *wdev) +{ + struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); + u64 regval; + + if (!wdev->pretimeout) + return -EINVAL; + + set_bit(WDOG_HW_RUNNING, &wdev->status); + + /* Clear any pending interrupt */ + writeq(GTI_CWD_INT_PENDING_STATUS(priv->wdt_timer_idx), + priv->base + GTI_CWD_INT); + + /* Enable Interrupt */ + writeq(GTI_CWD_INT_ENA_SET_VAL(priv->wdt_timer_idx), + priv->base + GTI_CWD_INT_ENA_SET); + + /* Set (Interrupt + SCP interrupt (DEL3T) + core domain reset) Mode */ + regval = readq(priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx)); + regval |= GTI_CWD_WDOG_MODE_INT_DEL3T_RST; + writeq(regval, priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx)); + + return 0; +} + +static int gti_wdt_stop(struct watchdog_device *wdev) +{ + struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); + u64 regval; + + /* Disable Interrupt */ + writeq(GTI_CWD_INT_ENA_CLR_VAL(priv->wdt_timer_idx), + priv->base + GTI_CWD_INT_ENA_CLR); + + /* Set GTI_CWD_WDOG.Mode = 0 to stop the timer */ + regval = readq(priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx)); + regval &= ~GTI_CWD_WDOG_MODE_MASK; + writeq(regval, priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx)); + + return 0; +} + +static int gti_wdt_settimeout(struct watchdog_device *wdev, + unsigned int timeout) +{ + struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); + u64 timeout_wdog, regval; + + /* Update new timeout */ + wdev->timeout = timeout; + + /* Pretimeout is 1/3 of timeout */ + wdev->pretimeout = timeout / 3; + + /* Get clock cycles from pretimeout */ + timeout_wdog = (u64)priv->clock_freq * wdev->pretimeout; + + /* Watchdog counts in 1024 cycle steps */ + timeout_wdog = timeout_wdog >> 10; + + /* GTI_CWD_WDOG.CNT: reload counter is 16-bit */ + timeout_wdog = (timeout_wdog + 0xff) >> 8; + if (timeout_wdog >= 0x10000) + timeout_wdog = 0xffff; + + /* + * GTI_CWD_WDOG.LEN is 24bit, lower 8-bits should be zero and + * upper 16-bits are same as GTI_CWD_WDOG.CNT + */ + regval = readq(priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx)); + regval &= GTI_CWD_WDOG_MODE_MASK; + regval |= (timeout_wdog << (GTI_CWD_WDOG_CNT_SHIFT + 8)) | + (timeout_wdog << GTI_CWD_WDOG_LEN_SHIFT); + writeq(regval, priv->base + GTI_CWD_WDOG(priv->wdt_timer_idx)); + + return 0; +} + +static int gti_wdt_set_pretimeout(struct watchdog_device *wdev, + unsigned int timeout) +{ + struct gti_wdt_priv *priv = watchdog_get_drvdata(wdev); + struct watchdog_device *wdog_dev = &priv->wdev; + + /* pretimeout should 1/3 of max_timeout */ + if (timeout * 3 <= wdog_dev->max_timeout) + return gti_wdt_settimeout(wdev, timeout * 3); + + return -EINVAL; +} + +static void gti_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static int gti_wdt_get_cntfrq(struct platform_device *pdev, + struct gti_wdt_priv *priv) +{ + int err; + + priv->sclk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(priv->sclk)) + return PTR_ERR(priv->sclk); + + err = devm_add_action_or_reset(&pdev->dev, + gti_clk_disable_unprepare, priv->sclk); + if (err) + return err; + + priv->clock_freq = clk_get_rate(priv->sclk); + if (!priv->clock_freq) + return -EINVAL; + + return 0; +} + +static const struct watchdog_info gti_wdt_ident = { + .identity = "Marvell GTI watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_PRETIMEOUT | WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | WDIOF_CARDRESET, +}; + +static const struct watchdog_ops gti_wdt_ops = { + .owner = THIS_MODULE, + .start = gti_wdt_start, + .stop = gti_wdt_stop, + .ping = gti_wdt_ping, + .set_timeout = gti_wdt_settimeout, + .set_pretimeout = gti_wdt_set_pretimeout, +}; + +static int gti_wdt_probe(struct platform_device *pdev) +{ + struct gti_wdt_priv *priv; + struct device *dev = &pdev->dev; + struct watchdog_device *wdog_dev; + u64 max_pretimeout; + u32 wdt_idx; + int irq; + int err; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), + "reg property not valid/found\n"); + + err = gti_wdt_get_cntfrq(pdev, priv); + if (err) + return dev_err_probe(&pdev->dev, err, + "GTI clock frequency not valid/found"); + + priv->data = of_device_get_match_data(dev); + + /* default use last timer for watchdog */ + priv->wdt_timer_idx = priv->data->gti_num_timers - 1; + + err = of_property_read_u32(dev->of_node, "marvell,wdt-timer-index", + &wdt_idx); + if (!err) { + if (wdt_idx >= priv->data->gti_num_timers) + return dev_err_probe(&pdev->dev, err, + "GTI wdog timer index not valid"); + + priv->wdt_timer_idx = wdt_idx; + } + + wdog_dev = &priv->wdev; + wdog_dev->info = >i_wdt_ident, + wdog_dev->ops = >i_wdt_ops, + wdog_dev->parent = dev; + /* + * Watchdog counter is 24 bit where lower 8 bits are zeros + * This counter decrements every 1024 clock cycles. + */ + max_pretimeout = (GTI_CWD_WDOG_CNT_MASK >> GTI_CWD_WDOG_CNT_SHIFT); + max_pretimeout &= ~0xFFUL; + max_pretimeout = (max_pretimeout * 1024) / priv->clock_freq; + wdog_dev->pretimeout = max_pretimeout; + + /* Maximum timeout is 3 times the pretimeout */ + wdog_dev->max_timeout = max_pretimeout * 3; + /* Minimum first timeout (pretimeout) is 1, so min_timeout as 3 */ + wdog_dev->min_timeout = 3; + wdog_dev->timeout = wdog_dev->pretimeout; + + watchdog_set_drvdata(wdog_dev, priv); + platform_set_drvdata(pdev, priv); + gti_wdt_settimeout(wdog_dev, wdog_dev->timeout); + watchdog_stop_on_reboot(wdog_dev); + watchdog_stop_on_unregister(wdog_dev); + + err = devm_watchdog_register_device(dev, wdog_dev); + if (err) + return err; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return dev_err_probe(&pdev->dev, irq, "IRQ resource not found\n"); + + err = devm_request_irq(dev, irq, gti_wdt_interrupt, 0, + pdev->name, &priv->wdev); + if (err) + return dev_err_probe(dev, err, "Failed to register interrupt handler\n"); + + dev_info(dev, "Watchdog enabled (timeout=%d sec)\n", wdog_dev->timeout); + return 0; +} + +static const struct of_device_id gti_wdt_of_match[] = { + { .compatible = "marvell,cn9670-wdt", .data = &match_data_octeontx2}, + { .compatible = "marvell,cn10624-wdt", .data = &match_data_cn10k}, + { }, +}; +MODULE_DEVICE_TABLE(of, gti_wdt_of_match); + +static struct platform_driver gti_wdt_driver = { + .driver = { + .name = "gti-wdt", + .of_match_table = gti_wdt_of_match, + }, + .probe = gti_wdt_probe, +}; +module_platform_driver(gti_wdt_driver); + +MODULE_AUTHOR("Bharat Bhushan "); +MODULE_DESCRIPTION("Marvell GTI watchdog driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/menz69_wdt.c b/drivers/watchdog/menz69_wdt.c index 3c98030b9fcd0b5db7eb9a2c53a46c8904107f60..c7de30270043d8a9d89a048c16e0a25fba6ddfa8 100644 --- a/drivers/watchdog/menz69_wdt.c +++ b/drivers/watchdog/menz69_wdt.c @@ -153,7 +153,6 @@ MODULE_DEVICE_TABLE(mcb, men_z069_ids); static struct mcb_driver men_z069_driver = { .driver = { .name = "z069-wdt", - .owner = THIS_MODULE, }, .probe = men_z069_probe, .remove = men_z069_remove, diff --git a/drivers/watchdog/meson_gxbb_wdt.c b/drivers/watchdog/meson_gxbb_wdt.c index 35d80cb39856c137095671339ab65255b749ad77..a48622d11ad728b9f784e716d619113686831a92 100644 --- a/drivers/watchdog/meson_gxbb_wdt.c +++ b/drivers/watchdog/meson_gxbb_wdt.c @@ -22,7 +22,6 @@ #define GXBB_WDT_CTRL_CLKDIV_EN BIT(25) #define GXBB_WDT_CTRL_CLK_EN BIT(24) -#define GXBB_WDT_CTRL_EE_RESET BIT(21) #define GXBB_WDT_CTRL_EN BIT(18) #define GXBB_WDT_CTRL_DIV_MASK (BIT(18) - 1) @@ -45,6 +44,10 @@ struct meson_gxbb_wdt { struct clk *clk; }; +struct wdt_params { + u32 rst; +}; + static int meson_gxbb_wdt_start(struct watchdog_device *wdt_dev) { struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev); @@ -140,8 +143,17 @@ static const struct dev_pm_ops meson_gxbb_wdt_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(meson_gxbb_wdt_suspend, meson_gxbb_wdt_resume) }; +static const struct wdt_params gxbb_params = { + .rst = BIT(21), +}; + +static const struct wdt_params t7_params = { + .rst = BIT(22), +}; + static const struct of_device_id meson_gxbb_wdt_dt_ids[] = { - { .compatible = "amlogic,meson-gxbb-wdt", }, + { .compatible = "amlogic,meson-gxbb-wdt", .data = &gxbb_params, }, + { .compatible = "amlogic,t7-wdt", .data = &t7_params, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, meson_gxbb_wdt_dt_ids); @@ -150,6 +162,7 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct meson_gxbb_wdt *data; + struct wdt_params *params; u32 ctrl_reg; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); @@ -164,6 +177,8 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) if (IS_ERR(data->clk)) return PTR_ERR(data->clk); + params = (struct wdt_params *)of_device_get_match_data(dev); + platform_set_drvdata(pdev, data); data->wdt_dev.parent = dev; @@ -191,7 +206,7 @@ static int meson_gxbb_wdt_probe(struct platform_device *pdev) /* Setup with 1ms timebase */ ctrl_reg |= ((clk_get_rate(data->clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) | - GXBB_WDT_CTRL_EE_RESET | + params->rst | GXBB_WDT_CTRL_CLK_EN | GXBB_WDT_CTRL_CLKDIV_EN; diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index 539feaa1f904dd6a8dd1781d70b3d42c1a1cea59..497496f64f55657efb389c46e9c781203b8f201c 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c @@ -11,11 +11,11 @@ #include #include #include +#include #include #include -#include -#include #include +#include #include #include diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 1c569be72ea29fd90744f51d31f964ba7ff8722f..867f9f311379719f7e647d61589afd08d6513250 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include diff --git a/drivers/watchdog/mtk_wdt.c b/drivers/watchdog/mtk_wdt.c index a9c437598e7eba1480b62577b0b4a91bbec39f69..b2330b16b497a6d2427babbee9e4599367094c4f 100644 --- a/drivers/watchdog/mtk_wdt.c +++ b/drivers/watchdog/mtk_wdt.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c index 2a079ca04aa3bd04762a3c65a2e7d543cae54def..05657dc1d36a0181349d901a607d48e8a81c3998 100644 --- a/drivers/watchdog/of_xilinx_wdt.c +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -10,14 +10,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include /* Register offsets for the Wdt device */ #define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */ diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c index bc4ccddc75a3f0fc8bc8e5c02460d2051636551a..ab0682492c85ad63160bb7da61cc74336698a4e9 100644 --- a/drivers/watchdog/pic32-dmt.c +++ b/drivers/watchdog/pic32-dmt.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c index 6d1a00222991f11ed8122a1a6cacc955d398151e..1d282de312ef1bd683529088fc88456983e8efb9 100644 --- a/drivers/watchdog/pic32-wdt.c +++ b/drivers/watchdog/pic32-wdt.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index a98abd0d31467b9b12db6a8669058f05706b3ee5..782b8c23d99cdcc495d40bafc8692ba7355fc2a3 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -23,8 +23,8 @@ #include #include #include +#include #include -#include #define DRV_NAME "PIKA-WDT" diff --git a/drivers/watchdog/pm8916_wdt.c b/drivers/watchdog/pm8916_wdt.c index f4bfbffaf49ceda5c47da5fa47cc6d70befedc63..f3fcbeb0852cc824fe31289eb9f8a8b63a8ab345 100644 --- a/drivers/watchdog/pm8916_wdt.c +++ b/drivers/watchdog/pm8916_wdt.c @@ -266,7 +266,7 @@ static struct platform_driver pm8916_wdt_driver = { .probe = pm8916_wdt_probe, .driver = { .name = "pm8916-wdt", - .of_match_table = of_match_ptr(pm8916_wdt_id_table), + .of_match_table = pm8916_wdt_id_table, .pm = &pm8916_wdt_pm_ops, }, }; diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c index d776474dcdf34c3953cd98884aab53e5044bc080..9e790f0c2096cdbd5ab349690b036670a0e17216 100644 --- a/drivers/watchdog/qcom-wdt.c +++ b/drivers/watchdog/qcom-wdt.c @@ -11,7 +11,6 @@ #include #include #include -#include enum wdt_reg { WDT_RST, diff --git a/drivers/watchdog/rave-sp-wdt.c b/drivers/watchdog/rave-sp-wdt.c index 2c95615b6354729b9e234a662cbcefa94cbbd89b..5d1c2176d445d76f9a7714f9bd826c25c26711a3 100644 --- a/drivers/watchdog/rave-sp-wdt.c +++ b/drivers/watchdog/rave-sp-wdt.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index c04b383e1712ebc4f831cc6d8fcde68826d3059e..b293792a292a3b33b241df0c8c26ce8575b38c90 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/watchdog/rti_wdt.c b/drivers/watchdog/rti_wdt.c index ce8f18e93aa9d2e729de015b11cb772cc1db1eb9..8e1be7ba010398907f1fbae32a89c382dae70ca0 100644 --- a/drivers/watchdog/rti_wdt.c +++ b/drivers/watchdog/rti_wdt.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -52,6 +54,11 @@ #define DWDST BIT(1) +#define PON_REASON_SOF_NUM 0xBBBBCCCC +#define PON_REASON_MAGIC_NUM 0xDDDDDDDD +#define PON_REASON_EOF_NUM 0xCCCCBBBB +#define RESERVED_MEM_MIN_SIZE 12 + static int heartbeat = DEFAULT_HEARTBEAT; /* @@ -198,6 +205,11 @@ static int rti_wdt_probe(struct platform_device *pdev) struct rti_wdt_device *wdt; struct clk *clk; u32 last_ping = 0; + struct device_node *node; + u32 reserved_mem_size; + struct resource res; + u32 *vaddr; + u64 paddr; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -284,6 +296,42 @@ static int rti_wdt_probe(struct platform_device *pdev) } } + node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); + if (node) { + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(dev, "No memory address assigned to the region.\n"); + goto err_iomap; + } + + /* + * If reserved memory is defined for watchdog reset cause. + * Readout the Power-on(PON) reason and pass to bootstatus. + */ + paddr = res.start; + reserved_mem_size = resource_size(&res); + if (reserved_mem_size < RESERVED_MEM_MIN_SIZE) { + dev_err(dev, "The size of reserved memory is too small.\n"); + ret = -EINVAL; + goto err_iomap; + } + + vaddr = memremap(paddr, reserved_mem_size, MEMREMAP_WB); + if (!vaddr) { + dev_err(dev, "Failed to map memory-region.\n"); + ret = -ENOMEM; + goto err_iomap; + } + + if (vaddr[0] == PON_REASON_SOF_NUM && + vaddr[1] == PON_REASON_MAGIC_NUM && + vaddr[2] == PON_REASON_EOF_NUM) { + wdd->bootstatus |= WDIOF_CARDRESET; + } + memset(vaddr, 0, reserved_mem_size); + memunmap(vaddr); + } + watchdog_init_timeout(wdd, heartbeat, dev); ret = watchdog_register_device(wdd); diff --git a/drivers/watchdog/rza_wdt.c b/drivers/watchdog/rza_wdt.c index fe6c2ed35e04cc4bb403085c1f7c4a4bfe841cdf..cb4901b3f7778914995f0339213bd04f9fae0d51 100644 --- a/drivers/watchdog/rza_wdt.c +++ b/drivers/watchdog/rza_wdt.c @@ -9,9 +9,9 @@ #include #include #include +#include #include -#include -#include +#include #include #include diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c index d404953d0e0f44c7ef45138420bba384eba39c5c..1741f98ca67c527a326d3376a667e6403ea4380f 100644 --- a/drivers/watchdog/rzg2l_wdt.c +++ b/drivers/watchdog/rzg2l_wdt.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 95416a9bdd4b466752d93362fb50bbf9756ac44d..0b4bd883ff28a8a03572c86b0b2717aaba9d58bc 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -379,10 +378,11 @@ static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en) static int s3c2410wdt_keepalive(struct watchdog_device *wdd) { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long flags; - spin_lock(&wdt->lock); + spin_lock_irqsave(&wdt->lock, flags); writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); - spin_unlock(&wdt->lock); + spin_unlock_irqrestore(&wdt->lock, flags); return 0; } @@ -399,10 +399,11 @@ static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt) static int s3c2410wdt_stop(struct watchdog_device *wdd) { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long flags; - spin_lock(&wdt->lock); + spin_lock_irqsave(&wdt->lock, flags); __s3c2410wdt_stop(wdt); - spin_unlock(&wdt->lock); + spin_unlock_irqrestore(&wdt->lock, flags); return 0; } @@ -411,8 +412,9 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) { unsigned long wtcon; struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long flags; - spin_lock(&wdt->lock); + spin_lock_irqsave(&wdt->lock, flags); __s3c2410wdt_stop(wdt); @@ -433,7 +435,7 @@ static int s3c2410wdt_start(struct watchdog_device *wdd) writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); writel(wtcon, wdt->reg_base + S3C2410_WTCON); - spin_unlock(&wdt->lock); + spin_unlock_irqrestore(&wdt->lock, flags); return 0; } diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c index aeee934ca51bf140d9af3982e2a345134427d792..13e72918338a20c1775d647036c78e6520390b72 100644 --- a/drivers/watchdog/sama5d4_wdt.c +++ b/drivers/watchdog/sama5d4_wdt.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -255,6 +254,7 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) struct sama5d4_wdt *wdt; void __iomem *regs; u32 irq = 0; + u32 reg; int ret; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); @@ -305,6 +305,12 @@ static int sama5d4_wdt_probe(struct platform_device *pdev) watchdog_init_timeout(wdd, wdt_timeout, dev); + reg = wdt_read(wdt, AT91_WDT_MR); + if (!(reg & AT91_WDT_WDDIS)) { + wdt->mr &= ~AT91_WDT_WDDIS; + set_bit(WDOG_HW_RUNNING, &wdd->status); + } + ret = sama5d4_wdt_init(wdt); if (ret) return ret; diff --git a/drivers/watchdog/sbsa_gwdt.c b/drivers/watchdog/sbsa_gwdt.c index fd3cfdda49491ffe6b3124c946e746581ae19bde..421ebcda62e645af6808a16bbb2aa4a3a9b8066a 100644 --- a/drivers/watchdog/sbsa_gwdt.c +++ b/drivers/watchdog/sbsa_gwdt.c @@ -43,10 +43,9 @@ #include #include #include +#include #include #include -#include -#include #include #include #include diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c index 8058fca4d05d3709d100f7b81dfc0dd023c21c2a..5f501b41faf9d98524b525d5aef62ce6ec2e3181 100644 --- a/drivers/watchdog/starfive-wdt.c +++ b/drivers/watchdog/starfive-wdt.c @@ -8,7 +8,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -526,7 +527,6 @@ static void starfive_wdt_shutdown(struct platform_device *pdev) starfive_wdt_pm_stop(&wdt->wdd); } -#ifdef CONFIG_PM_SLEEP static int starfive_wdt_suspend(struct device *dev) { struct starfive_wdt *wdt = dev_get_drvdata(dev); @@ -556,9 +556,7 @@ static int starfive_wdt_resume(struct device *dev) return starfive_wdt_start(wdt); } -#endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM static int starfive_wdt_runtime_suspend(struct device *dev) { struct starfive_wdt *wdt = dev_get_drvdata(dev); @@ -574,11 +572,10 @@ static int starfive_wdt_runtime_resume(struct device *dev) return starfive_wdt_enable_clock(wdt); } -#endif /* CONFIG_PM */ static const struct dev_pm_ops starfive_wdt_pm_ops = { - SET_RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) + RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) }; static const struct of_device_id starfive_wdt_match[] = { @@ -594,7 +591,7 @@ static struct platform_driver starfive_wdt_driver = { .shutdown = starfive_wdt_shutdown, .driver = { .name = "starfive-wdt", - .pm = &starfive_wdt_pm_ops, + .pm = pm_ptr(&starfive_wdt_pm_ops), .of_match_table = starfive_wdt_match, }, }; diff --git a/drivers/watchdog/stm32_iwdg.c b/drivers/watchdog/stm32_iwdg.c index 570a71509d2a9b0070494de4419b3b6df862a51a..d9fd50df9802ce036b9c49d249da043fc4cef232 100644 --- a/drivers/watchdog/stm32_iwdg.c +++ b/drivers/watchdog/stm32_iwdg.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -288,7 +287,7 @@ static struct platform_driver stm32_iwdg_driver = { .probe = stm32_iwdg_probe, .driver = { .name = "iwdg", - .of_match_table = of_match_ptr(stm32_iwdg_of_match), + .of_match_table = stm32_iwdg_of_match, }, }; module_platform_driver(stm32_iwdg_driver); diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c index 6cf82922d3fb56c1b7facb602733cbdda0cc2180..b85354a995826e13d89503436bcde6dc6d06d374 100644 --- a/drivers/watchdog/sunxi_wdt.c +++ b/drivers/watchdog/sunxi_wdt.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index d4c5a736fdcb6950eb6793e688b0500da9b9820c..5b55ccae06d4b02a32637c4cd4662d7f1917863b 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -161,7 +161,7 @@ static int watchdog_reboot_notifier(struct notifier_block *nb, struct watchdog_device *wdd; wdd = container_of(nb, struct watchdog_device, reboot_nb); - if (code == SYS_DOWN || code == SYS_HALT) { + if (code == SYS_DOWN || code == SYS_HALT || code == SYS_POWER_OFF) { if (watchdog_hw_running(wdd)) { int ret; diff --git a/drivers/watchdog/xilinx_wwdt.c b/drivers/watchdog/xilinx_wwdt.c index 2585038d557589d4d0fb43bc055ef0604a84e57e..d271e2e8d6e27141e4112edddcb58d72556509bb 100644 --- a/drivers/watchdog/xilinx_wwdt.c +++ b/drivers/watchdog/xilinx_wwdt.c @@ -9,9 +9,10 @@ #include #include #include +#include +#include #include -#include -#include +#include #include /* Max timeout is calculated at 100MHz source clock */ @@ -71,7 +72,7 @@ static int xilinx_wwdt_start(struct watchdog_device *wdd) /* Calculate timeout count */ time_out = xdev->freq * wdd->timeout; - closed_timeout = (time_out * xdev->close_percent) / 100; + closed_timeout = div_u64(time_out * xdev->close_percent, 100); open_timeout = time_out - closed_timeout; wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout; diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 3bdd5b59661de53300744be47abc5cb6cb6722ee..1b2136fe0fa51901123f928ed99a56facb19a9ca 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,7 @@ enum xen_irq_type { struct irq_info { struct list_head list; struct list_head eoi_list; + struct rcu_work rwork; short refcnt; u8 spurious_cnt; u8 is_accounted; @@ -146,23 +148,13 @@ const struct evtchn_ops *evtchn_ops; */ static DEFINE_MUTEX(irq_mapping_update_lock); -/* - * Lock protecting event handling loop against removing event channels. - * Adding of event channels is no issue as the associated IRQ becomes active - * only after everything is setup (before request_[threaded_]irq() the handler - * can't be entered for an event, as the event channel will be unmasked only - * then). - */ -static DEFINE_RWLOCK(evtchn_rwlock); - /* * Lock hierarchy: * * irq_mapping_update_lock - * evtchn_rwlock - * IRQ-desc lock - * percpu eoi_list_lock - * irq_info->lock + * IRQ-desc lock + * percpu eoi_list_lock + * irq_info->lock */ static LIST_HEAD(xen_irq_list_head); @@ -306,6 +298,22 @@ static void channels_on_cpu_inc(struct irq_info *info) info->is_accounted = 1; } +static void delayed_free_irq(struct work_struct *work) +{ + struct irq_info *info = container_of(to_rcu_work(work), struct irq_info, + rwork); + unsigned int irq = info->irq; + + /* Remove the info pointer only now, with no potential users left. */ + set_info_for_irq(irq, NULL); + + kfree(info); + + /* Legacy IRQ descriptors are managed by the arch. */ + if (irq >= nr_legacy_irqs()) + irq_free_desc(irq); +} + /* Constructors for packed IRQ information. */ static int xen_irq_info_common_setup(struct irq_info *info, unsigned irq, @@ -668,33 +676,36 @@ static void xen_irq_lateeoi_worker(struct work_struct *work) eoi = container_of(to_delayed_work(work), struct lateeoi_work, delayed); - read_lock_irqsave(&evtchn_rwlock, flags); + rcu_read_lock(); while (true) { - spin_lock(&eoi->eoi_list_lock); + spin_lock_irqsave(&eoi->eoi_list_lock, flags); info = list_first_entry_or_null(&eoi->eoi_list, struct irq_info, eoi_list); - if (info == NULL || now < info->eoi_time) { - spin_unlock(&eoi->eoi_list_lock); + if (info == NULL) + break; + + if (now < info->eoi_time) { + mod_delayed_work_on(info->eoi_cpu, system_wq, + &eoi->delayed, + info->eoi_time - now); break; } list_del_init(&info->eoi_list); - spin_unlock(&eoi->eoi_list_lock); + spin_unlock_irqrestore(&eoi->eoi_list_lock, flags); info->eoi_time = 0; xen_irq_lateeoi_locked(info, false); } - if (info) - mod_delayed_work_on(info->eoi_cpu, system_wq, - &eoi->delayed, info->eoi_time - now); + spin_unlock_irqrestore(&eoi->eoi_list_lock, flags); - read_unlock_irqrestore(&evtchn_rwlock, flags); + rcu_read_unlock(); } static void xen_cpu_init_eoi(unsigned int cpu) @@ -709,16 +720,15 @@ static void xen_cpu_init_eoi(unsigned int cpu) void xen_irq_lateeoi(unsigned int irq, unsigned int eoi_flags) { struct irq_info *info; - unsigned long flags; - read_lock_irqsave(&evtchn_rwlock, flags); + rcu_read_lock(); info = info_for_irq(irq); if (info) xen_irq_lateeoi_locked(info, eoi_flags & XEN_EOI_FLAG_SPURIOUS); - read_unlock_irqrestore(&evtchn_rwlock, flags); + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(xen_irq_lateeoi); @@ -732,6 +742,7 @@ static void xen_irq_init(unsigned irq) info->type = IRQT_UNBOUND; info->refcnt = -1; + INIT_RCU_WORK(&info->rwork, delayed_free_irq); set_info_for_irq(irq, info); /* @@ -789,31 +800,18 @@ static int __must_check xen_allocate_irq_gsi(unsigned gsi) static void xen_free_irq(unsigned irq) { struct irq_info *info = info_for_irq(irq); - unsigned long flags; if (WARN_ON(!info)) return; - write_lock_irqsave(&evtchn_rwlock, flags); - if (!list_empty(&info->eoi_list)) lateeoi_list_del(info); list_del(&info->list); - set_info_for_irq(irq, NULL); - WARN_ON(info->refcnt > 0); - write_unlock_irqrestore(&evtchn_rwlock, flags); - - kfree(info); - - /* Legacy IRQ descriptors are managed by the arch. */ - if (irq < nr_legacy_irqs()) - return; - - irq_free_desc(irq); + queue_rcu_work(system_wq, &info->rwork); } /* Not called for lateeoi events. */ @@ -1704,14 +1702,21 @@ void handle_irq_for_port(evtchn_port_t port, struct evtchn_loop_ctrl *ctrl) generic_handle_irq(irq); } -static int __xen_evtchn_do_upcall(void) +int xen_evtchn_do_upcall(void) { struct vcpu_info *vcpu_info = __this_cpu_read(xen_vcpu); int ret = vcpu_info->evtchn_upcall_pending ? IRQ_HANDLED : IRQ_NONE; int cpu = smp_processor_id(); struct evtchn_loop_ctrl ctrl = { 0 }; - read_lock(&evtchn_rwlock); + /* + * When closing an event channel the associated IRQ must not be freed + * until all cpus have left the event handling loop. This is ensured + * by taking the rcu_read_lock() while handling events, as freeing of + * the IRQ is handled via queue_rcu_work() _after_ closing the event + * channel. + */ + rcu_read_lock(); do { vcpu_info->evtchn_upcall_pending = 0; @@ -1724,7 +1729,7 @@ static int __xen_evtchn_do_upcall(void) } while (vcpu_info->evtchn_upcall_pending); - read_unlock(&evtchn_rwlock); + rcu_read_unlock(); /* * Increment irq_epoch only now to defer EOIs only for @@ -1735,24 +1740,7 @@ static int __xen_evtchn_do_upcall(void) return ret; } - -void xen_evtchn_do_upcall(struct pt_regs *regs) -{ - struct pt_regs *old_regs = set_irq_regs(regs); - - irq_enter(); - - __xen_evtchn_do_upcall(); - - irq_exit(); - set_irq_regs(old_regs); -} - -int xen_hvm_evtchn_do_upcall(void) -{ - return __xen_evtchn_do_upcall(); -} -EXPORT_SYMBOL_GPL(xen_hvm_evtchn_do_upcall); +EXPORT_SYMBOL_GPL(xen_evtchn_do_upcall); /* Rebind a new event channel to an existing irq. */ void rebind_evtchn_irq(evtchn_port_t evtchn, int irq) diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c index fcc8191315723c6701ec5e7c5b97a73374b899a8..544d3f9010b92aca1e65a5c192b3ff79e6c19816 100644 --- a/drivers/xen/platform-pci.c +++ b/drivers/xen/platform-pci.c @@ -64,7 +64,7 @@ static uint64_t get_callback_via(struct pci_dev *pdev) static irqreturn_t do_hvm_evtchn_intr(int irq, void *dev_id) { - return xen_hvm_evtchn_do_upcall(); + return xen_evtchn_do_upcall(); } static int xen_allocate_irq(struct pci_dev *pdev) diff --git a/fs/aio.c b/fs/aio.c index a4c2a6bac72ce9976b6b0ea20b1d9213cc4c2af8..f8589caef9c10ec829bc6470cab5ce159915114c 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -80,7 +80,7 @@ struct aio_ring { struct kioctx_table { struct rcu_head rcu; unsigned nr; - struct kioctx __rcu *table[]; + struct kioctx __rcu *table[] __counted_by(nr); }; struct kioctx_cpu { diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 43b2a2851ba30b724d459c08d754bad225ac23cb..206812ce544aebbf131c5647808774ceb5afb015 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -345,10 +345,9 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) /* there's now no turning back... the old userspace image is dead, * defunct, deceased, etc. */ + SET_PERSONALITY(exec_params.hdr); if (elf_check_fdpic(&exec_params.hdr)) - set_personality(PER_LINUX_FDPIC); - else - set_personality(PER_LINUX); + current->personality |= PER_LINUX_FDPIC; if (elf_read_implies_exec(&exec_params.hdr, executable_stack)) current->personality |= READ_IMPLIES_EXEC; diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 3282adc84d5215bf6439faca37874a6af22c3096..a25c9910d90b2010531b1a84b3bd2b98268fe9c6 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -31,7 +31,7 @@ config BTRFS_FS continue to be mountable and usable by newer kernels. For more information, please see the web pages at - http://btrfs.wiki.kernel.org. + https://btrfs.readthedocs.io To compile this file system support as a module, choose M here. The module will be called btrfs. diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 0cb1dee965a02ab8b8866856bd5355053af3b88e..b2e5107b7cecc4021bea90b5a67fd41142415f79 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -3028,8 +3028,16 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); fail: btrfs_release_path(path); - /* We didn't update the block group item, need to revert @commit_used. */ - if (ret < 0) { + /* + * We didn't update the block group item, need to revert commit_used + * unless the block group item didn't exist yet - this is to prevent a + * race with a concurrent insertion of the block group item, with + * insert_block_group_item(), that happened just after we attempted to + * update. In that case we would reset commit_used to 0 just after the + * insertion set it to a value greater than 0 - if the block group later + * becomes with 0 used bytes, we would incorrectly skip its update. + */ + if (ret < 0 && ret != -ENOENT) { spin_lock(&cache->lock); cache->commit_used = old_commit_used; spin_unlock(&cache->lock); diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index a4cb4b6429870169eadbef99d359cdab4d84db0c..da519c1b6ad086785f90ea1369dd781ac28bec5d 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -682,18 +682,30 @@ noinline int btrfs_cow_block(struct btrfs_trans_handle *trans, u64 search_start; int ret; - if (test_bit(BTRFS_ROOT_DELETING, &root->state)) - btrfs_err(fs_info, - "COW'ing blocks on a fs root that's being dropped"); - - if (trans->transaction != fs_info->running_transaction) - WARN(1, KERN_CRIT "trans %llu running %llu\n", - trans->transid, - fs_info->running_transaction->transid); + if (unlikely(test_bit(BTRFS_ROOT_DELETING, &root->state))) { + btrfs_abort_transaction(trans, -EUCLEAN); + btrfs_crit(fs_info, + "attempt to COW block %llu on root %llu that is being deleted", + buf->start, btrfs_root_id(root)); + return -EUCLEAN; + } - if (trans->transid != fs_info->generation) - WARN(1, KERN_CRIT "trans %llu running %llu\n", - trans->transid, fs_info->generation); + /* + * COWing must happen through a running transaction, which always + * matches the current fs generation (it's a transaction with a state + * less than TRANS_STATE_UNBLOCKED). If it doesn't, then turn the fs + * into error state to prevent the commit of any transaction. + */ + if (unlikely(trans->transaction != fs_info->running_transaction || + trans->transid != fs_info->generation)) { + btrfs_abort_transaction(trans, -EUCLEAN); + btrfs_crit(fs_info, +"unexpected transaction when attempting to COW block %llu on root %llu, transaction %llu running transaction %llu fs generation %llu", + buf->start, btrfs_root_id(root), trans->transid, + fs_info->running_transaction->transid, + fs_info->generation); + return -EUCLEAN; + } if (!should_cow_block(trans, root, buf)) { *cow_ret = buf; @@ -805,8 +817,22 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans, int progress_passed = 0; struct btrfs_disk_key disk_key; - WARN_ON(trans->transaction != fs_info->running_transaction); - WARN_ON(trans->transid != fs_info->generation); + /* + * COWing must happen through a running transaction, which always + * matches the current fs generation (it's a transaction with a state + * less than TRANS_STATE_UNBLOCKED). If it doesn't, then turn the fs + * into error state to prevent the commit of any transaction. + */ + if (unlikely(trans->transaction != fs_info->running_transaction || + trans->transid != fs_info->generation)) { + btrfs_abort_transaction(trans, -EUCLEAN); + btrfs_crit(fs_info, +"unexpected transaction when attempting to reallocate parent %llu for root %llu, transaction %llu running transaction %llu fs generation %llu", + parent->start, btrfs_root_id(root), trans->transid, + fs_info->running_transaction->transid, + fs_info->generation); + return -EUCLEAN; + } parent_nritems = btrfs_header_nritems(parent); blocksize = fs_info->nodesize; diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 53c1211dd60bab23b4574820a1b3a70f2b2c8c15..caf0bbd028d11070ea8d87524c9e8c670e7d905d 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -412,6 +412,7 @@ static void finish_one_item(struct btrfs_delayed_root *delayed_root) static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) { + struct btrfs_delayed_node *delayed_node = delayed_item->delayed_node; struct rb_root_cached *root; struct btrfs_delayed_root *delayed_root; @@ -419,18 +420,21 @@ static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) if (RB_EMPTY_NODE(&delayed_item->rb_node)) return; - delayed_root = delayed_item->delayed_node->root->fs_info->delayed_root; + /* If it's in a rbtree, then we need to have delayed node locked. */ + lockdep_assert_held(&delayed_node->mutex); + + delayed_root = delayed_node->root->fs_info->delayed_root; BUG_ON(!delayed_root); if (delayed_item->type == BTRFS_DELAYED_INSERTION_ITEM) - root = &delayed_item->delayed_node->ins_root; + root = &delayed_node->ins_root; else - root = &delayed_item->delayed_node->del_root; + root = &delayed_node->del_root; rb_erase_cached(&delayed_item->rb_node, root); RB_CLEAR_NODE(&delayed_item->rb_node); - delayed_item->delayed_node->count--; + delayed_node->count--; finish_one_item(delayed_root); } @@ -1153,20 +1157,33 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr) ret = __btrfs_commit_inode_delayed_items(trans, path, curr_node); if (ret) { - btrfs_release_delayed_node(curr_node); - curr_node = NULL; btrfs_abort_transaction(trans, ret); break; } prev_node = curr_node; curr_node = btrfs_next_delayed_node(curr_node); + /* + * See the comment below about releasing path before releasing + * node. If the commit of delayed items was successful the path + * should always be released, but in case of an error, it may + * point to locked extent buffers (a leaf at the very least). + */ + ASSERT(path->nodes[0] == NULL); btrfs_release_delayed_node(prev_node); } + /* + * Release the path to avoid a potential deadlock and lockdep splat when + * releasing the delayed node, as that requires taking the delayed node's + * mutex. If another task starts running delayed items before we take + * the mutex, it will first lock the mutex and then it may try to lock + * the same btree path (leaf). + */ + btrfs_free_path(path); + if (curr_node) btrfs_release_delayed_node(curr_node); - btrfs_free_path(path); trans->block_rsv = block_rsv; return ret; @@ -1413,7 +1430,29 @@ void btrfs_balance_delayed_items(struct btrfs_fs_info *fs_info) btrfs_wq_run_delayed_node(delayed_root, fs_info, BTRFS_DELAYED_BATCH); } -/* Will return 0 or -ENOMEM */ +static void btrfs_release_dir_index_item_space(struct btrfs_trans_handle *trans) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + const u64 bytes = btrfs_calc_insert_metadata_size(fs_info, 1); + + if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags)) + return; + + /* + * Adding the new dir index item does not require touching another + * leaf, so we can release 1 unit of metadata that was previously + * reserved when starting the transaction. This applies only to + * the case where we had a transaction start and excludes the + * transaction join case (when replaying log trees). + */ + trace_btrfs_space_reservation(fs_info, "transaction", + trans->transid, bytes, 0); + btrfs_block_rsv_release(fs_info, trans->block_rsv, bytes, NULL); + ASSERT(trans->bytes_reserved >= bytes); + trans->bytes_reserved -= bytes; +} + +/* Will return 0, -ENOMEM or -EEXIST (index number collision, unexpected). */ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, const char *name, int name_len, struct btrfs_inode *dir, @@ -1455,6 +1494,27 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, mutex_lock(&delayed_node->mutex); + /* + * First attempt to insert the delayed item. This is to make the error + * handling path simpler in case we fail (-EEXIST). There's no risk of + * any other task coming in and running the delayed item before we do + * the metadata space reservation below, because we are holding the + * delayed node's mutex and that mutex must also be locked before the + * node's delayed items can be run. + */ + ret = __btrfs_add_delayed_item(delayed_node, delayed_item); + if (unlikely(ret)) { + btrfs_err(trans->fs_info, +"error adding delayed dir index item, name: %.*s, index: %llu, root: %llu, dir: %llu, dir->index_cnt: %llu, delayed_node->index_cnt: %llu, error: %d", + name_len, name, index, btrfs_root_id(delayed_node->root), + delayed_node->inode_id, dir->index_cnt, + delayed_node->index_cnt, ret); + btrfs_release_delayed_item(delayed_item); + btrfs_release_dir_index_item_space(trans); + mutex_unlock(&delayed_node->mutex); + goto release_node; + } + if (delayed_node->index_item_leaves == 0 || delayed_node->curr_index_batch_size + data_len > leaf_data_size) { delayed_node->curr_index_batch_size = data_len; @@ -1472,36 +1532,14 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, * impossible. */ if (WARN_ON(ret)) { - mutex_unlock(&delayed_node->mutex); btrfs_release_delayed_item(delayed_item); + mutex_unlock(&delayed_node->mutex); goto release_node; } delayed_node->index_item_leaves++; - } else if (!test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags)) { - const u64 bytes = btrfs_calc_insert_metadata_size(fs_info, 1); - - /* - * Adding the new dir index item does not require touching another - * leaf, so we can release 1 unit of metadata that was previously - * reserved when starting the transaction. This applies only to - * the case where we had a transaction start and excludes the - * transaction join case (when replaying log trees). - */ - trace_btrfs_space_reservation(fs_info, "transaction", - trans->transid, bytes, 0); - btrfs_block_rsv_release(fs_info, trans->block_rsv, bytes, NULL); - ASSERT(trans->bytes_reserved >= bytes); - trans->bytes_reserved -= bytes; - } - - ret = __btrfs_add_delayed_item(delayed_node, delayed_item); - if (unlikely(ret)) { - btrfs_err(trans->fs_info, - "err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)", - name_len, name, delayed_node->root->root_key.objectid, - delayed_node->inode_id, ret); - BUG(); + } else { + btrfs_release_dir_index_item_space(trans); } mutex_unlock(&delayed_node->mutex); diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 6a13cf00218bc3b8ab21d5d4b56c43dd0412ed49..9fe4ccca50a0604ad00cfa9ebb63f3f3db58f833 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -103,24 +103,17 @@ void btrfs_update_delayed_refs_rsv(struct btrfs_trans_handle *trans) * Transfer bytes to our delayed refs rsv. * * @fs_info: the filesystem - * @src: source block rsv to transfer from * @num_bytes: number of bytes to transfer * - * This transfers up to the num_bytes amount from the src rsv to the + * This transfers up to the num_bytes amount, previously reserved, to the * delayed_refs_rsv. Any extra bytes are returned to the space info. */ void btrfs_migrate_to_delayed_refs_rsv(struct btrfs_fs_info *fs_info, - struct btrfs_block_rsv *src, u64 num_bytes) { struct btrfs_block_rsv *delayed_refs_rsv = &fs_info->delayed_refs_rsv; u64 to_free = 0; - spin_lock(&src->lock); - src->reserved -= num_bytes; - src->size -= num_bytes; - spin_unlock(&src->lock); - spin_lock(&delayed_refs_rsv->lock); if (delayed_refs_rsv->size > delayed_refs_rsv->reserved) { u64 delta = delayed_refs_rsv->size - @@ -163,6 +156,8 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info, struct btrfs_block_rsv *block_rsv = &fs_info->delayed_refs_rsv; u64 limit = btrfs_calc_delayed_ref_bytes(fs_info, 1); u64 num_bytes = 0; + u64 refilled_bytes; + u64 to_free; int ret = -ENOSPC; spin_lock(&block_rsv->lock); @@ -178,9 +173,38 @@ int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info, ret = btrfs_reserve_metadata_bytes(fs_info, block_rsv, num_bytes, flush); if (ret) return ret; - btrfs_block_rsv_add_bytes(block_rsv, num_bytes, false); - trace_btrfs_space_reservation(fs_info, "delayed_refs_rsv", - 0, num_bytes, 1); + + /* + * We may have raced with someone else, so check again if we the block + * reserve is still not full and release any excess space. + */ + spin_lock(&block_rsv->lock); + if (block_rsv->reserved < block_rsv->size) { + u64 needed = block_rsv->size - block_rsv->reserved; + + if (num_bytes >= needed) { + block_rsv->reserved += needed; + block_rsv->full = true; + to_free = num_bytes - needed; + refilled_bytes = needed; + } else { + block_rsv->reserved += num_bytes; + to_free = 0; + refilled_bytes = num_bytes; + } + } else { + to_free = num_bytes; + refilled_bytes = 0; + } + spin_unlock(&block_rsv->lock); + + if (to_free > 0) + btrfs_space_info_free_bytes_may_use(fs_info, block_rsv->space_info, + to_free); + + if (refilled_bytes > 0) + trace_btrfs_space_reservation(fs_info, "delayed_refs_rsv", 0, + refilled_bytes, 1); return 0; } diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index b8e14b0ba5f162fbb2bfec87e075d9051d5d2564..fd9bf2b709c0e461044b5829d9bcdd9a43bfefa6 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -407,7 +407,6 @@ void btrfs_update_delayed_refs_rsv(struct btrfs_trans_handle *trans); int btrfs_delayed_refs_rsv_refill(struct btrfs_fs_info *fs_info, enum btrfs_reserve_flush_enum flush); void btrfs_migrate_to_delayed_refs_rsv(struct btrfs_fs_info *fs_info, - struct btrfs_block_rsv *src, u64 num_bytes); bool btrfs_check_space_for_delayed_refs(struct btrfs_fs_info *fs_info); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 0a96ea8c1d3a627fd2950d97187cbd73baf6e0bf..68f60d50e1fd0c2fc1a643cf9d44bdcb5ae36036 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -520,6 +520,7 @@ static bool btree_dirty_folio(struct address_space *mapping, struct folio *folio) { struct btrfs_fs_info *fs_info = btrfs_sb(mapping->host->i_sb); + struct btrfs_subpage_info *spi = fs_info->subpage_info; struct btrfs_subpage *subpage; struct extent_buffer *eb; int cur_bit = 0; @@ -533,18 +534,19 @@ static bool btree_dirty_folio(struct address_space *mapping, btrfs_assert_tree_write_locked(eb); return filemap_dirty_folio(mapping, folio); } + + ASSERT(spi); subpage = folio_get_private(folio); - ASSERT(subpage->dirty_bitmap); - while (cur_bit < BTRFS_SUBPAGE_BITMAP_SIZE) { + for (cur_bit = spi->dirty_offset; + cur_bit < spi->dirty_offset + spi->bitmap_nr_bits; + cur_bit++) { unsigned long flags; u64 cur; - u16 tmp = (1 << cur_bit); spin_lock_irqsave(&subpage->lock, flags); - if (!(tmp & subpage->dirty_bitmap)) { + if (!test_bit(cur_bit, subpage->bitmaps)) { spin_unlock_irqrestore(&subpage->lock, flags); - cur_bit++; continue; } spin_unlock_irqrestore(&subpage->lock, flags); @@ -557,7 +559,7 @@ static bool btree_dirty_folio(struct address_space *mapping, btrfs_assert_tree_write_locked(eb); free_extent_buffer(eb); - cur_bit += (fs_info->nodesize >> fs_info->sectorsize_bits); + cur_bit += (fs_info->nodesize >> fs_info->sectorsize_bits) - 1; } return filemap_dirty_folio(mapping, folio); } @@ -1547,7 +1549,7 @@ static int transaction_kthread(void *arg) delta = ktime_get_seconds() - cur->start_time; if (!test_and_clear_bit(BTRFS_FS_COMMIT_TRANS, &fs_info->flags) && - cur->state < TRANS_STATE_COMMIT_START && + cur->state < TRANS_STATE_COMMIT_PREP && delta < fs_info->commit_interval) { spin_unlock(&fs_info->trans_lock); delay -= msecs_to_jiffies((delta - 1) * 1000); @@ -2682,8 +2684,8 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) btrfs_lockdep_init_map(fs_info, btrfs_trans_num_extwriters); btrfs_lockdep_init_map(fs_info, btrfs_trans_pending_ordered); btrfs_lockdep_init_map(fs_info, btrfs_ordered_extent); - btrfs_state_lockdep_init_map(fs_info, btrfs_trans_commit_start, - BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_state_lockdep_init_map(fs_info, btrfs_trans_commit_prep, + BTRFS_LOCKDEP_TRANS_COMMIT_PREP); btrfs_state_lockdep_init_map(fs_info, btrfs_trans_unblocked, BTRFS_LOCKDEP_TRANS_UNBLOCKED); btrfs_state_lockdep_init_map(fs_info, btrfs_trans_super_committed, @@ -4870,7 +4872,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info) while (!list_empty(&fs_info->trans_list)) { t = list_first_entry(&fs_info->trans_list, struct btrfs_transaction, list); - if (t->state >= TRANS_STATE_COMMIT_START) { + if (t->state >= TRANS_STATE_COMMIT_PREP) { refcount_inc(&t->use_count); spin_unlock(&fs_info->trans_lock); btrfs_wait_for_commit(fs_info, t->transid); diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f356f08b55cb8e4646e105791454afc98c2cb840..fc313fce5bbdc7d9e5e2a7994b7e3ba0ab12105a 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -1514,15 +1514,14 @@ static int __btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, btrfs_release_path(path); /* now insert the actual backref */ - if (owner < BTRFS_FIRST_FREE_OBJECTID) { - BUG_ON(refs_to_add != 1); + if (owner < BTRFS_FIRST_FREE_OBJECTID) ret = insert_tree_block_ref(trans, path, bytenr, parent, root_objectid); - } else { + else ret = insert_extent_data_ref(trans, path, bytenr, parent, root_objectid, owner, offset, refs_to_add); - } + if (ret) btrfs_abort_transaction(trans, ret); out: @@ -1656,7 +1655,10 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans, goto again; } } else { - err = -EIO; + err = -EUCLEAN; + btrfs_err(fs_info, + "missing extent item for extent %llu num_bytes %llu level %d", + head->bytenr, head->num_bytes, extent_op->level); goto out; } } @@ -1699,12 +1701,12 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans, parent = ref->parent; ref_root = ref->root; - if (node->ref_mod != 1) { + if (unlikely(node->ref_mod != 1)) { btrfs_err(trans->fs_info, - "btree block(%llu) has %d references rather than 1: action %d ref_root %llu parent %llu", + "btree block %llu has %d references rather than 1: action %d ref_root %llu parent %llu", node->bytenr, node->ref_mod, node->action, ref_root, parent); - return -EIO; + return -EUCLEAN; } if (node->action == BTRFS_ADD_DELAYED_REF && insert_reserved) { BUG_ON(!extent_op || !extent_op->update_flags); diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index ac3fca5a5e4162ddc2769f4e5932370d622ae0b4..caccd0376342b7b6c0ba4737f93728e80d9d3d98 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -484,10 +484,8 @@ static void end_bio_extent_writepage(struct btrfs_bio *bbio) bvec->bv_offset, bvec->bv_len); btrfs_finish_ordered_extent(bbio->ordered, page, start, len, !error); - if (error) { - btrfs_page_clear_uptodate(fs_info, page, start, len); + if (error) mapping_set_error(page->mapping, error); - } btrfs_page_clear_writeback(fs_info, page, start, len); } @@ -1456,8 +1454,6 @@ static int __extent_writepage(struct page *page, struct btrfs_bio_ctrl *bio_ctrl if (ret) { btrfs_mark_ordered_io_finished(BTRFS_I(inode), page, page_start, PAGE_SIZE, !ret); - btrfs_page_clear_uptodate(btrfs_sb(inode->i_sb), page, - page_start, PAGE_SIZE); mapping_set_error(page->mapping, ret); } unlock_page(page); @@ -1624,8 +1620,6 @@ static void extent_buffer_write_end_io(struct btrfs_bio *bbio) struct page *page = bvec->bv_page; u32 len = bvec->bv_len; - if (!uptodate) - btrfs_page_clear_uptodate(fs_info, page, start, len); btrfs_page_clear_writeback(fs_info, page, start, len); bio_offset += len; } @@ -2201,7 +2195,6 @@ void extent_write_locked_range(struct inode *inode, struct page *locked_page, if (ret) { btrfs_mark_ordered_io_finished(BTRFS_I(inode), page, cur, cur_len, !ret); - btrfs_page_clear_uptodate(fs_info, page, cur, cur_len); mapping_set_error(page->mapping, ret); } btrfs_page_unlock_writer(fs_info, page, cur, cur_len); @@ -4002,8 +3995,14 @@ void read_extent_buffer(const struct extent_buffer *eb, void *dstv, char *dst = (char *)dstv; unsigned long i = get_eb_page_index(start); - if (check_eb_range(eb, start, len)) + if (check_eb_range(eb, start, len)) { + /* + * Invalid range hit, reset the memory, so callers won't get + * some random garbage for their uninitialzed memory. + */ + memset(dstv, 0, len); return; + } offset = get_eb_offset_in_page(eb, start); diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index ca46a529d56b1999cff4a785661db40dca049e47..361535c71c0f5a172328832e32b6d00f7b14f131 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1106,6 +1106,25 @@ void btrfs_check_nocow_unlock(struct btrfs_inode *inode) btrfs_drew_write_unlock(&inode->root->snapshot_lock); } +static void update_time_for_write(struct inode *inode) +{ + struct timespec64 now, ctime; + + if (IS_NOCMTIME(inode)) + return; + + now = current_time(inode); + if (!timespec64_equal(&inode->i_mtime, &now)) + inode->i_mtime = now; + + ctime = inode_get_ctime(inode); + if (!timespec64_equal(&ctime, &now)) + inode_set_ctime_to_ts(inode, now); + + if (IS_I_VERSION(inode)) + inode_inc_iversion(inode); +} + static int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from, size_t count) { @@ -1137,10 +1156,7 @@ static int btrfs_write_check(struct kiocb *iocb, struct iov_iter *from, * need to start yet another transaction to update the inode as we will * update the inode when we finish writing whatever data we write. */ - if (!IS_NOCMTIME(inode)) { - inode->i_mtime = inode_set_ctime_current(inode); - inode_inc_iversion(inode); - } + update_time_for_write(inode); start_pos = round_down(pos, fs_info->sectorsize); oldsize = i_size_read(inode); @@ -1451,8 +1467,13 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (iocb->ki_flags & IOCB_NOWAIT) ilock_flags |= BTRFS_ILOCK_TRY; - /* If the write DIO is within EOF, use a shared lock */ - if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode)) + /* + * If the write DIO is within EOF, use a shared lock and also only if + * security bits will likely not be dropped by file_remove_privs() called + * from btrfs_write_check(). Either will need to be rechecked after the + * lock was acquired. + */ + if (iocb->ki_pos + iov_iter_count(from) <= i_size_read(inode) && IS_NOSEC(inode)) ilock_flags |= BTRFS_ILOCK_SHARED; relock: @@ -1460,6 +1481,13 @@ static ssize_t btrfs_direct_write(struct kiocb *iocb, struct iov_iter *from) if (err < 0) return err; + /* Shared lock cannot be used with security bits set. */ + if ((ilock_flags & BTRFS_ILOCK_SHARED) && !IS_NOSEC(inode)) { + btrfs_inode_unlock(BTRFS_I(inode), ilock_flags); + ilock_flags &= ~BTRFS_ILOCK_SHARED; + goto relock; + } + err = generic_write_checks(iocb, from); if (err <= 0) { btrfs_inode_unlock(BTRFS_I(inode), ilock_flags); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index f09fbdc43f0f5b15c71d67564cfb264764ff1169..7814b9d654ce1234465bf8ad37a7890e06fd88aa 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1085,9 +1085,6 @@ static void submit_uncompressed_range(struct btrfs_inode *inode, btrfs_mark_ordered_io_finished(inode, locked_page, page_start, PAGE_SIZE, !ret); - btrfs_page_clear_uptodate(inode->root->fs_info, - locked_page, page_start, - PAGE_SIZE); mapping_set_error(locked_page->mapping, ret); unlock_page(locked_page); } @@ -2791,7 +2788,6 @@ static void btrfs_writepage_fixup_worker(struct btrfs_work *work) mapping_set_error(page->mapping, ret); btrfs_mark_ordered_io_finished(inode, page, page_start, PAGE_SIZE, !ret); - btrfs_page_clear_uptodate(fs_info, page, page_start, PAGE_SIZE); clear_page_dirty_for_io(page); } btrfs_page_clear_checked(fs_info, page, page_start, PAGE_SIZE); @@ -5769,20 +5765,24 @@ static int btrfs_set_inode_index_count(struct btrfs_inode *inode) static int btrfs_get_dir_last_index(struct btrfs_inode *dir, u64 *index) { - if (dir->index_cnt == (u64)-1) { - int ret; + int ret = 0; + btrfs_inode_lock(dir, 0); + if (dir->index_cnt == (u64)-1) { ret = btrfs_inode_delayed_dir_index_count(dir); if (ret) { ret = btrfs_set_inode_index_count(dir); if (ret) - return ret; + goto out; } } - *index = dir->index_cnt; + /* index_cnt is the index number of next new entry, so decrement it. */ + *index = dir->index_cnt - 1; +out: + btrfs_inode_unlock(dir, 0); - return 0; + return ret; } /* @@ -5817,6 +5817,19 @@ static int btrfs_opendir(struct inode *inode, struct file *file) return 0; } +static loff_t btrfs_dir_llseek(struct file *file, loff_t offset, int whence) +{ + struct btrfs_file_private *private = file->private_data; + int ret; + + ret = btrfs_get_dir_last_index(BTRFS_I(file_inode(file)), + &private->last_index); + if (ret) + return ret; + + return generic_file_llseek(file, offset, whence); +} + struct dir_entry { u64 ino; u64 offset; @@ -10868,7 +10881,7 @@ static const struct inode_operations btrfs_dir_inode_operations = { }; static const struct file_operations btrfs_dir_file_operations = { - .llseek = generic_file_llseek, + .llseek = btrfs_dir_llseek, .read = generic_read_dir, .iterate_shared = btrfs_real_readdir, .open = btrfs_opendir, diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a18ee7b5a166649141e9570dee5a45b366537b04..8e7d03bc1b56512df6c338907fb2792da025bdf7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1958,6 +1958,13 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, goto out_put; } + /* + * We don't need the path anymore, so release it and + * avoid deadlocks and lockdep warnings in case + * btrfs_iget() needs to lookup the inode from its root + * btree and lock the same leaf. + */ + btrfs_release_path(path); temp_inode = btrfs_iget(sb, key2.objectid, root); if (IS_ERR(temp_inode)) { ret = PTR_ERR(temp_inode); @@ -1978,7 +1985,6 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, goto out_put; } - btrfs_release_path(path); key.objectid = key.offset; key.offset = (u64)-1; dirid = key.objectid; @@ -2972,7 +2978,7 @@ static void get_block_group_info(struct list_head *groups_list, static long btrfs_ioctl_space_info(struct btrfs_fs_info *fs_info, void __user *arg) { - struct btrfs_ioctl_space_args space_args; + struct btrfs_ioctl_space_args space_args = { 0 }; struct btrfs_ioctl_space_info space; struct btrfs_ioctl_space_info *dest; struct btrfs_ioctl_space_info *dest_orig; @@ -4332,7 +4338,7 @@ static int _btrfs_ioctl_send(struct inode *inode, void __user *argp, bool compat if (compat) { #if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) - struct btrfs_ioctl_send_args_32 args32; + struct btrfs_ioctl_send_args_32 args32 = { 0 }; ret = copy_from_user(&args32, argp, sizeof(args32)); if (ret) diff --git a/fs/btrfs/locking.h b/fs/btrfs/locking.h index edb9b4a0dba15d7e444da3f2945138d1aaa4881e..7d6ee1e609bf2bec62338ac11aa088652997c177 100644 --- a/fs/btrfs/locking.h +++ b/fs/btrfs/locking.h @@ -79,7 +79,7 @@ enum btrfs_lock_nesting { }; enum btrfs_lockdep_trans_states { - BTRFS_LOCKDEP_TRANS_COMMIT_START, + BTRFS_LOCKDEP_TRANS_COMMIT_PREP, BTRFS_LOCKDEP_TRANS_UNBLOCKED, BTRFS_LOCKDEP_TRANS_SUPER_COMMITTED, BTRFS_LOCKDEP_TRANS_COMPLETED, diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index b46ab348e8e5b72773a168cea943ca9abb94cc75..345c449d588ccbdfb4e3b7cf430bd9cd6392a87f 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -639,7 +639,7 @@ void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode, refcount_inc(&trans->use_count); spin_unlock(&fs_info->trans_lock); - ASSERT(trans); + ASSERT(trans || BTRFS_FS_ERROR(fs_info)); if (trans) { if (atomic_dec_and_test(&trans->pending_ordered)) wake_up(&trans->pending_wait); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 09bfe68d2ea3fcc8ca68da3deaf3b9a3ea87342d..f49e597e197f0822d5bc94ed22fb9312f6981fee 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -954,6 +954,10 @@ static int btrfs_parse_subvol_options(const char *options, char **subvol_name, *subvol_objectid = subvolid; break; + case Opt_err: + btrfs_err(NULL, "unrecognized mount option '%s'", p); + error = -EINVAL; + goto out; default: break; } @@ -2117,7 +2121,7 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf) * calculated f_bavail. */ if (!mixed && block_rsv->space_info->full && - total_free_meta - thresh < block_rsv->size) + (total_free_meta < thresh || total_free_meta - thresh < block_rsv->size)) buf->f_bavail = 0; buf->f_type = BTRFS_SUPER_MAGIC; @@ -2150,7 +2154,7 @@ static struct file_system_type btrfs_fs_type = { .name = "btrfs", .mount = btrfs_mount, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_MGTIME, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA, }; static struct file_system_type btrfs_root_fs_type = { @@ -2158,8 +2162,7 @@ static struct file_system_type btrfs_root_fs_type = { .name = "btrfs", .mount = btrfs_mount_root, .kill_sb = btrfs_kill_super, - .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | - FS_ALLOW_IDMAP | FS_MGTIME, + .fs_flags = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA | FS_ALLOW_IDMAP, }; MODULE_ALIAS_FS("btrfs"); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 874e4394df860d379a4b766ae40135343bd08db1..c780d37294636852144c8082899e1da4c1189b78 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -56,12 +56,17 @@ static struct kmem_cache *btrfs_trans_handle_cachep; * | Call btrfs_commit_transaction() on any trans handle attached to * | transaction N * V - * Transaction N [[TRANS_STATE_COMMIT_START]] + * Transaction N [[TRANS_STATE_COMMIT_PREP]] + * | + * | If there are simultaneous calls to btrfs_commit_transaction() one will win + * | the race and the rest will wait for the winner to commit the transaction. + * | + * | The winner will wait for previous running transaction to completely finish + * | if there is one. * | - * | Will wait for previous running transaction to completely finish if there - * | is one + * Transaction N [[TRANS_STATE_COMMIT_START]] * | - * | Then one of the following happes: + * | Then one of the following happens: * | - Wait for all other trans handle holders to release. * | The btrfs_commit_transaction() caller will do the commit work. * | - Wait for current transaction to be committed by others. @@ -112,6 +117,7 @@ static struct kmem_cache *btrfs_trans_handle_cachep; */ static const unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = { [TRANS_STATE_RUNNING] = 0U, + [TRANS_STATE_COMMIT_PREP] = 0U, [TRANS_STATE_COMMIT_START] = (__TRANS_START | __TRANS_ATTACH), [TRANS_STATE_COMMIT_DOING] = (__TRANS_START | __TRANS_ATTACH | @@ -625,14 +631,14 @@ start_transaction(struct btrfs_root *root, unsigned int num_items, reloc_reserved = true; } - ret = btrfs_block_rsv_add(fs_info, rsv, num_bytes, flush); + ret = btrfs_reserve_metadata_bytes(fs_info, rsv, num_bytes, flush); if (ret) goto reserve_fail; if (delayed_refs_bytes) { - btrfs_migrate_to_delayed_refs_rsv(fs_info, rsv, - delayed_refs_bytes); + btrfs_migrate_to_delayed_refs_rsv(fs_info, delayed_refs_bytes); num_bytes -= delayed_refs_bytes; } + btrfs_block_rsv_add_bytes(rsv, num_bytes, true); if (rsv->space_info->force_alloc) do_chunk_alloc = true; @@ -1982,7 +1988,7 @@ void btrfs_commit_transaction_async(struct btrfs_trans_handle *trans) * Wait for the current transaction commit to start and block * subsequent transaction joins */ - btrfs_might_wait_for_state(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_might_wait_for_state(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); wait_event(fs_info->transaction_blocked_wait, cur_trans->state >= TRANS_STATE_COMMIT_START || TRANS_ABORTED(cur_trans)); @@ -2129,7 +2135,7 @@ static void add_pending_snapshot(struct btrfs_trans_handle *trans) return; lockdep_assert_held(&trans->fs_info->trans_lock); - ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_START); + ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_PREP); list_add(&trans->pending_snapshot->list, &cur_trans->pending_snapshots); } @@ -2153,7 +2159,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) ktime_t interval; ASSERT(refcount_read(&trans->use_count) == 1); - btrfs_trans_state_lockdep_acquire(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_trans_state_lockdep_acquire(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); clear_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags); @@ -2213,7 +2219,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) } spin_lock(&fs_info->trans_lock); - if (cur_trans->state >= TRANS_STATE_COMMIT_START) { + if (cur_trans->state >= TRANS_STATE_COMMIT_PREP) { enum btrfs_trans_state want_state = TRANS_STATE_COMPLETED; add_pending_snapshot(trans); @@ -2225,7 +2231,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) want_state = TRANS_STATE_SUPER_COMMITTED; btrfs_trans_state_lockdep_release(fs_info, - BTRFS_LOCKDEP_TRANS_COMMIT_START); + BTRFS_LOCKDEP_TRANS_COMMIT_PREP); ret = btrfs_end_transaction(trans); wait_for_commit(cur_trans, want_state); @@ -2237,9 +2243,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) return ret; } - cur_trans->state = TRANS_STATE_COMMIT_START; + cur_trans->state = TRANS_STATE_COMMIT_PREP; wake_up(&fs_info->transaction_blocked_wait); - btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); if (cur_trans->list.prev != &fs_info->trans_list) { enum btrfs_trans_state want_state = TRANS_STATE_COMPLETED; @@ -2260,11 +2266,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) btrfs_put_transaction(prev_trans); if (ret) goto lockdep_release; - } else { - spin_unlock(&fs_info->trans_lock); + spin_lock(&fs_info->trans_lock); } } else { - spin_unlock(&fs_info->trans_lock); /* * The previous transaction was aborted and was already removed * from the list of transactions at fs_info->trans_list. So we @@ -2272,11 +2276,16 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) * corrupt state (pointing to trees with unwritten nodes/leafs). */ if (BTRFS_FS_ERROR(fs_info)) { + spin_unlock(&fs_info->trans_lock); ret = -EROFS; goto lockdep_release; } } + cur_trans->state = TRANS_STATE_COMMIT_START; + wake_up(&fs_info->transaction_blocked_wait); + spin_unlock(&fs_info->trans_lock); + /* * Get the time spent on the work done by the commit thread and not * the time spent waiting on a previous commit @@ -2586,7 +2595,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) goto cleanup_transaction; lockdep_trans_commit_start_release: - btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); btrfs_end_transaction(trans); return ret; } diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 8e9fa23bd7fed73acffeafc4b3655c6b64c7a58f..93869cda6af99c70adbf3c77c11d5f713edc8d32 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -14,6 +14,7 @@ enum btrfs_trans_state { TRANS_STATE_RUNNING, + TRANS_STATE_COMMIT_PREP, TRANS_STATE_COMMIT_START, TRANS_STATE_COMMIT_DOING, TRANS_STATE_UNBLOCKED, @@ -218,8 +219,8 @@ do { \ (errno))) { \ /* Stack trace printed. */ \ } else { \ - btrfs_debug((trans)->fs_info, \ - "Transaction aborted (error %d)", \ + btrfs_err((trans)->fs_info, \ + "Transaction aborted (error %d)", \ (errno)); \ } \ } \ diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d1e46b839519ca0cdd5560fc990c99ff59ffe973..cbb17b542131770e8893d47d057046241f465629 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4722,7 +4722,7 @@ static int btrfs_log_prealloc_extents(struct btrfs_trans_handle *trans, struct extent_buffer *leaf; int slot; int ins_nr = 0; - int start_slot; + int start_slot = 0; int ret; if (!(inode->flags & BTRFS_INODE_PREALLOC)) diff --git a/fs/btrfs/verity.c b/fs/btrfs/verity.c index c5ff16f9e9fa53aaa8779059a903f9bd636ceb02..744f4f4d4c681139648cbb04394cf6d7bce572c9 100644 --- a/fs/btrfs/verity.c +++ b/fs/btrfs/verity.c @@ -715,7 +715,7 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, pgoff_t index, unsigned long num_ra_pages) { - struct page *page; + struct folio *folio; u64 off = (u64)index << PAGE_SHIFT; loff_t merkle_pos = merkle_file_pos(inode); int ret; @@ -726,29 +726,36 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, return ERR_PTR(-EFBIG); index += merkle_pos >> PAGE_SHIFT; again: - page = find_get_page_flags(inode->i_mapping, index, FGP_ACCESSED); - if (page) { - if (PageUptodate(page)) - return page; + folio = __filemap_get_folio(inode->i_mapping, index, FGP_ACCESSED, 0); + if (!IS_ERR(folio)) { + if (folio_test_uptodate(folio)) + goto out; - lock_page(page); - /* - * We only insert uptodate pages, so !Uptodate has to be - * an error - */ - if (!PageUptodate(page)) { - unlock_page(page); - put_page(page); + folio_lock(folio); + /* If it's not uptodate after we have the lock, we got a read error. */ + if (!folio_test_uptodate(folio)) { + folio_unlock(folio); + folio_put(folio); return ERR_PTR(-EIO); } - unlock_page(page); - return page; + folio_unlock(folio); + goto out; } - page = __page_cache_alloc(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS)); - if (!page) + folio = filemap_alloc_folio(mapping_gfp_constraint(inode->i_mapping, ~__GFP_FS), + 0); + if (!folio) return ERR_PTR(-ENOMEM); + ret = filemap_add_folio(inode->i_mapping, folio, index, GFP_NOFS); + if (ret) { + folio_put(folio); + /* Did someone else insert a folio here? */ + if (ret == -EEXIST) + goto again; + return ERR_PTR(ret); + } + /* * Merkle item keys are indexed from byte 0 in the merkle tree. * They have the form: @@ -756,28 +763,19 @@ static struct page *btrfs_read_merkle_tree_page(struct inode *inode, * [ inode objectid, BTRFS_MERKLE_ITEM_KEY, offset in bytes ] */ ret = read_key_bytes(BTRFS_I(inode), BTRFS_VERITY_MERKLE_ITEM_KEY, off, - page_address(page), PAGE_SIZE, page); + folio_address(folio), PAGE_SIZE, &folio->page); if (ret < 0) { - put_page(page); + folio_put(folio); return ERR_PTR(ret); } if (ret < PAGE_SIZE) - memzero_page(page, ret, PAGE_SIZE - ret); + folio_zero_segment(folio, ret, PAGE_SIZE); - SetPageUptodate(page); - ret = add_to_page_cache_lru(page, inode->i_mapping, index, GFP_NOFS); + folio_mark_uptodate(folio); + folio_unlock(folio); - if (!ret) { - /* Inserted and ready for fsverity */ - unlock_page(page); - } else { - put_page(page); - /* Did someone race us into inserting this page? */ - if (ret == -EEXIST) - goto again; - page = ERR_PTR(ret); - } - return page; +out: + return folio_file_page(folio, index); } /* diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 9621455edebc9da384d249c8c34cfcaa245d9888..5a5a8d488a7bcd042175ca7499bea3a3c7d84ff4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1594,7 +1594,7 @@ static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, u64 search_start; u64 hole_size; u64 max_hole_start; - u64 max_hole_size; + u64 max_hole_size = 0; u64 extent_end; u64 search_end = device->total_bytes; int ret; @@ -1602,17 +1602,16 @@ static int find_free_dev_extent(struct btrfs_device *device, u64 num_bytes, struct extent_buffer *l; search_start = dev_extent_search_start(device); + max_hole_start = search_start; WARN_ON(device->zone_info && !IS_ALIGNED(num_bytes, device->zone_info->zone_size)); path = btrfs_alloc_path(); - if (!path) - return -ENOMEM; - - max_hole_start = search_start; - max_hole_size = 0; - + if (!path) { + ret = -ENOMEM; + goto out; + } again: if (search_start >= search_end || test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)) { diff --git a/fs/buffer.c b/fs/buffer.c index 2379564e5aeadf5bebc786375d17b744cacd5f75..12e9a71c693d740a80ad5aa92b4653004a381711 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2011,7 +2011,7 @@ void folio_zero_new_buffers(struct folio *folio, size_t from, size_t to) } EXPORT_SYMBOL(folio_zero_new_buffers); -static void +static int iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, const struct iomap *iomap) { @@ -2025,7 +2025,8 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, * current block, then do not map the buffer and let the caller * handle it. */ - BUG_ON(offset >= iomap->offset + iomap->length); + if (offset >= iomap->offset + iomap->length) + return -EIO; switch (iomap->type) { case IOMAP_HOLE: @@ -2037,7 +2038,7 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, if (!buffer_uptodate(bh) || (offset >= i_size_read(inode))) set_buffer_new(bh); - break; + return 0; case IOMAP_DELALLOC: if (!buffer_uptodate(bh) || (offset >= i_size_read(inode))) @@ -2045,7 +2046,7 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, set_buffer_uptodate(bh); set_buffer_mapped(bh); set_buffer_delay(bh); - break; + return 0; case IOMAP_UNWRITTEN: /* * For unwritten regions, we always need to ensure that regions @@ -2057,12 +2058,24 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, fallthrough; case IOMAP_MAPPED: if ((iomap->flags & IOMAP_F_NEW) || - offset >= i_size_read(inode)) + offset >= i_size_read(inode)) { + /* + * This can happen if truncating the block device races + * with the check in the caller as i_size updates on + * block devices aren't synchronized by i_rwsem for + * block devices. + */ + if (S_ISBLK(inode->i_mode)) + return -EIO; set_buffer_new(bh); + } bh->b_blocknr = (iomap->addr + offset - iomap->offset) >> inode->i_blkbits; set_buffer_mapped(bh); - break; + return 0; + default: + WARN_ON_ONCE(1); + return -EIO; } } @@ -2103,13 +2116,12 @@ int __block_write_begin_int(struct folio *folio, loff_t pos, unsigned len, clear_buffer_new(bh); if (!buffer_mapped(bh)) { WARN_ON(bh->b_size != blocksize); - if (get_block) { + if (get_block) err = get_block(inode, block, bh, 1); - if (err) - break; - } else { - iomap_to_bh(inode, block, bh, iomap); - } + else + err = iomap_to_bh(inode, block, bh, iomap); + if (err) + break; if (buffer_new(bh)) { clean_bdev_bh_alias(bh); diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 50c635dc7f711c4446ac537cc44d8a08a467ba39..1f77ca04c426fdb9be333b891ebbdee504e64d30 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -12,3 +12,4 @@ ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \ ceph-$(CONFIG_CEPH_FSCACHE) += cache.o ceph-$(CONFIG_CEPH_FS_POSIX_ACL) += acl.o +ceph-$(CONFIG_FS_ENCRYPTION) += crypto.o diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c index c91b293267d748af2f35e4fdcfa67563337e15f9..c53a1d2206225ca26f669642a22cfde767e45bc1 100644 --- a/fs/ceph/acl.c +++ b/fs/ceph/acl.c @@ -140,7 +140,7 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, newattrs.ia_ctime = current_time(inode); newattrs.ia_mode = new_mode; newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - ret = __ceph_setattr(inode, &newattrs); + ret = __ceph_setattr(inode, &newattrs, NULL); if (ret) goto out_free; } @@ -151,7 +151,7 @@ int ceph_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, newattrs.ia_ctime = old_ctime; newattrs.ia_mode = old_mode; newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - __ceph_setattr(inode, &newattrs); + __ceph_setattr(inode, &newattrs, NULL); } goto out_free; } diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 59cbfb80edbda9071e2960cb02deb8b6fbf2dae3..f4863078f7fe55f7d9ccf911ddbcb29a41df51c9 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -18,6 +18,7 @@ #include "mds_client.h" #include "cache.h" #include "metric.h" +#include "crypto.h" #include #include @@ -242,11 +243,13 @@ static bool ceph_netfs_clamp_length(struct netfs_io_subrequest *subreq) static void finish_netfs_read(struct ceph_osd_request *req) { - struct ceph_fs_client *fsc = ceph_inode_to_client(req->r_inode); + struct inode *inode = req->r_inode; + struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_osd_data *osd_data = osd_req_op_extent_osd_data(req, 0); struct netfs_io_subrequest *subreq = req->r_priv; - int num_pages; + struct ceph_osd_req_op *op = &req->r_ops[0]; int err = req->r_result; + bool sparse = (op->op == CEPH_OSD_OP_SPARSE_READ); ceph_update_read_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, osd_data->length, err); @@ -260,14 +263,29 @@ static void finish_netfs_read(struct ceph_osd_request *req) else if (err == -EBLOCKLISTED) fsc->blocklisted = true; - if (err >= 0 && err < subreq->len) - __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); + if (err >= 0) { + if (sparse && err > 0) + err = ceph_sparse_ext_map_end(op); + if (err < subreq->len) + __set_bit(NETFS_SREQ_CLEAR_TAIL, &subreq->flags); + if (IS_ENCRYPTED(inode) && err > 0) { + err = ceph_fscrypt_decrypt_extents(inode, + osd_data->pages, subreq->start, + op->extent.sparse_ext, + op->extent.sparse_ext_cnt); + if (err > subreq->len) + err = subreq->len; + } + } + if (osd_data->type == CEPH_OSD_DATA_TYPE_PAGES) { + ceph_put_page_vector(osd_data->pages, + calc_pages_for(osd_data->alignment, + osd_data->length), false); + } netfs_subreq_terminated(subreq, err, false); - - num_pages = calc_pages_for(osd_data->alignment, osd_data->length); - ceph_put_page_vector(osd_data->pages, num_pages, false); iput(req->r_inode); + ceph_dec_osd_stopping_blocker(fsc->mdsc); } static bool ceph_netfs_issue_op_inline(struct netfs_io_subrequest *subreq) @@ -334,10 +352,10 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) struct ceph_osd_request *req = NULL; struct ceph_vino vino = ceph_vino(inode); struct iov_iter iter; - struct page **pages; - size_t page_off; int err = 0; u64 len = subreq->len; + bool sparse = IS_ENCRYPTED(inode) || ceph_test_mount_opt(fsc, SPARSEREAD); + u64 off = subreq->start; if (ceph_inode_is_shutdown(inode)) { err = -EIO; @@ -347,8 +365,10 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq)) return; - req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, subreq->start, &len, - 0, 1, CEPH_OSD_OP_READ, + ceph_fscrypt_adjust_off_and_len(inode, &off, &len); + + req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, + off, &len, 0, 1, sparse ? CEPH_OSD_OP_SPARSE_READ : CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ | fsc->client->osdc.client->options->read_from_replica, NULL, ci->i_truncate_seq, ci->i_truncate_size, false); if (IS_ERR(req)) { @@ -357,20 +377,48 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq) goto out; } + if (sparse) { + err = ceph_alloc_sparse_ext_map(&req->r_ops[0]); + if (err) + goto out; + } + dout("%s: pos=%llu orig_len=%zu len=%llu\n", __func__, subreq->start, subreq->len, len); + iov_iter_xarray(&iter, ITER_DEST, &rreq->mapping->i_pages, subreq->start, len); - err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off); - if (err < 0) { - dout("%s: iov_ter_get_pages_alloc returned %d\n", __func__, err); - goto out; - } - /* should always give us a page-aligned read */ - WARN_ON_ONCE(page_off); - len = err; - err = 0; + /* + * FIXME: For now, use CEPH_OSD_DATA_TYPE_PAGES instead of _ITER for + * encrypted inodes. We'd need infrastructure that handles an iov_iter + * instead of page arrays, and we don't have that as of yet. Once the + * dust settles on the write helpers and encrypt/decrypt routines for + * netfs, we should be able to rework this. + */ + if (IS_ENCRYPTED(inode)) { + struct page **pages; + size_t page_off; + + err = iov_iter_get_pages_alloc2(&iter, &pages, len, &page_off); + if (err < 0) { + dout("%s: iov_ter_get_pages_alloc returned %d\n", + __func__, err); + goto out; + } + + /* should always give us a page-aligned read */ + WARN_ON_ONCE(page_off); + len = err; + err = 0; - osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false, false); + osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, false, + false); + } else { + osd_req_op_extent_osd_iter(req, 0, &iter); + } + if (!ceph_inc_osd_stopping_blocker(fsc->mdsc)) { + err = -EIO; + goto out; + } req->r_callback = finish_netfs_read; req->r_priv = subreq; req->r_inode = inode; @@ -571,10 +619,12 @@ static u64 get_writepages_data_length(struct inode *inode, struct page *page, u64 start) { struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_snap_context *snapc = page_snap_context(page); + struct ceph_snap_context *snapc; struct ceph_cap_snap *capsnap = NULL; u64 end = i_size_read(inode); + u64 ret; + snapc = page_snap_context(ceph_fscrypt_pagecache_page(page)); if (snapc != ci->i_head_snapc) { bool found = false; spin_lock(&ci->i_ceph_lock); @@ -589,9 +639,12 @@ static u64 get_writepages_data_length(struct inode *inode, spin_unlock(&ci->i_ceph_lock); WARN_ON(!found); } - if (end > page_offset(page) + thp_size(page)) - end = page_offset(page) + thp_size(page); - return end > start ? end - start : 0; + if (end > ceph_fscrypt_page_offset(page) + thp_size(page)) + end = ceph_fscrypt_page_offset(page) + thp_size(page); + ret = end > start ? end - start : 0; + if (ret && fscrypt_is_bounce_page(page)) + ret = round_up(ret, CEPH_FSCRYPT_BLOCK_SIZE); + return ret; } /* @@ -610,10 +663,12 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) loff_t page_off = page_offset(page); int err; loff_t len = thp_size(page); + loff_t wlen; struct ceph_writeback_ctl ceph_wbc; struct ceph_osd_client *osdc = &fsc->client->osdc; struct ceph_osd_request *req; bool caching = ceph_is_cache_enabled(inode); + struct page *bounce_page = NULL; dout("writepage %p idx %lu\n", page, page->index); @@ -649,31 +704,51 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) if (ceph_wbc.i_size < page_off + len) len = ceph_wbc.i_size - page_off; + wlen = IS_ENCRYPTED(inode) ? round_up(len, CEPH_FSCRYPT_BLOCK_SIZE) : len; dout("writepage %p page %p index %lu on %llu~%llu snapc %p seq %lld\n", - inode, page, page->index, page_off, len, snapc, snapc->seq); + inode, page, page->index, page_off, wlen, snapc, snapc->seq); if (atomic_long_inc_return(&fsc->writeback_count) > CONGESTION_ON_THRESH(fsc->mount_options->congestion_kb)) fsc->write_congested = true; - req = ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode), page_off, &len, 0, 1, - CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE, snapc, - ceph_wbc.truncate_seq, ceph_wbc.truncate_size, - true); + req = ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode), + page_off, &wlen, 0, 1, CEPH_OSD_OP_WRITE, + CEPH_OSD_FLAG_WRITE, snapc, + ceph_wbc.truncate_seq, + ceph_wbc.truncate_size, true); if (IS_ERR(req)) { redirty_page_for_writepage(wbc, page); return PTR_ERR(req); } + if (wlen < len) + len = wlen; + set_page_writeback(page); if (caching) ceph_set_page_fscache(page); ceph_fscache_write_to_cache(inode, page_off, len, caching); + if (IS_ENCRYPTED(inode)) { + bounce_page = fscrypt_encrypt_pagecache_blocks(page, + CEPH_FSCRYPT_BLOCK_SIZE, 0, + GFP_NOFS); + if (IS_ERR(bounce_page)) { + redirty_page_for_writepage(wbc, page); + end_page_writeback(page); + ceph_osdc_put_request(req); + return PTR_ERR(bounce_page); + } + } + /* it may be a short write due to an object boundary */ WARN_ON_ONCE(len > thp_size(page)); - osd_req_op_extent_osd_data_pages(req, 0, &page, len, 0, false, false); - dout("writepage %llu~%llu (%llu bytes)\n", page_off, len, len); + osd_req_op_extent_osd_data_pages(req, 0, + bounce_page ? &bounce_page : &page, wlen, 0, + false, false); + dout("writepage %llu~%llu (%llu bytes, %sencrypted)\n", + page_off, len, wlen, IS_ENCRYPTED(inode) ? "" : "not "); req->r_mtime = inode->i_mtime; ceph_osdc_start_request(osdc, req); @@ -681,7 +756,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc) ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, err); - + fscrypt_free_bounce_page(bounce_page); ceph_osdc_put_request(req); if (err == 0) err = len; @@ -800,6 +875,11 @@ static void writepages_finish(struct ceph_osd_request *req) total_pages += num_pages; for (j = 0; j < num_pages; j++) { page = osd_data->pages[j]; + if (fscrypt_is_bounce_page(page)) { + page = fscrypt_pagecache_page(page); + fscrypt_free_bounce_page(osd_data->pages[j]); + osd_data->pages[j] = page; + } BUG_ON(!page); WARN_ON(!PageUptodate(page)); @@ -835,6 +915,7 @@ static void writepages_finish(struct ceph_osd_request *req) else kfree(osd_data->pages); ceph_osdc_put_request(req); + ceph_dec_osd_stopping_blocker(fsc->mdsc); } /* @@ -1070,9 +1151,28 @@ static int ceph_writepages_start(struct address_space *mapping, fsc->mount_options->congestion_kb)) fsc->write_congested = true; - pages[locked_pages++] = page; - fbatch.folios[i] = NULL; + if (IS_ENCRYPTED(inode)) { + pages[locked_pages] = + fscrypt_encrypt_pagecache_blocks(page, + PAGE_SIZE, 0, + locked_pages ? GFP_NOWAIT : GFP_NOFS); + if (IS_ERR(pages[locked_pages])) { + if (PTR_ERR(pages[locked_pages]) == -EINVAL) + pr_err("%s: inode->i_blkbits=%hhu\n", + __func__, inode->i_blkbits); + /* better not fail on first page! */ + BUG_ON(locked_pages == 0); + pages[locked_pages] = NULL; + redirty_page_for_writepage(wbc, page); + unlock_page(page); + break; + } + ++locked_pages; + } else { + pages[locked_pages++] = page; + } + fbatch.folios[i] = NULL; len += thp_size(page); } @@ -1100,7 +1200,7 @@ static int ceph_writepages_start(struct address_space *mapping, } new_request: - offset = page_offset(pages[0]); + offset = ceph_fscrypt_page_offset(pages[0]); len = wsize; req = ceph_osdc_new_request(&fsc->client->osdc, @@ -1121,9 +1221,13 @@ static int ceph_writepages_start(struct address_space *mapping, ceph_wbc.truncate_size, true); BUG_ON(IS_ERR(req)); } - BUG_ON(len < page_offset(pages[locked_pages - 1]) + - thp_size(page) - offset); + BUG_ON(len < ceph_fscrypt_page_offset(pages[locked_pages - 1]) + + thp_size(pages[locked_pages - 1]) - offset); + if (!ceph_inc_osd_stopping_blocker(fsc->mdsc)) { + rc = -EIO; + goto release_folios; + } req->r_callback = writepages_finish; req->r_inode = inode; @@ -1132,7 +1236,9 @@ static int ceph_writepages_start(struct address_space *mapping, data_pages = pages; op_idx = 0; for (i = 0; i < locked_pages; i++) { - u64 cur_offset = page_offset(pages[i]); + struct page *page = ceph_fscrypt_pagecache_page(pages[i]); + + u64 cur_offset = page_offset(page); /* * Discontinuity in page range? Ceph can handle that by just passing * multiple extents in the write op. @@ -1161,9 +1267,9 @@ static int ceph_writepages_start(struct address_space *mapping, op_idx++; } - set_page_writeback(pages[i]); + set_page_writeback(page); if (caching) - ceph_set_page_fscache(pages[i]); + ceph_set_page_fscache(page); len += thp_size(page); } ceph_fscache_write_to_cache(inode, offset, len, caching); @@ -1179,8 +1285,16 @@ static int ceph_writepages_start(struct address_space *mapping, offset); len = max(len, min_len); } + if (IS_ENCRYPTED(inode)) + len = round_up(len, CEPH_FSCRYPT_BLOCK_SIZE); + dout("writepages got pages at %llu~%llu\n", offset, len); + if (IS_ENCRYPTED(inode) && + ((offset | len) & ~CEPH_FSCRYPT_BLOCK_MASK)) + pr_warn("%s: bad encrypted write offset=%lld len=%llu\n", + __func__, offset, len); + osd_req_op_extent_osd_data_pages(req, op_idx, data_pages, len, 0, from_pool, false); osd_req_op_extent_update(req, op_idx, len); diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 09cd6d334604e7b7c47beed10f35a8e843b968d7..14215ec646f7ae6bd9a6ee0fdc6878351850ab49 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -14,6 +14,7 @@ #include "super.h" #include "mds_client.h" #include "cache.h" +#include "crypto.h" #include #include @@ -1216,15 +1217,11 @@ struct cap_msg_args { umode_t mode; bool inline_data; bool wake; + bool encrypted; + u32 fscrypt_auth_len; + u8 fscrypt_auth[sizeof(struct ceph_fscrypt_auth)]; // for context }; -/* - * cap struct size + flock buffer size + inline version + inline data size + - * osd_epoch_barrier + oldest_flush_tid - */ -#define CAP_MSG_SIZE (sizeof(struct ceph_mds_caps) + \ - 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4) - /* Marshal up the cap msg to the MDS */ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg) { @@ -1240,7 +1237,7 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg) arg->size, arg->max_size, arg->xattr_version, arg->xattr_buf ? (int)arg->xattr_buf->vec.iov_len : 0); - msg->hdr.version = cpu_to_le16(10); + msg->hdr.version = cpu_to_le16(12); msg->hdr.tid = cpu_to_le64(arg->flush_tid); fc = msg->front.iov_base; @@ -1257,7 +1254,13 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg) fc->ino = cpu_to_le64(arg->ino); fc->snap_follows = cpu_to_le64(arg->follows); - fc->size = cpu_to_le64(arg->size); +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + if (arg->encrypted) + fc->size = cpu_to_le64(round_up(arg->size, + CEPH_FSCRYPT_BLOCK_SIZE)); + else +#endif + fc->size = cpu_to_le64(arg->size); fc->max_size = cpu_to_le64(arg->max_size); ceph_encode_timespec64(&fc->mtime, &arg->mtime); ceph_encode_timespec64(&fc->atime, &arg->atime); @@ -1311,6 +1314,27 @@ static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg) /* Advisory flags (version 10) */ ceph_encode_32(&p, arg->flags); + + /* dirstats (version 11) - these are r/o on the client */ + ceph_encode_64(&p, 0); + ceph_encode_64(&p, 0); + +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + /* + * fscrypt_auth and fscrypt_file (version 12) + * + * fscrypt_auth holds the crypto context (if any). fscrypt_file + * tracks the real i_size as an __le64 field (and we use a rounded-up + * i_size in the traditional size field). + */ + ceph_encode_32(&p, arg->fscrypt_auth_len); + ceph_encode_copy(&p, arg->fscrypt_auth, arg->fscrypt_auth_len); + ceph_encode_32(&p, sizeof(__le64)); + ceph_encode_64(&p, arg->size); +#else /* CONFIG_FS_ENCRYPTION */ + ceph_encode_32(&p, 0); + ceph_encode_32(&p, 0); +#endif /* CONFIG_FS_ENCRYPTION */ } /* @@ -1378,7 +1402,6 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap, arg->follows = flushing ? ci->i_head_snapc->seq : 0; arg->flush_tid = flush_tid; arg->oldest_flush_tid = oldest_flush_tid; - arg->size = i_size_read(inode); ci->i_reported_size = arg->size; arg->max_size = ci->i_wanted_max_size; @@ -1432,8 +1455,39 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap, } } arg->flags = flags; + arg->encrypted = IS_ENCRYPTED(inode); +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + if (ci->fscrypt_auth_len && + WARN_ON_ONCE(ci->fscrypt_auth_len > sizeof(struct ceph_fscrypt_auth))) { + /* Don't set this if it's too big */ + arg->fscrypt_auth_len = 0; + } else { + arg->fscrypt_auth_len = ci->fscrypt_auth_len; + memcpy(arg->fscrypt_auth, ci->fscrypt_auth, + min_t(size_t, ci->fscrypt_auth_len, + sizeof(arg->fscrypt_auth))); + } +#endif /* CONFIG_FS_ENCRYPTION */ } +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +#define CAP_MSG_FIXED_FIELDS (sizeof(struct ceph_mds_caps) + \ + 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4 + 8) + +static inline int cap_msg_size(struct cap_msg_args *arg) +{ + return CAP_MSG_FIXED_FIELDS + arg->fscrypt_auth_len; +} +#else +#define CAP_MSG_FIXED_FIELDS (sizeof(struct ceph_mds_caps) + \ + 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4 + 8 + 8 + 4 + 4) + +static inline int cap_msg_size(struct cap_msg_args *arg) +{ + return CAP_MSG_FIXED_FIELDS; +} +#endif /* CONFIG_FS_ENCRYPTION */ + /* * Send a cap msg on the given inode. * @@ -1444,7 +1498,8 @@ static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci) struct ceph_msg *msg; struct inode *inode = &ci->netfs.inode; - msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false); + msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, cap_msg_size(arg), GFP_NOFS, + false); if (!msg) { pr_err("error allocating cap msg: ino (%llx.%llx) flushing %s tid %llu, requeuing cap.\n", ceph_vinop(inode), ceph_cap_string(arg->dirty), @@ -1470,10 +1525,6 @@ static inline int __send_flush_snap(struct inode *inode, struct cap_msg_args arg; struct ceph_msg *msg; - msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false); - if (!msg) - return -ENOMEM; - arg.session = session; arg.ino = ceph_vino(inode).ino; arg.cid = 0; @@ -1510,6 +1561,15 @@ static inline int __send_flush_snap(struct inode *inode, arg.inline_data = capsnap->inline_data; arg.flags = 0; arg.wake = false; + arg.encrypted = IS_ENCRYPTED(inode); + + /* No fscrypt_auth changes from a capsnap.*/ + arg.fscrypt_auth_len = 0; + + msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, cap_msg_size(&arg), + GFP_NOFS, false); + if (!msg) + return -ENOMEM; encode_cap_msg(msg, &arg); ceph_con_send(&arg.session->s_con, msg); @@ -2900,10 +2960,9 @@ int ceph_try_get_caps(struct inode *inode, int need, int want, * due to a small max_size, make sure we check_max_size (and possibly * ask the mds) so we don't get hung up indefinitely. */ -int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got) +int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, int need, + int want, loff_t endoff, int *got) { - struct ceph_file_info *fi = filp->private_data; - struct inode *inode = file_inode(filp); struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); int ret, _got, flags; @@ -2912,7 +2971,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got if (ret < 0) return ret; - if ((fi->fmode & CEPH_FILE_MODE_WR) && + if (fi && (fi->fmode & CEPH_FILE_MODE_WR) && fi->filp_gen != READ_ONCE(fsc->filp_gen)) return -EBADF; @@ -2965,7 +3024,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got continue; } - if ((fi->fmode & CEPH_FILE_MODE_WR) && + if (fi && (fi->fmode & CEPH_FILE_MODE_WR) && fi->filp_gen != READ_ONCE(fsc->filp_gen)) { if (ret >= 0 && _got) ceph_put_cap_refs(ci, _got); @@ -3028,6 +3087,15 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got return 0; } +int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, + int *got) +{ + struct ceph_file_info *fi = filp->private_data; + struct inode *inode = file_inode(filp); + + return __ceph_get_caps(inode, fi, need, want, endoff, got); +} + /* * Take cap refs. Caller must already know we hold at least one ref * on the caps in question or we don't know this is safe. @@ -3323,6 +3391,9 @@ struct cap_extra_info { /* currently issued */ int issued; struct timespec64 btime; + u8 *fscrypt_auth; + u32 fscrypt_auth_len; + u64 fscrypt_file_size; }; /* @@ -3355,6 +3426,14 @@ static void handle_cap_grant(struct inode *inode, bool deleted_inode = false; bool fill_inline = false; + /* + * If there is at least one crypto block then we'll trust + * fscrypt_file_size. If the real length of the file is 0, then + * ignore it (it has probably been truncated down to 0 by the MDS). + */ + if (IS_ENCRYPTED(inode) && size) + size = extra_info->fscrypt_file_size; + dout("handle_cap_grant inode %p cap %p mds%d seq %d %s\n", inode, cap, session->s_mds, seq, ceph_cap_string(newcaps)); dout(" size %llu max_size %llu, i_size %llu\n", size, max_size, @@ -3421,6 +3500,14 @@ static void handle_cap_grant(struct inode *inode, dout("%p mode 0%o uid.gid %d.%d\n", inode, inode->i_mode, from_kuid(&init_user_ns, inode->i_uid), from_kgid(&init_user_ns, inode->i_gid)); +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + if (ci->fscrypt_auth_len != extra_info->fscrypt_auth_len || + memcmp(ci->fscrypt_auth, extra_info->fscrypt_auth, + ci->fscrypt_auth_len)) + pr_warn_ratelimited("%s: cap grant attempt to change fscrypt_auth on non-I_NEW inode (old len %d new len %d)\n", + __func__, ci->fscrypt_auth_len, + extra_info->fscrypt_auth_len); +#endif } if ((newcaps & CEPH_CAP_LINK_SHARED) && @@ -3837,7 +3924,8 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid, */ static bool handle_cap_trunc(struct inode *inode, struct ceph_mds_caps *trunc, - struct ceph_mds_session *session) + struct ceph_mds_session *session, + struct cap_extra_info *extra_info) { struct ceph_inode_info *ci = ceph_inode(inode); int mds = session->s_mds; @@ -3854,8 +3942,16 @@ static bool handle_cap_trunc(struct inode *inode, issued |= implemented | dirty; - dout("handle_cap_trunc inode %p mds%d seq %d to %lld seq %d\n", - inode, mds, seq, truncate_size, truncate_seq); + /* + * If there is at least one crypto block then we'll trust + * fscrypt_file_size. If the real length of the file is 0, then + * ignore it (it has probably been truncated down to 0 by the MDS). + */ + if (IS_ENCRYPTED(inode) && size) + size = extra_info->fscrypt_file_size; + + dout("%s inode %p mds%d seq %d to %lld truncate seq %d\n", + __func__, inode, mds, seq, truncate_size, truncate_seq); queue_trunc = ceph_fill_file_size(inode, issued, truncate_seq, truncate_size, size); return queue_trunc; @@ -4075,6 +4171,52 @@ static void handle_cap_import(struct ceph_mds_client *mdsc, *target_cap = cap; } +#ifdef CONFIG_FS_ENCRYPTION +static int parse_fscrypt_fields(void **p, void *end, + struct cap_extra_info *extra) +{ + u32 len; + + ceph_decode_32_safe(p, end, extra->fscrypt_auth_len, bad); + if (extra->fscrypt_auth_len) { + ceph_decode_need(p, end, extra->fscrypt_auth_len, bad); + extra->fscrypt_auth = kmalloc(extra->fscrypt_auth_len, + GFP_KERNEL); + if (!extra->fscrypt_auth) + return -ENOMEM; + ceph_decode_copy_safe(p, end, extra->fscrypt_auth, + extra->fscrypt_auth_len, bad); + } + + ceph_decode_32_safe(p, end, len, bad); + if (len >= sizeof(u64)) { + ceph_decode_64_safe(p, end, extra->fscrypt_file_size, bad); + len -= sizeof(u64); + } + ceph_decode_skip_n(p, end, len, bad); + return 0; +bad: + return -EIO; +} +#else +static int parse_fscrypt_fields(void **p, void *end, + struct cap_extra_info *extra) +{ + u32 len; + + /* Don't care about these fields unless we're encryption-capable */ + ceph_decode_32_safe(p, end, len, bad); + if (len) + ceph_decode_skip_n(p, end, len, bad); + ceph_decode_32_safe(p, end, len, bad); + if (len) + ceph_decode_skip_n(p, end, len, bad); + return 0; +bad: + return -EIO; +} +#endif + /* * Handle a caps message from the MDS. * @@ -4105,6 +4247,9 @@ void ceph_handle_caps(struct ceph_mds_session *session, dout("handle_caps from mds%d\n", session->s_mds); + if (!ceph_inc_mds_stopping_blocker(mdsc, session)) + return; + /* decode */ end = msg->front.iov_base + msg->front.iov_len; if (msg->front.iov_len < sizeof(*h)) @@ -4195,13 +4340,17 @@ void ceph_handle_caps(struct ceph_mds_session *session, ceph_decode_64_safe(&p, end, extra_info.nsubdirs, bad); } + if (msg_version >= 12) { + if (parse_fscrypt_fields(&p, end, &extra_info)) + goto bad; + } + /* lookup ino */ inode = ceph_find_inode(mdsc->fsc->sb, vino); dout(" op %s ino %llx.%llx inode %p\n", ceph_cap_op_name(op), vino.ino, vino.snap, inode); mutex_lock(&session->s_mutex); - inc_session_sequence(session); dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq, (unsigned)seq); @@ -4292,7 +4441,8 @@ void ceph_handle_caps(struct ceph_mds_session *session, break; case CEPH_CAP_OP_TRUNC: - queue_trunc = handle_cap_trunc(inode, h, session); + queue_trunc = handle_cap_trunc(inode, h, session, + &extra_info); spin_unlock(&ci->i_ceph_lock); if (queue_trunc) ceph_queue_vmtruncate(inode); @@ -4309,12 +4459,15 @@ void ceph_handle_caps(struct ceph_mds_session *session, done_unlocked: iput(inode); out: + ceph_dec_mds_stopping_blocker(mdsc); + ceph_put_string(extra_info.pool_ns); /* Defer closing the sessions after s_mutex lock being released */ if (close_sessions) ceph_mdsc_close_sessions(mdsc); + kfree(extra_info.fscrypt_auth); return; flush_cap_releases: @@ -4611,6 +4764,18 @@ int ceph_encode_inode_release(void **p, struct inode *inode, return ret; } +/** + * ceph_encode_dentry_release - encode a dentry release into an outgoing request + * @p: outgoing request buffer + * @dentry: dentry to release + * @dir: dir to release it from + * @mds: mds that we're speaking to + * @drop: caps being dropped + * @unless: unless we have these caps + * + * Encode a dentry release into an outgoing request buffer. Returns 1 if the + * thing was released, or a negative error code otherwise. + */ int ceph_encode_dentry_release(void **p, struct dentry *dentry, struct inode *dir, int mds, int drop, int unless) @@ -4643,13 +4808,25 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry, if (ret && di->lease_session && di->lease_session->s_mds == mds) { dout("encode_dentry_release %p mds%d seq %d\n", dentry, mds, (int)di->lease_seq); - rel->dname_len = cpu_to_le32(dentry->d_name.len); - memcpy(*p, dentry->d_name.name, dentry->d_name.len); - *p += dentry->d_name.len; rel->dname_seq = cpu_to_le32(di->lease_seq); __ceph_mdsc_drop_dentry_lease(dentry); + spin_unlock(&dentry->d_lock); + if (IS_ENCRYPTED(dir) && fscrypt_has_encryption_key(dir)) { + int ret2 = ceph_encode_encrypted_fname(dir, dentry, *p); + + if (ret2 < 0) + return ret2; + + rel->dname_len = cpu_to_le32(ret2); + *p += ret2; + } else { + rel->dname_len = cpu_to_le32(dentry->d_name.len); + memcpy(*p, dentry->d_name.name, dentry->d_name.len); + *p += dentry->d_name.len; + } + } else { + spin_unlock(&dentry->d_lock); } - spin_unlock(&dentry->d_lock); return ret; } diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c new file mode 100644 index 0000000000000000000000000000000000000000..e1f31b86fd4801c6e71f77352d1cd1067e03690b --- /dev/null +++ b/fs/ceph/crypto.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The base64 encode/decode code was copied from fscrypt: + * Copyright (C) 2015, Google, Inc. + * Copyright (C) 2015, Motorola Mobility + * Written by Uday Savagaonkar, 2014. + * Modified by Jaegeuk Kim, 2015. + */ +#include +#include +#include +#include + +#include "super.h" +#include "mds_client.h" +#include "crypto.h" + +/* + * The base64url encoding used by fscrypt includes the '_' character, which may + * cause problems in snapshot names (which can not start with '_'). Thus, we + * used the base64 encoding defined for IMAP mailbox names (RFC 3501) instead, + * which replaces '-' and '_' by '+' and ','. + */ +static const char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; + +int ceph_base64_encode(const u8 *src, int srclen, char *dst) +{ + u32 ac = 0; + int bits = 0; + int i; + char *cp = dst; + + for (i = 0; i < srclen; i++) { + ac = (ac << 8) | src[i]; + bits += 8; + do { + bits -= 6; + *cp++ = base64_table[(ac >> bits) & 0x3f]; + } while (bits >= 6); + } + if (bits) + *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; + return cp - dst; +} + +int ceph_base64_decode(const char *src, int srclen, u8 *dst) +{ + u32 ac = 0; + int bits = 0; + int i; + u8 *bp = dst; + + for (i = 0; i < srclen; i++) { + const char *p = strchr(base64_table, src[i]); + + if (p == NULL || src[i] == 0) + return -1; + ac = (ac << 6) | (p - base64_table); + bits += 6; + if (bits >= 8) { + bits -= 8; + *bp++ = (u8)(ac >> bits); + } + } + if (ac & ((1 << bits) - 1)) + return -1; + return bp - dst; +} + +static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_fscrypt_auth *cfa = (struct ceph_fscrypt_auth *)ci->fscrypt_auth; + u32 ctxlen; + + /* Non existent or too short? */ + if (!cfa || (ci->fscrypt_auth_len < (offsetof(struct ceph_fscrypt_auth, cfa_blob) + 1))) + return -ENOBUFS; + + /* Some format we don't recognize? */ + if (le32_to_cpu(cfa->cfa_version) != CEPH_FSCRYPT_AUTH_VERSION) + return -ENOBUFS; + + ctxlen = le32_to_cpu(cfa->cfa_blob_len); + if (len < ctxlen) + return -ERANGE; + + memcpy(ctx, cfa->cfa_blob, ctxlen); + return ctxlen; +} + +static int ceph_crypt_set_context(struct inode *inode, const void *ctx, + size_t len, void *fs_data) +{ + int ret; + struct iattr attr = { }; + struct ceph_iattr cia = { }; + struct ceph_fscrypt_auth *cfa; + + WARN_ON_ONCE(fs_data); + + if (len > FSCRYPT_SET_CONTEXT_MAX_SIZE) + return -EINVAL; + + cfa = kzalloc(sizeof(*cfa), GFP_KERNEL); + if (!cfa) + return -ENOMEM; + + cfa->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION); + cfa->cfa_blob_len = cpu_to_le32(len); + memcpy(cfa->cfa_blob, ctx, len); + + cia.fscrypt_auth = cfa; + + ret = __ceph_setattr(inode, &attr, &cia); + if (ret == 0) + inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED); + kfree(cia.fscrypt_auth); + return ret; +} + +static bool ceph_crypt_empty_dir(struct inode *inode) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + + return ci->i_rsubdirs + ci->i_rfiles == 1; +} + +static const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb) +{ + return ceph_sb_to_client(sb)->fsc_dummy_enc_policy.policy; +} + +static struct fscrypt_operations ceph_fscrypt_ops = { + .get_context = ceph_crypt_get_context, + .set_context = ceph_crypt_set_context, + .get_dummy_policy = ceph_get_dummy_policy, + .empty_dir = ceph_crypt_empty_dir, +}; + +void ceph_fscrypt_set_ops(struct super_block *sb) +{ + fscrypt_set_ops(sb, &ceph_fscrypt_ops); +} + +void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc) +{ + fscrypt_free_dummy_policy(&fsc->fsc_dummy_enc_policy); +} + +int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode, + struct ceph_acl_sec_ctx *as) +{ + int ret, ctxsize; + bool encrypted = false; + struct ceph_inode_info *ci = ceph_inode(inode); + + ret = fscrypt_prepare_new_inode(dir, inode, &encrypted); + if (ret) + return ret; + if (!encrypted) + return 0; + + as->fscrypt_auth = kzalloc(sizeof(*as->fscrypt_auth), GFP_KERNEL); + if (!as->fscrypt_auth) + return -ENOMEM; + + ctxsize = fscrypt_context_for_new_inode(as->fscrypt_auth->cfa_blob, + inode); + if (ctxsize < 0) + return ctxsize; + + as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION); + as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize); + + WARN_ON_ONCE(ci->fscrypt_auth); + kfree(ci->fscrypt_auth); + ci->fscrypt_auth_len = ceph_fscrypt_auth_len(as->fscrypt_auth); + ci->fscrypt_auth = kmemdup(as->fscrypt_auth, ci->fscrypt_auth_len, + GFP_KERNEL); + if (!ci->fscrypt_auth) + return -ENOMEM; + + inode->i_flags |= S_ENCRYPTED; + + return 0; +} + +void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, + struct ceph_acl_sec_ctx *as) +{ + swap(req->r_fscrypt_auth, as->fscrypt_auth); +} + +/* + * User-created snapshots can't start with '_'. Snapshots that start with this + * character are special (hint: there aren't real snapshots) and use the + * following format: + * + * __ + * + * where: + * - - the real snapshot name that may need to be decrypted, + * - - the inode number (in decimal) for the actual snapshot + * + * This function parses these snapshot names and returns the inode + * . 'name_len' will also bet set with the + * length. + */ +static struct inode *parse_longname(const struct inode *parent, + const char *name, int *name_len) +{ + struct inode *dir = NULL; + struct ceph_vino vino = { .snap = CEPH_NOSNAP }; + char *inode_number; + char *name_end; + int orig_len = *name_len; + int ret = -EIO; + + /* Skip initial '_' */ + name++; + name_end = strrchr(name, '_'); + if (!name_end) { + dout("Failed to parse long snapshot name: %s\n", name); + return ERR_PTR(-EIO); + } + *name_len = (name_end - name); + if (*name_len <= 0) { + pr_err("Failed to parse long snapshot name\n"); + return ERR_PTR(-EIO); + } + + /* Get the inode number */ + inode_number = kmemdup_nul(name_end + 1, + orig_len - *name_len - 2, + GFP_KERNEL); + if (!inode_number) + return ERR_PTR(-ENOMEM); + ret = kstrtou64(inode_number, 10, &vino.ino); + if (ret) { + dout("Failed to parse inode number: %s\n", name); + dir = ERR_PTR(ret); + goto out; + } + + /* And finally the inode */ + dir = ceph_find_inode(parent->i_sb, vino); + if (!dir) { + /* This can happen if we're not mounting cephfs on the root */ + dir = ceph_get_inode(parent->i_sb, vino, NULL); + if (IS_ERR(dir)) + dout("Can't find inode %s (%s)\n", inode_number, name); + } + +out: + kfree(inode_number); + return dir; +} + +int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, + char *buf) +{ + struct inode *dir = parent; + struct qstr iname; + u32 len; + int name_len; + int elen; + int ret; + u8 *cryptbuf = NULL; + + iname.name = d_name->name; + name_len = d_name->len; + + /* Handle the special case of snapshot names that start with '_' */ + if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && + (iname.name[0] == '_')) { + dir = parse_longname(parent, iname.name, &name_len); + if (IS_ERR(dir)) + return PTR_ERR(dir); + iname.name++; /* skip initial '_' */ + } + iname.len = name_len; + + if (!fscrypt_has_encryption_key(dir)) { + memcpy(buf, d_name->name, d_name->len); + elen = d_name->len; + goto out; + } + + /* + * Convert cleartext d_name to ciphertext. If result is longer than + * CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes + * + * See: fscrypt_setup_filename + */ + if (!fscrypt_fname_encrypted_size(dir, iname.len, NAME_MAX, &len)) { + elen = -ENAMETOOLONG; + goto out; + } + + /* Allocate a buffer appropriate to hold the result */ + cryptbuf = kmalloc(len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len, + GFP_KERNEL); + if (!cryptbuf) { + elen = -ENOMEM; + goto out; + } + + ret = fscrypt_fname_encrypt(dir, &iname, cryptbuf, len); + if (ret) { + elen = ret; + goto out; + } + + /* hash the end if the name is long enough */ + if (len > CEPH_NOHASH_NAME_MAX) { + u8 hash[SHA256_DIGEST_SIZE]; + u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX; + + /* + * hash the extra bytes and overwrite crypttext beyond that + * point with it + */ + sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash); + memcpy(extra, hash, SHA256_DIGEST_SIZE); + len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE; + } + + /* base64 encode the encrypted name */ + elen = ceph_base64_encode(cryptbuf, len, buf); + dout("base64-encoded ciphertext name = %.*s\n", elen, buf); + + /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ + WARN_ON(elen > 240); + if ((elen > 0) && (dir != parent)) { + char tmp_buf[NAME_MAX]; + + elen = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", + elen, buf, dir->i_ino); + memcpy(buf, tmp_buf, elen); + } + +out: + kfree(cryptbuf); + if (dir != parent) { + if ((dir->i_state & I_NEW)) + discard_new_inode(dir); + else + iput(dir); + } + return elen; +} + +int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, + char *buf) +{ + WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)); + + return ceph_encode_encrypted_dname(parent, &dentry->d_name, buf); +} + +/** + * ceph_fname_to_usr - convert a filename for userland presentation + * @fname: ceph_fname to be converted + * @tname: temporary name buffer to use for conversion (may be NULL) + * @oname: where converted name should be placed + * @is_nokey: set to true if key wasn't available during conversion (may be NULL) + * + * Given a filename (usually from the MDS), format it for presentation to + * userland. If @parent is not encrypted, just pass it back as-is. + * + * Otherwise, base64 decode the string, and then ask fscrypt to format it + * for userland presentation. + * + * Returns 0 on success or negative error code on error. + */ +int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, + struct fscrypt_str *oname, bool *is_nokey) +{ + struct inode *dir = fname->dir; + struct fscrypt_str _tname = FSTR_INIT(NULL, 0); + struct fscrypt_str iname; + char *name = fname->name; + int name_len = fname->name_len; + int ret; + + /* Sanity check that the resulting name will fit in the buffer */ + if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX) + return -EIO; + + /* Handle the special case of snapshot names that start with '_' */ + if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) && + (name[0] == '_')) { + dir = parse_longname(dir, name, &name_len); + if (IS_ERR(dir)) + return PTR_ERR(dir); + name++; /* skip initial '_' */ + } + + if (!IS_ENCRYPTED(dir)) { + oname->name = fname->name; + oname->len = fname->name_len; + ret = 0; + goto out_inode; + } + + ret = ceph_fscrypt_prepare_readdir(dir); + if (ret) + goto out_inode; + + /* + * Use the raw dentry name as sent by the MDS instead of + * generating a nokey name via fscrypt. + */ + if (!fscrypt_has_encryption_key(dir)) { + if (fname->no_copy) + oname->name = fname->name; + else + memcpy(oname->name, fname->name, fname->name_len); + oname->len = fname->name_len; + if (is_nokey) + *is_nokey = true; + ret = 0; + goto out_inode; + } + + if (fname->ctext_len == 0) { + int declen; + + if (!tname) { + ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); + if (ret) + goto out_inode; + tname = &_tname; + } + + declen = ceph_base64_decode(name, name_len, tname->name); + if (declen <= 0) { + ret = -EIO; + goto out; + } + iname.name = tname->name; + iname.len = declen; + } else { + iname.name = fname->ctext; + iname.len = fname->ctext_len; + } + + ret = fscrypt_fname_disk_to_usr(dir, 0, 0, &iname, oname); + if (!ret && (dir != fname->dir)) { + char tmp_buf[CEPH_BASE64_CHARS(NAME_MAX)]; + + name_len = snprintf(tmp_buf, sizeof(tmp_buf), "_%.*s_%ld", + oname->len, oname->name, dir->i_ino); + memcpy(oname->name, tmp_buf, name_len); + oname->len = name_len; + } + +out: + fscrypt_fname_free_buffer(&_tname); +out_inode: + if ((dir != fname->dir) && !IS_ERR(dir)) { + if ((dir->i_state & I_NEW)) + discard_new_inode(dir); + else + iput(dir); + } + return ret; +} + +/** + * ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir() wrapper + * @dir: directory inode for readdir prep + * + * Simple wrapper around __fscrypt_prepare_readdir() that will mark directory as + * non-complete if this call results in having the directory unlocked. + * + * Returns: + * 1 - if directory was locked and key is now loaded (i.e. dir is unlocked) + * 0 - if directory is still locked + * < 0 - if __fscrypt_prepare_readdir() fails + */ +int ceph_fscrypt_prepare_readdir(struct inode *dir) +{ + bool had_key = fscrypt_has_encryption_key(dir); + int err; + + if (!IS_ENCRYPTED(dir)) + return 0; + + err = __fscrypt_prepare_readdir(dir); + if (err) + return err; + if (!had_key && fscrypt_has_encryption_key(dir)) { + /* directory just got unlocked, mark it as not complete */ + ceph_dir_clear_complete(dir); + return 1; + } + return 0; +} + +int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num) +{ + dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); + return fscrypt_decrypt_block_inplace(inode, page, len, offs, lblk_num); +} + +int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags) +{ + dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); + return fscrypt_encrypt_block_inplace(inode, page, len, offs, lblk_num, + gfp_flags); +} + +/** + * ceph_fscrypt_decrypt_pages - decrypt an array of pages + * @inode: pointer to inode associated with these pages + * @page: pointer to page array + * @off: offset into the file that the read data starts + * @len: max length to decrypt + * + * Decrypt an array of fscrypt'ed pages and return the amount of + * data decrypted. Any data in the page prior to the start of the + * first complete block in the read is ignored. Any incomplete + * crypto blocks at the end of the array are ignored (and should + * probably be zeroed by the caller). + * + * Returns the length of the decrypted data or a negative errno. + */ +int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page, + u64 off, int len) +{ + int i, num_blocks; + u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; + int ret = 0; + + /* + * We can't deal with partial blocks on an encrypted file, so mask off + * the last bit. + */ + num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK); + + /* Decrypt each block */ + for (i = 0; i < num_blocks; ++i) { + int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; + int pgidx = blkoff >> PAGE_SHIFT; + unsigned int pgoffs = offset_in_page(blkoff); + int fret; + + fret = ceph_fscrypt_decrypt_block_inplace(inode, page[pgidx], + CEPH_FSCRYPT_BLOCK_SIZE, pgoffs, + baseblk + i); + if (fret < 0) { + if (ret == 0) + ret = fret; + break; + } + ret += CEPH_FSCRYPT_BLOCK_SIZE; + } + return ret; +} + +/** + * ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer + * @inode: inode associated with pages being decrypted + * @page: pointer to page array + * @off: offset into the file that the data in page[0] starts + * @map: pointer to extent array + * @ext_cnt: length of extent array + * + * Given an extent map and a page array, decrypt the received data in-place, + * skipping holes. Returns the offset into buffer of end of last decrypted + * block. + */ +int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page, + u64 off, struct ceph_sparse_extent *map, + u32 ext_cnt) +{ + int i, ret = 0; + struct ceph_inode_info *ci = ceph_inode(inode); + u64 objno, objoff; + u32 xlen; + + /* Nothing to do for empty array */ + if (ext_cnt == 0) { + dout("%s: empty array, ret 0\n", __func__); + return 0; + } + + ceph_calc_file_object_mapping(&ci->i_layout, off, map[0].len, + &objno, &objoff, &xlen); + + for (i = 0; i < ext_cnt; ++i) { + struct ceph_sparse_extent *ext = &map[i]; + int pgsoff = ext->off - objoff; + int pgidx = pgsoff >> PAGE_SHIFT; + int fret; + + if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) { + pr_warn("%s: bad encrypted sparse extent idx %d off %llx len %llx\n", + __func__, i, ext->off, ext->len); + return -EIO; + } + fret = ceph_fscrypt_decrypt_pages(inode, &page[pgidx], + off + pgsoff, ext->len); + dout("%s: [%d] 0x%llx~0x%llx fret %d\n", __func__, i, + ext->off, ext->len, fret); + if (fret < 0) { + if (ret == 0) + ret = fret; + break; + } + ret = pgsoff + fret; + } + dout("%s: ret %d\n", __func__, ret); + return ret; +} + +/** + * ceph_fscrypt_encrypt_pages - encrypt an array of pages + * @inode: pointer to inode associated with these pages + * @page: pointer to page array + * @off: offset into the file that the data starts + * @len: max length to encrypt + * @gfp: gfp flags to use for allocation + * + * Decrypt an array of cleartext pages and return the amount of + * data encrypted. Any data in the page prior to the start of the + * first complete block in the read is ignored. Any incomplete + * crypto blocks at the end of the array are ignored. + * + * Returns the length of the encrypted data or a negative errno. + */ +int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off, + int len, gfp_t gfp) +{ + int i, num_blocks; + u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; + int ret = 0; + + /* + * We can't deal with partial blocks on an encrypted file, so mask off + * the last bit. + */ + num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK); + + /* Encrypt each block */ + for (i = 0; i < num_blocks; ++i) { + int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; + int pgidx = blkoff >> PAGE_SHIFT; + unsigned int pgoffs = offset_in_page(blkoff); + int fret; + + fret = ceph_fscrypt_encrypt_block_inplace(inode, page[pgidx], + CEPH_FSCRYPT_BLOCK_SIZE, pgoffs, + baseblk + i, gfp); + if (fret < 0) { + if (ret == 0) + ret = fret; + break; + } + ret += CEPH_FSCRYPT_BLOCK_SIZE; + } + return ret; +} diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h new file mode 100644 index 0000000000000000000000000000000000000000..47e0c319fc68cfa07582f6c04cfd603a64b66f82 --- /dev/null +++ b/fs/ceph/crypto.h @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Ceph fscrypt functionality + */ + +#ifndef _CEPH_CRYPTO_H +#define _CEPH_CRYPTO_H + +#include +#include + +#define CEPH_FSCRYPT_BLOCK_SHIFT 12 +#define CEPH_FSCRYPT_BLOCK_SIZE (_AC(1, UL) << CEPH_FSCRYPT_BLOCK_SHIFT) +#define CEPH_FSCRYPT_BLOCK_MASK (~(CEPH_FSCRYPT_BLOCK_SIZE-1)) + +struct ceph_fs_client; +struct ceph_acl_sec_ctx; +struct ceph_mds_request; + +struct ceph_fname { + struct inode *dir; + char *name; // b64 encoded, possibly hashed + unsigned char *ctext; // binary crypttext (if any) + u32 name_len; // length of name buffer + u32 ctext_len; // length of crypttext + bool no_copy; +}; + +/* + * Header for the crypted file when truncating the size, this + * will be sent to MDS, and the MDS will update the encrypted + * last block and then truncate the size. + */ +struct ceph_fscrypt_truncate_size_header { + __u8 ver; + __u8 compat; + + /* + * It will be sizeof(assert_ver + file_offset + block_size) + * if the last block is empty when it's located in a file + * hole. Or the data_len will plus CEPH_FSCRYPT_BLOCK_SIZE. + */ + __le32 data_len; + + __le64 change_attr; + __le64 file_offset; + __le32 block_size; +} __packed; + +struct ceph_fscrypt_auth { + __le32 cfa_version; + __le32 cfa_blob_len; + u8 cfa_blob[FSCRYPT_SET_CONTEXT_MAX_SIZE]; +} __packed; + +#define CEPH_FSCRYPT_AUTH_VERSION 1 +static inline u32 ceph_fscrypt_auth_len(struct ceph_fscrypt_auth *fa) +{ + u32 ctxsize = le32_to_cpu(fa->cfa_blob_len); + + return offsetof(struct ceph_fscrypt_auth, cfa_blob) + ctxsize; +} + +#ifdef CONFIG_FS_ENCRYPTION +/* + * We want to encrypt filenames when creating them, but the encrypted + * versions of those names may have illegal characters in them. To mitigate + * that, we base64 encode them, but that gives us a result that can exceed + * NAME_MAX. + * + * Follow a similar scheme to fscrypt itself, and cap the filename to a + * smaller size. If the ciphertext name is longer than the value below, then + * sha256 hash the remaining bytes. + * + * For the fscrypt_nokey_name struct the dirhash[2] member is useless in ceph + * so the corresponding struct will be: + * + * struct fscrypt_ceph_nokey_name { + * u8 bytes[157]; + * u8 sha256[SHA256_DIGEST_SIZE]; + * }; // 180 bytes => 240 bytes base64-encoded, which is <= NAME_MAX (255) + * + * (240 bytes is the maximum size allowed for snapshot names to take into + * account the format: '__'.) + * + * Note that for long names that end up having their tail portion hashed, we + * must also store the full encrypted name (in the dentry's alternate_name + * field). + */ +#define CEPH_NOHASH_NAME_MAX (180 - SHA256_DIGEST_SIZE) + +#define CEPH_BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) + +int ceph_base64_encode(const u8 *src, int srclen, char *dst); +int ceph_base64_decode(const char *src, int srclen, u8 *dst); + +void ceph_fscrypt_set_ops(struct super_block *sb); + +void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc); + +int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode, + struct ceph_acl_sec_ctx *as); +void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, + struct ceph_acl_sec_ctx *as); +int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, + char *buf); +int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, + char *buf); + +static inline int ceph_fname_alloc_buffer(struct inode *parent, + struct fscrypt_str *fname) +{ + if (!IS_ENCRYPTED(parent)) + return 0; + return fscrypt_fname_alloc_buffer(NAME_MAX, fname); +} + +static inline void ceph_fname_free_buffer(struct inode *parent, + struct fscrypt_str *fname) +{ + if (IS_ENCRYPTED(parent)) + fscrypt_fname_free_buffer(fname); +} + +int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, + struct fscrypt_str *oname, bool *is_nokey); +int ceph_fscrypt_prepare_readdir(struct inode *dir); + +static inline unsigned int ceph_fscrypt_blocks(u64 off, u64 len) +{ + /* crypto blocks cannot span more than one page */ + BUILD_BUG_ON(CEPH_FSCRYPT_BLOCK_SHIFT > PAGE_SHIFT); + + return ((off+len+CEPH_FSCRYPT_BLOCK_SIZE-1) >> CEPH_FSCRYPT_BLOCK_SHIFT) - + (off >> CEPH_FSCRYPT_BLOCK_SHIFT); +} + +/* + * If we have an encrypted inode then we must adjust the offset and + * range of the on-the-wire read to cover an entire encryption block. + * The copy will be done using the original offset and length, after + * we've decrypted the result. + */ +static inline void ceph_fscrypt_adjust_off_and_len(struct inode *inode, + u64 *off, u64 *len) +{ + if (IS_ENCRYPTED(inode)) { + *len = ceph_fscrypt_blocks(*off, *len) * CEPH_FSCRYPT_BLOCK_SIZE; + *off &= CEPH_FSCRYPT_BLOCK_MASK; + } +} + +int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num); +int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags); +int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page, + u64 off, int len); +int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page, + u64 off, struct ceph_sparse_extent *map, + u32 ext_cnt); +int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off, + int len, gfp_t gfp); + +static inline struct page *ceph_fscrypt_pagecache_page(struct page *page) +{ + return fscrypt_is_bounce_page(page) ? fscrypt_pagecache_page(page) : page; +} + +#else /* CONFIG_FS_ENCRYPTION */ + +static inline void ceph_fscrypt_set_ops(struct super_block *sb) +{ +} + +static inline void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc) +{ +} + +static inline int ceph_fscrypt_prepare_context(struct inode *dir, + struct inode *inode, + struct ceph_acl_sec_ctx *as) +{ + if (IS_ENCRYPTED(dir)) + return -EOPNOTSUPP; + return 0; +} + +static inline void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, + struct ceph_acl_sec_ctx *as_ctx) +{ +} + +static inline int ceph_encode_encrypted_dname(struct inode *parent, + struct qstr *d_name, char *buf) +{ + memcpy(buf, d_name->name, d_name->len); + return d_name->len; +} + +static inline int ceph_encode_encrypted_fname(struct inode *parent, + struct dentry *dentry, char *buf) +{ + return -EOPNOTSUPP; +} + +static inline int ceph_fname_alloc_buffer(struct inode *parent, + struct fscrypt_str *fname) +{ + return 0; +} + +static inline void ceph_fname_free_buffer(struct inode *parent, + struct fscrypt_str *fname) +{ +} + +static inline int ceph_fname_to_usr(const struct ceph_fname *fname, + struct fscrypt_str *tname, + struct fscrypt_str *oname, bool *is_nokey) +{ + oname->name = fname->name; + oname->len = fname->name_len; + return 0; +} + +static inline int ceph_fscrypt_prepare_readdir(struct inode *dir) +{ + return 0; +} + +static inline void ceph_fscrypt_adjust_off_and_len(struct inode *inode, + u64 *off, u64 *len) +{ +} + +static inline int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num) +{ + return 0; +} + +static inline int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode, + struct page *page, unsigned int len, + unsigned int offs, u64 lblk_num, + gfp_t gfp_flags) +{ + return 0; +} + +static inline int ceph_fscrypt_decrypt_pages(struct inode *inode, + struct page **page, u64 off, + int len) +{ + return 0; +} + +static inline int ceph_fscrypt_decrypt_extents(struct inode *inode, + struct page **page, u64 off, + struct ceph_sparse_extent *map, + u32 ext_cnt) +{ + return 0; +} + +static inline int ceph_fscrypt_encrypt_pages(struct inode *inode, + struct page **page, u64 off, + int len, gfp_t gfp) +{ + return 0; +} + +static inline struct page *ceph_fscrypt_pagecache_page(struct page *page) +{ + return page; +} +#endif /* CONFIG_FS_ENCRYPTION */ + +static inline loff_t ceph_fscrypt_page_offset(struct page *page) +{ + return page_offset(ceph_fscrypt_pagecache_page(page)); +} + +#endif /* _CEPH_CRYPTO_H */ diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index bdcffb04513fb537c561ad246f9fd5b6ae56494b..854cbdd666619aeef65f1ecca063112a68e2ac7e 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -9,6 +9,7 @@ #include "super.h" #include "mds_client.h" +#include "crypto.h" /* * Directory operations: readdir, lookup, create, link, unlink, @@ -241,7 +242,9 @@ static int __dcache_readdir(struct file *file, struct dir_context *ctx, di = ceph_dentry(dentry); if (d_unhashed(dentry) || d_really_is_negative(dentry) || - di->lease_shared_gen != shared_gen) { + di->lease_shared_gen != shared_gen || + ((dentry->d_flags & DCACHE_NOKEY_NAME) && + fscrypt_has_encryption_key(dir))) { spin_unlock(&dentry->d_lock); dput(dentry); err = -EAGAIN; @@ -340,6 +343,10 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) ctx->pos = 2; } + err = ceph_fscrypt_prepare_readdir(inode); + if (err < 0) + return err; + spin_lock(&ci->i_ceph_lock); /* request Fx cap. if have Fx, we don't need to release Fs cap * for later create/unlink. */ @@ -389,6 +396,7 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); if (IS_ERR(req)) return PTR_ERR(req); + err = ceph_alloc_readdir_reply_buffer(req, inode); if (err) { ceph_mdsc_put_request(req); @@ -402,11 +410,21 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) req->r_inode_drop = CEPH_CAP_FILE_EXCL; } if (dfi->last_name) { - req->r_path2 = kstrdup(dfi->last_name, GFP_KERNEL); + struct qstr d_name = { .name = dfi->last_name, + .len = strlen(dfi->last_name) }; + + req->r_path2 = kzalloc(NAME_MAX + 1, GFP_KERNEL); if (!req->r_path2) { ceph_mdsc_put_request(req); return -ENOMEM; } + + err = ceph_encode_encrypted_dname(inode, &d_name, + req->r_path2); + if (err < 0) { + ceph_mdsc_put_request(req); + return err; + } } else if (is_hash_order(ctx->pos)) { req->r_args.readdir.offset_hash = cpu_to_le32(fpos_hash(ctx->pos)); @@ -511,15 +529,20 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) for (; i < rinfo->dir_nr; i++) { struct ceph_mds_reply_dir_entry *rde = rinfo->dir_entries + i; - BUG_ON(rde->offset < ctx->pos); + if (rde->offset < ctx->pos) { + pr_warn("%s: rde->offset 0x%llx ctx->pos 0x%llx\n", + __func__, rde->offset, ctx->pos); + return -EIO; + } + + if (WARN_ON_ONCE(!rde->inode.in)) + return -EIO; ctx->pos = rde->offset; dout("readdir (%d/%d) -> %llx '%.*s' %p\n", i, rinfo->dir_nr, ctx->pos, rde->name_len, rde->name, &rde->inode.in); - BUG_ON(!rde->inode.in); - if (!dir_emit(ctx, rde->name, rde->name_len, ceph_present_ino(inode->i_sb, le64_to_cpu(rde->inode.in->ino)), le32_to_cpu(rde->inode.in->mode) >> 12)) { @@ -532,6 +555,8 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) dout("filldir stopping us...\n"); return 0; } + + /* Reset the lengths to their original allocated vals */ ctx->pos++; } @@ -586,7 +611,6 @@ static int ceph_readdir(struct file *file, struct dir_context *ctx) dfi->dir_ordered_count); spin_unlock(&ci->i_ceph_lock); } - dout("readdir %p file %p done.\n", inode, file); return 0; } @@ -760,6 +784,18 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, if (dentry->d_name.len > NAME_MAX) return ERR_PTR(-ENAMETOOLONG); + if (IS_ENCRYPTED(dir)) { + bool had_key = fscrypt_has_encryption_key(dir); + + err = fscrypt_prepare_lookup_partial(dir, dentry); + if (err < 0) + return ERR_PTR(err); + + /* mark directory as incomplete if it has been unlocked */ + if (!had_key && fscrypt_has_encryption_key(dir)) + ceph_dir_clear_complete(dir); + } + /* can we conclude ENOENT locally? */ if (d_really_is_negative(dentry)) { struct ceph_inode_info *ci = ceph_inode(dir); @@ -865,13 +901,6 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir, goto out; } - err = ceph_pre_init_acls(dir, &mode, &as_ctx); - if (err < 0) - goto out; - err = ceph_security_init_secctx(dentry, mode, &as_ctx); - if (err < 0) - goto out; - dout("mknod in dir %p dentry %p mode 0%ho rdev %d\n", dir, dentry, mode, rdev); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_MKNOD, USE_AUTH_MDS); @@ -879,6 +908,17 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir, err = PTR_ERR(req); goto out; } + + req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); + if (IS_ERR(req->r_new_inode)) { + err = PTR_ERR(req->r_new_inode); + req->r_new_inode = NULL; + goto out_req; + } + + if (S_ISREG(mode) && IS_ENCRYPTED(dir)) + set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); + req->r_dentry = dget(dentry); req->r_num_caps = 2; req->r_parent = dir; @@ -889,13 +929,13 @@ static int ceph_mknod(struct mnt_idmap *idmap, struct inode *dir, req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | CEPH_CAP_XATTR_EXCL; req->r_dentry_unless = CEPH_CAP_FILE_EXCL; - if (as_ctx.pagelist) { - req->r_pagelist = as_ctx.pagelist; - as_ctx.pagelist = NULL; - } + + ceph_as_ctx_to_req(req, &as_ctx); + err = ceph_mdsc_do_request(mdsc, dir, req); if (!err && !req->r_reply_info.head->is_dentry) err = ceph_handle_notrace_create(dir, dentry); +out_req: ceph_mdsc_put_request(req); out: if (!err) @@ -912,12 +952,50 @@ static int ceph_create(struct mnt_idmap *idmap, struct inode *dir, return ceph_mknod(idmap, dir, dentry, mode, 0); } +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +static int prep_encrypted_symlink_target(struct ceph_mds_request *req, + const char *dest) +{ + int err; + int len = strlen(dest); + struct fscrypt_str osd_link = FSTR_INIT(NULL, 0); + + err = fscrypt_prepare_symlink(req->r_parent, dest, len, PATH_MAX, + &osd_link); + if (err) + goto out; + + err = fscrypt_encrypt_symlink(req->r_new_inode, dest, len, &osd_link); + if (err) + goto out; + + req->r_path2 = kmalloc(CEPH_BASE64_CHARS(osd_link.len) + 1, GFP_KERNEL); + if (!req->r_path2) { + err = -ENOMEM; + goto out; + } + + len = ceph_base64_encode(osd_link.name, osd_link.len, req->r_path2); + req->r_path2[len] = '\0'; +out: + fscrypt_fname_free_buffer(&osd_link); + return err; +} +#else +static int prep_encrypted_symlink_target(struct ceph_mds_request *req, + const char *dest) +{ + return -EOPNOTSUPP; +} +#endif + static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir, struct dentry *dentry, const char *dest) { struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); struct ceph_mds_request *req; struct ceph_acl_sec_ctx as_ctx = {}; + umode_t mode = S_IFLNK | 0777; int err; if (ceph_snap(dir) != CEPH_NOSNAP) @@ -932,38 +1010,48 @@ static int ceph_symlink(struct mnt_idmap *idmap, struct inode *dir, goto out; } - err = ceph_security_init_secctx(dentry, S_IFLNK | 0777, &as_ctx); - if (err < 0) - goto out; - dout("symlink in dir %p dentry %p to '%s'\n", dir, dentry, dest); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_SYMLINK, USE_AUTH_MDS); if (IS_ERR(req)) { err = PTR_ERR(req); goto out; } - req->r_path2 = kstrdup(dest, GFP_KERNEL); - if (!req->r_path2) { - err = -ENOMEM; - ceph_mdsc_put_request(req); - goto out; + + req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); + if (IS_ERR(req->r_new_inode)) { + err = PTR_ERR(req->r_new_inode); + req->r_new_inode = NULL; + goto out_req; } + req->r_parent = dir; ihold(dir); + if (IS_ENCRYPTED(req->r_new_inode)) { + err = prep_encrypted_symlink_target(req, dest); + if (err) + goto out_req; + } else { + req->r_path2 = kstrdup(dest, GFP_KERNEL); + if (!req->r_path2) { + err = -ENOMEM; + goto out_req; + } + } + set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); req->r_dentry = dget(dentry); req->r_num_caps = 2; req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | CEPH_CAP_XATTR_EXCL; req->r_dentry_unless = CEPH_CAP_FILE_EXCL; - if (as_ctx.pagelist) { - req->r_pagelist = as_ctx.pagelist; - as_ctx.pagelist = NULL; - } + + ceph_as_ctx_to_req(req, &as_ctx); + err = ceph_mdsc_do_request(mdsc, dir, req); if (!err && !req->r_reply_info.head->is_dentry) err = ceph_handle_notrace_create(dir, dentry); +out_req: ceph_mdsc_put_request(req); out: if (err) @@ -1003,14 +1091,12 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir, err = -EDQUOT; goto out; } - - mode |= S_IFDIR; - err = ceph_pre_init_acls(dir, &mode, &as_ctx); - if (err < 0) - goto out; - err = ceph_security_init_secctx(dentry, mode, &as_ctx); - if (err < 0) + if ((op == CEPH_MDS_OP_MKSNAP) && IS_ENCRYPTED(dir) && + !fscrypt_has_encryption_key(dir)) { + err = -ENOKEY; goto out; + } + req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); if (IS_ERR(req)) { @@ -1018,6 +1104,14 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir, goto out; } + mode |= S_IFDIR; + req->r_new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); + if (IS_ERR(req->r_new_inode)) { + err = PTR_ERR(req->r_new_inode); + req->r_new_inode = NULL; + goto out_req; + } + req->r_dentry = dget(dentry); req->r_num_caps = 2; req->r_parent = dir; @@ -1027,15 +1121,15 @@ static int ceph_mkdir(struct mnt_idmap *idmap, struct inode *dir, req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | CEPH_CAP_XATTR_EXCL; req->r_dentry_unless = CEPH_CAP_FILE_EXCL; - if (as_ctx.pagelist) { - req->r_pagelist = as_ctx.pagelist; - as_ctx.pagelist = NULL; - } + + ceph_as_ctx_to_req(req, &as_ctx); + err = ceph_mdsc_do_request(mdsc, dir, req); if (!err && !req->r_reply_info.head->is_target && !req->r_reply_info.head->is_dentry) err = ceph_handle_notrace_create(dir, dentry); +out_req: ceph_mdsc_put_request(req); out: if (!err) @@ -1063,6 +1157,10 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir, if (ceph_snap(dir) != CEPH_NOSNAP) return -EROFS; + err = fscrypt_prepare_link(old_dentry, dir, dentry); + if (err) + return err; + dout("link in dir %p %llx.%llx old_dentry %p:'%pd' dentry %p:'%pd'\n", dir, ceph_vinop(dir), old_dentry, old_dentry, dentry, dentry); req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LINK, USE_AUTH_MDS); @@ -1310,6 +1408,11 @@ static int ceph_rename(struct mnt_idmap *idmap, struct inode *old_dir, if (err) return err; + err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, + flags); + if (err) + return err; + dout("rename dir %p dentry %p to dir %p dentry %p\n", old_dir, old_dentry, new_dir, new_dentry); req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS); @@ -1765,6 +1868,10 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags) struct inode *dir, *inode; struct ceph_mds_client *mdsc; + valid = fscrypt_d_revalidate(dentry, flags); + if (valid <= 0) + return valid; + if (flags & LOOKUP_RCU) { parent = READ_ONCE(dentry->d_parent); dir = d_inode_rcu(parent); @@ -1777,8 +1884,9 @@ static int ceph_d_revalidate(struct dentry *dentry, unsigned int flags) inode = d_inode(dentry); } - dout("d_revalidate %p '%pd' inode %p offset 0x%llx\n", dentry, - dentry, inode, ceph_dentry(dentry)->offset); + dout("d_revalidate %p '%pd' inode %p offset 0x%llx nokey %d\n", dentry, + dentry, inode, ceph_dentry(dentry)->offset, + !!(dentry->d_flags & DCACHE_NOKEY_NAME)); mdsc = ceph_sb_to_client(dir->i_sb)->mdsc; diff --git a/fs/ceph/export.c b/fs/ceph/export.c index f780e4e0d06295cad7bf8721b6ca2aaa701d454e..8559990a59a5c5a5dcf8acc66abab67770f7c935 100644 --- a/fs/ceph/export.c +++ b/fs/ceph/export.c @@ -7,6 +7,7 @@ #include "super.h" #include "mds_client.h" +#include "crypto.h" /* * Basic fh @@ -535,7 +536,9 @@ static int ceph_get_name(struct dentry *parent, char *name, { struct ceph_mds_client *mdsc; struct ceph_mds_request *req; + struct inode *dir = d_inode(parent); struct inode *inode = d_inode(child); + struct ceph_mds_reply_info_parsed *rinfo; int err; if (ceph_snap(inode) != CEPH_NOSNAP) @@ -547,30 +550,47 @@ static int ceph_get_name(struct dentry *parent, char *name, if (IS_ERR(req)) return PTR_ERR(req); - inode_lock(d_inode(parent)); - + inode_lock(dir); req->r_inode = inode; ihold(inode); req->r_ino2 = ceph_vino(d_inode(parent)); - req->r_parent = d_inode(parent); - ihold(req->r_parent); + req->r_parent = dir; + ihold(dir); set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); req->r_num_caps = 2; err = ceph_mdsc_do_request(mdsc, NULL, req); + inode_unlock(dir); - inode_unlock(d_inode(parent)); + if (err) + goto out; - if (!err) { - struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; + rinfo = &req->r_reply_info; + if (!IS_ENCRYPTED(dir)) { memcpy(name, rinfo->dname, rinfo->dname_len); name[rinfo->dname_len] = 0; - dout("get_name %p ino %llx.%llx name %s\n", - child, ceph_vinop(inode), name); } else { - dout("get_name %p ino %llx.%llx err %d\n", - child, ceph_vinop(inode), err); - } + struct fscrypt_str oname = FSTR_INIT(NULL, 0); + struct ceph_fname fname = { .dir = dir, + .name = rinfo->dname, + .ctext = rinfo->altname, + .name_len = rinfo->dname_len, + .ctext_len = rinfo->altname_len }; + + err = ceph_fname_alloc_buffer(dir, &oname); + if (err < 0) + goto out; + err = ceph_fname_to_usr(&fname, NULL, &oname, NULL); + if (!err) { + memcpy(name, oname.name, oname.len); + name[oname.len] = 0; + } + ceph_fname_free_buffer(dir, &oname); + } +out: + dout("get_name %p ino %llx.%llx err %d %s%s\n", + child, ceph_vinop(inode), err, + err ? "" : "name ", err ? "" : name); ceph_mdsc_put_request(req); return err; } diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 63efe5389783ce7aa3d61959188e00fb47e328dc..b1da02f5dbe3b4f31a6e9b8b259ad974b1d9238f 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -366,8 +366,13 @@ int ceph_open(struct inode *inode, struct file *file) /* filter out O_CREAT|O_EXCL; vfs did that already. yuck. */ flags = file->f_flags & ~(O_CREAT|O_EXCL); - if (S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) { flags = O_DIRECTORY; /* mds likes to know */ + } else if (S_ISREG(inode->i_mode)) { + err = fscrypt_file_open(inode, file); + if (err) + return err; + } dout("open inode %p ino %llx.%llx file %p flags %d (%d)\n", inode, ceph_vinop(inode), file, flags, file->f_flags); @@ -604,7 +609,8 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc, ceph_mdsc_release_dir_caps(req); } -static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, +static int ceph_finish_async_create(struct inode *dir, struct inode *inode, + struct dentry *dentry, struct file *file, umode_t mode, struct ceph_mds_request *req, struct ceph_acl_sec_ctx *as_ctx, @@ -616,7 +622,6 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, struct ceph_mds_reply_info_in iinfo = { .in = &in }; struct ceph_inode_info *ci = ceph_inode(dir); struct ceph_dentry_info *di = ceph_dentry(dentry); - struct inode *inode; struct timespec64 now; struct ceph_string *pool_ns; struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb); @@ -625,10 +630,6 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, ktime_get_real_ts64(&now); - inode = ceph_get_inode(dentry->d_sb, vino); - if (IS_ERR(inode)) - return PTR_ERR(inode); - iinfo.inline_version = CEPH_INLINE_NONE; iinfo.change_attr = 1; ceph_encode_timespec64(&iinfo.btime, &now); @@ -686,8 +687,7 @@ static int ceph_finish_async_create(struct inode *dir, struct dentry *dentry, ceph_dir_clear_complete(dir); if (!d_unhashed(dentry)) d_drop(dentry); - if (inode->i_state & I_NEW) - discard_new_inode(inode); + discard_new_inode(inode); } else { struct dentry *dn; @@ -733,6 +733,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); struct ceph_mds_client *mdsc = fsc->mdsc; struct ceph_mds_request *req; + struct inode *new_inode = NULL; struct dentry *dn; struct ceph_acl_sec_ctx as_ctx = {}; bool try_async = ceph_test_mount_opt(fsc, ASYNC_DIROPS); @@ -755,15 +756,16 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, */ flags &= ~O_TRUNC; +retry: if (flags & O_CREAT) { if (ceph_quota_is_max_files_exceeded(dir)) return -EDQUOT; - err = ceph_pre_init_acls(dir, &mode, &as_ctx); - if (err < 0) - return err; - err = ceph_security_init_secctx(dentry, mode, &as_ctx); - if (err < 0) + + new_inode = ceph_new_inode(dir, dentry, &mode, &as_ctx); + if (IS_ERR(new_inode)) { + err = PTR_ERR(new_inode); goto out_ctx; + } /* Async create can't handle more than a page of xattrs */ if (as_ctx.pagelist && !list_is_singular(&as_ctx.pagelist->head)) @@ -772,7 +774,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, /* If it's not being looked up, it's negative */ return -ENOENT; } -retry: + /* do the open */ req = prepare_open_request(dir->i_sb, flags, mode); if (IS_ERR(req)) { @@ -787,6 +789,12 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, req->r_args.open.mask = cpu_to_le32(mask); req->r_parent = dir; ihold(dir); + if (IS_ENCRYPTED(dir)) { + set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); + err = fscrypt_prepare_lookup_partial(dir, dentry); + if (err < 0) + goto out_req; + } if (flags & O_CREAT) { struct ceph_file_layout lo; @@ -794,32 +802,47 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, req->r_dentry_drop = CEPH_CAP_FILE_SHARED | CEPH_CAP_AUTH_EXCL | CEPH_CAP_XATTR_EXCL; req->r_dentry_unless = CEPH_CAP_FILE_EXCL; - if (as_ctx.pagelist) { - req->r_pagelist = as_ctx.pagelist; - as_ctx.pagelist = NULL; - } - if (try_async && - (req->r_dir_caps = - try_prep_async_create(dir, dentry, &lo, - &req->r_deleg_ino))) { + + ceph_as_ctx_to_req(req, &as_ctx); + + if (try_async && (req->r_dir_caps = + try_prep_async_create(dir, dentry, &lo, + &req->r_deleg_ino))) { + struct ceph_vino vino = { .ino = req->r_deleg_ino, + .snap = CEPH_NOSNAP }; struct ceph_dentry_info *di = ceph_dentry(dentry); set_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags); req->r_args.open.flags |= cpu_to_le32(CEPH_O_EXCL); req->r_callback = ceph_async_create_cb; + /* Hash inode before RPC */ + new_inode = ceph_get_inode(dir->i_sb, vino, new_inode); + if (IS_ERR(new_inode)) { + err = PTR_ERR(new_inode); + new_inode = NULL; + goto out_req; + } + WARN_ON_ONCE(!(new_inode->i_state & I_NEW)); + spin_lock(&dentry->d_lock); di->flags |= CEPH_DENTRY_ASYNC_CREATE; spin_unlock(&dentry->d_lock); err = ceph_mdsc_submit_request(mdsc, dir, req); if (!err) { - err = ceph_finish_async_create(dir, dentry, - file, mode, req, - &as_ctx, &lo); + err = ceph_finish_async_create(dir, new_inode, + dentry, file, + mode, req, + &as_ctx, &lo); + new_inode = NULL; } else if (err == -EJUKEBOX) { restore_deleg_ino(dir, req->r_deleg_ino); ceph_mdsc_put_request(req); + discard_new_inode(new_inode); + ceph_release_acl_sec_ctx(&as_ctx); + memset(&as_ctx, 0, sizeof(as_ctx)); + new_inode = NULL; try_async = false; ceph_put_string(rcu_dereference_raw(lo.pool_ns)); goto retry; @@ -830,6 +853,8 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, } set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags); + req->r_new_inode = new_inode; + new_inode = NULL; err = ceph_mdsc_do_request(mdsc, (flags & O_CREAT) ? dir : NULL, req); if (err == -ENOENT) { dentry = ceph_handle_snapdir(req, dentry); @@ -858,6 +883,13 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, dout("atomic_open finish_no_open on dn %p\n", dn); err = finish_no_open(file, dn); } else { + if (IS_ENCRYPTED(dir) && + !fscrypt_has_permitted_context(dir, d_inode(dentry))) { + pr_warn("Inconsistent encryption context (parent %llx:%llx child %llx:%llx)\n", + ceph_vinop(dir), ceph_vinop(d_inode(dentry))); + goto out_req; + } + dout("atomic_open finish_open on dn %p\n", dn); if (req->r_op == CEPH_MDS_OP_CREATE && req->r_reply_info.has_create_ino) { struct inode *newino = d_inode(dentry); @@ -870,6 +902,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, } out_req: ceph_mdsc_put_request(req); + iput(new_inode); out_ctx: ceph_release_acl_sec_ctx(&as_ctx); dout("atomic_open result=%d\n", err); @@ -924,21 +957,24 @@ enum { * If we get a short result from the OSD, check against i_size; we need to * only return a short read to the caller if we hit EOF. */ -static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, - int *retry_op) +ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos, + struct iov_iter *to, int *retry_op, + u64 *last_objver) { - struct file *file = iocb->ki_filp; - struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_osd_client *osdc = &fsc->client->osdc; ssize_t ret; - u64 off = iocb->ki_pos; + u64 off = *ki_pos; u64 len = iov_iter_count(to); u64 i_size = i_size_read(inode); + bool sparse = IS_ENCRYPTED(inode) || ceph_test_mount_opt(fsc, SPARSEREAD); + u64 objver = 0; - dout("sync_read on file %p %llu~%u %s\n", file, off, (unsigned)len, - (file->f_flags & O_DIRECT) ? "O_DIRECT" : ""); + dout("sync_read on inode %p %llx~%llx\n", inode, *ki_pos, len); + + if (ceph_inode_is_shutdown(inode)) + return -EIO; if (!len) return 0; @@ -962,10 +998,21 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, bool more; int idx; size_t left; + struct ceph_osd_req_op *op; + u64 read_off = off; + u64 read_len = len; + + /* determine new offset/length if encrypted */ + ceph_fscrypt_adjust_off_and_len(inode, &read_off, &read_len); + + dout("sync_read orig %llu~%llu reading %llu~%llu", + off, len, read_off, read_len); req = ceph_osdc_new_request(osdc, &ci->i_layout, - ci->i_vino, off, &len, 0, 1, - CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, + ci->i_vino, read_off, &read_len, 0, 1, + sparse ? CEPH_OSD_OP_SPARSE_READ : + CEPH_OSD_OP_READ, + CEPH_OSD_FLAG_READ, NULL, ci->i_truncate_seq, ci->i_truncate_size, false); if (IS_ERR(req)) { @@ -973,10 +1020,13 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, break; } + /* adjust len downward if the request truncated the len */ + if (off + len > read_off + read_len) + len = read_off + read_len - off; more = len < iov_iter_count(to); - num_pages = calc_pages_for(off, len); - page_off = off & ~PAGE_MASK; + num_pages = calc_pages_for(read_off, read_len); + page_off = offset_in_page(off); pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); if (IS_ERR(pages)) { ceph_osdc_put_request(req); @@ -984,29 +1034,75 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, break; } - osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_off, + osd_req_op_extent_osd_data_pages(req, 0, pages, read_len, + offset_in_page(read_off), false, false); + + op = &req->r_ops[0]; + if (sparse) { + ret = ceph_alloc_sparse_ext_map(op); + if (ret) { + ceph_osdc_put_request(req); + break; + } + } + ceph_osdc_start_request(osdc, req); ret = ceph_osdc_wait_request(osdc, req); ceph_update_read_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, - len, ret); + read_len, ret); - ceph_osdc_put_request(req); + if (ret > 0) + objver = req->r_version; i_size = i_size_read(inode); dout("sync_read %llu~%llu got %zd i_size %llu%s\n", off, len, ret, i_size, (more ? " MORE" : "")); - if (ret == -ENOENT) + /* Fix it to go to end of extent map */ + if (sparse && ret >= 0) + ret = ceph_sparse_ext_map_end(op); + else if (ret == -ENOENT) ret = 0; + + if (ret > 0 && IS_ENCRYPTED(inode)) { + int fret; + + fret = ceph_fscrypt_decrypt_extents(inode, pages, + read_off, op->extent.sparse_ext, + op->extent.sparse_ext_cnt); + if (fret < 0) { + ret = fret; + ceph_osdc_put_request(req); + break; + } + + /* account for any partial block at the beginning */ + fret -= (off - read_off); + + /* + * Short read after big offset adjustment? + * Nothing is usable, just call it a zero + * len read. + */ + fret = max(fret, 0); + + /* account for partial block at the end */ + ret = min_t(ssize_t, fret, len); + } + + ceph_osdc_put_request(req); + + /* Short read but not EOF? Zero out the remainder. */ if (ret >= 0 && ret < len && (off + ret < i_size)) { int zlen = min(len - ret, i_size - off - ret); int zoff = page_off + ret; + dout("sync_read zero gap %llu~%llu\n", - off + ret, off + ret + zlen); + off + ret, off + ret + zlen); ceph_zero_page_vector_range(zoff, zlen, pages); ret += zlen; } @@ -1014,15 +1110,16 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, idx = 0; left = ret > 0 ? ret : 0; while (left > 0) { - size_t len, copied; - page_off = off & ~PAGE_MASK; - len = min_t(size_t, left, PAGE_SIZE - page_off); + size_t plen, copied; + + plen = min_t(size_t, left, PAGE_SIZE - page_off); SetPageUptodate(pages[idx]); copied = copy_page_to_iter(pages[idx++], - page_off, len, to); + page_off, plen, to); off += copied; left -= copied; - if (copied < len) { + page_off = 0; + if (copied < plen) { ret = -EFAULT; break; } @@ -1039,21 +1136,37 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, break; } - if (off > iocb->ki_pos) { - if (off >= i_size) { - *retry_op = CHECK_EOF; - ret = i_size - iocb->ki_pos; - iocb->ki_pos = i_size; - } else { - ret = off - iocb->ki_pos; - iocb->ki_pos = off; + if (ret > 0) { + if (off > *ki_pos) { + if (off >= i_size) { + *retry_op = CHECK_EOF; + ret = i_size - *ki_pos; + *ki_pos = i_size; + } else { + ret = off - *ki_pos; + *ki_pos = off; + } } - } + if (last_objver) + *last_objver = objver; + } dout("sync_read result %zd retry_op %d\n", ret, *retry_op); return ret; } +static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to, + int *retry_op) +{ + struct file *file = iocb->ki_filp; + struct inode *inode = file_inode(file); + + dout("sync_read on file %p %llx~%zx %s\n", file, iocb->ki_pos, + iov_iter_count(to), (file->f_flags & O_DIRECT) ? "O_DIRECT" : ""); + + return __ceph_sync_read(inode, &iocb->ki_pos, to, retry_op, NULL); +} + struct ceph_aio_request { struct kiocb *iocb; size_t total_len; @@ -1125,8 +1238,10 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req) struct inode *inode = req->r_inode; struct ceph_aio_request *aio_req = req->r_priv; struct ceph_osd_data *osd_data = osd_req_op_extent_osd_data(req, 0); + struct ceph_osd_req_op *op = &req->r_ops[0]; struct ceph_client_metric *metric = &ceph_sb_to_mdsc(inode->i_sb)->metric; unsigned int len = osd_data->bvec_pos.iter.bi_size; + bool sparse = (op->op == CEPH_OSD_OP_SPARSE_READ); BUG_ON(osd_data->type != CEPH_OSD_DATA_TYPE_BVECS); BUG_ON(!osd_data->num_bvecs); @@ -1147,6 +1262,8 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req) } rc = -ENOMEM; } else if (!aio_req->write) { + if (sparse && rc >= 0) + rc = ceph_sparse_ext_map_end(op); if (rc == -ENOENT) rc = 0; if (rc >= 0 && len > rc) { @@ -1283,6 +1400,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, loff_t pos = iocb->ki_pos; bool write = iov_iter_rw(iter) == WRITE; bool should_dirty = !write && user_backed_iter(iter); + bool sparse = ceph_test_mount_opt(fsc, SPARSEREAD); if (write && ceph_snap(file_inode(file)) != CEPH_NOSNAP) return -EROFS; @@ -1310,6 +1428,8 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, while (iov_iter_count(iter) > 0) { u64 size = iov_iter_count(iter); ssize_t len; + struct ceph_osd_req_op *op; + int readop = sparse ? CEPH_OSD_OP_SPARSE_READ : CEPH_OSD_OP_READ; if (write) size = min_t(u64, size, fsc->mount_options->wsize); @@ -1320,8 +1440,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, vino, pos, &size, 0, 1, - write ? CEPH_OSD_OP_WRITE : - CEPH_OSD_OP_READ, + write ? CEPH_OSD_OP_WRITE : readop, flags, snapc, ci->i_truncate_seq, ci->i_truncate_size, @@ -1372,6 +1491,14 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, } osd_req_op_extent_osd_data_bvecs(req, 0, bvecs, num_pages, len); + op = &req->r_ops[0]; + if (sparse) { + ret = ceph_alloc_sparse_ext_map(op); + if (ret) { + ceph_osdc_put_request(req); + break; + } + } if (aio_req) { aio_req->total_len += len; @@ -1399,8 +1526,11 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter, size = i_size_read(inode); if (!write) { - if (ret == -ENOENT) + if (sparse && ret >= 0) + ret = ceph_sparse_ext_map_end(op); + else if (ret == -ENOENT) ret = 0; + if (ret >= 0 && ret < len && pos + ret < size) { struct iov_iter i; int zlen = min_t(size_t, len - ret, @@ -1481,13 +1611,12 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); - struct ceph_vino vino; + struct ceph_osd_client *osdc = &fsc->client->osdc; struct ceph_osd_request *req; struct page **pages; u64 len; int num_pages; int written = 0; - int flags; int ret; bool check_caps = false; struct timespec64 mtime = current_time(inode); @@ -1505,79 +1634,350 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, return ret; ceph_fscache_invalidate(inode, false); - ret = invalidate_inode_pages2_range(inode->i_mapping, - pos >> PAGE_SHIFT, - (pos + count - 1) >> PAGE_SHIFT); - if (ret < 0) - dout("invalidate_inode_pages2_range returned %d\n", ret); - - flags = /* CEPH_OSD_FLAG_ORDERSNAP | */ CEPH_OSD_FLAG_WRITE; while ((len = iov_iter_count(from)) > 0) { size_t left; int n; - - vino = ceph_vino(inode); - req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout, - vino, pos, &len, 0, 1, - CEPH_OSD_OP_WRITE, flags, snapc, - ci->i_truncate_seq, - ci->i_truncate_size, - false); - if (IS_ERR(req)) { - ret = PTR_ERR(req); - break; - } + u64 write_pos = pos; + u64 write_len = len; + u64 objnum, objoff; + u32 xlen; + u64 assert_ver = 0; + bool rmw; + bool first, last; + struct iov_iter saved_iter = *from; + size_t off; + + ceph_fscrypt_adjust_off_and_len(inode, &write_pos, &write_len); + + /* clamp the length to the end of first object */ + ceph_calc_file_object_mapping(&ci->i_layout, write_pos, + write_len, &objnum, &objoff, + &xlen); + write_len = xlen; + + /* adjust len downward if it goes beyond current object */ + if (pos + len > write_pos + write_len) + len = write_pos + write_len - pos; /* - * write from beginning of first page, - * regardless of io alignment + * If we had to adjust the length or position to align with a + * crypto block, then we must do a read/modify/write cycle. We + * use a version assertion to redrive the thing if something + * changes in between. */ - num_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + first = pos != write_pos; + last = (pos + len) != (write_pos + write_len); + rmw = first || last; + + dout("sync_write ino %llx %lld~%llu adjusted %lld~%llu -- %srmw\n", + ci->i_vino.ino, pos, len, write_pos, write_len, + rmw ? "" : "no "); + /* + * The data is emplaced into the page as it would be if it were + * in an array of pagecache pages. + */ + num_pages = calc_pages_for(write_pos, write_len); pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL); if (IS_ERR(pages)) { ret = PTR_ERR(pages); - goto out; + break; + } + + /* Do we need to preload the pages? */ + if (rmw) { + u64 first_pos = write_pos; + u64 last_pos = (write_pos + write_len) - CEPH_FSCRYPT_BLOCK_SIZE; + u64 read_len = CEPH_FSCRYPT_BLOCK_SIZE; + struct ceph_osd_req_op *op; + + /* We should only need to do this for encrypted inodes */ + WARN_ON_ONCE(!IS_ENCRYPTED(inode)); + + /* No need to do two reads if first and last blocks are same */ + if (first && last_pos == first_pos) + last = false; + + /* + * Allocate a read request for one or two extents, + * depending on how the request was aligned. + */ + req = ceph_osdc_new_request(osdc, &ci->i_layout, + ci->i_vino, first ? first_pos : last_pos, + &read_len, 0, (first && last) ? 2 : 1, + CEPH_OSD_OP_SPARSE_READ, CEPH_OSD_FLAG_READ, + NULL, ci->i_truncate_seq, + ci->i_truncate_size, false); + if (IS_ERR(req)) { + ceph_release_page_vector(pages, num_pages); + ret = PTR_ERR(req); + break; + } + + /* Something is misaligned! */ + if (read_len != CEPH_FSCRYPT_BLOCK_SIZE) { + ceph_osdc_put_request(req); + ceph_release_page_vector(pages, num_pages); + ret = -EIO; + break; + } + + /* Add extent for first block? */ + op = &req->r_ops[0]; + + if (first) { + osd_req_op_extent_osd_data_pages(req, 0, pages, + CEPH_FSCRYPT_BLOCK_SIZE, + offset_in_page(first_pos), + false, false); + /* We only expect a single extent here */ + ret = __ceph_alloc_sparse_ext_map(op, 1); + if (ret) { + ceph_osdc_put_request(req); + ceph_release_page_vector(pages, num_pages); + break; + } + } + + /* Add extent for last block */ + if (last) { + /* Init the other extent if first extent has been used */ + if (first) { + op = &req->r_ops[1]; + osd_req_op_extent_init(req, 1, + CEPH_OSD_OP_SPARSE_READ, + last_pos, CEPH_FSCRYPT_BLOCK_SIZE, + ci->i_truncate_size, + ci->i_truncate_seq); + } + + ret = __ceph_alloc_sparse_ext_map(op, 1); + if (ret) { + ceph_osdc_put_request(req); + ceph_release_page_vector(pages, num_pages); + break; + } + + osd_req_op_extent_osd_data_pages(req, first ? 1 : 0, + &pages[num_pages - 1], + CEPH_FSCRYPT_BLOCK_SIZE, + offset_in_page(last_pos), + false, false); + } + + ceph_osdc_start_request(osdc, req); + ret = ceph_osdc_wait_request(osdc, req); + + /* FIXME: length field is wrong if there are 2 extents */ + ceph_update_read_metrics(&fsc->mdsc->metric, + req->r_start_latency, + req->r_end_latency, + read_len, ret); + + /* Ok if object is not already present */ + if (ret == -ENOENT) { + /* + * If there is no object, then we can't assert + * on its version. Set it to 0, and we'll use an + * exclusive create instead. + */ + ceph_osdc_put_request(req); + ret = 0; + + /* + * zero out the soon-to-be uncopied parts of the + * first and last pages. + */ + if (first) + zero_user_segment(pages[0], 0, + offset_in_page(first_pos)); + if (last) + zero_user_segment(pages[num_pages - 1], + offset_in_page(last_pos), + PAGE_SIZE); + } else { + if (ret < 0) { + ceph_osdc_put_request(req); + ceph_release_page_vector(pages, num_pages); + break; + } + + op = &req->r_ops[0]; + if (op->extent.sparse_ext_cnt == 0) { + if (first) + zero_user_segment(pages[0], 0, + offset_in_page(first_pos)); + else + zero_user_segment(pages[num_pages - 1], + offset_in_page(last_pos), + PAGE_SIZE); + } else if (op->extent.sparse_ext_cnt != 1 || + ceph_sparse_ext_map_end(op) != + CEPH_FSCRYPT_BLOCK_SIZE) { + ret = -EIO; + ceph_osdc_put_request(req); + ceph_release_page_vector(pages, num_pages); + break; + } + + if (first && last) { + op = &req->r_ops[1]; + if (op->extent.sparse_ext_cnt == 0) { + zero_user_segment(pages[num_pages - 1], + offset_in_page(last_pos), + PAGE_SIZE); + } else if (op->extent.sparse_ext_cnt != 1 || + ceph_sparse_ext_map_end(op) != + CEPH_FSCRYPT_BLOCK_SIZE) { + ret = -EIO; + ceph_osdc_put_request(req); + ceph_release_page_vector(pages, num_pages); + break; + } + } + + /* Grab assert version. It must be non-zero. */ + assert_ver = req->r_version; + WARN_ON_ONCE(ret > 0 && assert_ver == 0); + + ceph_osdc_put_request(req); + if (first) { + ret = ceph_fscrypt_decrypt_block_inplace(inode, + pages[0], CEPH_FSCRYPT_BLOCK_SIZE, + offset_in_page(first_pos), + first_pos >> CEPH_FSCRYPT_BLOCK_SHIFT); + if (ret < 0) { + ceph_release_page_vector(pages, num_pages); + break; + } + } + if (last) { + ret = ceph_fscrypt_decrypt_block_inplace(inode, + pages[num_pages - 1], + CEPH_FSCRYPT_BLOCK_SIZE, + offset_in_page(last_pos), + last_pos >> CEPH_FSCRYPT_BLOCK_SHIFT); + if (ret < 0) { + ceph_release_page_vector(pages, num_pages); + break; + } + } + } } left = len; + off = offset_in_page(pos); for (n = 0; n < num_pages; n++) { - size_t plen = min_t(size_t, left, PAGE_SIZE); - ret = copy_page_from_iter(pages[n], 0, plen, from); + size_t plen = min_t(size_t, left, PAGE_SIZE - off); + + /* copy the data */ + ret = copy_page_from_iter(pages[n], off, plen, from); if (ret != plen) { ret = -EFAULT; break; } + off = 0; left -= ret; } - if (ret < 0) { + dout("sync_write write failed with %d\n", ret); ceph_release_page_vector(pages, num_pages); - goto out; + break; } - req->r_inode = inode; + if (IS_ENCRYPTED(inode)) { + ret = ceph_fscrypt_encrypt_pages(inode, pages, + write_pos, write_len, + GFP_KERNEL); + if (ret < 0) { + dout("encryption failed with %d\n", ret); + ceph_release_page_vector(pages, num_pages); + break; + } + } - osd_req_op_extent_osd_data_pages(req, 0, pages, len, 0, - false, true); + req = ceph_osdc_new_request(osdc, &ci->i_layout, + ci->i_vino, write_pos, &write_len, + rmw ? 1 : 0, rmw ? 2 : 1, + CEPH_OSD_OP_WRITE, + CEPH_OSD_FLAG_WRITE, + snapc, ci->i_truncate_seq, + ci->i_truncate_size, false); + if (IS_ERR(req)) { + ret = PTR_ERR(req); + ceph_release_page_vector(pages, num_pages); + break; + } + dout("sync_write write op %lld~%llu\n", write_pos, write_len); + osd_req_op_extent_osd_data_pages(req, rmw ? 1 : 0, pages, write_len, + offset_in_page(write_pos), false, + true); + req->r_inode = inode; req->r_mtime = mtime; - ceph_osdc_start_request(&fsc->client->osdc, req); - ret = ceph_osdc_wait_request(&fsc->client->osdc, req); + + /* Set up the assertion */ + if (rmw) { + /* + * Set up the assertion. If we don't have a version + * number, then the object doesn't exist yet. Use an + * exclusive create instead of a version assertion in + * that case. + */ + if (assert_ver) { + osd_req_op_init(req, 0, CEPH_OSD_OP_ASSERT_VER, 0); + req->r_ops[0].assert_ver.ver = assert_ver; + } else { + osd_req_op_init(req, 0, CEPH_OSD_OP_CREATE, + CEPH_OSD_OP_FLAG_EXCL); + } + } + + ceph_osdc_start_request(osdc, req); + ret = ceph_osdc_wait_request(osdc, req); ceph_update_write_metrics(&fsc->mdsc->metric, req->r_start_latency, req->r_end_latency, len, ret); -out: ceph_osdc_put_request(req); if (ret != 0) { + dout("sync_write osd write returned %d\n", ret); + /* Version changed! Must re-do the rmw cycle */ + if ((assert_ver && (ret == -ERANGE || ret == -EOVERFLOW)) || + (!assert_ver && ret == -EEXIST)) { + /* We should only ever see this on a rmw */ + WARN_ON_ONCE(!rmw); + + /* The version should never go backward */ + WARN_ON_ONCE(ret == -EOVERFLOW); + + *from = saved_iter; + + /* FIXME: limit number of times we loop? */ + continue; + } ceph_set_error_write(ci); break; } ceph_clear_error_write(ci); + + /* + * We successfully wrote to a range of the file. Declare + * that region of the pagecache invalid. + */ + ret = invalidate_inode_pages2_range( + inode->i_mapping, + pos >> PAGE_SHIFT, + (pos + len - 1) >> PAGE_SHIFT); + if (ret < 0) { + dout("invalidate_inode_pages2_range returned %d\n", + ret); + ret = 0; + } pos += len; written += len; + dout("sync_write written %d\n", written); if (pos > i_size_read(inode)) { check_caps = ceph_inode_set_size(inode, pos); if (check_caps) @@ -1591,6 +1991,7 @@ ceph_sync_write(struct kiocb *iocb, struct iov_iter *from, loff_t pos, ret = written; iocb->ki_pos = pos; } + dout("sync_write returning %d\n", ret); return ret; } @@ -1648,7 +2049,9 @@ static ssize_t ceph_read_iter(struct kiocb *iocb, struct iov_iter *to) ceph_cap_string(got)); if (!ceph_has_inline_data(ci)) { - if (!retry_op && (iocb->ki_flags & IOCB_DIRECT)) { + if (!retry_op && + (iocb->ki_flags & IOCB_DIRECT) && + !IS_ENCRYPTED(inode)) { ret = ceph_direct_read_write(iocb, to, NULL, NULL); if (ret >= 0 && ret < len) @@ -1934,7 +2337,7 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from) /* we might need to revert back to that point */ data = *from; - if (iocb->ki_flags & IOCB_DIRECT) + if ((iocb->ki_flags & IOCB_DIRECT) && !IS_ENCRYPTED(inode)) written = ceph_direct_read_write(iocb, &data, snapc, &prealloc_cf); else @@ -2165,6 +2568,9 @@ static long ceph_fallocate(struct file *file, int mode, if (!S_ISREG(inode->i_mode)) return -EOPNOTSUPP; + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + prealloc_cf = ceph_alloc_cap_flush(); if (!prealloc_cf) return -ENOMEM; @@ -2486,6 +2892,10 @@ static ssize_t __ceph_copy_file_range(struct file *src_file, loff_t src_off, return -EOPNOTSUPP; } + /* Every encrypted inode gets its own key, so we can't offload them */ + if (IS_ENCRYPTED(src_inode) || IS_ENCRYPTED(dst_inode)) + return -EOPNOTSUPP; + if (len < src_ci->i_layout.object_size) return -EOPNOTSUPP; /* no remote copy will be done */ diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index fd05d68e2990878dabc75729b18b6586faa4c71b..800ab79205137942c9b3c843218e5ea998b3802d 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -14,10 +14,12 @@ #include #include #include +#include #include "super.h" #include "mds_client.h" #include "cache.h" +#include "crypto.h" #include /* @@ -33,6 +35,7 @@ */ static const struct inode_operations ceph_symlink_iops; +static const struct inode_operations ceph_encrypted_symlink_iops; static void ceph_inode_work(struct work_struct *work); @@ -52,17 +55,99 @@ static int ceph_set_ino_cb(struct inode *inode, void *data) return 0; } -struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino) +/** + * ceph_new_inode - allocate a new inode in advance of an expected create + * @dir: parent directory for new inode + * @dentry: dentry that may eventually point to new inode + * @mode: mode of new inode + * @as_ctx: pointer to inherited security context + * + * Allocate a new inode in advance of an operation to create a new inode. + * This allocates the inode and sets up the acl_sec_ctx with appropriate + * info for the new inode. + * + * Returns a pointer to the new inode or an ERR_PTR. + */ +struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry, + umode_t *mode, struct ceph_acl_sec_ctx *as_ctx) +{ + int err; + struct inode *inode; + + inode = new_inode(dir->i_sb); + if (!inode) + return ERR_PTR(-ENOMEM); + + if (!S_ISLNK(*mode)) { + err = ceph_pre_init_acls(dir, mode, as_ctx); + if (err < 0) + goto out_err; + } + + inode->i_state = 0; + inode->i_mode = *mode; + + err = ceph_security_init_secctx(dentry, *mode, as_ctx); + if (err < 0) + goto out_err; + + /* + * We'll skip setting fscrypt context for snapshots, leaving that for + * the handle_reply(). + */ + if (ceph_snap(dir) != CEPH_SNAPDIR) { + err = ceph_fscrypt_prepare_context(dir, inode, as_ctx); + if (err) + goto out_err; + } + + return inode; +out_err: + iput(inode); + return ERR_PTR(err); +} + +void ceph_as_ctx_to_req(struct ceph_mds_request *req, + struct ceph_acl_sec_ctx *as_ctx) +{ + if (as_ctx->pagelist) { + req->r_pagelist = as_ctx->pagelist; + as_ctx->pagelist = NULL; + } + ceph_fscrypt_as_ctx_to_req(req, as_ctx); +} + +/** + * ceph_get_inode - find or create/hash a new inode + * @sb: superblock to search and allocate in + * @vino: vino to search for + * @newino: optional new inode to insert if one isn't found (may be NULL) + * + * Search for or insert a new inode into the hash for the given vino, and + * return a reference to it. If new is non-NULL, its reference is consumed. + */ +struct inode *ceph_get_inode(struct super_block *sb, struct ceph_vino vino, + struct inode *newino) { struct inode *inode; if (ceph_vino_is_reserved(vino)) return ERR_PTR(-EREMOTEIO); - inode = iget5_locked(sb, (unsigned long)vino.ino, ceph_ino_compare, - ceph_set_ino_cb, &vino); - if (!inode) + if (newino) { + inode = inode_insert5(newino, (unsigned long)vino.ino, + ceph_ino_compare, ceph_set_ino_cb, &vino); + if (inode != newino) + iput(newino); + } else { + inode = iget5_locked(sb, (unsigned long)vino.ino, + ceph_ino_compare, ceph_set_ino_cb, &vino); + } + + if (!inode) { + dout("No inode found for %llx.%llx\n", vino.ino, vino.snap); return ERR_PTR(-ENOMEM); + } dout("get_inode on %llu=%llx.%llx got %p new %d\n", ceph_present_inode(inode), ceph_vinop(inode), inode, !!(inode->i_state & I_NEW)); @@ -78,8 +163,9 @@ struct inode *ceph_get_snapdir(struct inode *parent) .ino = ceph_ino(parent), .snap = CEPH_SNAPDIR, }; - struct inode *inode = ceph_get_inode(parent->i_sb, vino); + struct inode *inode = ceph_get_inode(parent->i_sb, vino, NULL); struct ceph_inode_info *ci = ceph_inode(inode); + int ret = -ENOTDIR; if (IS_ERR(inode)) return inode; @@ -105,6 +191,24 @@ struct inode *ceph_get_snapdir(struct inode *parent) ci->i_rbytes = 0; ci->i_btime = ceph_inode(parent)->i_btime; +#ifdef CONFIG_FS_ENCRYPTION + /* if encrypted, just borrow fscrypt_auth from parent */ + if (IS_ENCRYPTED(parent)) { + struct ceph_inode_info *pci = ceph_inode(parent); + + ci->fscrypt_auth = kmemdup(pci->fscrypt_auth, + pci->fscrypt_auth_len, + GFP_KERNEL); + if (ci->fscrypt_auth) { + inode->i_flags |= S_ENCRYPTED; + ci->fscrypt_auth_len = pci->fscrypt_auth_len; + } else { + dout("Failed to alloc snapdir fscrypt_auth\n"); + ret = -ENOMEM; + goto err; + } + } +#endif if (inode->i_state & I_NEW) { inode->i_op = &ceph_snapdir_iops; inode->i_fop = &ceph_snapdir_fops; @@ -118,7 +222,7 @@ struct inode *ceph_get_snapdir(struct inode *parent) discard_new_inode(inode); else iput(inode); - return ERR_PTR(-ENOTDIR); + return ERR_PTR(ret); } const struct inode_operations ceph_file_iops = { @@ -517,6 +621,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb) ci->i_truncate_seq = 0; ci->i_truncate_size = 0; ci->i_truncate_pending = 0; + ci->i_truncate_pagecache_size = 0; ci->i_max_size = 0; ci->i_reported_size = 0; @@ -547,6 +652,10 @@ struct inode *ceph_alloc_inode(struct super_block *sb) INIT_WORK(&ci->i_work, ceph_inode_work); ci->i_work_mask = 0; memset(&ci->i_btime, '\0', sizeof(ci->i_btime)); +#ifdef CONFIG_FS_ENCRYPTION + ci->fscrypt_auth = NULL; + ci->fscrypt_auth_len = 0; +#endif return &ci->netfs.inode; } @@ -555,6 +664,10 @@ void ceph_free_inode(struct inode *inode) struct ceph_inode_info *ci = ceph_inode(inode); kfree(ci->i_symlink); +#ifdef CONFIG_FS_ENCRYPTION + kfree(ci->fscrypt_auth); +#endif + fscrypt_free_inode(inode); kmem_cache_free(ceph_inode_cachep, ci); } @@ -575,6 +688,7 @@ void ceph_evict_inode(struct inode *inode) clear_inode(inode); ceph_fscache_unregister_inode_cookie(ci); + fscrypt_put_encryption_info(inode); __ceph_remove_caps(ci); @@ -650,7 +764,7 @@ int ceph_fill_file_size(struct inode *inode, int issued, ceph_fscache_update(inode); ci->i_reported_size = size; if (truncate_seq != ci->i_truncate_seq) { - dout("truncate_seq %u -> %u\n", + dout("%s truncate_seq %u -> %u\n", __func__, ci->i_truncate_seq, truncate_seq); ci->i_truncate_seq = truncate_seq; @@ -674,11 +788,26 @@ int ceph_fill_file_size(struct inode *inode, int issued, } } } - if (ceph_seq_cmp(truncate_seq, ci->i_truncate_seq) >= 0 && - ci->i_truncate_size != truncate_size) { - dout("truncate_size %lld -> %llu\n", ci->i_truncate_size, - truncate_size); + + /* + * It's possible that the new sizes of the two consecutive + * size truncations will be in the same fscrypt last block, + * and we need to truncate the corresponding page caches + * anyway. + */ + if (ceph_seq_cmp(truncate_seq, ci->i_truncate_seq) >= 0) { + dout("%s truncate_size %lld -> %llu, encrypted %d\n", __func__, + ci->i_truncate_size, truncate_size, !!IS_ENCRYPTED(inode)); + ci->i_truncate_size = truncate_size; + + if (IS_ENCRYPTED(inode)) { + dout("%s truncate_pagecache_size %lld -> %llu\n", + __func__, ci->i_truncate_pagecache_size, size); + ci->i_truncate_pagecache_size = size; + } else { + ci->i_truncate_pagecache_size = truncate_size; + } } return queue_trunc; } @@ -752,6 +881,34 @@ void ceph_fill_file_time(struct inode *inode, int issued, inode, time_warp_seq, ci->i_time_warp_seq); } +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +static int decode_encrypted_symlink(const char *encsym, int enclen, u8 **decsym) +{ + int declen; + u8 *sym; + + sym = kmalloc(enclen + 1, GFP_NOFS); + if (!sym) + return -ENOMEM; + + declen = ceph_base64_decode(encsym, enclen, sym); + if (declen < 0) { + pr_err("%s: can't decode symlink (%d). Content: %.*s\n", + __func__, declen, enclen, encsym); + kfree(sym); + return -EIO; + } + sym[declen + 1] = '\0'; + *decsym = sym; + return declen; +} +#else +static int decode_encrypted_symlink(const char *encsym, int symlen, u8 **decsym) +{ + return -EOPNOTSUPP; +} +#endif + /* * Populate an inode based on info from mds. May be called on new or * existing inodes. @@ -857,15 +1014,20 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, issued |= __ceph_caps_dirty(ci); new_issued = ~issued & info_caps; - /* directories have fl_stripe_unit set to zero */ - if (le32_to_cpu(info->layout.fl_stripe_unit)) - inode->i_blkbits = - fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1; - else - inode->i_blkbits = CEPH_BLOCK_SHIFT; - __ceph_update_quota(ci, iinfo->max_bytes, iinfo->max_files); +#ifdef CONFIG_FS_ENCRYPTION + if (iinfo->fscrypt_auth_len && + ((inode->i_state & I_NEW) || (ci->fscrypt_auth_len == 0))) { + kfree(ci->fscrypt_auth); + ci->fscrypt_auth_len = iinfo->fscrypt_auth_len; + ci->fscrypt_auth = iinfo->fscrypt_auth; + iinfo->fscrypt_auth = NULL; + iinfo->fscrypt_auth_len = 0; + inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED); + } +#endif + if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) && (issued & CEPH_CAP_AUTH_EXCL) == 0) { inode->i_mode = mode; @@ -878,6 +1040,15 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, ceph_decode_timespec64(&ci->i_snap_btime, &iinfo->snap_btime); } + /* directories have fl_stripe_unit set to zero */ + if (IS_ENCRYPTED(inode)) + inode->i_blkbits = CEPH_FSCRYPT_BLOCK_SHIFT; + else if (le32_to_cpu(info->layout.fl_stripe_unit)) + inode->i_blkbits = + fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1; + else + inode->i_blkbits = CEPH_BLOCK_SHIFT; + if ((new_version || (new_issued & CEPH_CAP_LINK_SHARED)) && (issued & CEPH_CAP_LINK_EXCL) == 0) set_nlink(inode, le32_to_cpu(info->nlink)); @@ -899,6 +1070,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, if (new_version || (new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) { + u64 size = le64_to_cpu(info->size); s64 old_pool = ci->i_layout.pool_id; struct ceph_string *old_ns; @@ -912,10 +1084,22 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, pool_ns = old_ns; + if (IS_ENCRYPTED(inode) && size && + iinfo->fscrypt_file_len == sizeof(__le64)) { + u64 fsize = __le64_to_cpu(*(__le64 *)iinfo->fscrypt_file); + + if (size == round_up(fsize, CEPH_FSCRYPT_BLOCK_SIZE)) { + size = fsize; + } else { + pr_warn("fscrypt size mismatch: size=%llu fscrypt_file=%llu, discarding fscrypt_file size.\n", + info->size, size); + } + } + queue_trunc = ceph_fill_file_size(inode, issued, le32_to_cpu(info->truncate_seq), le64_to_cpu(info->truncate_size), - le64_to_cpu(info->size)); + size); /* only update max_size on auth cap */ if ((info->cap.flags & CEPH_CAP_FLAG_AUTH) && ci->i_max_size != le64_to_cpu(info->max_size)) { @@ -975,26 +1159,42 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, inode->i_fop = &ceph_file_fops; break; case S_IFLNK: - inode->i_op = &ceph_symlink_iops; if (!ci->i_symlink) { u32 symlen = iinfo->symlink_len; char *sym; spin_unlock(&ci->i_ceph_lock); - if (symlen != i_size_read(inode)) { - pr_err("%s %llx.%llx BAD symlink " - "size %lld\n", __func__, - ceph_vinop(inode), - i_size_read(inode)); + if (IS_ENCRYPTED(inode)) { + if (symlen != i_size_read(inode)) + pr_err("%s %llx.%llx BAD symlink size %lld\n", + __func__, ceph_vinop(inode), + i_size_read(inode)); + + err = decode_encrypted_symlink(iinfo->symlink, + symlen, (u8 **)&sym); + if (err < 0) { + pr_err("%s decoding encrypted symlink failed: %d\n", + __func__, err); + goto out; + } + symlen = err; i_size_write(inode, symlen); inode->i_blocks = calc_inode_blocks(symlen); - } + } else { + if (symlen != i_size_read(inode)) { + pr_err("%s %llx.%llx BAD symlink size %lld\n", + __func__, ceph_vinop(inode), + i_size_read(inode)); + i_size_write(inode, symlen); + inode->i_blocks = calc_inode_blocks(symlen); + } - err = -ENOMEM; - sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS); - if (!sym) - goto out; + err = -ENOMEM; + sym = kstrndup(iinfo->symlink, symlen, GFP_NOFS); + if (!sym) + goto out; + } spin_lock(&ci->i_ceph_lock); if (!ci->i_symlink) @@ -1002,7 +1202,17 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, else kfree(sym); /* lost a race */ } - inode->i_link = ci->i_symlink; + + if (IS_ENCRYPTED(inode)) { + /* + * Encrypted symlinks need to be decrypted before we can + * cache their targets in i_link. Don't touch it here. + */ + inode->i_op = &ceph_encrypted_symlink_iops; + } else { + inode->i_link = ci->i_symlink; + inode->i_op = &ceph_symlink_iops; + } break; case S_IFDIR: inode->i_op = &ceph_dir_iops; @@ -1310,8 +1520,15 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) if (dir && req->r_op == CEPH_MDS_OP_LOOKUPNAME && test_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags) && !test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags)) { + bool is_nokey = false; struct qstr dname; struct dentry *dn, *parent; + struct fscrypt_str oname = FSTR_INIT(NULL, 0); + struct ceph_fname fname = { .dir = dir, + .name = rinfo->dname, + .ctext = rinfo->altname, + .name_len = rinfo->dname_len, + .ctext_len = rinfo->altname_len }; BUG_ON(!rinfo->head->is_target); BUG_ON(req->r_dentry); @@ -1319,8 +1536,20 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) parent = d_find_any_alias(dir); BUG_ON(!parent); - dname.name = rinfo->dname; - dname.len = rinfo->dname_len; + err = ceph_fname_alloc_buffer(dir, &oname); + if (err < 0) { + dput(parent); + goto done; + } + + err = ceph_fname_to_usr(&fname, NULL, &oname, &is_nokey); + if (err < 0) { + dput(parent); + ceph_fname_free_buffer(dir, &oname); + goto done; + } + dname.name = oname.name; + dname.len = oname.len; dname.hash = full_name_hash(parent, dname.name, dname.len); tvino.ino = le64_to_cpu(rinfo->targeti.in->ino); tvino.snap = le64_to_cpu(rinfo->targeti.in->snapid); @@ -1335,9 +1564,15 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) dname.len, dname.name, dn); if (!dn) { dput(parent); + ceph_fname_free_buffer(dir, &oname); err = -ENOMEM; goto done; } + if (is_nokey) { + spin_lock(&dn->d_lock); + dn->d_flags |= DCACHE_NOKEY_NAME; + spin_unlock(&dn->d_lock); + } err = 0; } else if (d_really_is_positive(dn) && (ceph_ino(d_inode(dn)) != tvino.ino || @@ -1349,6 +1584,7 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) dput(dn); goto retry_lookup; } + ceph_fname_free_buffer(dir, &oname); req->r_dentry = dn; dput(parent); @@ -1552,7 +1788,7 @@ static int readdir_prepopulate_inodes_only(struct ceph_mds_request *req, vino.ino = le64_to_cpu(rde->inode.in->ino); vino.snap = le64_to_cpu(rde->inode.in->snapid); - in = ceph_get_inode(req->r_dentry->d_sb, vino); + in = ceph_get_inode(req->r_dentry->d_sb, vino, NULL); if (IS_ERR(in)) { err = PTR_ERR(in); dout("new_inode badness got %d\n", err); @@ -1630,7 +1866,8 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, struct ceph_mds_session *session) { struct dentry *parent = req->r_dentry; - struct ceph_inode_info *ci = ceph_inode(d_inode(parent)); + struct inode *inode = d_inode(parent); + struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info; struct qstr dname; struct dentry *dn; @@ -1704,9 +1941,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, tvino.snap = le64_to_cpu(rde->inode.in->snapid); if (rinfo->hash_order) { - u32 hash = ceph_str_hash(ci->i_dir_layout.dl_dir_hash, - rde->name, rde->name_len); - hash = ceph_frag_value(hash); + u32 hash = ceph_frag_value(rde->raw_hash); if (hash != last_hash) fpos_offset = 2; last_hash = hash; @@ -1729,6 +1964,11 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, err = -ENOMEM; goto out; } + if (rde->is_nokey) { + spin_lock(&dn->d_lock); + dn->d_flags |= DCACHE_NOKEY_NAME; + spin_unlock(&dn->d_lock); + } } else if (d_really_is_positive(dn) && (ceph_ino(d_inode(dn)) != tvino.ino || ceph_snap(d_inode(dn)) != tvino.snap)) { @@ -1754,7 +1994,7 @@ int ceph_readdir_prepopulate(struct ceph_mds_request *req, if (d_really_is_positive(dn)) { in = d_inode(dn); } else { - in = ceph_get_inode(parent->d_sb, tvino); + in = ceph_get_inode(parent->d_sb, tvino, NULL); if (IS_ERR(in)) { dout("new_inode badness\n"); d_drop(dn); @@ -1927,7 +2167,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) retry: spin_lock(&ci->i_ceph_lock); if (ci->i_truncate_pending == 0) { - dout("__do_pending_vmtruncate %p none pending\n", inode); + dout("%s %p none pending\n", __func__, inode); spin_unlock(&ci->i_ceph_lock); mutex_unlock(&ci->i_truncate_mutex); return; @@ -1939,8 +2179,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) */ if (ci->i_wrbuffer_ref_head < ci->i_wrbuffer_ref) { spin_unlock(&ci->i_ceph_lock); - dout("__do_pending_vmtruncate %p flushing snaps first\n", - inode); + dout("%s %p flushing snaps first\n", __func__, inode); filemap_write_and_wait_range(&inode->i_data, 0, inode->i_sb->s_maxbytes); goto retry; @@ -1949,9 +2188,9 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) /* there should be no reader or writer */ WARN_ON_ONCE(ci->i_rd_ref || ci->i_wr_ref); - to = ci->i_truncate_size; + to = ci->i_truncate_pagecache_size; wrbuffer_refs = ci->i_wrbuffer_ref; - dout("__do_pending_vmtruncate %p (%d) to %lld\n", inode, + dout("%s %p (%d) to %lld\n", __func__, inode, ci->i_truncate_pending, to); spin_unlock(&ci->i_ceph_lock); @@ -1959,7 +2198,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) truncate_pagecache(inode, to); spin_lock(&ci->i_ceph_lock); - if (to == ci->i_truncate_size) { + if (to == ci->i_truncate_pagecache_size) { ci->i_truncate_pending = 0; finish = 1; } @@ -2000,6 +2239,32 @@ static void ceph_inode_work(struct work_struct *work) iput(inode); } +static const char *ceph_encrypted_get_link(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + + if (!dentry) + return ERR_PTR(-ECHILD); + + return fscrypt_get_symlink(inode, ci->i_symlink, i_size_read(inode), + done); +} + +static int ceph_encrypted_symlink_getattr(struct mnt_idmap *idmap, + const struct path *path, + struct kstat *stat, u32 request_mask, + unsigned int query_flags) +{ + int ret; + + ret = ceph_getattr(idmap, path, stat, request_mask, query_flags); + if (ret) + return ret; + return fscrypt_symlink_getattr(path, stat); +} + /* * symlinks */ @@ -2010,20 +2275,173 @@ static const struct inode_operations ceph_symlink_iops = { .listxattr = ceph_listxattr, }; -int __ceph_setattr(struct inode *inode, struct iattr *attr) +static const struct inode_operations ceph_encrypted_symlink_iops = { + .get_link = ceph_encrypted_get_link, + .setattr = ceph_setattr, + .getattr = ceph_encrypted_symlink_getattr, + .listxattr = ceph_listxattr, +}; + +/* + * Transfer the encrypted last block to the MDS and the MDS + * will help update it when truncating a smaller size. + * + * We don't support a PAGE_SIZE that is smaller than the + * CEPH_FSCRYPT_BLOCK_SIZE. + */ +static int fill_fscrypt_truncate(struct inode *inode, + struct ceph_mds_request *req, + struct iattr *attr) +{ + struct ceph_inode_info *ci = ceph_inode(inode); + int boff = attr->ia_size % CEPH_FSCRYPT_BLOCK_SIZE; + loff_t pos, orig_pos = round_down(attr->ia_size, + CEPH_FSCRYPT_BLOCK_SIZE); + u64 block = orig_pos >> CEPH_FSCRYPT_BLOCK_SHIFT; + struct ceph_pagelist *pagelist = NULL; + struct kvec iov = {0}; + struct iov_iter iter; + struct page *page = NULL; + struct ceph_fscrypt_truncate_size_header header; + int retry_op = 0; + int len = CEPH_FSCRYPT_BLOCK_SIZE; + loff_t i_size = i_size_read(inode); + int got, ret, issued; + u64 objver; + + ret = __ceph_get_caps(inode, NULL, CEPH_CAP_FILE_RD, 0, -1, &got); + if (ret < 0) + return ret; + + issued = __ceph_caps_issued(ci, NULL); + + dout("%s size %lld -> %lld got cap refs on %s, issued %s\n", __func__, + i_size, attr->ia_size, ceph_cap_string(got), + ceph_cap_string(issued)); + + /* Try to writeback the dirty pagecaches */ + if (issued & (CEPH_CAP_FILE_BUFFER)) { + loff_t lend = orig_pos + CEPH_FSCRYPT_BLOCK_SHIFT - 1; + + ret = filemap_write_and_wait_range(inode->i_mapping, + orig_pos, lend); + if (ret < 0) + goto out; + } + + page = __page_cache_alloc(GFP_KERNEL); + if (page == NULL) { + ret = -ENOMEM; + goto out; + } + + pagelist = ceph_pagelist_alloc(GFP_KERNEL); + if (!pagelist) { + ret = -ENOMEM; + goto out; + } + + iov.iov_base = kmap_local_page(page); + iov.iov_len = len; + iov_iter_kvec(&iter, READ, &iov, 1, len); + + pos = orig_pos; + ret = __ceph_sync_read(inode, &pos, &iter, &retry_op, &objver); + if (ret < 0) + goto out; + + /* Insert the header first */ + header.ver = 1; + header.compat = 1; + header.change_attr = cpu_to_le64(inode_peek_iversion_raw(inode)); + + /* + * Always set the block_size to CEPH_FSCRYPT_BLOCK_SIZE, + * because in MDS it may need this to do the truncate. + */ + header.block_size = cpu_to_le32(CEPH_FSCRYPT_BLOCK_SIZE); + + /* + * If we hit a hole here, we should just skip filling + * the fscrypt for the request, because once the fscrypt + * is enabled, the file will be split into many blocks + * with the size of CEPH_FSCRYPT_BLOCK_SIZE, if there + * has a hole, the hole size should be multiple of block + * size. + * + * If the Rados object doesn't exist, it will be set to 0. + */ + if (!objver) { + dout("%s hit hole, ppos %lld < size %lld\n", __func__, + pos, i_size); + + header.data_len = cpu_to_le32(8 + 8 + 4); + header.file_offset = 0; + ret = 0; + } else { + header.data_len = cpu_to_le32(8 + 8 + 4 + CEPH_FSCRYPT_BLOCK_SIZE); + header.file_offset = cpu_to_le64(orig_pos); + + dout("%s encrypt block boff/bsize %d/%lu\n", __func__, + boff, CEPH_FSCRYPT_BLOCK_SIZE); + + /* truncate and zero out the extra contents for the last block */ + memset(iov.iov_base + boff, 0, PAGE_SIZE - boff); + + /* encrypt the last block */ + ret = ceph_fscrypt_encrypt_block_inplace(inode, page, + CEPH_FSCRYPT_BLOCK_SIZE, + 0, block, + GFP_KERNEL); + if (ret) + goto out; + } + + /* Insert the header */ + ret = ceph_pagelist_append(pagelist, &header, sizeof(header)); + if (ret) + goto out; + + if (header.block_size) { + /* Append the last block contents to pagelist */ + ret = ceph_pagelist_append(pagelist, iov.iov_base, + CEPH_FSCRYPT_BLOCK_SIZE); + if (ret) + goto out; + } + req->r_pagelist = pagelist; +out: + dout("%s %p size dropping cap refs on %s\n", __func__, + inode, ceph_cap_string(got)); + ceph_put_cap_refs(ci, got); + if (iov.iov_base) + kunmap_local(iov.iov_base); + if (page) + __free_pages(page, 0); + if (ret && pagelist) + ceph_pagelist_release(pagelist); + return ret; +} + +int __ceph_setattr(struct inode *inode, struct iattr *attr, + struct ceph_iattr *cia) { struct ceph_inode_info *ci = ceph_inode(inode); unsigned int ia_valid = attr->ia_valid; struct ceph_mds_request *req; struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; struct ceph_cap_flush *prealloc_cf; + loff_t isize = i_size_read(inode); int issued; int release = 0, dirtied = 0; int mask = 0; int err = 0; int inode_dirty_flags = 0; bool lock_snap_rwsem = false; + bool fill_fscrypt; + int truncate_retry = 20; /* The RMW will take around 50ms */ +retry: prealloc_cf = ceph_alloc_cap_flush(); if (!prealloc_cf) return -ENOMEM; @@ -2035,6 +2453,7 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) return PTR_ERR(req); } + fill_fscrypt = false; spin_lock(&ci->i_ceph_lock); issued = __ceph_caps_issued(ci, NULL); @@ -2050,6 +2469,43 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) } dout("setattr %p issued %s\n", inode, ceph_cap_string(issued)); +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + if (cia && cia->fscrypt_auth) { + u32 len = ceph_fscrypt_auth_len(cia->fscrypt_auth); + + if (len > sizeof(*cia->fscrypt_auth)) { + err = -EINVAL; + spin_unlock(&ci->i_ceph_lock); + goto out; + } + + dout("setattr %llx:%llx fscrypt_auth len %u to %u)\n", + ceph_vinop(inode), ci->fscrypt_auth_len, len); + + /* It should never be re-set once set */ + WARN_ON_ONCE(ci->fscrypt_auth); + + if (issued & CEPH_CAP_AUTH_EXCL) { + dirtied |= CEPH_CAP_AUTH_EXCL; + kfree(ci->fscrypt_auth); + ci->fscrypt_auth = (u8 *)cia->fscrypt_auth; + ci->fscrypt_auth_len = len; + } else if ((issued & CEPH_CAP_AUTH_SHARED) == 0 || + ci->fscrypt_auth_len != len || + memcmp(ci->fscrypt_auth, cia->fscrypt_auth, len)) { + req->r_fscrypt_auth = cia->fscrypt_auth; + mask |= CEPH_SETATTR_FSCRYPT_AUTH; + release |= CEPH_CAP_AUTH_SHARED; + } + cia->fscrypt_auth = NULL; + } +#else + if (cia && cia->fscrypt_auth) { + err = -EINVAL; + spin_unlock(&ci->i_ceph_lock); + goto out; + } +#endif /* CONFIG_FS_ENCRYPTION */ if (ia_valid & ATTR_UID) { dout("setattr %p uid %d -> %d\n", inode, @@ -2119,10 +2575,27 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) } } if (ia_valid & ATTR_SIZE) { - loff_t isize = i_size_read(inode); - dout("setattr %p size %lld -> %lld\n", inode, isize, attr->ia_size); - if ((issued & CEPH_CAP_FILE_EXCL) && attr->ia_size >= isize) { + /* + * Only when the new size is smaller and not aligned to + * CEPH_FSCRYPT_BLOCK_SIZE will the RMW is needed. + */ + if (IS_ENCRYPTED(inode) && attr->ia_size < isize && + (attr->ia_size % CEPH_FSCRYPT_BLOCK_SIZE)) { + mask |= CEPH_SETATTR_SIZE; + release |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL | + CEPH_CAP_FILE_RD | CEPH_CAP_FILE_WR; + set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); + mask |= CEPH_SETATTR_FSCRYPT_FILE; + req->r_args.setattr.size = + cpu_to_le64(round_up(attr->ia_size, + CEPH_FSCRYPT_BLOCK_SIZE)); + req->r_args.setattr.old_size = + cpu_to_le64(round_up(isize, + CEPH_FSCRYPT_BLOCK_SIZE)); + req->r_fscrypt_file = attr->ia_size; + fill_fscrypt = true; + } else if ((issued & CEPH_CAP_FILE_EXCL) && attr->ia_size >= isize) { if (attr->ia_size > isize) { i_size_write(inode, attr->ia_size); inode->i_blocks = calc_inode_blocks(attr->ia_size); @@ -2132,11 +2605,24 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) } } else if ((issued & CEPH_CAP_FILE_SHARED) == 0 || attr->ia_size != isize) { - req->r_args.setattr.size = cpu_to_le64(attr->ia_size); - req->r_args.setattr.old_size = cpu_to_le64(isize); mask |= CEPH_SETATTR_SIZE; release |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_EXCL | CEPH_CAP_FILE_RD | CEPH_CAP_FILE_WR; + if (IS_ENCRYPTED(inode) && attr->ia_size) { + set_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags); + mask |= CEPH_SETATTR_FSCRYPT_FILE; + req->r_args.setattr.size = + cpu_to_le64(round_up(attr->ia_size, + CEPH_FSCRYPT_BLOCK_SIZE)); + req->r_args.setattr.old_size = + cpu_to_le64(round_up(isize, + CEPH_FSCRYPT_BLOCK_SIZE)); + req->r_fscrypt_file = attr->ia_size; + } else { + req->r_args.setattr.size = cpu_to_le64(attr->ia_size); + req->r_args.setattr.old_size = cpu_to_le64(isize); + req->r_fscrypt_file = 0; + } } } if (ia_valid & ATTR_MTIME) { @@ -2199,8 +2685,10 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) release &= issued; spin_unlock(&ci->i_ceph_lock); - if (lock_snap_rwsem) + if (lock_snap_rwsem) { up_read(&mdsc->snap_rwsem); + lock_snap_rwsem = false; + } if (inode_dirty_flags) __mark_inode_dirty(inode, inode_dirty_flags); @@ -2212,8 +2700,29 @@ int __ceph_setattr(struct inode *inode, struct iattr *attr) req->r_args.setattr.mask = cpu_to_le32(mask); req->r_num_caps = 1; req->r_stamp = attr->ia_ctime; + if (fill_fscrypt) { + err = fill_fscrypt_truncate(inode, req, attr); + if (err) + goto out; + } + + /* + * The truncate request will return -EAGAIN when the + * last block has been updated just before the MDS + * successfully gets the xlock for the FILE lock. To + * avoid corrupting the file contents we need to retry + * it. + */ err = ceph_mdsc_do_request(mdsc, NULL, req); + if (err == -EAGAIN && truncate_retry--) { + dout("setattr %p result=%d (%s locally, %d remote), retry it!\n", + inode, err, ceph_cap_string(dirtied), mask); + ceph_mdsc_put_request(req); + ceph_free_cap_flush(prealloc_cf); + goto retry; + } } +out: dout("setattr %p result=%d (%s locally, %d remote)\n", inode, err, ceph_cap_string(dirtied), mask); @@ -2242,6 +2751,10 @@ int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry, if (ceph_inode_is_shutdown(inode)) return -ESTALE; + err = fscrypt_prepare_setattr(dentry, attr); + if (err) + return err; + err = setattr_prepare(&nop_mnt_idmap, dentry, attr); if (err != 0) return err; @@ -2254,7 +2767,7 @@ int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry, ceph_quota_is_max_bytes_exceeded(inode, attr->ia_size)) return -EDQUOT; - err = __ceph_setattr(inode, attr); + err = __ceph_setattr(inode, attr, NULL); if (err >= 0 && (attr->ia_valid & ATTR_MODE)) err = posix_acl_chmod(&nop_mnt_idmap, dentry, attr->ia_mode); @@ -2525,8 +3038,12 @@ int ceph_getattr(struct mnt_idmap *idmap, const struct path *path, stat->nlink = 1 + 1 + ci->i_subdirs; } - stat->attributes_mask |= STATX_ATTR_CHANGE_MONOTONIC; stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC; + if (IS_ENCRYPTED(inode)) + stat->attributes |= STATX_ATTR_ENCRYPTED; + stat->attributes_mask |= (STATX_ATTR_CHANGE_MONOTONIC | + STATX_ATTR_ENCRYPTED); + stat->result_mask = request_mask & valid_mask; return err; } diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index deac817647eb09ed1c64bbdce1871383f6fa0770..91a84917d203c54527ba676a32c23bb1a00cf1da 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -6,6 +6,7 @@ #include "mds_client.h" #include "ioctl.h" #include +#include /* * ioctls @@ -268,9 +269,96 @@ static long ceph_ioctl_syncio(struct file *file) return 0; } +static int vet_mds_for_fscrypt(struct file *file) +{ + int i, ret = -EOPNOTSUPP; + struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(file_inode(file)->i_sb); + + mutex_lock(&mdsc->mutex); + for (i = 0; i < mdsc->max_sessions; i++) { + struct ceph_mds_session *s = mdsc->sessions[i]; + + if (!s) + continue; + if (test_bit(CEPHFS_FEATURE_ALTERNATE_NAME, &s->s_features)) + ret = 0; + break; + } + mutex_unlock(&mdsc->mutex); + return ret; +} + +static long ceph_set_encryption_policy(struct file *file, unsigned long arg) +{ + int ret, got = 0; + struct inode *inode = file_inode(file); + struct ceph_inode_info *ci = ceph_inode(inode); + + /* encrypted directories can't have striped layout */ + if (ci->i_layout.stripe_count > 1) + return -EINVAL; + + ret = vet_mds_for_fscrypt(file); + if (ret) + return ret; + + /* + * Ensure we hold these caps so that we _know_ that the rstats check + * in the empty_dir check is reliable. + */ + ret = ceph_get_caps(file, CEPH_CAP_FILE_SHARED, 0, -1, &got); + if (ret) + return ret; + + ret = fscrypt_ioctl_set_policy(file, (const void __user *)arg); + if (got) + ceph_put_cap_refs(ci, got); + + return ret; +} + +static const char *ceph_ioctl_cmd_name(const unsigned int cmd) +{ + switch (cmd) { + case CEPH_IOC_GET_LAYOUT: + return "get_layout"; + case CEPH_IOC_SET_LAYOUT: + return "set_layout"; + case CEPH_IOC_SET_LAYOUT_POLICY: + return "set_layout_policy"; + case CEPH_IOC_GET_DATALOC: + return "get_dataloc"; + case CEPH_IOC_LAZYIO: + return "lazyio"; + case CEPH_IOC_SYNCIO: + return "syncio"; + case FS_IOC_SET_ENCRYPTION_POLICY: + return "set_encryption_policy"; + case FS_IOC_GET_ENCRYPTION_POLICY: + return "get_encryption_policy"; + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + return "get_encryption_policy_ex"; + case FS_IOC_ADD_ENCRYPTION_KEY: + return "add_encryption_key"; + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return "remove_encryption_key"; + case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: + return "remove_encryption_key_all_users"; + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return "get_encryption_key_status"; + case FS_IOC_GET_ENCRYPTION_NONCE: + return "get_encryption_nonce"; + default: + return "unknown"; + } +} + long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg); + int ret; + + dout("ioctl file %p cmd %s arg %lu\n", file, + ceph_ioctl_cmd_name(cmd), arg); switch (cmd) { case CEPH_IOC_GET_LAYOUT: return ceph_ioctl_get_layout(file, (void __user *)arg); @@ -289,6 +377,43 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case CEPH_IOC_SYNCIO: return ceph_ioctl_syncio(file); + + case FS_IOC_SET_ENCRYPTION_POLICY: + return ceph_set_encryption_policy(file, arg); + + case FS_IOC_GET_ENCRYPTION_POLICY: + ret = vet_mds_for_fscrypt(file); + if (ret) + return ret; + return fscrypt_ioctl_get_policy(file, (void __user *)arg); + + case FS_IOC_GET_ENCRYPTION_POLICY_EX: + ret = vet_mds_for_fscrypt(file); + if (ret) + return ret; + return fscrypt_ioctl_get_policy_ex(file, (void __user *)arg); + + case FS_IOC_ADD_ENCRYPTION_KEY: + ret = vet_mds_for_fscrypt(file); + if (ret) + return ret; + return fscrypt_ioctl_add_key(file, (void __user *)arg); + + case FS_IOC_REMOVE_ENCRYPTION_KEY: + return fscrypt_ioctl_remove_key(file, (void __user *)arg); + + case FS_IOC_REMOVE_ENCRYPTION_KEY_ALL_USERS: + return fscrypt_ioctl_remove_key_all_users(file, + (void __user *)arg); + + case FS_IOC_GET_ENCRYPTION_KEY_STATUS: + return fscrypt_ioctl_get_key_status(file, (void __user *)arg); + + case FS_IOC_GET_ENCRYPTION_NONCE: + ret = vet_mds_for_fscrypt(file); + if (ret) + return ret; + return fscrypt_ioctl_get_nonce(file, (void __user *)arg); } return -ENOTTY; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 5fb367b1d4b066545ea36b9046a3c04fec4d314f..615db141b6c4b54362815910a1ab13c470dffe8c 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -15,6 +15,7 @@ #include "super.h" #include "mds_client.h" +#include "crypto.h" #include #include @@ -184,8 +185,54 @@ static int parse_reply_info_in(void **p, void *end, info->rsnaps = 0; } + if (struct_v >= 5) { + u32 alen; + + ceph_decode_32_safe(p, end, alen, bad); + + while (alen--) { + u32 len; + + /* key */ + ceph_decode_32_safe(p, end, len, bad); + ceph_decode_skip_n(p, end, len, bad); + /* value */ + ceph_decode_32_safe(p, end, len, bad); + ceph_decode_skip_n(p, end, len, bad); + } + } + + /* fscrypt flag -- ignore */ + if (struct_v >= 6) + ceph_decode_skip_8(p, end, bad); + + info->fscrypt_auth = NULL; + info->fscrypt_auth_len = 0; + info->fscrypt_file = NULL; + info->fscrypt_file_len = 0; + if (struct_v >= 7) { + ceph_decode_32_safe(p, end, info->fscrypt_auth_len, bad); + if (info->fscrypt_auth_len) { + info->fscrypt_auth = kmalloc(info->fscrypt_auth_len, + GFP_KERNEL); + if (!info->fscrypt_auth) + return -ENOMEM; + ceph_decode_copy_safe(p, end, info->fscrypt_auth, + info->fscrypt_auth_len, bad); + } + ceph_decode_32_safe(p, end, info->fscrypt_file_len, bad); + if (info->fscrypt_file_len) { + info->fscrypt_file = kmalloc(info->fscrypt_file_len, + GFP_KERNEL); + if (!info->fscrypt_file) + return -ENOMEM; + ceph_decode_copy_safe(p, end, info->fscrypt_file, + info->fscrypt_file_len, bad); + } + } *p = end; } else { + /* legacy (unversioned) struct */ if (features & CEPH_FEATURE_MDS_INLINE_DATA) { ceph_decode_64_safe(p, end, info->inline_version, bad); ceph_decode_32_safe(p, end, info->inline_len, bad); @@ -263,27 +310,47 @@ static int parse_reply_info_dir(void **p, void *end, static int parse_reply_info_lease(void **p, void *end, struct ceph_mds_reply_lease **lease, - u64 features) + u64 features, u32 *altname_len, u8 **altname) { + u8 struct_v; + u32 struct_len; + void *lend; + if (features == (u64)-1) { - u8 struct_v, struct_compat; - u32 struct_len; + u8 struct_compat; + ceph_decode_8_safe(p, end, struct_v, bad); ceph_decode_8_safe(p, end, struct_compat, bad); + /* struct_v is expected to be >= 1. we only understand * encoding whose struct_compat == 1. */ if (!struct_v || struct_compat != 1) goto bad; + ceph_decode_32_safe(p, end, struct_len, bad); - ceph_decode_need(p, end, struct_len, bad); - end = *p + struct_len; + } else { + struct_len = sizeof(**lease); + *altname_len = 0; + *altname = NULL; } - ceph_decode_need(p, end, sizeof(**lease), bad); + lend = *p + struct_len; + ceph_decode_need(p, end, struct_len, bad); *lease = *p; *p += sizeof(**lease); - if (features == (u64)-1) - *p = end; + + if (features == (u64)-1) { + if (struct_v >= 2) { + ceph_decode_32_safe(p, end, *altname_len, bad); + ceph_decode_need(p, end, *altname_len, bad); + *altname = *p; + *p += *altname_len; + } else { + *altname = NULL; + *altname_len = 0; + } + } + *p = lend; return 0; bad: return -EIO; @@ -313,7 +380,8 @@ static int parse_reply_info_trace(void **p, void *end, info->dname = *p; *p += info->dname_len; - err = parse_reply_info_lease(p, end, &info->dlease, features); + err = parse_reply_info_lease(p, end, &info->dlease, features, + &info->altname_len, &info->altname); if (err < 0) goto out_bad; } @@ -339,9 +407,10 @@ static int parse_reply_info_trace(void **p, void *end, * parse readdir results */ static int parse_reply_info_readdir(void **p, void *end, - struct ceph_mds_reply_info_parsed *info, - u64 features) + struct ceph_mds_request *req, + u64 features) { + struct ceph_mds_reply_info_parsed *info = &req->r_reply_info; u32 num, i = 0; int err; @@ -371,18 +440,87 @@ static int parse_reply_info_readdir(void **p, void *end, info->dir_nr = num; while (num) { + struct inode *inode = d_inode(req->r_dentry); + struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_mds_reply_dir_entry *rde = info->dir_entries + i; + struct fscrypt_str tname = FSTR_INIT(NULL, 0); + struct fscrypt_str oname = FSTR_INIT(NULL, 0); + struct ceph_fname fname; + u32 altname_len, _name_len; + u8 *altname, *_name; + /* dentry */ - ceph_decode_32_safe(p, end, rde->name_len, bad); - ceph_decode_need(p, end, rde->name_len, bad); - rde->name = *p; - *p += rde->name_len; - dout("parsed dir dname '%.*s'\n", rde->name_len, rde->name); + ceph_decode_32_safe(p, end, _name_len, bad); + ceph_decode_need(p, end, _name_len, bad); + _name = *p; + *p += _name_len; + dout("parsed dir dname '%.*s'\n", _name_len, _name); + + if (info->hash_order) + rde->raw_hash = ceph_str_hash(ci->i_dir_layout.dl_dir_hash, + _name, _name_len); /* dentry lease */ - err = parse_reply_info_lease(p, end, &rde->lease, features); + err = parse_reply_info_lease(p, end, &rde->lease, features, + &altname_len, &altname); if (err) goto out_bad; + + /* + * Try to dencrypt the dentry names and update them + * in the ceph_mds_reply_dir_entry struct. + */ + fname.dir = inode; + fname.name = _name; + fname.name_len = _name_len; + fname.ctext = altname; + fname.ctext_len = altname_len; + /* + * The _name_len maybe larger than altname_len, such as + * when the human readable name length is in range of + * (CEPH_NOHASH_NAME_MAX, CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE), + * then the copy in ceph_fname_to_usr will corrupt the + * data if there has no encryption key. + * + * Just set the no_copy flag and then if there has no + * encryption key the oname.name will be assigned to + * _name always. + */ + fname.no_copy = true; + if (altname_len == 0) { + /* + * Set tname to _name, and this will be used + * to do the base64_decode in-place. It's + * safe because the decoded string should + * always be shorter, which is 3/4 of origin + * string. + */ + tname.name = _name; + + /* + * Set oname to _name too, and this will be + * used to do the dencryption in-place. + */ + oname.name = _name; + oname.len = _name_len; + } else { + /* + * This will do the decryption only in-place + * from altname cryptext directly. + */ + oname.name = altname; + oname.len = altname_len; + } + rde->is_nokey = false; + err = ceph_fname_to_usr(&fname, &tname, &oname, &rde->is_nokey); + if (err) { + pr_err("%s unable to decode %.*s, got %d\n", __func__, + _name_len, _name, err); + goto out_bad; + } + rde->name = oname.name; + rde->name_len = oname.len; + /* inode */ err = parse_reply_info_in(p, end, &rde->inode, features); if (err < 0) @@ -581,15 +719,16 @@ static int parse_reply_info_getvxattr(void **p, void *end, * parse extra results */ static int parse_reply_info_extra(void **p, void *end, - struct ceph_mds_reply_info_parsed *info, + struct ceph_mds_request *req, u64 features, struct ceph_mds_session *s) { + struct ceph_mds_reply_info_parsed *info = &req->r_reply_info; u32 op = le32_to_cpu(info->head->op); if (op == CEPH_MDS_OP_GETFILELOCK) return parse_reply_info_filelock(p, end, info, features); else if (op == CEPH_MDS_OP_READDIR || op == CEPH_MDS_OP_LSSNAP) - return parse_reply_info_readdir(p, end, info, features); + return parse_reply_info_readdir(p, end, req, features); else if (op == CEPH_MDS_OP_CREATE) return parse_reply_info_create(p, end, info, features, s); else if (op == CEPH_MDS_OP_GETVXATTR) @@ -602,9 +741,9 @@ static int parse_reply_info_extra(void **p, void *end, * parse entire mds reply */ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg, - struct ceph_mds_reply_info_parsed *info, - u64 features) + struct ceph_mds_request *req, u64 features) { + struct ceph_mds_reply_info_parsed *info = &req->r_reply_info; void *p, *end; u32 len; int err; @@ -626,7 +765,7 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg, ceph_decode_32_safe(&p, end, len, bad); if (len > 0) { ceph_decode_need(&p, end, len, bad); - err = parse_reply_info_extra(&p, p+len, info, features, s); + err = parse_reply_info_extra(&p, p+len, req, features, s); if (err < 0) goto out_bad; } @@ -651,8 +790,21 @@ static int parse_reply_info(struct ceph_mds_session *s, struct ceph_msg *msg, static void destroy_reply_info(struct ceph_mds_reply_info_parsed *info) { + int i; + + kfree(info->diri.fscrypt_auth); + kfree(info->diri.fscrypt_file); + kfree(info->targeti.fscrypt_auth); + kfree(info->targeti.fscrypt_file); if (!info->dir_entries) return; + + for (i = 0; i < info->dir_nr; i++) { + struct ceph_mds_reply_dir_entry *rde = info->dir_entries + i; + + kfree(rde->inode.fscrypt_auth); + kfree(rde->inode.fscrypt_file); + } free_pages((unsigned long)info->dir_entries, get_order(info->dir_buf_size)); } @@ -945,6 +1097,7 @@ void ceph_mdsc_release_request(struct kref *kref) iput(req->r_parent); } iput(req->r_target_inode); + iput(req->r_new_inode); if (req->r_dentry) dput(req->r_dentry); if (req->r_old_dentry) @@ -965,6 +1118,8 @@ void ceph_mdsc_release_request(struct kref *kref) put_cred(req->r_cred); if (req->r_pagelist) ceph_pagelist_release(req->r_pagelist); + kfree(req->r_fscrypt_auth); + kfree(req->r_altname); put_request_session(req); ceph_unreserve_caps(req->r_mdsc, &req->r_caps_reservation); WARN_ON_ONCE(!list_empty(&req->r_wait)); @@ -2373,20 +2528,90 @@ static inline u64 __get_oldest_tid(struct ceph_mds_client *mdsc) return mdsc->oldest_tid; } -/* - * Build a dentry's path. Allocate on heap; caller must kfree. Based - * on build_path_from_dentry in fs/cifs/dir.c. +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen) +{ + struct inode *dir = req->r_parent; + struct dentry *dentry = req->r_dentry; + u8 *cryptbuf = NULL; + u32 len = 0; + int ret = 0; + + /* only encode if we have parent and dentry */ + if (!dir || !dentry) + goto success; + + /* No-op unless this is encrypted */ + if (!IS_ENCRYPTED(dir)) + goto success; + + ret = ceph_fscrypt_prepare_readdir(dir); + if (ret < 0) + return ERR_PTR(ret); + + /* No key? Just ignore it. */ + if (!fscrypt_has_encryption_key(dir)) + goto success; + + if (!fscrypt_fname_encrypted_size(dir, dentry->d_name.len, NAME_MAX, + &len)) { + WARN_ON_ONCE(1); + return ERR_PTR(-ENAMETOOLONG); + } + + /* No need to append altname if name is short enough */ + if (len <= CEPH_NOHASH_NAME_MAX) { + len = 0; + goto success; + } + + cryptbuf = kmalloc(len, GFP_KERNEL); + if (!cryptbuf) + return ERR_PTR(-ENOMEM); + + ret = fscrypt_fname_encrypt(dir, &dentry->d_name, cryptbuf, len); + if (ret) { + kfree(cryptbuf); + return ERR_PTR(ret); + } +success: + *plen = len; + return cryptbuf; +} +#else +static u8 *get_fscrypt_altname(const struct ceph_mds_request *req, u32 *plen) +{ + *plen = 0; + return NULL; +} +#endif + +/** + * ceph_mdsc_build_path - build a path string to a given dentry + * @dentry: dentry to which path should be built + * @plen: returned length of string + * @pbase: returned base inode number + * @for_wire: is this path going to be sent to the MDS? + * + * Build a string that represents the path to the dentry. This is mostly called + * for two different purposes: + * + * 1) we need to build a path string to send to the MDS (for_wire == true) + * 2) we need a path string for local presentation (e.g. debugfs) + * (for_wire == false) * - * If @stop_on_nosnap, generate path relative to the first non-snapped - * inode. + * The path is built in reverse, starting with the dentry. Walk back up toward + * the root, building the path until the first non-snapped inode is reached + * (for_wire) or the root inode is reached (!for_wire). * * Encode hidden .snap dirs as a double /, i.e. * foo/.snap/bar -> foo//bar */ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, - int stop_on_nosnap) + int for_wire) { - struct dentry *temp; + struct dentry *cur; + struct inode *inode; char *path; int pos; unsigned seq; @@ -2403,34 +2628,72 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, path[pos] = '\0'; seq = read_seqbegin(&rename_lock); - rcu_read_lock(); - temp = dentry; + cur = dget(dentry); for (;;) { - struct inode *inode; + struct dentry *parent; - spin_lock(&temp->d_lock); - inode = d_inode(temp); + spin_lock(&cur->d_lock); + inode = d_inode(cur); if (inode && ceph_snap(inode) == CEPH_SNAPDIR) { dout("build_path path+%d: %p SNAPDIR\n", - pos, temp); - } else if (stop_on_nosnap && inode && dentry != temp && + pos, cur); + spin_unlock(&cur->d_lock); + parent = dget_parent(cur); + } else if (for_wire && inode && dentry != cur && ceph_snap(inode) == CEPH_NOSNAP) { - spin_unlock(&temp->d_lock); + spin_unlock(&cur->d_lock); pos++; /* get rid of any prepended '/' */ break; + } else if (!for_wire || !IS_ENCRYPTED(d_inode(cur->d_parent))) { + pos -= cur->d_name.len; + if (pos < 0) { + spin_unlock(&cur->d_lock); + break; + } + memcpy(path + pos, cur->d_name.name, cur->d_name.len); + spin_unlock(&cur->d_lock); + parent = dget_parent(cur); } else { - pos -= temp->d_name.len; + int len, ret; + char buf[NAME_MAX]; + + /* + * Proactively copy name into buf, in case we need to + * present it as-is. + */ + memcpy(buf, cur->d_name.name, cur->d_name.len); + len = cur->d_name.len; + spin_unlock(&cur->d_lock); + parent = dget_parent(cur); + + ret = ceph_fscrypt_prepare_readdir(d_inode(parent)); + if (ret < 0) { + dput(parent); + dput(cur); + return ERR_PTR(ret); + } + + if (fscrypt_has_encryption_key(d_inode(parent))) { + len = ceph_encode_encrypted_fname(d_inode(parent), + cur, buf); + if (len < 0) { + dput(parent); + dput(cur); + return ERR_PTR(len); + } + } + pos -= len; if (pos < 0) { - spin_unlock(&temp->d_lock); + dput(parent); break; } - memcpy(path + pos, temp->d_name.name, temp->d_name.len); + memcpy(path + pos, buf, len); } - spin_unlock(&temp->d_lock); - temp = READ_ONCE(temp->d_parent); + dput(cur); + cur = parent; /* Are we at the root? */ - if (IS_ROOT(temp)) + if (IS_ROOT(cur)) break; /* Are we out of buffer? */ @@ -2439,8 +2702,9 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, path[pos] = '/'; } - base = ceph_ino(d_inode(temp)); - rcu_read_unlock(); + inode = d_inode(cur); + base = inode ? ceph_ino(inode) : 0; + dput(cur); if (read_seqretry(&rename_lock, seq)) goto retry; @@ -2450,8 +2714,8 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *pbase, * A rename didn't occur, but somehow we didn't end up where * we thought we would. Throw a warning and try again. */ - pr_warn("build_path did not end path lookup where " - "expected, pos is %d\n", pos); + pr_warn("build_path did not end path lookup where expected (pos = %d)\n", + pos); goto retry; } @@ -2471,7 +2735,8 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir, rcu_read_lock(); if (!dir) dir = d_inode_rcu(dentry->d_parent); - if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP) { + if (dir && parent_locked && ceph_snap(dir) == CEPH_NOSNAP && + !IS_ENCRYPTED(dir)) { *pino = ceph_ino(dir); rcu_read_unlock(); *ppath = dentry->d_name.name; @@ -2539,8 +2804,8 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, return r; } -static void encode_timestamp_and_gids(void **p, - const struct ceph_mds_request *req) +static void encode_mclientrequest_tail(void **p, + const struct ceph_mds_request *req) { struct ceph_timespec ts; int i; @@ -2548,11 +2813,43 @@ static void encode_timestamp_and_gids(void **p, ceph_encode_timespec64(&ts, &req->r_stamp); ceph_encode_copy(p, &ts, sizeof(ts)); - /* gid_list */ + /* v4: gid_list */ ceph_encode_32(p, req->r_cred->group_info->ngroups); for (i = 0; i < req->r_cred->group_info->ngroups; i++) ceph_encode_64(p, from_kgid(&init_user_ns, req->r_cred->group_info->gid[i])); + + /* v5: altname */ + ceph_encode_32(p, req->r_altname_len); + ceph_encode_copy(p, req->r_altname, req->r_altname_len); + + /* v6: fscrypt_auth and fscrypt_file */ + if (req->r_fscrypt_auth) { + u32 authlen = ceph_fscrypt_auth_len(req->r_fscrypt_auth); + + ceph_encode_32(p, authlen); + ceph_encode_copy(p, req->r_fscrypt_auth, authlen); + } else { + ceph_encode_32(p, 0); + } + if (test_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags)) { + ceph_encode_32(p, sizeof(__le64)); + ceph_encode_64(p, req->r_fscrypt_file); + } else { + ceph_encode_32(p, 0); + } +} + +static struct ceph_mds_request_head_legacy * +find_legacy_request_head(void *p, u64 features) +{ + bool legacy = !(features & CEPH_FEATURE_FS_BTIME); + struct ceph_mds_request_head_old *ohead; + + if (legacy) + return (struct ceph_mds_request_head_legacy *)p; + ohead = (struct ceph_mds_request_head_old *)p; + return (struct ceph_mds_request_head_legacy *)&ohead->oldest_client_tid; } /* @@ -2565,7 +2862,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, int mds = session->s_mds; struct ceph_mds_client *mdsc = session->s_mdsc; struct ceph_msg *msg; - struct ceph_mds_request_head_old *head; + struct ceph_mds_request_head_legacy *lhead; const char *path1 = NULL; const char *path2 = NULL; u64 ino1 = 0, ino2 = 0; @@ -2577,6 +2874,8 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, void *p, *end; int ret; bool legacy = !(session->s_con.peer_features & CEPH_FEATURE_FS_BTIME); + bool old_version = !test_bit(CEPHFS_FEATURE_32BITS_RETRY_FWD, + &session->s_features); ret = set_request_path_attr(req->r_inode, req->r_dentry, req->r_parent, req->r_path1, req->r_ino1.ino, @@ -2601,12 +2900,32 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, goto out_free1; } - len = legacy ? sizeof(*head) : sizeof(struct ceph_mds_request_head); - len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) + - sizeof(struct ceph_timespec); - len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups); + req->r_altname = get_fscrypt_altname(req, &req->r_altname_len); + if (IS_ERR(req->r_altname)) { + msg = ERR_CAST(req->r_altname); + req->r_altname = NULL; + goto out_free2; + } + + /* + * For old cephs without supporting the 32bit retry/fwd feature + * it will copy the raw memories directly when decoding the + * requests. While new cephs will decode the head depending the + * version member, so we need to make sure it will be compatible + * with them both. + */ + if (legacy) + len = sizeof(struct ceph_mds_request_head_legacy); + else if (old_version) + len = sizeof(struct ceph_mds_request_head_old); + else + len = sizeof(struct ceph_mds_request_head); - /* calculate (max) length for cap releases */ + /* filepaths */ + len += 2 * (1 + sizeof(u32) + sizeof(u64)); + len += pathlen1 + pathlen2; + + /* cap releases */ len += sizeof(struct ceph_mds_request_release) * (!!req->r_inode_drop + !!req->r_dentry_drop + !!req->r_old_inode_drop + !!req->r_old_dentry_drop); @@ -2616,6 +2935,27 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, if (req->r_old_dentry_drop) len += pathlen2; + /* MClientRequest tail */ + + /* req->r_stamp */ + len += sizeof(struct ceph_timespec); + + /* gid list */ + len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups); + + /* alternate name */ + len += sizeof(u32) + req->r_altname_len; + + /* fscrypt_auth */ + len += sizeof(u32); // fscrypt_auth + if (req->r_fscrypt_auth) + len += ceph_fscrypt_auth_len(req->r_fscrypt_auth); + + /* fscrypt_file */ + len += sizeof(u32); + if (test_bit(CEPH_MDS_R_FSCRYPT_FILE, &req->r_req_flags)) + len += sizeof(__le64); + msg = ceph_msg_new2(CEPH_MSG_CLIENT_REQUEST, len, 1, GFP_NOFS, false); if (!msg) { msg = ERR_PTR(-ENOMEM); @@ -2624,33 +2964,40 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, msg->hdr.tid = cpu_to_le64(req->r_tid); + lhead = find_legacy_request_head(msg->front.iov_base, + session->s_con.peer_features); + /* - * The old ceph_mds_request_head didn't contain a version field, and + * The ceph_mds_request_head_legacy didn't contain a version field, and * one was added when we moved the message version from 3->4. */ if (legacy) { msg->hdr.version = cpu_to_le16(3); - head = msg->front.iov_base; - p = msg->front.iov_base + sizeof(*head); - } else { - struct ceph_mds_request_head *new_head = msg->front.iov_base; + p = msg->front.iov_base + sizeof(*lhead); + } else if (old_version) { + struct ceph_mds_request_head_old *ohead = msg->front.iov_base; msg->hdr.version = cpu_to_le16(4); - new_head->version = cpu_to_le16(CEPH_MDS_REQUEST_HEAD_VERSION); - head = (struct ceph_mds_request_head_old *)&new_head->oldest_client_tid; - p = msg->front.iov_base + sizeof(*new_head); + ohead->version = cpu_to_le16(1); + p = msg->front.iov_base + sizeof(*ohead); + } else { + struct ceph_mds_request_head *nhead = msg->front.iov_base; + + msg->hdr.version = cpu_to_le16(6); + nhead->version = cpu_to_le16(CEPH_MDS_REQUEST_HEAD_VERSION); + p = msg->front.iov_base + sizeof(*nhead); } end = msg->front.iov_base + msg->front.iov_len; - head->mdsmap_epoch = cpu_to_le32(mdsc->mdsmap->m_epoch); - head->op = cpu_to_le32(req->r_op); - head->caller_uid = cpu_to_le32(from_kuid(&init_user_ns, - req->r_cred->fsuid)); - head->caller_gid = cpu_to_le32(from_kgid(&init_user_ns, - req->r_cred->fsgid)); - head->ino = cpu_to_le64(req->r_deleg_ino); - head->args = req->r_args; + lhead->mdsmap_epoch = cpu_to_le32(mdsc->mdsmap->m_epoch); + lhead->op = cpu_to_le32(req->r_op); + lhead->caller_uid = cpu_to_le32(from_kuid(&init_user_ns, + req->r_cred->fsuid)); + lhead->caller_gid = cpu_to_le32(from_kgid(&init_user_ns, + req->r_cred->fsgid)); + lhead->ino = cpu_to_le64(req->r_deleg_ino); + lhead->args = req->r_args; ceph_encode_filepath(&p, end, ino1, path1); ceph_encode_filepath(&p, end, ino2, path2); @@ -2665,15 +3012,23 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, req->r_inode ? req->r_inode : d_inode(req->r_dentry), mds, req->r_inode_drop, req->r_inode_unless, req->r_op == CEPH_MDS_OP_READDIR); - if (req->r_dentry_drop) - releases += ceph_encode_dentry_release(&p, req->r_dentry, + if (req->r_dentry_drop) { + ret = ceph_encode_dentry_release(&p, req->r_dentry, req->r_parent, mds, req->r_dentry_drop, req->r_dentry_unless); - if (req->r_old_dentry_drop) - releases += ceph_encode_dentry_release(&p, req->r_old_dentry, + if (ret < 0) + goto out_err; + releases += ret; + } + if (req->r_old_dentry_drop) { + ret = ceph_encode_dentry_release(&p, req->r_old_dentry, req->r_old_dentry_dir, mds, req->r_old_dentry_drop, req->r_old_dentry_unless); + if (ret < 0) + goto out_err; + releases += ret; + } if (req->r_old_inode_drop) releases += ceph_encode_inode_release(&p, d_inode(req->r_old_dentry), @@ -2684,9 +3039,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, p = msg->front.iov_base + req->r_request_release_offset; } - head->num_releases = cpu_to_le16(releases); + lhead->num_releases = cpu_to_le16(releases); - encode_timestamp_and_gids(&p, req); + encode_mclientrequest_tail(&p, req); if (WARN_ON_ONCE(p > end)) { ceph_msg_put(msg); @@ -2715,6 +3070,10 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session, ceph_mdsc_free_path((char *)path1, pathlen1); out: return msg; +out_err: + ceph_msg_put(msg); + msg = ERR_PTR(ret); + goto out_free2; } /* @@ -2731,18 +3090,6 @@ static void complete_request(struct ceph_mds_client *mdsc, complete_all(&req->r_completion); } -static struct ceph_mds_request_head_old * -find_old_request_head(void *p, u64 features) -{ - bool legacy = !(features & CEPH_FEATURE_FS_BTIME); - struct ceph_mds_request_head *new_head; - - if (legacy) - return (struct ceph_mds_request_head_old *)p; - new_head = (struct ceph_mds_request_head *)p; - return (struct ceph_mds_request_head_old *)&new_head->oldest_client_tid; -} - /* * called under mdsc->mutex */ @@ -2752,29 +3099,28 @@ static int __prepare_send_request(struct ceph_mds_session *session, { int mds = session->s_mds; struct ceph_mds_client *mdsc = session->s_mdsc; - struct ceph_mds_request_head_old *rhead; + struct ceph_mds_request_head_legacy *lhead; + struct ceph_mds_request_head *nhead; struct ceph_msg *msg; - int flags = 0, max_retry; + int flags = 0, old_max_retry; + bool old_version = !test_bit(CEPHFS_FEATURE_32BITS_RETRY_FWD, + &session->s_features); /* - * The type of 'r_attempts' in kernel 'ceph_mds_request' - * is 'int', while in 'ceph_mds_request_head' the type of - * 'num_retry' is '__u8'. So in case the request retries - * exceeding 256 times, the MDS will receive a incorrect - * retry seq. - * - * In this case it's ususally a bug in MDS and continue - * retrying the request makes no sense. - * - * In future this could be fixed in ceph code, so avoid - * using the hardcode here. + * Avoid inifinite retrying after overflow. The client will + * increase the retry count and if the MDS is old version, + * so we limit to retry at most 256 times. */ - max_retry = sizeof_field(struct ceph_mds_request_head, num_retry); - max_retry = 1 << (max_retry * BITS_PER_BYTE); - if (req->r_attempts >= max_retry) { - pr_warn_ratelimited("%s request tid %llu seq overflow\n", - __func__, req->r_tid); - return -EMULTIHOP; + if (req->r_attempts) { + old_max_retry = sizeof_field(struct ceph_mds_request_head_old, + num_retry); + old_max_retry = 1 << (old_max_retry * BITS_PER_BYTE); + if ((old_version && req->r_attempts >= old_max_retry) || + ((uint32_t)req->r_attempts >= U32_MAX)) { + pr_warn_ratelimited("%s request tid %llu seq overflow\n", + __func__, req->r_tid); + return -EMULTIHOP; + } } req->r_attempts++; @@ -2800,23 +3146,27 @@ static int __prepare_send_request(struct ceph_mds_session *session, * d_move mangles the src name. */ msg = req->r_request; - rhead = find_old_request_head(msg->front.iov_base, - session->s_con.peer_features); + lhead = find_legacy_request_head(msg->front.iov_base, + session->s_con.peer_features); - flags = le32_to_cpu(rhead->flags); + flags = le32_to_cpu(lhead->flags); flags |= CEPH_MDS_FLAG_REPLAY; - rhead->flags = cpu_to_le32(flags); + lhead->flags = cpu_to_le32(flags); if (req->r_target_inode) - rhead->ino = cpu_to_le64(ceph_ino(req->r_target_inode)); + lhead->ino = cpu_to_le64(ceph_ino(req->r_target_inode)); - rhead->num_retry = req->r_attempts - 1; + lhead->num_retry = req->r_attempts - 1; + if (!old_version) { + nhead = (struct ceph_mds_request_head*)msg->front.iov_base; + nhead->ext_num_retry = cpu_to_le32(req->r_attempts - 1); + } /* remove cap/dentry releases from message */ - rhead->num_releases = 0; + lhead->num_releases = 0; p = msg->front.iov_base + req->r_request_release_offset; - encode_timestamp_and_gids(&p, req); + encode_mclientrequest_tail(&p, req); msg->front.iov_len = p - msg->front.iov_base; msg->hdr.front_len = cpu_to_le32(msg->front.iov_len); @@ -2834,18 +3184,23 @@ static int __prepare_send_request(struct ceph_mds_session *session, } req->r_request = msg; - rhead = find_old_request_head(msg->front.iov_base, - session->s_con.peer_features); - rhead->oldest_client_tid = cpu_to_le64(__get_oldest_tid(mdsc)); + lhead = find_legacy_request_head(msg->front.iov_base, + session->s_con.peer_features); + lhead->oldest_client_tid = cpu_to_le64(__get_oldest_tid(mdsc)); if (test_bit(CEPH_MDS_R_GOT_UNSAFE, &req->r_req_flags)) flags |= CEPH_MDS_FLAG_REPLAY; if (test_bit(CEPH_MDS_R_ASYNC, &req->r_req_flags)) flags |= CEPH_MDS_FLAG_ASYNC; if (req->r_parent) flags |= CEPH_MDS_FLAG_WANT_DENTRY; - rhead->flags = cpu_to_le32(flags); - rhead->num_fwd = req->r_num_fwd; - rhead->num_retry = req->r_attempts - 1; + lhead->flags = cpu_to_le32(flags); + lhead->num_fwd = req->r_num_fwd; + lhead->num_retry = req->r_attempts - 1; + if (!old_version) { + nhead = (struct ceph_mds_request_head*)msg->front.iov_base; + nhead->ext_num_fwd = cpu_to_le32(req->r_num_fwd); + nhead->ext_num_retry = cpu_to_le32(req->r_attempts - 1); + } dout(" r_parent = %p\n", req->r_parent); return 0; @@ -3348,22 +3703,35 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) } dout("handle_reply tid %lld result %d\n", tid, result); - rinfo = &req->r_reply_info; if (test_bit(CEPHFS_FEATURE_REPLY_ENCODING, &session->s_features)) - err = parse_reply_info(session, msg, rinfo, (u64)-1); + err = parse_reply_info(session, msg, req, (u64)-1); else - err = parse_reply_info(session, msg, rinfo, session->s_con.peer_features); + err = parse_reply_info(session, msg, req, + session->s_con.peer_features); mutex_unlock(&mdsc->mutex); /* Must find target inode outside of mutexes to avoid deadlocks */ + rinfo = &req->r_reply_info; if ((err >= 0) && rinfo->head->is_target) { - struct inode *in; + struct inode *in = xchg(&req->r_new_inode, NULL); struct ceph_vino tvino = { .ino = le64_to_cpu(rinfo->targeti.in->ino), .snap = le64_to_cpu(rinfo->targeti.in->snapid) }; - in = ceph_get_inode(mdsc->fsc->sb, tvino); + /* + * If we ended up opening an existing inode, discard + * r_new_inode + */ + if (req->r_op == CEPH_MDS_OP_CREATE && + !req->r_reply_info.has_create_ino) { + /* This should never happen on an async create */ + WARN_ON_ONCE(req->r_deleg_ino); + iput(in); + in = NULL; + } + + in = ceph_get_inode(mdsc->fsc->sb, tvino, in); if (IS_ERR(in)) { err = PTR_ERR(in); mutex_lock(&session->s_mutex); @@ -3406,7 +3774,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) if (err == 0) { if (result == 0 && (req->r_op == CEPH_MDS_OP_READDIR || req->r_op == CEPH_MDS_OP_LSSNAP)) - ceph_readdir_prepopulate(req, req->r_session); + err = ceph_readdir_prepopulate(req, req->r_session); } current->journal_info = NULL; mutex_unlock(&req->r_fill_mutex); @@ -3491,33 +3859,21 @@ static void handle_forward(struct ceph_mds_client *mdsc, if (test_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags)) { dout("forward tid %llu aborted, unregistering\n", tid); __unregister_request(mdsc, req); - } else if (fwd_seq <= req->r_num_fwd) { + } else if (fwd_seq <= req->r_num_fwd || (uint32_t)fwd_seq >= U32_MAX) { /* - * The type of 'num_fwd' in ceph 'MClientRequestForward' - * is 'int32_t', while in 'ceph_mds_request_head' the - * type is '__u8'. So in case the request bounces between - * MDSes exceeding 256 times, the client will get stuck. - * - * In this case it's ususally a bug in MDS and continue - * bouncing the request makes no sense. + * Avoid inifinite retrying after overflow. * - * In future this could be fixed in ceph code, so avoid - * using the hardcode here. + * The MDS will increase the fwd count and in client side + * if the num_fwd is less than the one saved in request + * that means the MDS is an old version and overflowed of + * 8 bits. */ - int max = sizeof_field(struct ceph_mds_request_head, num_fwd); - max = 1 << (max * BITS_PER_BYTE); - if (req->r_num_fwd >= max) { - mutex_lock(&req->r_fill_mutex); - req->r_err = -EMULTIHOP; - set_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags); - mutex_unlock(&req->r_fill_mutex); - aborted = true; - pr_warn_ratelimited("forward tid %llu seq overflow\n", - tid); - } else { - dout("forward tid %llu to mds%d - old seq %d <= %d\n", - tid, next_mds, req->r_num_fwd, fwd_seq); - } + mutex_lock(&req->r_fill_mutex); + req->r_err = -EMULTIHOP; + set_bit(CEPH_MDS_R_ABORTED, &req->r_req_flags); + mutex_unlock(&req->r_fill_mutex); + aborted = true; + pr_warn_ratelimited("forward tid %llu seq overflow\n", tid); } else { /* resend. forward race not possible; mds would drop */ dout("forward tid %llu to mds%d (we resend)\n", tid, next_mds); @@ -4550,6 +4906,9 @@ static void handle_lease(struct ceph_mds_client *mdsc, dout("handle_lease from mds%d\n", mds); + if (!ceph_inc_mds_stopping_blocker(mdsc, session)) + return; + /* decode */ if (msg->front.iov_len < sizeof(*h) + sizeof(u32)) goto bad; @@ -4568,8 +4927,6 @@ static void handle_lease(struct ceph_mds_client *mdsc, dname.len, dname.name); mutex_lock(&session->s_mutex); - inc_session_sequence(session); - if (!inode) { dout("handle_lease no inode %llx\n", vino.ino); goto release; @@ -4631,9 +4988,13 @@ static void handle_lease(struct ceph_mds_client *mdsc, out: mutex_unlock(&session->s_mutex); iput(inode); + + ceph_dec_mds_stopping_blocker(mdsc); return; bad: + ceph_dec_mds_stopping_blocker(mdsc); + pr_err("corrupt lease message\n"); ceph_msg_dump(msg); } @@ -4829,6 +5190,9 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) } init_completion(&mdsc->safe_umount_waiters); + spin_lock_init(&mdsc->stopping_lock); + atomic_set(&mdsc->stopping_blockers, 0); + init_completion(&mdsc->stopping_waiter); init_waitqueue_head(&mdsc->session_close_wq); INIT_LIST_HEAD(&mdsc->waiting_for_map); mdsc->quotarealms_inodes = RB_ROOT; diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 86d2965e68a1fd1a52dde85f742b5733e62ec7c4..5a3714bdd64a8e163077f4baba72b47b6087c564 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -32,8 +32,9 @@ enum ceph_feature_type { CEPHFS_FEATURE_ALTERNATE_NAME, CEPHFS_FEATURE_NOTIFY_SESSION_STATE, CEPHFS_FEATURE_OP_GETVXATTR, + CEPHFS_FEATURE_32BITS_RETRY_FWD, - CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_OP_GETVXATTR, + CEPHFS_FEATURE_MAX = CEPHFS_FEATURE_32BITS_RETRY_FWD, }; #define CEPHFS_FEATURES_CLIENT_SUPPORTED { \ @@ -44,8 +45,10 @@ enum ceph_feature_type { CEPHFS_FEATURE_MULTI_RECONNECT, \ CEPHFS_FEATURE_DELEG_INO, \ CEPHFS_FEATURE_METRIC_COLLECT, \ + CEPHFS_FEATURE_ALTERNATE_NAME, \ CEPHFS_FEATURE_NOTIFY_SESSION_STATE, \ CEPHFS_FEATURE_OP_GETVXATTR, \ + CEPHFS_FEATURE_32BITS_RETRY_FWD, \ } /* @@ -86,13 +89,19 @@ struct ceph_mds_reply_info_in { s32 dir_pin; struct ceph_timespec btime; struct ceph_timespec snap_btime; + u8 *fscrypt_auth; + u8 *fscrypt_file; + u32 fscrypt_auth_len; + u32 fscrypt_file_len; u64 rsnaps; u64 change_attr; }; struct ceph_mds_reply_dir_entry { + bool is_nokey; char *name; u32 name_len; + u32 raw_hash; struct ceph_mds_reply_lease *lease; struct ceph_mds_reply_info_in inode; loff_t offset; @@ -116,7 +125,9 @@ struct ceph_mds_reply_info_parsed { struct ceph_mds_reply_info_in diri, targeti; struct ceph_mds_reply_dirfrag *dirfrag; char *dname; + u8 *altname; u32 dname_len; + u32 altname_len; struct ceph_mds_reply_lease *dlease; struct ceph_mds_reply_xattr xattr_info; @@ -263,6 +274,7 @@ struct ceph_mds_request { struct inode *r_parent; /* parent dir inode */ struct inode *r_target_inode; /* resulting inode */ + struct inode *r_new_inode; /* new inode (for creates) */ #define CEPH_MDS_R_DIRECT_IS_HASH (1) /* r_direct_hash is valid */ #define CEPH_MDS_R_ABORTED (2) /* call was aborted */ @@ -272,11 +284,19 @@ struct ceph_mds_request { #define CEPH_MDS_R_DID_PREPOPULATE (6) /* prepopulated readdir */ #define CEPH_MDS_R_PARENT_LOCKED (7) /* is r_parent->i_rwsem wlocked? */ #define CEPH_MDS_R_ASYNC (8) /* async request */ +#define CEPH_MDS_R_FSCRYPT_FILE (9) /* must marshal fscrypt_file field */ unsigned long r_req_flags; struct mutex r_fill_mutex; union ceph_mds_request_args r_args; + + struct ceph_fscrypt_auth *r_fscrypt_auth; + u64 r_fscrypt_file; + + u8 *r_altname; /* fscrypt binary crypttext for long filenames */ + u32 r_altname_len; /* length of r_altname */ + int r_fmode; /* file mode, if expecting cap */ int r_request_release_offset; const struct cred *r_cred; @@ -381,8 +401,9 @@ struct cap_wait { }; enum { - CEPH_MDSC_STOPPING_BEGIN = 1, - CEPH_MDSC_STOPPING_FLUSHED = 2, + CEPH_MDSC_STOPPING_BEGIN = 1, + CEPH_MDSC_STOPPING_FLUSHING = 2, + CEPH_MDSC_STOPPING_FLUSHED = 3, }; /* @@ -401,7 +422,11 @@ struct ceph_mds_client { struct ceph_mds_session **sessions; /* NULL for mds if no session */ atomic_t num_sessions; int max_sessions; /* len of sessions array */ - int stopping; /* true if shutting down */ + + spinlock_t stopping_lock; /* protect snap_empty */ + int stopping; /* the stage of shutting down */ + atomic_t stopping_blockers; + struct completion stopping_waiter; atomic64_t quotarealms_count; /* # realms with quota */ /* @@ -557,7 +582,7 @@ static inline void ceph_mdsc_free_path(char *path, int len) } extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, - int stop_on_nosnap); + int for_wire); extern void __ceph_mdsc_drop_dentry_lease(struct dentry *dentry); extern void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session, diff --git a/fs/ceph/quota.c b/fs/ceph/quota.c index 64592adfe48fbc70a31d100f260f7e1bb68368da..f7fcf7f08ec64270319d7644d5fbf9a2d7ab7e9f 100644 --- a/fs/ceph/quota.c +++ b/fs/ceph/quota.c @@ -47,25 +47,23 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc, struct inode *inode; struct ceph_inode_info *ci; + if (!ceph_inc_mds_stopping_blocker(mdsc, session)) + return; + if (msg->front.iov_len < sizeof(*h)) { pr_err("%s corrupt message mds%d len %d\n", __func__, session->s_mds, (int)msg->front.iov_len); ceph_msg_dump(msg); - return; + goto out; } - /* increment msg sequence number */ - mutex_lock(&session->s_mutex); - inc_session_sequence(session); - mutex_unlock(&session->s_mutex); - /* lookup inode */ vino.ino = le64_to_cpu(h->ino); vino.snap = CEPH_NOSNAP; inode = ceph_find_inode(sb, vino); if (!inode) { pr_warn("Failed to find inode %llu\n", vino.ino); - return; + goto out; } ci = ceph_inode(inode); @@ -78,6 +76,8 @@ void ceph_handle_quota(struct ceph_mds_client *mdsc, spin_unlock(&ci->i_ceph_lock); iput(inode); +out: + ceph_dec_mds_stopping_blocker(mdsc); } static struct ceph_quotarealm_inode * diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index c9920ade15f5f497a8acdb2378ec99b5f2960186..813f21add992c144678de60faae62b341dff4d38 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -1015,6 +1015,9 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, int locked_rwsem = 0; bool close_sessions = false; + if (!ceph_inc_mds_stopping_blocker(mdsc, session)) + return; + /* decode */ if (msg->front.iov_len < sizeof(*h)) goto bad; @@ -1030,10 +1033,6 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, dout("%s from mds%d op %s split %llx tracelen %d\n", __func__, mds, ceph_snap_op_name(op), split, trace_len); - mutex_lock(&session->s_mutex); - inc_session_sequence(session); - mutex_unlock(&session->s_mutex); - down_write(&mdsc->snap_rwsem); locked_rwsem = 1; @@ -1151,6 +1150,7 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, up_write(&mdsc->snap_rwsem); flush_snaps(mdsc); + ceph_dec_mds_stopping_blocker(mdsc); return; bad: @@ -1160,6 +1160,8 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc, if (locked_rwsem) up_write(&mdsc->snap_rwsem); + ceph_dec_mds_stopping_blocker(mdsc); + if (close_sessions) ceph_mdsc_close_sessions(mdsc); return; diff --git a/fs/ceph/super.c b/fs/ceph/super.c index a5f52013314d65d2c4ff576a01c08deffba872ae..2d7f5a8d4a926017524791a69af8b727e1f49731 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -20,6 +20,7 @@ #include "super.h" #include "mds_client.h" #include "cache.h" +#include "crypto.h" #include #include @@ -46,6 +47,7 @@ static void ceph_put_super(struct super_block *s) struct ceph_fs_client *fsc = ceph_sb_to_client(s); dout("put_super\n"); + ceph_fscrypt_free_dummy_policy(fsc); ceph_mdsc_close_sessions(fsc->mdsc); } @@ -151,6 +153,7 @@ enum { Opt_recover_session, Opt_source, Opt_mon_addr, + Opt_test_dummy_encryption, /* string args above */ Opt_dirstat, Opt_rbytes, @@ -165,6 +168,7 @@ enum { Opt_copyfrom, Opt_wsync, Opt_pagecache, + Opt_sparseread, }; enum ceph_recover_session_mode { @@ -192,6 +196,7 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = { fsparam_string ("fsc", Opt_fscache), // fsc=... fsparam_flag_no ("ino32", Opt_ino32), fsparam_string ("mds_namespace", Opt_mds_namespace), + fsparam_string ("mon_addr", Opt_mon_addr), fsparam_flag_no ("poolperm", Opt_poolperm), fsparam_flag_no ("quotadf", Opt_quotadf), fsparam_u32 ("rasize", Opt_rasize), @@ -203,10 +208,12 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = { fsparam_u32 ("rsize", Opt_rsize), fsparam_string ("snapdirname", Opt_snapdirname), fsparam_string ("source", Opt_source), - fsparam_string ("mon_addr", Opt_mon_addr), + fsparam_flag ("test_dummy_encryption", Opt_test_dummy_encryption), + fsparam_string ("test_dummy_encryption", Opt_test_dummy_encryption), fsparam_u32 ("wsize", Opt_wsize), fsparam_flag_no ("wsync", Opt_wsync), fsparam_flag_no ("pagecache", Opt_pagecache), + fsparam_flag_no ("sparseread", Opt_sparseread), {} }; @@ -576,6 +583,29 @@ static int ceph_parse_mount_param(struct fs_context *fc, else fsopt->flags &= ~CEPH_MOUNT_OPT_NOPAGECACHE; break; + case Opt_sparseread: + if (result.negated) + fsopt->flags &= ~CEPH_MOUNT_OPT_SPARSEREAD; + else + fsopt->flags |= CEPH_MOUNT_OPT_SPARSEREAD; + break; + case Opt_test_dummy_encryption: +#ifdef CONFIG_FS_ENCRYPTION + fscrypt_free_dummy_policy(&fsopt->dummy_enc_policy); + ret = fscrypt_parse_test_dummy_encryption(param, + &fsopt->dummy_enc_policy); + if (ret == -EINVAL) { + warnfc(fc, "Value of option \"%s\" is unrecognized", + param->key); + } else if (ret == -EEXIST) { + warnfc(fc, "Conflicting test_dummy_encryption options"); + ret = -EINVAL; + } +#else + warnfc(fc, + "FS encryption not supported: test_dummy_encryption mount option ignored"); +#endif + break; default: BUG(); } @@ -596,6 +626,7 @@ static void destroy_mount_options(struct ceph_mount_options *args) kfree(args->server_path); kfree(args->fscache_uniq); kfree(args->mon_addr); + fscrypt_free_dummy_policy(&args->dummy_enc_policy); kfree(args); } @@ -710,9 +741,12 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) if (!(fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS)) seq_puts(m, ",wsync"); - if (fsopt->flags & CEPH_MOUNT_OPT_NOPAGECACHE) seq_puts(m, ",nopagecache"); + if (fsopt->flags & CEPH_MOUNT_OPT_SPARSEREAD) + seq_puts(m, ",sparseread"); + + fscrypt_show_test_dummy_encryption(m, ',', root->d_sb); if (fsopt->wsize != CEPH_MAX_WRITE_SIZE) seq_printf(m, ",wsize=%u", fsopt->wsize); @@ -1052,6 +1086,50 @@ static struct dentry *open_root_dentry(struct ceph_fs_client *fsc, return root; } +#ifdef CONFIG_FS_ENCRYPTION +static int ceph_apply_test_dummy_encryption(struct super_block *sb, + struct fs_context *fc, + struct ceph_mount_options *fsopt) +{ + struct ceph_fs_client *fsc = sb->s_fs_info; + + if (!fscrypt_is_dummy_policy_set(&fsopt->dummy_enc_policy)) + return 0; + + /* No changing encryption context on remount. */ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE && + !fscrypt_is_dummy_policy_set(&fsc->fsc_dummy_enc_policy)) { + if (fscrypt_dummy_policies_equal(&fsopt->dummy_enc_policy, + &fsc->fsc_dummy_enc_policy)) + return 0; + errorfc(fc, "Can't set test_dummy_encryption on remount"); + return -EINVAL; + } + + /* Also make sure fsopt doesn't contain a conflicting value. */ + if (fscrypt_is_dummy_policy_set(&fsc->fsc_dummy_enc_policy)) { + if (fscrypt_dummy_policies_equal(&fsopt->dummy_enc_policy, + &fsc->fsc_dummy_enc_policy)) + return 0; + errorfc(fc, "Conflicting test_dummy_encryption options"); + return -EINVAL; + } + + fsc->fsc_dummy_enc_policy = fsopt->dummy_enc_policy; + memset(&fsopt->dummy_enc_policy, 0, sizeof(fsopt->dummy_enc_policy)); + + warnfc(fc, "test_dummy_encryption mode enabled"); + return 0; +} +#else +static int ceph_apply_test_dummy_encryption(struct super_block *sb, + struct fs_context *fc, + struct ceph_mount_options *fsopt) +{ + return 0; +} +#endif + /* * mount: join the ceph cluster, and open root directory. */ @@ -1080,6 +1158,11 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc, goto out; } + err = ceph_apply_test_dummy_encryption(fsc->sb, fc, + fsc->mount_options); + if (err) + goto out; + dout("mount opening path '%s'\n", path); ceph_fs_debugfs_init(fsc); @@ -1101,6 +1184,7 @@ static struct dentry *ceph_real_mount(struct ceph_fs_client *fsc, out: mutex_unlock(&fsc->client->mount_mutex); + ceph_fscrypt_free_dummy_policy(fsc); return ERR_PTR(err); } @@ -1126,6 +1210,8 @@ static int ceph_set_super(struct super_block *s, struct fs_context *fc) s->s_time_max = U32_MAX; s->s_flags |= SB_NODIRATIME | SB_NOATIME; + ceph_fscrypt_set_ops(s); + ret = set_anon_super_fc(s, fc); if (ret != 0) fsc->sb = NULL; @@ -1287,15 +1373,26 @@ static void ceph_free_fc(struct fs_context *fc) static int ceph_reconfigure_fc(struct fs_context *fc) { + int err; struct ceph_parse_opts_ctx *pctx = fc->fs_private; struct ceph_mount_options *fsopt = pctx->opts; - struct ceph_fs_client *fsc = ceph_sb_to_client(fc->root->d_sb); + struct super_block *sb = fc->root->d_sb; + struct ceph_fs_client *fsc = ceph_sb_to_client(sb); + + err = ceph_apply_test_dummy_encryption(sb, fc, fsopt); + if (err) + return err; if (fsopt->flags & CEPH_MOUNT_OPT_ASYNC_DIROPS) ceph_set_mount_opt(fsc, ASYNC_DIROPS); else ceph_clear_mount_opt(fsc, ASYNC_DIROPS); + if (fsopt->flags & CEPH_MOUNT_OPT_SPARSEREAD) + ceph_set_mount_opt(fsc, SPARSEREAD); + else + ceph_clear_mount_opt(fsc, SPARSEREAD); + if (strcmp_null(fsc->mount_options->mon_addr, fsopt->mon_addr)) { kfree(fsc->mount_options->mon_addr); fsc->mount_options->mon_addr = fsopt->mon_addr; @@ -1303,7 +1400,7 @@ static int ceph_reconfigure_fc(struct fs_context *fc) pr_notice("ceph: monitor addresses recorded, but not used for reconnection"); } - sync_filesystem(fc->root->d_sb); + sync_filesystem(sb); return 0; } @@ -1365,25 +1462,101 @@ static int ceph_init_fs_context(struct fs_context *fc) return -ENOMEM; } +/* + * Return true if it successfully increases the blocker counter, + * or false if the mdsc is in stopping and flushed state. + */ +static bool __inc_stopping_blocker(struct ceph_mds_client *mdsc) +{ + spin_lock(&mdsc->stopping_lock); + if (mdsc->stopping >= CEPH_MDSC_STOPPING_FLUSHING) { + spin_unlock(&mdsc->stopping_lock); + return false; + } + atomic_inc(&mdsc->stopping_blockers); + spin_unlock(&mdsc->stopping_lock); + return true; +} + +static void __dec_stopping_blocker(struct ceph_mds_client *mdsc) +{ + spin_lock(&mdsc->stopping_lock); + if (!atomic_dec_return(&mdsc->stopping_blockers) && + mdsc->stopping >= CEPH_MDSC_STOPPING_FLUSHING) + complete_all(&mdsc->stopping_waiter); + spin_unlock(&mdsc->stopping_lock); +} + +/* For metadata IO requests */ +bool ceph_inc_mds_stopping_blocker(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + mutex_lock(&session->s_mutex); + inc_session_sequence(session); + mutex_unlock(&session->s_mutex); + + return __inc_stopping_blocker(mdsc); +} + +void ceph_dec_mds_stopping_blocker(struct ceph_mds_client *mdsc) +{ + __dec_stopping_blocker(mdsc); +} + +/* For data IO requests */ +bool ceph_inc_osd_stopping_blocker(struct ceph_mds_client *mdsc) +{ + return __inc_stopping_blocker(mdsc); +} + +void ceph_dec_osd_stopping_blocker(struct ceph_mds_client *mdsc) +{ + __dec_stopping_blocker(mdsc); +} + static void ceph_kill_sb(struct super_block *s) { struct ceph_fs_client *fsc = ceph_sb_to_client(s); + struct ceph_mds_client *mdsc = fsc->mdsc; + bool wait; dout("kill_sb %p\n", s); - ceph_mdsc_pre_umount(fsc->mdsc); + ceph_mdsc_pre_umount(mdsc); flush_fs_workqueues(fsc); /* * Though the kill_anon_super() will finally trigger the - * sync_filesystem() anyway, we still need to do it here - * and then bump the stage of shutdown to stop the work - * queue as earlier as possible. + * sync_filesystem() anyway, we still need to do it here and + * then bump the stage of shutdown. This will allow us to + * drop any further message, which will increase the inodes' + * i_count reference counters but makes no sense any more, + * from MDSs. + * + * Without this when evicting the inodes it may fail in the + * kill_anon_super(), which will trigger a warning when + * destroying the fscrypt keyring and then possibly trigger + * a further crash in ceph module when the iput() tries to + * evict the inodes later. */ sync_filesystem(s); - fsc->mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHED; + spin_lock(&mdsc->stopping_lock); + mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHING; + wait = !!atomic_read(&mdsc->stopping_blockers); + spin_unlock(&mdsc->stopping_lock); + + if (wait && atomic_read(&mdsc->stopping_blockers)) { + long timeleft = wait_for_completion_killable_timeout( + &mdsc->stopping_waiter, + fsc->client->options->mount_timeout); + if (!timeleft) /* timed out */ + pr_warn("umount timed out, %ld\n", timeleft); + else if (timeleft < 0) /* killed */ + pr_warn("umount was killed, %ld\n", timeleft); + } + mdsc->stopping = CEPH_MDSC_STOPPING_FLUSHED; kill_anon_super(s); fsc->client->extra_mon_dispatch = NULL; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 3bfddf34d488b3216bcbea56a881a20f8311df19..51c7f2b14f6f870f72f9b12dfee211f30b8a9185 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -22,6 +22,7 @@ #include #include +#include "crypto.h" /* large granularity for statfs utilization stats to facilitate * large volume sizes on 32-bit machines. */ @@ -42,6 +43,7 @@ #define CEPH_MOUNT_OPT_NOCOPYFROM (1<<14) /* don't use RADOS 'copy-from' op */ #define CEPH_MOUNT_OPT_ASYNC_DIROPS (1<<15) /* allow async directory ops */ #define CEPH_MOUNT_OPT_NOPAGECACHE (1<<16) /* bypass pagecache altogether */ +#define CEPH_MOUNT_OPT_SPARSEREAD (1<<17) /* always do sparse reads */ #define CEPH_MOUNT_OPT_DEFAULT \ (CEPH_MOUNT_OPT_DCACHE | \ @@ -98,6 +100,7 @@ struct ceph_mount_options { char *server_path; /* default NULL (means "/") */ char *fscache_uniq; /* default NULL */ char *mon_addr; + struct fscrypt_dummy_policy dummy_enc_policy; }; /* mount state */ @@ -154,9 +157,11 @@ struct ceph_fs_client { #ifdef CONFIG_CEPH_FSCACHE struct fscache_volume *fscache; #endif +#ifdef CONFIG_FS_ENCRYPTION + struct fscrypt_dummy_policy fsc_dummy_enc_policy; +#endif }; - /* * File i/o capability. This tracks shared state with the metadata * server that allows us to cache or writeback attributes or to read @@ -419,6 +424,11 @@ struct ceph_inode_info { u32 i_truncate_seq; /* last truncate to smaller size */ u64 i_truncate_size; /* and the size we last truncated down to */ int i_truncate_pending; /* still need to call vmtruncate */ + /* + * For none fscrypt case it equals to i_truncate_size or it will + * equals to fscrypt_file_size + */ + u64 i_truncate_pagecache_size; u64 i_max_size; /* max file size authorized by mds */ u64 i_reported_size; /* (max_)size reported to or requested of mds */ @@ -449,6 +459,13 @@ struct ceph_inode_info { struct work_struct i_work; unsigned long i_work_mask; + +#ifdef CONFIG_FS_ENCRYPTION + u32 fscrypt_auth_len; + u32 fscrypt_file_len; + u8 *fscrypt_auth; + u8 *fscrypt_file; +#endif }; struct ceph_netfs_request_data { @@ -998,6 +1015,7 @@ static inline bool __ceph_have_pending_cap_snap(struct ceph_inode_info *ci) /* inode.c */ struct ceph_mds_reply_info_in; struct ceph_mds_reply_dirfrag; +struct ceph_acl_sec_ctx; extern const struct inode_operations ceph_file_iops; @@ -1005,8 +1023,14 @@ extern struct inode *ceph_alloc_inode(struct super_block *sb); extern void ceph_evict_inode(struct inode *inode); extern void ceph_free_inode(struct inode *inode); +struct inode *ceph_new_inode(struct inode *dir, struct dentry *dentry, + umode_t *mode, struct ceph_acl_sec_ctx *as_ctx); +void ceph_as_ctx_to_req(struct ceph_mds_request *req, + struct ceph_acl_sec_ctx *as_ctx); + extern struct inode *ceph_get_inode(struct super_block *sb, - struct ceph_vino vino); + struct ceph_vino vino, + struct inode *newino); extern struct inode *ceph_get_snapdir(struct inode *parent); extern int ceph_fill_file_size(struct inode *inode, int issued, u32 truncate_seq, u64 truncate_size, u64 size); @@ -1065,7 +1089,13 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force) } extern int ceph_permission(struct mnt_idmap *idmap, struct inode *inode, int mask); -extern int __ceph_setattr(struct inode *inode, struct iattr *attr); + +struct ceph_iattr { + struct ceph_fscrypt_auth *fscrypt_auth; +}; + +extern int __ceph_setattr(struct inode *inode, struct iattr *attr, + struct ceph_iattr *cia); extern int ceph_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr); extern int ceph_getattr(struct mnt_idmap *idmap, @@ -1099,6 +1129,9 @@ struct ceph_acl_sec_ctx { #ifdef CONFIG_CEPH_FS_SECURITY_LABEL void *sec_ctx; u32 sec_ctxlen; +#endif +#ifdef CONFIG_FS_ENCRYPTION + struct ceph_fscrypt_auth *fscrypt_auth; #endif struct ceph_pagelist *pagelist; }; @@ -1237,6 +1270,8 @@ extern int ceph_encode_dentry_release(void **p, struct dentry *dn, struct inode *dir, int mds, int drop, int unless); +extern int __ceph_get_caps(struct inode *inode, struct ceph_file_info *fi, + int need, int want, loff_t endoff, int *got); extern int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got); extern int ceph_try_get_caps(struct inode *inode, @@ -1272,6 +1307,9 @@ extern int ceph_renew_caps(struct inode *inode, int fmode); extern int ceph_open(struct inode *inode, struct file *file); extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry, struct file *file, unsigned flags, umode_t mode); +extern ssize_t __ceph_sync_read(struct inode *inode, loff_t *ki_pos, + struct iov_iter *to, int *retry_op, + u64 *last_objver); extern int ceph_release(struct inode *inode, struct file *filp); extern void ceph_fill_inline_data(struct inode *inode, struct page *locked_page, char *data, size_t len); @@ -1375,4 +1413,9 @@ extern bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf); extern void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc); +bool ceph_inc_mds_stopping_blocker(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session); +void ceph_dec_mds_stopping_blocker(struct ceph_mds_client *mdsc); +bool ceph_inc_osd_stopping_blocker(struct ceph_mds_client *mdsc); +void ceph_dec_osd_stopping_blocker(struct ceph_mds_client *mdsc); #endif /* _FS_CEPH_SUPER_H */ diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 1cbd84cc82a83724193a4e8d0a33ab2f7b73cc92..0deae4a0f5f169b4738019487ca4a25624d3f014 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -352,6 +352,24 @@ static ssize_t ceph_vxattrcb_auth_mds(struct ceph_inode_info *ci, return ret; } +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) +static bool ceph_vxattrcb_fscrypt_auth_exists(struct ceph_inode_info *ci) +{ + return ci->fscrypt_auth_len; +} + +static ssize_t ceph_vxattrcb_fscrypt_auth(struct ceph_inode_info *ci, + char *val, size_t size) +{ + if (size) { + if (size < ci->fscrypt_auth_len) + return -ERANGE; + memcpy(val, ci->fscrypt_auth, ci->fscrypt_auth_len); + } + return ci->fscrypt_auth_len; +} +#endif /* CONFIG_FS_ENCRYPTION */ + #define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name #define CEPH_XATTR_NAME2(_type, _name, _name2) \ XATTR_CEPH_PREFIX #_type "." #_name "." #_name2 @@ -500,6 +518,15 @@ static struct ceph_vxattr ceph_common_vxattrs[] = { .exists_cb = NULL, .flags = VXATTR_FLAG_READONLY, }, +#if IS_ENABLED(CONFIG_FS_ENCRYPTION) + { + .name = "ceph.fscrypt.auth", + .name_size = sizeof("ceph.fscrypt.auth"), + .getxattr_cb = ceph_vxattrcb_fscrypt_auth, + .exists_cb = ceph_vxattrcb_fscrypt_auth_exists, + .flags = VXATTR_FLAG_READONLY, + }, +#endif /* CONFIG_FS_ENCRYPTION */ { .name = NULL, 0 } /* Required table terminator */ }; @@ -1407,6 +1434,9 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx) #endif #ifdef CONFIG_CEPH_FS_SECURITY_LABEL security_release_secctx(as_ctx->sec_ctx, as_ctx->sec_ctxlen); +#endif +#ifdef CONFIG_FS_ENCRYPTION + kfree(as_ctx->fscrypt_auth); #endif if (as_ctx->pagelist) ceph_pagelist_release(as_ctx->pagelist); diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index e028fafa04f38c51739bb56c796bb5d854c7c4ba..996271473609a0dbde8cd3db3eadb73e35d2510d 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -32,10 +32,16 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf) u64 storage_space, remaining_space, max_variable_size; efi_status_t status; - status = efivar_query_variable_info(attr, &storage_space, &remaining_space, - &max_variable_size); - if (status != EFI_SUCCESS) - return efi_status_to_err(status); + /* Some UEFI firmware does not implement QueryVariableInfo() */ + storage_space = remaining_space = 0; + if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) { + status = efivar_query_variable_info(attr, &storage_space, + &remaining_space, + &max_variable_size); + if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) + pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n", + status); + } /* * This is not a normal filesystem, so no point in pretending it has a block diff --git a/fs/erofs/decompressor_lzma.c b/fs/erofs/decompressor_lzma.c index 73091fbe3ea453d377f20bc4cfc0c2de5f76c1e5..dee10d22ada96e5f6031d752e00ef33dbe984018 100644 --- a/fs/erofs/decompressor_lzma.c +++ b/fs/erofs/decompressor_lzma.c @@ -217,9 +217,12 @@ int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq, strm->buf.out_size = min_t(u32, outlen, PAGE_SIZE - pageofs); outlen -= strm->buf.out_size; - if (!rq->out[no] && rq->fillgaps) /* deduped */ + if (!rq->out[no] && rq->fillgaps) { /* deduped */ rq->out[no] = erofs_allocpage(pagepool, GFP_KERNEL | __GFP_NOFAIL); + set_page_private(rq->out[no], + Z_EROFS_SHORTLIVED_PAGE); + } if (rq->out[no]) strm->buf.out = kmap(rq->out[no]) + pageofs; pageofs = 0; diff --git a/fs/erofs/super.c b/fs/erofs/super.c index 44a24d573f1fd31455608a6757fd98195ce79a78..3700af9ee17332f4f6ea766ba3297cdd1e3746fc 100644 --- a/fs/erofs/super.c +++ b/fs/erofs/super.c @@ -235,7 +235,7 @@ static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb, return PTR_ERR(ptr); dis = ptr + erofs_blkoff(sb, *pos); - if (!dif->path) { + if (!sbi->devs->flatdev && !dif->path) { if (!dis->tag[0]) { erofs_err(sb, "empty device tag @ pos %llu", *pos); return -EINVAL; diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index c91db9f57524c4eef9c8c33bb195721784969e7b..1e599305d85fa2b0a4356c2ac9cd4eef905ab456 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -6906,6 +6907,21 @@ __acquires(bitlock) return ret; } +static ext4_grpblk_t ext4_last_grp_cluster(struct super_block *sb, + ext4_group_t grp) +{ + if (grp < ext4_get_groups_count(sb)) + return EXT4_CLUSTERS_PER_GROUP(sb) - 1; + return (ext4_blocks_count(EXT4_SB(sb)->s_es) - + ext4_group_first_block_no(sb, grp) - 1) >> + EXT4_CLUSTER_BITS(sb); +} + +static bool ext4_trim_interrupted(void) +{ + return fatal_signal_pending(current) || freezing(current); +} + static int ext4_try_to_trim_range(struct super_block *sb, struct ext4_buddy *e4b, ext4_grpblk_t start, ext4_grpblk_t max, ext4_grpblk_t minblocks) @@ -6913,9 +6929,12 @@ __acquires(ext4_group_lock_ptr(sb, e4b->bd_group)) __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) { ext4_grpblk_t next, count, free_count; + bool set_trimmed = false; void *bitmap; bitmap = e4b->bd_bitmap; + if (start == 0 && max >= ext4_last_grp_cluster(sb, e4b->bd_group)) + set_trimmed = true; start = max(e4b->bd_info->bb_first_free, start); count = 0; free_count = 0; @@ -6930,16 +6949,14 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) int ret = ext4_trim_extent(sb, start, next - start, e4b); if (ret && ret != -EOPNOTSUPP) - break; + return count; count += next - start; } free_count += next - start; start = next + 1; - if (fatal_signal_pending(current)) { - count = -ERESTARTSYS; - break; - } + if (ext4_trim_interrupted()) + return count; if (need_resched()) { ext4_unlock_group(sb, e4b->bd_group); @@ -6951,6 +6968,9 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) break; } + if (set_trimmed) + EXT4_MB_GRP_SET_TRIMMED(e4b->bd_info); + return count; } @@ -6961,7 +6981,6 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) * @start: first group block to examine * @max: last group block to examine * @minblocks: minimum extent block count - * @set_trimmed: set the trimmed flag if at least one block is trimmed * * ext4_trim_all_free walks through group's block bitmap searching for free * extents. When the free extent is found, mark it as used in group buddy @@ -6971,7 +6990,7 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) static ext4_grpblk_t ext4_trim_all_free(struct super_block *sb, ext4_group_t group, ext4_grpblk_t start, ext4_grpblk_t max, - ext4_grpblk_t minblocks, bool set_trimmed) + ext4_grpblk_t minblocks) { struct ext4_buddy e4b; int ret; @@ -6988,13 +7007,10 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, ext4_lock_group(sb, group); if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) || - minblocks < EXT4_SB(sb)->s_last_trim_minblks) { + minblocks < EXT4_SB(sb)->s_last_trim_minblks) ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks); - if (ret >= 0 && set_trimmed) - EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info); - } else { + else ret = 0; - } ext4_unlock_group(sb, group); ext4_mb_unload_buddy(&e4b); @@ -7027,7 +7043,6 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) ext4_fsblk_t first_data_blk = le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block); ext4_fsblk_t max_blks = ext4_blocks_count(EXT4_SB(sb)->s_es); - bool whole_group, eof = false; int ret = 0; start = range->start >> sb->s_blocksize_bits; @@ -7046,10 +7061,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) if (minlen > EXT4_CLUSTERS_PER_GROUP(sb)) goto out; } - if (end >= max_blks - 1) { + if (end >= max_blks - 1) end = max_blks - 1; - eof = true; - } if (end <= first_data_blk) goto out; if (start < first_data_blk) @@ -7063,9 +7076,10 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) /* end now represents the last cluster to discard in this group */ end = EXT4_CLUSTERS_PER_GROUP(sb) - 1; - whole_group = true; for (group = first_group; group <= last_group; group++) { + if (ext4_trim_interrupted()) + break; grp = ext4_get_group_info(sb, group); if (!grp) continue; @@ -7082,13 +7096,11 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) * change it for the last group, note that last_cluster is * already computed earlier by ext4_get_group_no_and_offset() */ - if (group == last_group) { + if (group == last_group) end = last_cluster; - whole_group = eof ? true : end == EXT4_CLUSTERS_PER_GROUP(sb) - 1; - } if (grp->bb_free >= minlen) { cnt = ext4_trim_all_free(sb, group, first_cluster, - end, minlen, whole_group); + end, minlen); if (cnt < 0) { ret = cnt; break; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 41a6411c600b1cba08ed24b667913a563f2d3b90..bbda587f76b85ab0a6840c4cf19b45151ea3f26b 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -343,17 +343,17 @@ static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, struct buffer_head *bh) { struct ext4_dir_entry_tail *t; + int blocksize = EXT4_BLOCK_SIZE(inode->i_sb); #ifdef PARANOID struct ext4_dir_entry *d, *top; d = (struct ext4_dir_entry *)bh->b_data; top = (struct ext4_dir_entry *)(bh->b_data + - (EXT4_BLOCK_SIZE(inode->i_sb) - - sizeof(struct ext4_dir_entry_tail))); - while (d < top && d->rec_len) + (blocksize - sizeof(struct ext4_dir_entry_tail))); + while (d < top && ext4_rec_len_from_disk(d->rec_len, blocksize)) d = (struct ext4_dir_entry *)(((void *)d) + - le16_to_cpu(d->rec_len)); + ext4_rec_len_from_disk(d->rec_len, blocksize)); if (d != top) return NULL; @@ -364,7 +364,8 @@ static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, #endif if (t->det_reserved_zero1 || - le16_to_cpu(t->det_rec_len) != sizeof(struct ext4_dir_entry_tail) || + (ext4_rec_len_from_disk(t->det_rec_len, blocksize) != + sizeof(struct ext4_dir_entry_tail)) || t->det_reserved_zero2 || t->det_reserved_ft != EXT4_FT_DIR_CSUM) return NULL; @@ -445,13 +446,14 @@ static struct dx_countlimit *get_dx_countlimit(struct inode *inode, struct ext4_dir_entry *dp; struct dx_root_info *root; int count_offset; + int blocksize = EXT4_BLOCK_SIZE(inode->i_sb); + unsigned int rlen = ext4_rec_len_from_disk(dirent->rec_len, blocksize); - if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb)) + if (rlen == blocksize) count_offset = 8; - else if (le16_to_cpu(dirent->rec_len) == 12) { + else if (rlen == 12) { dp = (struct ext4_dir_entry *)(((void *)dirent) + 12); - if (le16_to_cpu(dp->rec_len) != - EXT4_BLOCK_SIZE(inode->i_sb) - 12) + if (ext4_rec_len_from_disk(dp->rec_len, blocksize) != blocksize - 12) return NULL; root = (struct dx_root_info *)(((void *)dp + 12)); if (root->reserved_zero || @@ -1315,6 +1317,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, unsigned int buflen = bh->b_size; char *base = bh->b_data; struct dx_hash_info h = *hinfo; + int blocksize = EXT4_BLOCK_SIZE(dir->i_sb); if (ext4_has_metadata_csum(dir->i_sb)) buflen -= sizeof(struct ext4_dir_entry_tail); @@ -1335,11 +1338,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, map_tail--; map_tail->hash = h.hash; map_tail->offs = ((char *) de - base)>>2; - map_tail->size = le16_to_cpu(de->rec_len); + map_tail->size = ext4_rec_len_from_disk(de->rec_len, + blocksize); count++; cond_resched(); } - de = ext4_next_entry(de, dir->i_sb->s_blocksize); + de = ext4_next_entry(de, blocksize); } return count; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 38217422f938833f2eb9312f80da735190080dd6..dbebd8b3127e514e7dc165ced48da0b6ff3cd7da 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -7314,7 +7314,7 @@ static struct file_system_type ext4_fs_type = { .init_fs_context = ext4_init_fs_context, .parameters = ext4_param_specs, .kill_sb = ext4_kill_sb, - .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MGTIME, + .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; MODULE_ALIAS_FS("ext4"); diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 8fd3b7f9fb88ef741803b29db90aff61852d6aee..b0597a539fc54842922714c74f893452296164e2 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -1701,9 +1701,9 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc) } f2fs_restore_inmem_curseg(sbi); + stat_inc_cp_count(sbi); stop: unblock_operations(sbi); - stat_inc_cp_count(sbi->stat_info); if (cpc->reason & CP_RECOVERY) f2fs_notice(sbi, "checkpoint: version = %llx", ckpt_ver); diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c index 0f7df9c11af3150510646990df743dfc6e8b0c00..d820801f473e56ab7ea8d66f97c97e95293c4463 100644 --- a/fs/f2fs/compress.c +++ b/fs/f2fs/compress.c @@ -649,13 +649,8 @@ static int f2fs_compress_pages(struct compress_ctx *cc) goto destroy_compress_ctx; } - for (i = 0; i < cc->nr_cpages; i++) { + for (i = 0; i < cc->nr_cpages; i++) cc->cpages[i] = f2fs_compress_alloc_page(); - if (!cc->cpages[i]) { - ret = -ENOMEM; - goto out_free_cpages; - } - } cc->rbuf = f2fs_vmap(cc->rpages, cc->cluster_size); if (!cc->rbuf) { @@ -1574,8 +1569,6 @@ static int f2fs_prepare_decomp_mem(struct decompress_io_ctx *dic, } dic->tpages[i] = f2fs_compress_alloc_page(); - if (!dic->tpages[i]) - return -ENOMEM; } dic->rbuf = f2fs_vmap(dic->tpages, dic->cluster_size); @@ -1656,11 +1649,6 @@ struct decompress_io_ctx *f2fs_alloc_dic(struct compress_ctx *cc) struct page *page; page = f2fs_compress_alloc_page(); - if (!page) { - ret = -ENOMEM; - goto out_free; - } - f2fs_set_compressed_page(page, cc->inode, start_idx + i + 1, dic); dic->cpages[i] = page; diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 5882afe71d82bb1b36601f674898e671f08a6294..916e317ac925fc3e2b3b8ba5679654babfb9b08e 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1167,6 +1167,9 @@ static int f2fs_submit_page_read(struct inode *inode, struct page *page, f2fs_wait_on_block_writeback(inode, blkaddr); if (bio_add_page(bio, page, PAGE_SIZE, 0) < PAGE_SIZE) { + iostat_update_and_unbind_ctx(bio); + if (bio->bi_private) + mempool_free(bio->bi_private, bio_post_read_ctx_pool); bio_put(bio); return -EFAULT; } @@ -1389,18 +1392,14 @@ struct page *f2fs_get_lock_data_page(struct inode *inode, pgoff_t index, { struct address_space *mapping = inode->i_mapping; struct page *page; -repeat: + page = f2fs_get_read_data_page(inode, index, 0, for_write, NULL); if (IS_ERR(page)) return page; /* wait for read completion */ lock_page(page); - if (unlikely(page->mapping != mapping)) { - f2fs_put_page(page, 1); - goto repeat; - } - if (unlikely(!PageUptodate(page))) { + if (unlikely(page->mapping != mapping || !PageUptodate(page))) { f2fs_put_page(page, 1); return ERR_PTR(-EIO); } @@ -3236,8 +3235,7 @@ static int f2fs_write_cache_pages(struct address_space *mapping, } goto next; } - done_index = folio->index + - folio_nr_pages(folio); + done_index = folio_next_index(folio); done = 1; break; } diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c index 61c35b59126ecde97f4b020a0ac10a664f3f213e..fdbf994f12718c566ef03d22fae9a7050a2ba5ee 100644 --- a/fs/f2fs/debug.c +++ b/fs/f2fs/debug.c @@ -215,6 +215,9 @@ static void update_general_status(struct f2fs_sb_info *sbi) si->valid_blks[type] += blks; } + for (i = 0; i < MAX_CALL_TYPE; i++) + si->cp_call_count[i] = atomic_read(&sbi->cp_call_count[i]); + for (i = 0; i < 2; i++) { si->segment_count[i] = sbi->segment_count[i]; si->block_count[i] = sbi->block_count[i]; @@ -497,7 +500,9 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n", si->prefree_count, si->free_segs, si->free_secs); seq_printf(s, "CP calls: %d (BG: %d)\n", - si->cp_count, si->bg_cp_count); + si->cp_call_count[TOTAL_CALL], + si->cp_call_count[BACKGROUND]); + seq_printf(s, "CP count: %d\n", si->cp_count); seq_printf(s, " - cp blocks : %u\n", si->meta_count[META_CP]); seq_printf(s, " - sit blocks : %u\n", si->meta_count[META_SIT]); @@ -511,12 +516,24 @@ static int stat_show(struct seq_file *s, void *v) seq_printf(s, " - Total : %4d\n", si->nr_total_ckpt); seq_printf(s, " - Cur time : %4d(ms)\n", si->cur_ckpt_time); seq_printf(s, " - Peak time : %4d(ms)\n", si->peak_ckpt_time); - seq_printf(s, "GC calls: %d (BG: %d)\n", - si->call_count, si->bg_gc); - seq_printf(s, " - data segments : %d (%d)\n", - si->data_segs, si->bg_data_segs); - seq_printf(s, " - node segments : %d (%d)\n", - si->node_segs, si->bg_node_segs); + seq_printf(s, "GC calls: %d (gc_thread: %d)\n", + si->gc_call_count[BACKGROUND] + + si->gc_call_count[FOREGROUND], + si->gc_call_count[BACKGROUND]); + if (__is_large_section(sbi)) { + seq_printf(s, " - data sections : %d (BG: %d)\n", + si->gc_secs[DATA][BG_GC] + si->gc_secs[DATA][FG_GC], + si->gc_secs[DATA][BG_GC]); + seq_printf(s, " - node sections : %d (BG: %d)\n", + si->gc_secs[NODE][BG_GC] + si->gc_secs[NODE][FG_GC], + si->gc_secs[NODE][BG_GC]); + } + seq_printf(s, " - data segments : %d (BG: %d)\n", + si->gc_segs[DATA][BG_GC] + si->gc_segs[DATA][FG_GC], + si->gc_segs[DATA][BG_GC]); + seq_printf(s, " - node segments : %d (BG: %d)\n", + si->gc_segs[NODE][BG_GC] + si->gc_segs[NODE][FG_GC], + si->gc_segs[NODE][BG_GC]); seq_puts(s, " - Reclaimed segs :\n"); seq_printf(s, " - Normal : %d\n", sbi->gc_reclaimed_segs[GC_NORMAL]); seq_printf(s, " - Idle CB : %d\n", sbi->gc_reclaimed_segs[GC_IDLE_CB]); @@ -687,6 +704,8 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) atomic_set(&sbi->inplace_count, 0); for (i = META_CP; i < META_MAX; i++) atomic_set(&sbi->meta_count[i], 0); + for (i = 0; i < MAX_CALL_TYPE; i++) + atomic_set(&sbi->cp_call_count[i], 0); atomic_set(&sbi->max_aw_cnt, 0); diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 613132339d72616d42ac30c73b21257ebfb491da..6d688e42d89c59a84e4d17f681d304cb7c015431 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1383,6 +1383,13 @@ enum errors_option { MOUNT_ERRORS_PANIC, /* panic on errors */ }; +enum { + BACKGROUND, + FOREGROUND, + MAX_CALL_TYPE, + TOTAL_CALL = FOREGROUND, +}; + static inline int f2fs_test_bit(unsigned int nr, char *addr); static inline void f2fs_set_bit(unsigned int nr, char *addr); static inline void f2fs_clear_bit(unsigned int nr, char *addr); @@ -1695,6 +1702,7 @@ struct f2fs_sb_info { unsigned int io_skip_bggc; /* skip background gc for in-flight IO */ unsigned int other_skip_bggc; /* skip background gc for other reasons */ unsigned int ndirty_inode[NR_INODE_TYPE]; /* # of dirty inodes */ + atomic_t cp_call_count[MAX_CALL_TYPE]; /* # of cp call */ #endif spinlock_t stat_lock; /* lock for stat operations */ @@ -2114,15 +2122,6 @@ static inline int f2fs_down_read_trylock(struct f2fs_rwsem *sem) return down_read_trylock(&sem->internal_rwsem); } -#ifdef CONFIG_DEBUG_LOCK_ALLOC -static inline void f2fs_down_read_nested(struct f2fs_rwsem *sem, int subclass) -{ - down_read_nested(&sem->internal_rwsem, subclass); -} -#else -#define f2fs_down_read_nested(sem, subclass) f2fs_down_read(sem) -#endif - static inline void f2fs_up_read(struct f2fs_rwsem *sem) { up_read(&sem->internal_rwsem); @@ -2133,6 +2132,21 @@ static inline void f2fs_down_write(struct f2fs_rwsem *sem) down_write(&sem->internal_rwsem); } +#ifdef CONFIG_DEBUG_LOCK_ALLOC +static inline void f2fs_down_read_nested(struct f2fs_rwsem *sem, int subclass) +{ + down_read_nested(&sem->internal_rwsem, subclass); +} + +static inline void f2fs_down_write_nested(struct f2fs_rwsem *sem, int subclass) +{ + down_write_nested(&sem->internal_rwsem, subclass); +} +#else +#define f2fs_down_read_nested(sem, subclass) f2fs_down_read(sem) +#define f2fs_down_write_nested(sem, subclass) f2fs_down_write(sem) +#endif + static inline int f2fs_down_write_trylock(struct f2fs_rwsem *sem) { return down_write_trylock(&sem->internal_rwsem); @@ -3887,7 +3901,7 @@ struct f2fs_stat_info { int nats, dirty_nats, sits, dirty_sits; int free_nids, avail_nids, alloc_nids; int total_count, utilization; - int bg_gc, nr_wb_cp_data, nr_wb_data; + int nr_wb_cp_data, nr_wb_data; int nr_rd_data, nr_rd_node, nr_rd_meta; int nr_dio_read, nr_dio_write; unsigned int io_skip_bggc, other_skip_bggc; @@ -3907,9 +3921,11 @@ struct f2fs_stat_info { int rsvd_segs, overp_segs; int dirty_count, node_pages, meta_pages, compress_pages; int compress_page_hit; - int prefree_count, call_count, cp_count, bg_cp_count; - int tot_segs, node_segs, data_segs, free_segs, free_secs; - int bg_node_segs, bg_data_segs; + int prefree_count, free_segs, free_secs; + int cp_call_count[MAX_CALL_TYPE], cp_count; + int gc_call_count[MAX_CALL_TYPE]; + int gc_segs[2][2]; + int gc_secs[2][2]; int tot_blks, data_blks, node_blks; int bg_data_blks, bg_node_blks; int curseg[NR_CURSEG_TYPE]; @@ -3931,10 +3947,9 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) return (struct f2fs_stat_info *)sbi->stat_info; } -#define stat_inc_cp_count(si) ((si)->cp_count++) -#define stat_inc_bg_cp_count(si) ((si)->bg_cp_count++) -#define stat_inc_call_count(si) ((si)->call_count++) -#define stat_inc_bggc_count(si) ((si)->bg_gc++) +#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_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]++) @@ -4019,18 +4034,12 @@ static inline struct f2fs_stat_info *F2FS_STAT(struct f2fs_sb_info *sbi) if (cur > max) \ atomic_set(&F2FS_I_SB(inode)->max_aw_cnt, cur); \ } while (0) -#define stat_inc_seg_count(sbi, type, gc_type) \ - do { \ - struct f2fs_stat_info *si = F2FS_STAT(sbi); \ - si->tot_segs++; \ - if ((type) == SUM_TYPE_DATA) { \ - si->data_segs++; \ - si->bg_data_segs += (gc_type == BG_GC) ? 1 : 0; \ - } else { \ - si->node_segs++; \ - si->bg_node_segs += (gc_type == BG_GC) ? 1 : 0; \ - } \ - } while (0) +#define stat_inc_gc_call_count(sbi, foreground) \ + (F2FS_STAT(sbi)->gc_call_count[(foreground)]++) +#define stat_inc_gc_sec_count(sbi, type, gc_type) \ + (F2FS_STAT(sbi)->gc_secs[(type)][(gc_type)]++) +#define stat_inc_gc_seg_count(sbi, type, gc_type) \ + (F2FS_STAT(sbi)->gc_segs[(type)][(gc_type)]++) #define stat_inc_tot_blk_count(si, blks) \ ((si)->tot_blks += (blks)) @@ -4057,10 +4066,8 @@ void __init f2fs_create_root_stats(void); void f2fs_destroy_root_stats(void); void f2fs_update_sit_info(struct f2fs_sb_info *sbi); #else -#define stat_inc_cp_count(si) do { } while (0) -#define stat_inc_bg_cp_count(si) do { } while (0) -#define stat_inc_call_count(si) do { } while (0) -#define stat_inc_bggc_count(si) do { } while (0) +#define stat_inc_cp_call_count(sbi, foreground) do { } while (0) +#define stat_inc_cp_count(sbi) do { } while (0) #define stat_io_skip_bggc_count(sbi) do { } while (0) #define stat_other_skip_bggc_count(sbi) do { } while (0) #define stat_inc_dirty_inode(sbi, type) do { } while (0) @@ -4088,7 +4095,9 @@ void f2fs_update_sit_info(struct f2fs_sb_info *sbi); #define stat_inc_seg_type(sbi, curseg) do { } while (0) #define stat_inc_block_count(sbi, curseg) do { } while (0) #define stat_inc_inplace_blocks(sbi) do { } while (0) -#define stat_inc_seg_count(sbi, type, gc_type) do { } while (0) +#define stat_inc_gc_call_count(sbi, foreground) do { } while (0) +#define stat_inc_gc_sec_count(sbi, type, gc_type) do { } while (0) +#define stat_inc_gc_seg_count(sbi, type, gc_type) do { } while (0) #define stat_inc_tot_blk_count(si, blks) do { } while (0) #define stat_inc_data_blk_count(sbi, blks, gc_type) do { } while (0) #define stat_inc_node_blk_count(sbi, blks, gc_type) do { } while (0) @@ -4425,6 +4434,22 @@ static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi, } #endif +static inline int f2fs_bdev_index(struct f2fs_sb_info *sbi, + struct block_device *bdev) +{ + int i; + + if (!f2fs_is_multi_device(sbi)) + return 0; + + for (i = 0; i < sbi->s_ndevs; i++) + if (FDEV(i).bdev == bdev) + return i; + + WARN_ON(1); + return -1; +} + static inline bool f2fs_hw_should_discard(struct f2fs_sb_info *sbi) { return f2fs_sb_has_blkzoned(sbi); @@ -4485,7 +4510,8 @@ static inline bool f2fs_low_mem_mode(struct f2fs_sb_info *sbi) static inline bool f2fs_may_compress(struct inode *inode) { if (IS_SWAPFILE(inode) || f2fs_is_pinned_file(inode) || - f2fs_is_atomic_file(inode) || f2fs_has_inline_data(inode)) + f2fs_is_atomic_file(inode) || f2fs_has_inline_data(inode) || + f2fs_is_mmap_file(inode)) return false; return S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode); } diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index ce9d567cd5fe7f85d84f361f51d6cad84a25874b..ca5904129b1620690dc8cae13f365607b4ef0cf7 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -526,7 +526,11 @@ static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) file_accessed(file); vma->vm_ops = &f2fs_file_vm_ops; + + f2fs_down_read(&F2FS_I(inode)->i_sem); set_inode_flag(inode, FI_MMAP_FILE); + f2fs_up_read(&F2FS_I(inode)->i_sem); + return 0; } @@ -1724,6 +1728,7 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset, if (has_not_enough_free_secs(sbi, 0, GET_SEC_FROM_SEG(sbi, overprovision_segments(sbi)))) { f2fs_down_write(&sbi->gc_lock); + stat_inc_gc_call_count(sbi, FOREGROUND); err = f2fs_gc(sbi, &gc_control); if (err && err != -ENODATA) goto out_err; @@ -1919,12 +1924,19 @@ static int f2fs_setflags_common(struct inode *inode, u32 iflags, u32 mask) int err = f2fs_convert_inline_inode(inode); if (err) return err; - if (!f2fs_may_compress(inode)) - return -EINVAL; - if (S_ISREG(inode->i_mode) && F2FS_HAS_BLOCKS(inode)) + + f2fs_down_write(&F2FS_I(inode)->i_sem); + if (!f2fs_may_compress(inode) || + (S_ISREG(inode->i_mode) && + F2FS_HAS_BLOCKS(inode))) { + f2fs_up_write(&F2FS_I(inode)->i_sem); return -EINVAL; - if (set_compress_context(inode)) - return -EOPNOTSUPP; + } + err = set_compress_context(inode); + f2fs_up_write(&F2FS_I(inode)->i_sem); + + if (err) + return err; } } @@ -2465,6 +2477,7 @@ static int f2fs_ioc_gc(struct file *filp, unsigned long arg) gc_control.init_gc_type = sync ? FG_GC : BG_GC; gc_control.err_gc_skipped = sync; + stat_inc_gc_call_count(sbi, FOREGROUND); ret = f2fs_gc(sbi, &gc_control); out: mnt_drop_write_file(filp); @@ -2508,6 +2521,7 @@ static int __f2fs_ioc_gc_range(struct file *filp, struct f2fs_gc_range *range) } gc_control.victim_segno = GET_SEGNO(sbi, range->start); + stat_inc_gc_call_count(sbi, FOREGROUND); ret = f2fs_gc(sbi, &gc_control); if (ret) { if (ret == -EBUSY) @@ -2990,6 +3004,7 @@ static int f2fs_ioc_flush_device(struct file *filp, unsigned long arg) sm->last_victim[ALLOC_NEXT] = end_segno + 1; gc_control.victim_segno = start_segno; + stat_inc_gc_call_count(sbi, FOREGROUND); ret = f2fs_gc(sbi, &gc_control); if (ret == -EAGAIN) ret = 0; @@ -3976,6 +3991,7 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) file_start_write(filp); inode_lock(inode); + f2fs_down_write(&F2FS_I(inode)->i_sem); if (f2fs_is_mmap_file(inode) || get_dirty_pages(inode)) { ret = -EBUSY; goto out; @@ -3995,6 +4011,7 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg) f2fs_warn(sbi, "compression algorithm is successfully set, " "but current kernel doesn't support this algorithm."); out: + f2fs_up_write(&F2FS_I(inode)->i_sem); inode_unlock(inode); file_end_write(filp); @@ -4079,10 +4096,8 @@ static int f2fs_ioc_decompress_file(struct file *filp) last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); count = last_idx - page_idx; - while (count) { - int len = min(cluster_size, count); - - ret = redirty_blocks(inode, page_idx, len); + while (count && count >= cluster_size) { + ret = redirty_blocks(inode, page_idx, cluster_size); if (ret < 0) break; @@ -4092,8 +4107,14 @@ static int f2fs_ioc_decompress_file(struct file *filp) break; } - count -= len; - page_idx += len; + count -= cluster_size; + page_idx += cluster_size; + + cond_resched(); + if (fatal_signal_pending(current)) { + ret = -EINTR; + break; + } } if (!ret) @@ -4153,10 +4174,8 @@ static int f2fs_ioc_compress_file(struct file *filp) last_idx = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); count = last_idx - page_idx; - while (count) { - int len = min(cluster_size, count); - - ret = redirty_blocks(inode, page_idx, len); + while (count && count >= cluster_size) { + ret = redirty_blocks(inode, page_idx, cluster_size); if (ret < 0) break; @@ -4166,8 +4185,14 @@ static int f2fs_ioc_compress_file(struct file *filp) break; } - count -= len; - page_idx += len; + count -= cluster_size; + page_idx += cluster_size; + + cond_resched(); + if (fatal_signal_pending(current)) { + ret = -EINTR; + break; + } } if (!ret) @@ -4579,6 +4604,7 @@ static int f2fs_dio_write_end_io(struct kiocb *iocb, ssize_t size, int error, dec_page_count(sbi, F2FS_DIO_WRITE); if (error) return error; + f2fs_update_time(sbi, REQ_TIME); f2fs_update_iostat(sbi, NULL, APP_DIRECT_IO, size); return 0; } diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index a1ca394bc32765373f16ae95d35699bfe9566df9..f550cdeaa663845c61194f0c7e4be85304b62000 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -121,8 +121,8 @@ static int gc_thread_func(void *data) else increase_sleep_time(gc_th, &wait_ms); do_gc: - if (!foreground) - stat_inc_bggc_count(sbi->stat_info); + stat_inc_gc_call_count(sbi, foreground ? + FOREGROUND : BACKGROUND); sync_mode = F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_SYNC; @@ -1685,6 +1685,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, 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)) @@ -1766,7 +1767,7 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, segno, gc_type, force_migrate); - stat_inc_seg_count(sbi, type, gc_type); + stat_inc_gc_seg_count(sbi, data_type, gc_type); sbi->gc_reclaimed_segs[sbi->gc_mode]++; migrated++; @@ -1783,12 +1784,12 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, } if (submitted) - f2fs_submit_merged_write(sbi, - (type == SUM_TYPE_NODE) ? NODE : DATA); + f2fs_submit_merged_write(sbi, data_type); blk_finish_plug(&plug); - stat_inc_call_count(sbi->stat_info); + if (migrated) + stat_inc_gc_sec_count(sbi, data_type, gc_type); return seg_freed; } @@ -1839,6 +1840,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control) * secure free segments which doesn't need fggc any more. */ if (prefree_segments(sbi)) { + stat_inc_cp_call_count(sbi, TOTAL_CALL); ret = f2fs_write_checkpoint(sbi, &cpc); if (ret) goto stop; @@ -1887,6 +1889,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control) round++; if (skipped_round > MAX_SKIP_GC_COUNT && skipped_round * 2 >= round) { + stat_inc_cp_call_count(sbi, TOTAL_CALL); ret = f2fs_write_checkpoint(sbi, &cpc); goto stop; } @@ -1902,6 +1905,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control) */ if (free_sections(sbi) <= upper_secs + NR_GC_CHECKPOINT_SECS && prefree_segments(sbi)) { + stat_inc_cp_call_count(sbi, TOTAL_CALL); ret = f2fs_write_checkpoint(sbi, &cpc); if (ret) goto stop; @@ -2029,6 +2033,7 @@ static int free_segment_range(struct f2fs_sb_info *sbi, if (gc_only) goto out; + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); if (err) goto out; @@ -2223,6 +2228,7 @@ int f2fs_resize_fs(struct file *filp, __u64 block_count) clear_sbi_flag(sbi, SBI_IS_RESIZEFS); set_sbi_flag(sbi, SBI_IS_DIRTY); + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); if (err) { update_fs_metadata(sbi, secs); diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c index 88fc9208ffa73ac4d826ad26a6e693c52edfc179..2fe25619ccb5f55c7eba1e1a7e8ffd656785c93a 100644 --- a/fs/f2fs/inline.c +++ b/fs/f2fs/inline.c @@ -641,7 +641,8 @@ int f2fs_add_inline_entry(struct inode *dir, const struct f2fs_filename *fname, } if (inode) { - f2fs_down_write(&F2FS_I(inode)->i_sem); + f2fs_down_write_nested(&F2FS_I(inode)->i_sem, + SINGLE_DEPTH_NESTING); page = f2fs_init_inode_metadata(inode, dir, fname, ipage); if (IS_ERR(page)) { err = PTR_ERR(page); diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index c1c2ba9f28e5fb92e80c40ab8c5a1acd41592a47..cde243840abd19b1efc576cfaa34911d2f60e328 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -214,7 +214,7 @@ static bool sanity_check_compress_inode(struct inode *inode, f2fs_warn(sbi, "%s: inode (ino=%lx) has unsupported compress algorithm: %u, run fsck to fix", __func__, inode->i_ino, ri->i_compress_algorithm); - goto err; + return false; } if (le64_to_cpu(ri->i_compr_blocks) > SECTOR_TO_BLOCK(inode->i_blocks)) { @@ -222,14 +222,14 @@ static bool sanity_check_compress_inode(struct inode *inode, "%s: inode (ino=%lx) has inconsistent i_compr_blocks:%llu, i_blocks:%llu, run fsck to fix", __func__, inode->i_ino, le64_to_cpu(ri->i_compr_blocks), SECTOR_TO_BLOCK(inode->i_blocks)); - goto err; + return false; } if (ri->i_log_cluster_size < MIN_COMPRESS_LOG_SIZE || ri->i_log_cluster_size > MAX_COMPRESS_LOG_SIZE) { f2fs_warn(sbi, "%s: inode (ino=%lx) has unsupported log cluster size: %u, run fsck to fix", __func__, inode->i_ino, ri->i_log_cluster_size); - goto err; + return false; } clevel = le16_to_cpu(ri->i_compress_flag) >> @@ -273,8 +273,6 @@ static bool sanity_check_compress_inode(struct inode *inode, err_level: f2fs_warn(sbi, "%s: inode (ino=%lx) has unsupported compress level: %u, run fsck to fix", __func__, inode->i_ino, clevel); -err: - set_sbi_flag(sbi, SBI_NEED_FSCK); return false; } @@ -287,14 +285,12 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) iblocks = le64_to_cpu(F2FS_INODE(node_page)->i_blocks); if (!iblocks) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode i_blocks i_ino=%lx iblocks=%llu, run fsck to fix.", __func__, inode->i_ino, iblocks); return false; } if (ino_of_node(node_page) != nid_of_node(node_page)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode footer i_ino=%lx, ino,nid: [%u, %u] run fsck to fix.", __func__, inode->i_ino, ino_of_node(node_page), nid_of_node(node_page)); @@ -303,7 +299,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) if (f2fs_has_extra_attr(inode)) { if (!f2fs_sb_has_extra_attr(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: inode (ino=%lx) is with extra_attr, but extra_attr feature is off", __func__, inode->i_ino); return false; @@ -311,7 +306,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) if (fi->i_extra_isize > F2FS_TOTAL_EXTRA_ATTR_SIZE || fi->i_extra_isize < F2FS_MIN_EXTRA_ATTR_SIZE || fi->i_extra_isize % sizeof(__le32)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_extra_isize: %d, max: %zu", __func__, inode->i_ino, fi->i_extra_isize, F2FS_TOTAL_EXTRA_ATTR_SIZE); @@ -321,7 +315,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) f2fs_has_inline_xattr(inode) && (!fi->i_inline_xattr_size || fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %zu", __func__, inode->i_ino, fi->i_inline_xattr_size, MAX_INLINE_XATTR_SIZE); @@ -335,7 +328,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) return false; } } else if (f2fs_sb_has_flexible_inline_xattr(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode ino=%lx, run fsck to fix.", __func__, inode->i_ino); return false; @@ -343,31 +335,26 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) if (!f2fs_sb_has_extra_attr(sbi)) { if (f2fs_sb_has_project_quota(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.", __func__, inode->i_ino, F2FS_FEATURE_PRJQUOTA); return false; } if (f2fs_sb_has_inode_chksum(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.", __func__, inode->i_ino, F2FS_FEATURE_INODE_CHKSUM); return false; } if (f2fs_sb_has_flexible_inline_xattr(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.", __func__, inode->i_ino, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); return false; } if (f2fs_sb_has_inode_crtime(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.", __func__, inode->i_ino, F2FS_FEATURE_INODE_CRTIME); return false; } if (f2fs_sb_has_compression(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.", __func__, inode->i_ino, F2FS_FEATURE_COMPRESSION); return false; @@ -375,21 +362,18 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) } if (f2fs_sanity_check_inline_data(inode)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: inode (ino=%lx, mode=%u) should not have inline_data, run fsck to fix", __func__, inode->i_ino, inode->i_mode); return false; } if (f2fs_has_inline_dentry(inode) && !S_ISDIR(inode->i_mode)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: inode (ino=%lx, mode=%u) should not have inline_dentry, run fsck to fix", __func__, inode->i_ino, inode->i_mode); return false; } if ((fi->i_flags & F2FS_CASEFOLD_FL) && !f2fs_sb_has_casefold(sbi)) { - set_sbi_flag(sbi, SBI_NEED_FSCK); f2fs_warn(sbi, "%s: inode (ino=%lx) has casefold flag, but casefold feature is off", __func__, inode->i_ino); return false; @@ -475,6 +459,13 @@ static int do_read_inode(struct inode *inode) fi->i_inline_xattr_size = 0; } + if (!sanity_check_inode(inode, node_page)) { + f2fs_put_page(node_page, 1); + set_sbi_flag(sbi, SBI_NEED_FSCK); + f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); + return -EFSCORRUPTED; + } + /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) __recover_inline_status(inode, node_page); @@ -544,12 +535,6 @@ static int do_read_inode(struct inode *inode) f2fs_init_read_extent_tree(inode, node_page); f2fs_init_age_extent_tree(inode); - if (!sanity_check_inode(inode, node_page)) { - f2fs_put_page(node_page, 1); - f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); - return -EFSCORRUPTED; - } - if (!sanity_check_extent_cache(inode)) { f2fs_put_page(node_page, 1); f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index b8637e88d94f465210c2cdd8498f058d066d378f..7be60df277a5293f1bcb58aabc06d96b1c703666 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -924,6 +924,7 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) struct cp_control cpc = { .reason = CP_RECOVERY, }; + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); } } diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 0457d620011f6b17ddd28341413d6e11394ef1b3..d05b41608fc00513a186bdf4339fbc81505d925f 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -205,6 +205,8 @@ void f2fs_abort_atomic_write(struct inode *inode, bool clean) f2fs_i_size_write(inode, fi->original_i_size); fi->original_i_size = 0; } + /* avoid stale dirty inode during eviction */ + sync_inode_metadata(inode, 0); } static int __replace_atomic_write_block(struct inode *inode, pgoff_t index, @@ -433,6 +435,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need) .err_gc_skipped = false, .nr_free_secs = 1 }; f2fs_down_write(&sbi->gc_lock); + stat_inc_gc_call_count(sbi, FOREGROUND); f2fs_gc(sbi, &gc_control); } } @@ -510,8 +513,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi, bool from_bg) mutex_unlock(&sbi->flush_lock); } + stat_inc_cp_call_count(sbi, BACKGROUND); f2fs_sync_fs(sbi->sb, 1); - stat_inc_bg_cp_count(sbi->stat_info); } static int __submit_flush_wait(struct f2fs_sb_info *sbi, @@ -1258,8 +1261,16 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi, #ifdef CONFIG_BLK_DEV_ZONED if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) { - __submit_zone_reset_cmd(sbi, dc, flag, wait_list, issued); - return 0; + int devi = f2fs_bdev_index(sbi, bdev); + + if (devi < 0) + return -EINVAL; + + if (f2fs_blkz_is_seq(sbi, devi, dc->di.start)) { + __submit_zone_reset_cmd(sbi, dc, flag, + wait_list, issued); + return 0; + } } #endif @@ -1785,15 +1796,24 @@ static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr) dc = __lookup_discard_cmd(sbi, blkaddr); #ifdef CONFIG_BLK_DEV_ZONED if (dc && f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(dc->bdev)) { - /* force submit zone reset */ - if (dc->state == D_PREP) - __submit_zone_reset_cmd(sbi, dc, REQ_SYNC, - &dcc->wait_list, NULL); - dc->ref++; - mutex_unlock(&dcc->cmd_lock); - /* wait zone reset */ - __wait_one_discard_bio(sbi, dc); - return; + int devi = f2fs_bdev_index(sbi, dc->bdev); + + if (devi < 0) { + mutex_unlock(&dcc->cmd_lock); + return; + } + + if (f2fs_blkz_is_seq(sbi, devi, dc->di.start)) { + /* force submit zone reset */ + if (dc->state == D_PREP) + __submit_zone_reset_cmd(sbi, dc, REQ_SYNC, + &dcc->wait_list, NULL); + dc->ref++; + mutex_unlock(&dcc->cmd_lock); + /* wait zone reset */ + __wait_one_discard_bio(sbi, dc); + return; + } } #endif if (dc) { @@ -2193,7 +2213,7 @@ void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, len = next_pos - cur_pos; if (f2fs_sb_has_blkzoned(sbi) || - !force || len < cpc->trim_minlen) + (force && len < cpc->trim_minlen)) goto skip; f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos, @@ -3228,6 +3248,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range) goto out; f2fs_down_write(&sbi->gc_lock); + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); f2fs_up_write(&sbi->gc_lock); if (err) @@ -4846,17 +4867,17 @@ static int check_zone_write_pointer(struct f2fs_sb_info *sbi, { unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno; block_t zone_block, wp_block, last_valid_block; + unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; int i, s, b, ret; struct seg_entry *se; if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ) return 0; - wp_block = fdev->start_blk + (zone->wp >> sbi->log_sectors_per_block); + wp_block = fdev->start_blk + (zone->wp >> log_sectors_per_block); wp_segno = GET_SEGNO(sbi, wp_block); wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); - zone_block = fdev->start_blk + (zone->start >> - sbi->log_sectors_per_block); + zone_block = fdev->start_blk + (zone->start >> log_sectors_per_block); zone_segno = GET_SEGNO(sbi, zone_block); zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno); @@ -4906,7 +4927,7 @@ static int check_zone_write_pointer(struct f2fs_sb_info *sbi, "pointer. Reset the write pointer: wp[0x%x,0x%x]", wp_segno, wp_blkoff); ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block, - zone->len >> sbi->log_sectors_per_block); + zone->len >> log_sectors_per_block); if (ret) f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", fdev->path, ret); @@ -4927,12 +4948,19 @@ static int check_zone_write_pointer(struct f2fs_sb_info *sbi, GET_BLKOFF_FROM_SEG0(sbi, last_valid_block), wp_segno, wp_blkoff); - ret = blkdev_issue_zeroout(fdev->bdev, zone->wp, - zone->len - (zone->wp - zone->start), - GFP_NOFS, 0); - if (ret) - f2fs_err(sbi, "Fill up zone failed: %s (errno=%d)", - fdev->path, ret); + ret = blkdev_zone_mgmt(fdev->bdev, REQ_OP_ZONE_FINISH, + zone->start, zone->len, GFP_NOFS); + if (ret == -EOPNOTSUPP) { + ret = blkdev_issue_zeroout(fdev->bdev, zone->wp, + zone->len - (zone->wp - zone->start), + GFP_NOFS, 0); + if (ret) + f2fs_err(sbi, "Fill up zone failed: %s (errno=%d)", + fdev->path, ret); + } else if (ret) { + f2fs_err(sbi, "Finishing zone failed: %s (errno=%d)", + fdev->path, ret); + } return ret; } @@ -4967,6 +4995,7 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) struct blk_zone zone; unsigned int cs_section, wp_segno, wp_blkoff, wp_sector_off; block_t cs_zone_block, wp_block; + unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT; sector_t zone_sector; int err; @@ -4978,8 +5007,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) return 0; /* report zone for the sector the curseg points to */ - zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) << - sbi->log_sectors_per_block; + zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) + << log_sectors_per_block; err = blkdev_report_zones(zbd->bdev, zone_sector, 1, report_one_zone_cb, &zone); if (err != 1) { @@ -4991,10 +5020,10 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ) return 0; - wp_block = zbd->start_blk + (zone.wp >> sbi->log_sectors_per_block); + wp_block = zbd->start_blk + (zone.wp >> log_sectors_per_block); wp_segno = GET_SEGNO(sbi, wp_block); wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno); - wp_sector_off = zone.wp & GENMASK(sbi->log_sectors_per_block - 1, 0); + wp_sector_off = zone.wp & GENMASK(log_sectors_per_block - 1, 0); if (cs->segno == wp_segno && cs->next_blkoff == wp_blkoff && wp_sector_off == 0) @@ -5021,8 +5050,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) if (!zbd) return 0; - zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) << - sbi->log_sectors_per_block; + zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) + << log_sectors_per_block; err = blkdev_report_zones(zbd->bdev, zone_sector, 1, report_one_zone_cb, &zone); if (err != 1) { @@ -5040,7 +5069,7 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type) "Reset the zone: curseg[0x%x,0x%x]", type, cs->segno, cs->next_blkoff); err = __f2fs_issue_discard_zone(sbi, zbd->bdev, cs_zone_block, - zone.len >> sbi->log_sectors_per_block); + zone.len >> log_sectors_per_block); if (err) { f2fs_err(sbi, "Discard zone failed: %s (errno=%d)", zbd->path, err); diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index aa1f9a3a80378550ff1147e75b9dd4555575ab8e..a8c8232852bb18749f8a60238c15dfab7f55e0e7 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -591,7 +591,7 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str) unsigned int level; if (strlen(str) == 3) { - F2FS_OPTION(sbi).compress_level = LZ4HC_DEFAULT_CLEVEL; + F2FS_OPTION(sbi).compress_level = 0; return 0; } @@ -862,11 +862,6 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) if (!name) return -ENOMEM; if (!strcmp(name, "adaptive")) { - if (f2fs_sb_has_blkzoned(sbi)) { - f2fs_warn(sbi, "adaptive mode is not allowed with zoned block device feature"); - kfree(name); - return -EINVAL; - } F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE; } else if (!strcmp(name, "lfs")) { F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS; @@ -1331,6 +1326,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount) F2FS_OPTION(sbi).discard_unit = DISCARD_UNIT_SECTION; } + + if (F2FS_OPTION(sbi).fs_mode != FS_MODE_LFS) { + f2fs_info(sbi, "Only lfs mode is allowed with zoned block device feature"); + return -EINVAL; + } #else f2fs_err(sbi, "Zoned block device support is not enabled"); return -EINVAL; @@ -1561,7 +1561,8 @@ static void destroy_device_list(struct f2fs_sb_info *sbi) int i; for (i = 0; i < sbi->s_ndevs; i++) { - blkdev_put(FDEV(i).bdev, sbi->sb); + if (i > 0) + blkdev_put(FDEV(i).bdev, sbi->sb); #ifdef CONFIG_BLK_DEV_ZONED kvfree(FDEV(i).blkz_seq); #endif @@ -1600,6 +1601,7 @@ static void f2fs_put_super(struct super_block *sb) struct cp_control cpc = { .reason = CP_UMOUNT, }; + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); } @@ -1609,6 +1611,7 @@ static void f2fs_put_super(struct super_block *sb) struct cp_control cpc = { .reason = CP_UMOUNT | CP_TRIMMED, }; + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); } @@ -1705,8 +1708,10 @@ int f2fs_sync_fs(struct super_block *sb, int sync) if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) return -EAGAIN; - if (sync) + if (sync) { + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_issue_checkpoint(sbi); + } return err; } @@ -2205,6 +2210,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) .nr_free_secs = 1 }; f2fs_down_write(&sbi->gc_lock); + stat_inc_gc_call_count(sbi, FOREGROUND); err = f2fs_gc(sbi, &gc_control); if (err == -ENODATA) { err = 0; @@ -2230,6 +2236,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) f2fs_down_write(&sbi->gc_lock); cpc.reason = CP_PAUSE; set_sbi_flag(sbi, SBI_CP_DISABLED); + stat_inc_cp_call_count(sbi, TOTAL_CALL); err = f2fs_write_checkpoint(sbi, &cpc); if (err) goto out_unlock; @@ -4190,16 +4197,12 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) sbi->aligned_blksize = true; for (i = 0; i < max_devices; i++) { - - if (i > 0 && !RDEV(i).path[0]) + if (i == 0) + FDEV(0).bdev = sbi->sb->s_bdev; + else if (!RDEV(i).path[0]) break; - if (max_devices == 1) { - /* Single zoned block device mount */ - FDEV(0).bdev = - blkdev_get_by_dev(sbi->sb->s_bdev->bd_dev, mode, - sbi->sb, NULL); - } else { + if (max_devices > 1) { /* Multi-device mount */ memcpy(FDEV(i).path, RDEV(i).path, MAX_PATH_LEN); FDEV(i).total_segments = @@ -4215,9 +4218,9 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi) FDEV(i).end_blk = FDEV(i).start_blk + (FDEV(i).total_segments << sbi->log_blocks_per_seg) - 1; + FDEV(i).bdev = blkdev_get_by_path(FDEV(i).path, + mode, sbi->sb, NULL); } - FDEV(i).bdev = blkdev_get_by_path(FDEV(i).path, mode, - sbi->sb, NULL); } if (IS_ERR(FDEV(i).bdev)) return PTR_ERR(FDEV(i).bdev); @@ -4870,6 +4873,7 @@ static void kill_f2fs_super(struct super_block *sb) struct cp_control cpc = { .reason = CP_UMOUNT, }; + stat_inc_cp_call_count(sbi, TOTAL_CALL); f2fs_write_checkpoint(sbi, &cpc); } diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 48b7e0073884a873f779d4fbf2375f5f67da5325..417fae96890f67bdded952838c43c3d3b799adb1 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -356,6 +356,16 @@ static ssize_t f2fs_sbi_show(struct f2fs_attr *a, if (!strcmp(a->attr.name, "revoked_atomic_block")) return sysfs_emit(buf, "%llu\n", sbi->revoked_atomic_block); +#ifdef CONFIG_F2FS_STAT_FS + if (!strcmp(a->attr.name, "cp_foreground_calls")) + return sysfs_emit(buf, "%d\n", + atomic_read(&sbi->cp_call_count[TOTAL_CALL]) - + atomic_read(&sbi->cp_call_count[BACKGROUND])); + if (!strcmp(a->attr.name, "cp_background_calls")) + return sysfs_emit(buf, "%d\n", + atomic_read(&sbi->cp_call_count[BACKGROUND])); +#endif + ui = (unsigned int *)(ptr + a->offset); return sysfs_emit(buf, "%u\n", *ui); @@ -972,10 +982,10 @@ F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec); /* STAT_INFO ATTR */ #ifdef CONFIG_F2FS_STAT_FS -STAT_INFO_RO_ATTR(cp_foreground_calls, cp_count); -STAT_INFO_RO_ATTR(cp_background_calls, bg_cp_count); -STAT_INFO_RO_ATTR(gc_foreground_calls, call_count); -STAT_INFO_RO_ATTR(gc_background_calls, bg_gc); +STAT_INFO_RO_ATTR(cp_foreground_calls, cp_call_count[FOREGROUND]); +STAT_INFO_RO_ATTR(cp_background_calls, cp_call_count[BACKGROUND]); +STAT_INFO_RO_ATTR(gc_foreground_calls, gc_call_count[FOREGROUND]); +STAT_INFO_RO_ATTR(gc_background_calls, gc_call_count[BACKGROUND]); #endif /* FAULT_INFO ATTR */ diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 4ae93e1df4218bd0dd9b1cc1d32ff64fbb4c6de3..a657284faee30ad381c85f8f032f71709cdb1265 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -757,17 +757,17 @@ static int __f2fs_setxattr(struct inode *inode, int index, if (index == F2FS_XATTR_INDEX_ENCRYPTION && !strcmp(name, F2FS_XATTR_NAME_ENCRYPTION_CONTEXT)) f2fs_set_encrypted_inode(inode); - f2fs_mark_inode_dirty_sync(inode, true); - if (!error && S_ISDIR(inode->i_mode)) + if (S_ISDIR(inode->i_mode)) set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_CP); same: if (is_inode_flag_set(inode, FI_ACL_MODE)) { inode->i_mode = F2FS_I(inode)->i_acl_mode; - inode_set_ctime_current(inode); clear_inode_flag(inode, FI_ACL_MODE); } + inode_set_ctime_current(inode); + f2fs_mark_inode_dirty_sync(inode, true); exit: kfree(base_addr); return error; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 969ce991b0b03f6b49f3b92cdcfc4ca515a8f315..c1af01b2c42d708ede9b2842b1cc0bfec5153838 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1535,10 +1535,15 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb, if (wbc->pages_skipped) { /* - * writeback is not making progress due to locked - * buffers. Skip this inode for now. + * Writeback is not making progress due to locked buffers. + * Skip this inode for now. Although having skipped pages + * is odd for clean inodes, it can happen for some + * filesystems so handle that gracefully. */ - redirty_tail_locked(inode, wb); + if (inode->i_state & I_DIRTY_ALL) + redirty_tail_locked(inode, wb); + else + inode_cgwb_move_to_attached(inode, wb); return; } diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 881524b9a55aa818a2b0d37d0e6b91bb0bdfb694..d707e6987da91eaaf06eda157f8487d9488dd12b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -92,7 +92,7 @@ static void fuse_dentry_settime(struct dentry *dentry, u64 time) /* * Calculate the time in jiffies until a dentry/attributes are valid */ -static u64 time_to_jiffies(u64 sec, u32 nsec) +u64 fuse_time_to_jiffies(u64 sec, u32 nsec) { if (sec || nsec) { struct timespec64 ts = { @@ -112,17 +112,7 @@ static u64 time_to_jiffies(u64 sec, u32 nsec) void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o) { fuse_dentry_settime(entry, - time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); -} - -static u64 attr_timeout(struct fuse_attr_out *o) -{ - return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); -} - -u64 entry_attr_timeout(struct fuse_entry_out *o) -{ - return time_to_jiffies(o->attr_valid, o->attr_valid_nsec); + fuse_time_to_jiffies(o->entry_valid, o->entry_valid_nsec)); } void fuse_invalidate_attr_mask(struct inode *inode, u32 mask) @@ -265,8 +255,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) goto invalid; forget_all_cached_acls(inode); - fuse_change_attributes(inode, &outarg.attr, - entry_attr_timeout(&outarg), + fuse_change_attributes(inode, &outarg.attr, NULL, + ATTR_TIMEOUT(&outarg), attr_version); fuse_change_entry_timeout(entry, &outarg); } else if (inode) { @@ -360,10 +350,14 @@ int fuse_valid_type(int m) S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); } +static bool fuse_valid_size(u64 size) +{ + return size <= LLONG_MAX; +} + bool fuse_invalid_attr(struct fuse_attr *attr) { - return !fuse_valid_type(attr->mode) || - attr->size > LLONG_MAX; + return !fuse_valid_type(attr->mode) || !fuse_valid_size(attr->size); } int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name, @@ -399,7 +393,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name goto out_put_forget; *inode = fuse_iget(sb, outarg->nodeid, outarg->generation, - &outarg->attr, entry_attr_timeout(outarg), + &outarg->attr, ATTR_TIMEOUT(outarg), attr_version); err = -ENOMEM; if (!*inode) { @@ -686,7 +680,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ff->nodeid = outentry.nodeid; ff->open_flags = outopen.open_flags; inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, - &outentry.attr, entry_attr_timeout(&outentry), 0); + &outentry.attr, ATTR_TIMEOUT(&outentry), 0); if (!inode) { flags &= ~(O_CREAT | O_EXCL | O_TRUNC); fuse_sync_release(NULL, ff, flags); @@ -755,7 +749,8 @@ static int fuse_atomic_open(struct inode *dir, struct dentry *entry, if (err == -ENOSYS) { fc->no_create = 1; goto mknod; - } + } else if (err == -EEXIST) + fuse_invalidate_entry(entry); out_dput: dput(res); return err; @@ -813,7 +808,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args, goto out_put_forget_req; inode = fuse_iget(dir->i_sb, outarg.nodeid, outarg.generation, - &outarg.attr, entry_attr_timeout(&outarg), 0); + &outarg.attr, ATTR_TIMEOUT(&outarg), 0); if (!inode) { fuse_queue_forget(fm->fc, forget, outarg.nodeid, 1); return -ENOMEM; @@ -835,6 +830,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args, return 0; out_put_forget_req: + if (err == -EEXIST) + fuse_invalidate_entry(entry); kfree(forget); return err; } @@ -986,7 +983,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) if (!err) { fuse_dir_changed(dir); fuse_entry_unlinked(entry); - } else if (err == -EINTR) + } else if (err == -EINTR || err == -ENOENT) fuse_invalidate_entry(entry); return err; } @@ -1009,7 +1006,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) if (!err) { fuse_dir_changed(dir); fuse_entry_unlinked(entry); - } else if (err == -EINTR) + } else if (err == -EINTR || err == -ENOENT) fuse_invalidate_entry(entry); return err; } @@ -1050,7 +1047,7 @@ static int fuse_rename_common(struct inode *olddir, struct dentry *oldent, /* newent will end up negative */ if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) fuse_entry_unlinked(newent); - } else if (err == -EINTR) { + } else if (err == -EINTR || err == -ENOENT) { /* If request was interrupted, DEITY only knows if the rename actually took place. If the invalidation fails (e.g. some process has CWD under the renamed @@ -1153,6 +1150,87 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, stat->blksize = 1 << blkbits; } +static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr) +{ + memset(attr, 0, sizeof(*attr)); + attr->ino = sx->ino; + attr->size = sx->size; + attr->blocks = sx->blocks; + attr->atime = sx->atime.tv_sec; + attr->mtime = sx->mtime.tv_sec; + attr->ctime = sx->ctime.tv_sec; + attr->atimensec = sx->atime.tv_nsec; + attr->mtimensec = sx->mtime.tv_nsec; + attr->ctimensec = sx->ctime.tv_nsec; + attr->mode = sx->mode; + attr->nlink = sx->nlink; + attr->uid = sx->uid; + attr->gid = sx->gid; + attr->rdev = new_encode_dev(MKDEV(sx->rdev_major, sx->rdev_minor)); + attr->blksize = sx->blksize; +} + +static int fuse_do_statx(struct inode *inode, struct file *file, + struct kstat *stat) +{ + int err; + struct fuse_attr attr; + struct fuse_statx *sx; + struct fuse_statx_in inarg; + struct fuse_statx_out outarg; + struct fuse_mount *fm = get_fuse_mount(inode); + u64 attr_version = fuse_get_attr_version(fm->fc); + FUSE_ARGS(args); + + memset(&inarg, 0, sizeof(inarg)); + memset(&outarg, 0, sizeof(outarg)); + /* Directories have separate file-handle space */ + if (file && S_ISREG(inode->i_mode)) { + struct fuse_file *ff = file->private_data; + + inarg.getattr_flags |= FUSE_GETATTR_FH; + inarg.fh = ff->fh; + } + /* For now leave sync hints as the default, request all stats. */ + inarg.sx_flags = 0; + inarg.sx_mask = STATX_BASIC_STATS | STATX_BTIME; + args.opcode = FUSE_STATX; + args.nodeid = get_node_id(inode); + args.in_numargs = 1; + args.in_args[0].size = sizeof(inarg); + args.in_args[0].value = &inarg; + args.out_numargs = 1; + args.out_args[0].size = sizeof(outarg); + args.out_args[0].value = &outarg; + err = fuse_simple_request(fm, &args); + if (err) + return err; + + sx = &outarg.stat; + if (((sx->mask & STATX_SIZE) && !fuse_valid_size(sx->size)) || + ((sx->mask & STATX_TYPE) && (!fuse_valid_type(sx->mode) || + inode_wrong_type(inode, sx->mode)))) { + make_bad_inode(inode); + return -EIO; + } + + fuse_statx_to_attr(&outarg.stat, &attr); + if ((sx->mask & STATX_BASIC_STATS) == STATX_BASIC_STATS) { + fuse_change_attributes(inode, &attr, &outarg.stat, + ATTR_TIMEOUT(&outarg), attr_version); + } + + if (stat) { + stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME); + stat->btime.tv_sec = sx->btime.tv_sec; + stat->btime.tv_nsec = min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1); + fuse_fillattr(inode, &attr, stat); + stat->result_mask |= STATX_TYPE; + } + + return 0; +} + static int fuse_do_getattr(struct inode *inode, struct kstat *stat, struct file *file) { @@ -1189,8 +1267,8 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, fuse_make_bad(inode); err = -EIO; } else { - fuse_change_attributes(inode, &outarg.attr, - attr_timeout(&outarg), + fuse_change_attributes(inode, &outarg.attr, NULL, + ATTR_TIMEOUT(&outarg), attr_version); if (stat) fuse_fillattr(inode, &outarg.attr, stat); @@ -1204,12 +1282,22 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, unsigned int flags) { struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_conn *fc = get_fuse_conn(inode); int err = 0; bool sync; u32 inval_mask = READ_ONCE(fi->inval_mask); u32 cache_mask = fuse_get_cache_mask(inode); - if (flags & AT_STATX_FORCE_SYNC) + + /* FUSE only supports basic stats and possibly btime */ + request_mask &= STATX_BASIC_STATS | STATX_BTIME; +retry: + if (fc->no_statx) + request_mask &= STATX_BASIC_STATS; + + if (!request_mask) + sync = false; + else if (flags & AT_STATX_FORCE_SYNC) sync = true; else if (flags & AT_STATX_DONT_SYNC) sync = false; @@ -1220,11 +1308,24 @@ static int fuse_update_get_attr(struct inode *inode, struct file *file, if (sync) { forget_all_cached_acls(inode); - err = fuse_do_getattr(inode, stat, file); + /* Try statx if BTIME is requested */ + if (!fc->no_statx && (request_mask & ~STATX_BASIC_STATS)) { + err = fuse_do_statx(inode, file, stat); + if (err == -ENOSYS) { + fc->no_statx = 1; + goto retry; + } + } else { + err = fuse_do_getattr(inode, stat, file); + } } else if (stat) { generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); stat->mode = fi->orig_i_mode; stat->ino = fi->orig_ino; + if (test_bit(FUSE_I_BTIME, &fi->state)) { + stat->btime = fi->i_btime; + stat->result_mask |= STATX_BTIME; + } } return err; @@ -1861,8 +1962,8 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr, /* FIXME: clear I_DIRTY_SYNC? */ } - fuse_change_attributes_common(inode, &outarg.attr, - attr_timeout(&outarg), + fuse_change_attributes_common(inode, &outarg.attr, NULL, + ATTR_TIMEOUT(&outarg), fuse_get_cache_mask(inode)); oldsize = inode->i_size; /* see the comment in fuse_change_attributes() */ diff --git a/fs/fuse/file.c b/fs/fuse/file.c index bc4115288eec7547cad9b97c69b9594107a6d788..1cdb6327511ef843db8936302fe7f141c4016186 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -19,7 +19,6 @@ #include #include #include -#include static int fuse_send_open(struct fuse_mount *fm, u64 nodeid, unsigned int open_flags, int opcode, @@ -479,36 +478,48 @@ static void fuse_sync_writes(struct inode *inode) fuse_release_nowrite(inode); } -struct fuse_flush_args { - struct fuse_args args; - struct fuse_flush_in inarg; - struct work_struct work; - struct file *file; -}; - -static int fuse_do_flush(struct fuse_flush_args *fa) +static int fuse_flush(struct file *file, fl_owner_t id) { - int err; - struct inode *inode = file_inode(fa->file); + struct inode *inode = file_inode(file); struct fuse_mount *fm = get_fuse_mount(inode); + struct fuse_file *ff = file->private_data; + struct fuse_flush_in inarg; + FUSE_ARGS(args); + int err; + + if (fuse_is_bad(inode)) + return -EIO; + + if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache) + return 0; err = write_inode_now(inode, 1); if (err) - goto out; + return err; inode_lock(inode); fuse_sync_writes(inode); inode_unlock(inode); - err = filemap_check_errors(fa->file->f_mapping); + err = filemap_check_errors(file->f_mapping); if (err) - goto out; + return err; err = 0; if (fm->fc->no_flush) goto inval_attr_out; - err = fuse_simple_request(fm, &fa->args); + memset(&inarg, 0, sizeof(inarg)); + inarg.fh = ff->fh; + inarg.lock_owner = fuse_lock_owner_id(fm->fc, id); + args.opcode = FUSE_FLUSH; + args.nodeid = get_node_id(inode); + args.in_numargs = 1; + args.in_args[0].size = sizeof(inarg); + args.in_args[0].value = &inarg; + args.force = true; + + err = fuse_simple_request(fm, &args); if (err == -ENOSYS) { fm->fc->no_flush = 1; err = 0; @@ -521,57 +532,9 @@ static int fuse_do_flush(struct fuse_flush_args *fa) */ if (!err && fm->fc->writeback_cache) fuse_invalidate_attr_mask(inode, STATX_BLOCKS); - -out: - fput(fa->file); - kfree(fa); return err; } -static void fuse_flush_async(struct work_struct *work) -{ - struct fuse_flush_args *fa = container_of(work, typeof(*fa), work); - - fuse_do_flush(fa); -} - -static int fuse_flush(struct file *file, fl_owner_t id) -{ - struct fuse_flush_args *fa; - struct inode *inode = file_inode(file); - struct fuse_mount *fm = get_fuse_mount(inode); - struct fuse_file *ff = file->private_data; - - if (fuse_is_bad(inode)) - return -EIO; - - if (ff->open_flags & FOPEN_NOFLUSH && !fm->fc->writeback_cache) - return 0; - - fa = kzalloc(sizeof(*fa), GFP_KERNEL); - if (!fa) - return -ENOMEM; - - fa->inarg.fh = ff->fh; - fa->inarg.lock_owner = fuse_lock_owner_id(fm->fc, id); - fa->args.opcode = FUSE_FLUSH; - fa->args.nodeid = get_node_id(inode); - fa->args.in_numargs = 1; - fa->args.in_args[0].size = sizeof(fa->inarg); - fa->args.in_args[0].value = &fa->inarg; - fa->args.force = true; - fa->file = get_file(file); - - /* Don't wait if the task is exiting */ - if (current->flags & PF_EXITING) { - INIT_WORK(&fa->work, fuse_flush_async); - schedule_work(&fa->work); - return 0; - } - - return fuse_do_flush(fa); -} - int fuse_fsync_common(struct file *file, loff_t start, loff_t end, int datasync, int opcode) { @@ -1465,7 +1428,8 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, int write = flags & FUSE_DIO_WRITE; int cuse = flags & FUSE_DIO_CUSE; struct file *file = io->iocb->ki_filp; - struct inode *inode = file->f_mapping->host; + struct address_space *mapping = file->f_mapping; + struct inode *inode = mapping->host; struct fuse_file *ff = file->private_data; struct fuse_conn *fc = ff->fm->fc; size_t nmax = write ? fc->max_write : fc->max_read; @@ -1477,12 +1441,20 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, int err = 0; struct fuse_io_args *ia; unsigned int max_pages; + bool fopen_direct_io = ff->open_flags & FOPEN_DIRECT_IO; max_pages = iov_iter_npages(iter, fc->max_pages); ia = fuse_io_alloc(io, max_pages); if (!ia) return -ENOMEM; + if (fopen_direct_io && fc->direct_io_relax) { + res = filemap_write_and_wait_range(mapping, pos, pos + count - 1); + if (res) { + fuse_io_free(ia); + return res; + } + } if (!cuse && fuse_range_is_writeback(inode, idx_from, idx_to)) { if (!write) inode_lock(inode); @@ -1491,6 +1463,14 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, inode_unlock(inode); } + if (fopen_direct_io && write) { + res = invalidate_inode_pages2_range(mapping, idx_from, idx_to); + if (res) { + fuse_io_free(ia); + return res; + } + } + io->should_dirty = !write && user_backed_iter(iter); while (count) { ssize_t nres; @@ -2478,14 +2458,17 @@ static const struct vm_operations_struct fuse_file_vm_ops = { static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) { struct fuse_file *ff = file->private_data; + struct fuse_conn *fc = ff->fm->fc; /* DAX mmap is superior to direct_io mmap */ if (FUSE_IS_DAX(file_inode(file))) return fuse_dax_mmap(file, vma); if (ff->open_flags & FOPEN_DIRECT_IO) { - /* Can't provide the coherency needed for MAP_SHARED */ - if (vma->vm_flags & VM_MAYSHARE) + /* Can't provide the coherency needed for MAP_SHARED + * if FUSE_DIRECT_IO_RELAX isn't set. + */ + if ((vma->vm_flags & VM_MAYSHARE) && !fc->direct_io_relax) return -ENODEV; invalidate_inode_pages2(file->f_mapping); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 9b7fc7d3c7f15a268635a217daac3e4567fa5cfb..bf0b85d0b95c7d21bebe30f88f0ac92b1088aeb3 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -88,6 +88,9 @@ struct fuse_inode { preserve the original mode */ umode_t orig_i_mode; + /* Cache birthtime */ + struct timespec64 i_btime; + /** 64 bit inode number */ u64 orig_ino; @@ -167,6 +170,8 @@ enum { FUSE_I_SIZE_UNSTABLE, /* Bad inode */ FUSE_I_BAD, + /* Has btime */ + FUSE_I_BTIME, }; struct fuse_conn; @@ -792,6 +797,12 @@ struct fuse_conn { /* Is tmpfile not implemented by fs? */ unsigned int no_tmpfile:1; + /* relax restrictions in FOPEN_DIRECT_IO mode */ + unsigned int direct_io_relax:1; + + /* Is statx not implemented by fs? */ + unsigned int no_statx:1; + /** The number of requests waiting for completion */ atomic_t num_waiting; @@ -1058,9 +1069,11 @@ void fuse_init_symlink(struct inode *inode); * Change attributes of an inode */ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, + struct fuse_statx *sx, u64 attr_valid, u64 attr_version); void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, + struct fuse_statx *sx, u64 attr_valid, u32 cache_mask); u32 fuse_get_cache_mask(struct inode *inode); @@ -1111,7 +1124,10 @@ void fuse_invalidate_entry_cache(struct dentry *entry); void fuse_invalidate_atime(struct inode *inode); -u64 entry_attr_timeout(struct fuse_entry_out *o); +u64 fuse_time_to_jiffies(u64 sec, u32 nsec); +#define ATTR_TIMEOUT(o) \ + fuse_time_to_jiffies((o)->attr_valid, (o)->attr_valid_nsec) + void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o); /** diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 549358ffea8bcb3d5e689bc96620e6eaf6c599f0..2e4eb7cf26fb33363698c990a296b50a35aa792b 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -77,7 +77,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) return NULL; fi->i_time = 0; - fi->inval_mask = 0; + fi->inval_mask = ~0; fi->nodeid = 0; fi->nlookup = 0; fi->attr_version = 0; @@ -163,6 +163,7 @@ static ino_t fuse_squash_ino(u64 ino64) } void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, + struct fuse_statx *sx, u64 attr_valid, u32 cache_mask) { struct fuse_conn *fc = get_fuse_conn(inode); @@ -172,7 +173,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, fi->attr_version = atomic64_inc_return(&fc->attr_version); fi->i_time = attr_valid; - WRITE_ONCE(fi->inval_mask, 0); + /* Clear basic stats from invalid mask */ + set_mask_bits(&fi->inval_mask, STATX_BASIC_STATS, 0); inode->i_ino = fuse_squash_ino(attr->ino); inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777); @@ -196,6 +198,25 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, if (!(cache_mask & STATX_CTIME)) { inode_set_ctime(inode, attr->ctime, attr->ctimensec); } + if (sx) { + /* Sanitize nsecs */ + sx->btime.tv_nsec = + min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1); + + /* + * Btime has been queried, cache is valid (whether or not btime + * is available or not) so clear STATX_BTIME from inval_mask. + * + * Availability of the btime attribute is indicated in + * FUSE_I_BTIME + */ + set_mask_bits(&fi->inval_mask, STATX_BTIME, 0); + if (sx->mask & STATX_BTIME) { + set_bit(FUSE_I_BTIME, &fi->state); + fi->i_btime.tv_sec = sx->btime.tv_sec; + fi->i_btime.tv_nsec = sx->btime.tv_nsec; + } + } if (attr->blksize != 0) inode->i_blkbits = ilog2(attr->blksize); @@ -235,6 +256,7 @@ u32 fuse_get_cache_mask(struct inode *inode) } void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, + struct fuse_statx *sx, u64 attr_valid, u64 attr_version) { struct fuse_conn *fc = get_fuse_conn(inode); @@ -269,7 +291,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr, } old_mtime = inode->i_mtime; - fuse_change_attributes_common(inode, attr, attr_valid, cache_mask); + fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask); oldsize = inode->i_size; /* @@ -406,7 +428,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, spin_lock(&fi->lock); fi->nlookup++; spin_unlock(&fi->lock); - fuse_change_attributes(inode, attr, attr_valid, attr_version); + fuse_change_attributes(inode, attr, NULL, attr_valid, attr_version); return inode; } @@ -1210,6 +1232,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args, fc->init_security = 1; if (flags & FUSE_CREATE_SUPP_GROUP) fc->create_supp_group = 1; + if (flags & FUSE_DIRECT_IO_RELAX) + fc->direct_io_relax = 1; } else { ra_pages = fc->max_read / PAGE_SIZE; fc->no_lock = 1; @@ -1256,7 +1280,7 @@ void fuse_send_init(struct fuse_mount *fm) FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT | FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP | - FUSE_HAS_EXPIRE_ONLY; + FUSE_HAS_EXPIRE_ONLY | FUSE_DIRECT_IO_RELAX; #ifdef CONFIG_FUSE_DAX if (fm->fc->dax) flags |= FUSE_MAP_ALIGNMENT; diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c index dc603479b30efa3699adaaca733cb90c283572f6..9e6d587b3e67311f231baf434979abb8e40f6776 100644 --- a/fs/fuse/readdir.c +++ b/fs/fuse/readdir.c @@ -223,8 +223,8 @@ static int fuse_direntplus_link(struct file *file, spin_unlock(&fi->lock); forget_all_cached_acls(inode); - fuse_change_attributes(inode, &o->attr, - entry_attr_timeout(o), + fuse_change_attributes(inode, &o->attr, NULL, + ATTR_TIMEOUT(o), attr_version); /* * The other branch comes via fuse_iget() @@ -232,7 +232,7 @@ static int fuse_direntplus_link(struct file *file, */ } else { inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, - &o->attr, entry_attr_timeout(o), + &o->attr, ATTR_TIMEOUT(o), attr_version); if (!inode) inode = ERR_PTR(-ENOMEM); @@ -243,8 +243,16 @@ static int fuse_direntplus_link(struct file *file, dput(dentry); dentry = alias; } - if (IS_ERR(dentry)) + if (IS_ERR(dentry)) { + if (!IS_ERR(inode)) { + struct fuse_inode *fi = get_fuse_inode(inode); + + spin_lock(&fi->lock); + fi->nlookup--; + spin_unlock(&fi->lock); + } return PTR_ERR(dentry); + } } if (fc->readdirplus_auto) set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 9c4b26aec5803fbf89560a4ba20a40ce11308663..c26d48355cc2702759ffe8f52f68655f047d8d29 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -183,13 +183,13 @@ static int gfs2_writepages(struct address_space *mapping, int ret; /* - * Even if we didn't write any pages here, we might still be holding + * Even if we didn't write enough pages here, we might still be holding * dirty pages in the ail. We forcibly flush the ail because we don't * want balance_dirty_pages() to loop indefinitely trying to write out * pages held in the ail that it can't find. */ ret = iomap_writepages(mapping, wbc, &wpc, &gfs2_writeback_ops); - if (ret == 0) + if (ret == 0 && wbc->nr_to_write > 0) set_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags); return ret; } @@ -272,8 +272,7 @@ static int gfs2_write_jdata_batch(struct address_space *mapping, * not be suitable for data integrity * writeout). */ - *done_index = folio->index + - folio_nr_pages(folio); + *done_index = folio_next_index(folio); ret = 1; break; } diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index f62366be7587b925d356201cea2689dda8b35a8e..ef7017fb69512c589c91f3e78ae7e758550b0006 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -161,7 +161,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip) int error; down_write(&ip->i_rw_mutex); - page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS); + page = grab_cache_page(inode->i_mapping, 0); error = -ENOMEM; if (!page) goto out; diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 1438e7465e306ce584f7f2427b3d2a4ffbeabecb..4a280be229a6517b3a9bdd219051b0967af0249f 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -176,7 +176,7 @@ void gfs2_glock_free(struct gfs2_glock *gl) wake_up_glock(gl); call_rcu(&gl->gl_rcu, gfs2_glock_dealloc); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) - wake_up(&sdp->sd_glock_wait); + wake_up(&sdp->sd_kill_wait); } /** @@ -468,10 +468,10 @@ int gfs2_instantiate(struct gfs2_holder *gh) * do_promote - promote as many requests as possible on the current queue * @gl: The glock * - * Returns: 1 if there is a blocked holder at the head of the list + * Returns true on success (i.e., progress was made or there are no waiters). */ -static int do_promote(struct gfs2_glock *gl) +static bool do_promote(struct gfs2_glock *gl) { struct gfs2_holder *gh, *current_gh; @@ -484,10 +484,10 @@ static int do_promote(struct gfs2_glock *gl) * If we get here, it means we may not grant this * holder for some reason. If this holder is at the * head of the list, it means we have a blocked holder - * at the head, so return 1. + * at the head, so return false. */ if (list_is_first(&gh->gh_list, &gl->gl_holders)) - return 1; + return false; do_error(gl, 0); break; } @@ -497,7 +497,7 @@ static int do_promote(struct gfs2_glock *gl) if (!current_gh) current_gh = gh; } - return 0; + return true; } /** @@ -591,10 +591,11 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret) if (gh && !test_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags)) { /* move to back of queue and try next entry */ if (ret & LM_OUT_CANCELED) { - if ((gh->gh_flags & LM_FLAG_PRIORITY) == 0) - list_move_tail(&gh->gh_list, &gl->gl_holders); + list_move_tail(&gh->gh_list, &gl->gl_holders); gh = find_first_waiter(gl); gl->gl_target = gh->gh_state; + if (do_promote(gl)) + goto out; goto retry; } /* Some error or failed "try lock" - report it */ @@ -679,8 +680,7 @@ __acquires(&gl->gl_lockref.lock) gh && !(gh->gh_flags & LM_FLAG_NOEXP)) goto skip_inval; - lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP | - LM_FLAG_PRIORITY); + lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP); GLOCK_BUG_ON(gl, gl->gl_state == target); GLOCK_BUG_ON(gl, gl->gl_state == gl->gl_target); if ((target == LM_ST_UNLOCKED || target == LM_ST_DEFERRED) && @@ -834,7 +834,7 @@ __acquires(&gl->gl_lockref.lock) } else { if (test_bit(GLF_DEMOTE, &gl->gl_flags)) gfs2_demote_wake(gl); - if (do_promote(gl) == 0) + if (do_promote(gl)) goto out_unlock; gh = find_first_waiter(gl); gl->gl_target = gh->gh_state; @@ -1022,7 +1022,7 @@ static void delete_work_func(struct work_struct *work) * step entirely. */ if (gfs2_try_evict(gl)) { - if (test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + if (test_bit(SDF_KILL, &sdp->sd_flags)) goto out; if (gfs2_queue_verify_evict(gl)) return; @@ -1035,7 +1035,7 @@ static void delete_work_func(struct work_struct *work) GFS2_BLKST_UNLINKED); if (IS_ERR(inode)) { if (PTR_ERR(inode) == -EAGAIN && - !test_bit(SDF_DEACTIVATING, &sdp->sd_flags) && + !test_bit(SDF_KILL, &sdp->sd_flags) && gfs2_queue_verify_evict(gl)) return; } else { @@ -1231,7 +1231,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, out_free: gfs2_glock_dealloc(&gl->gl_rcu); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) - wake_up(&sdp->sd_glock_wait); + wake_up(&sdp->sd_kill_wait); out: return ret; @@ -1515,27 +1515,20 @@ __acquires(&gl->gl_lockref.lock) } if (test_bit(HIF_HOLDER, &gh2->gh_iflags)) continue; - if (unlikely((gh->gh_flags & LM_FLAG_PRIORITY) && !insert_pt)) - insert_pt = &gh2->gh_list; } trace_gfs2_glock_queue(gh, 1); gfs2_glstats_inc(gl, GFS2_LKS_QCOUNT); gfs2_sbstats_inc(gl, GFS2_LKS_QCOUNT); if (likely(insert_pt == NULL)) { list_add_tail(&gh->gh_list, &gl->gl_holders); - if (unlikely(gh->gh_flags & LM_FLAG_PRIORITY)) - goto do_cancel; return; } list_add_tail(&gh->gh_list, insert_pt); -do_cancel: gh = list_first_entry(&gl->gl_holders, struct gfs2_holder, gh_list); - if (!(gh->gh_flags & LM_FLAG_PRIORITY)) { - spin_unlock(&gl->gl_lockref.lock); - if (sdp->sd_lockstruct.ls_ops->lm_cancel) - sdp->sd_lockstruct.ls_ops->lm_cancel(gl); - spin_lock(&gl->gl_lockref.lock); - } + spin_unlock(&gl->gl_lockref.lock); + if (sdp->sd_lockstruct.ls_ops->lm_cancel) + sdp->sd_lockstruct.ls_ops->lm_cancel(gl); + spin_lock(&gl->gl_lockref.lock); return; trap_recursive: @@ -2017,7 +2010,9 @@ static long gfs2_scan_glock_lru(int nr) if (!test_bit(GLF_LOCK, &gl->gl_flags)) { if (!spin_trylock(&gl->gl_lockref.lock)) continue; - if (!gl->gl_lockref.count) { + if (gl->gl_lockref.count <= 1 && + (gl->gl_state == LM_ST_UNLOCKED || + demote_ok(gl))) { list_move(&gl->gl_lru, &dispose); atomic_dec(&lru_count); freed++; @@ -2195,7 +2190,7 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) flush_workqueue(glock_workqueue); glock_hash_walk(clear_glock, sdp); flush_workqueue(glock_workqueue); - wait_event_timeout(sdp->sd_glock_wait, + wait_event_timeout(sdp->sd_kill_wait, atomic_read(&sdp->sd_glock_disposal) == 0, HZ * 600); glock_hash_walk(dump_glock_func, sdp); @@ -2227,8 +2222,6 @@ static const char *hflags2str(char *buf, u16 flags, unsigned long iflags) *p++ = 'e'; if (flags & LM_FLAG_ANY) *p++ = 'A'; - if (flags & LM_FLAG_PRIORITY) - *p++ = 'p'; if (flags & LM_FLAG_NODE_SCOPE) *p++ = 'n'; if (flags & GL_ASYNC) diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 1f1ba92c15a8ff055ded885a5ef1ae5f907fda9c..c8685ca7d2a26a751553a25d95fd3b6e66765cb9 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -68,14 +68,6 @@ enum { * also be granted in SHARED. The preferred state is whichever is compatible * with other granted locks, or the specified state if no other locks exist. * - * LM_FLAG_PRIORITY - * Override fairness considerations. Suppose a lock is held in a shared state - * and there is a pending request for the deferred state. A shared lock - * request with the priority flag would be allowed to bypass the deferred - * request and directly join the other shared lock. A shared lock request - * without the priority flag might be forced to wait until the deferred - * requested had acquired and released the lock. - * * LM_FLAG_NODE_SCOPE * This holder agrees to share the lock within this node. In other words, * the glock is held in EX mode according to DLM, but local holders on the @@ -86,7 +78,6 @@ enum { #define LM_FLAG_TRY_1CB 0x0002 #define LM_FLAG_NOEXP 0x0004 #define LM_FLAG_ANY 0x0008 -#define LM_FLAG_PRIORITY 0x0010 #define LM_FLAG_NODE_SCOPE 0x0020 #define GL_ASYNC 0x0040 #define GL_EXACT 0x0080 diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index aecdac3cfbe17ff291b8fadfebabc812f5e63056..f41ca89d216bc243a405b70482301649098fcba0 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -567,15 +567,16 @@ static void freeze_go_callback(struct gfs2_glock *gl, bool remote) struct super_block *sb = sdp->sd_vfs; if (!remote || - gl->gl_state != LM_ST_SHARED || + (gl->gl_state != LM_ST_SHARED && + gl->gl_state != LM_ST_UNLOCKED) || gl->gl_demote_state != LM_ST_UNLOCKED) return; /* * Try to get an active super block reference to prevent racing with - * unmount (see trylock_super()). But note that unmount isn't the only - * place where a write lock on s_umount is taken, and we can fail here - * because of things like remount as well. + * unmount (see super_trylock_shared()). But note that unmount isn't + * the only place where a write lock on s_umount is taken, and we can + * fail here because of things like remount as well. */ if (down_read_trylock(&sb->s_umount)) { atomic_inc(&sb->s_active); @@ -637,7 +638,7 @@ static void iopen_go_callback(struct gfs2_glock *gl, bool remote) struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; if (!remote || sb_rdonly(sdp->sd_vfs) || - test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + test_bit(SDF_KILL, &sdp->sd_flags)) return; if (gl->gl_demote_state == LM_ST_UNLOCKED && diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 04f2d78e8658ad9b9473a13bf7242d0e577b810c..a8c95c5293c6cf854ca6c0124964a9e49b2f9bb0 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -452,7 +452,7 @@ struct gfs2_quota_data { s64 qd_change_sync; unsigned int qd_slot; - unsigned int qd_slot_count; + unsigned int qd_slot_ref; struct buffer_head *qd_bh; struct gfs2_quota_change *qd_bh_qc; @@ -537,6 +537,7 @@ struct gfs2_statfs_change_host { #define GFS2_QUOTA_OFF 0 #define GFS2_QUOTA_ACCOUNT 1 #define GFS2_QUOTA_ON 2 +#define GFS2_QUOTA_QUIET 3 /* on but not complaining */ #define GFS2_DATA_DEFAULT GFS2_DATA_ORDERED #define GFS2_DATA_WRITEBACK 1 @@ -606,7 +607,7 @@ enum { SDF_REMOTE_WITHDRAW = 13, /* Performing remote recovery */ SDF_WITHDRAW_RECOVERY = 14, /* Wait for journal recovery when we are withdrawing */ - SDF_DEACTIVATING = 15, + SDF_KILL = 15, SDF_EVICTING = 16, SDF_FROZEN = 17, }; @@ -716,7 +717,7 @@ struct gfs2_sbd { struct gfs2_glock *sd_rename_gl; struct gfs2_glock *sd_freeze_gl; struct work_struct sd_freeze_work; - wait_queue_head_t sd_glock_wait; + wait_queue_head_t sd_kill_wait; wait_queue_head_t sd_async_glock_wait; atomic_t sd_glock_disposal; struct completion sd_locking_init; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index a21ac41d66698e997b3465bd2ddf67a8967a380a..0eac0450790471d09b49741fbffad643ba74db85 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -276,10 +276,16 @@ struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) * gfs2_lookup_simple callers expect ENOENT * and do not check for NULL. */ - if (inode == NULL) - return ERR_PTR(-ENOENT); - else - return inode; + if (IS_ERR_OR_NULL(inode)) + return inode ? inode : ERR_PTR(-ENOENT); + + /* + * Must not call back into the filesystem when allocating + * pages in the metadata inode's address space. + */ + mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); + + return inode; } diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 54911294687c9a948c23f73462406091f1a0d010..59ab18c798890f9c8af9fdd98938354e7473ee47 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -222,11 +222,6 @@ static u32 make_flags(struct gfs2_glock *gl, const unsigned int gfs_flags, lkf |= DLM_LKF_NOQUEUEBAST; } - if (gfs_flags & LM_FLAG_PRIORITY) { - lkf |= DLM_LKF_NOORDER; - lkf |= DLM_LKF_HEADQUE; - } - if (gfs_flags & LM_FLAG_ANY) { if (req == DLM_LOCK_PR) lkf |= DLM_LKF_ALTCW; diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index aa568796207c0439d7b355511ba8e7004b896f39..e5271ae87d1c43997322ee1e585b2e13ca21c1cf 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -1227,6 +1227,21 @@ static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_log_unlock(sdp); } +static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) +{ + return atomic_read(&sdp->sd_log_pinned) + + atomic_read(&sdp->sd_log_blks_needed) >= + atomic_read(&sdp->sd_log_thresh1); +} + +static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) +{ + return sdp->sd_jdesc->jd_blocks - + atomic_read(&sdp->sd_log_blks_free) + + atomic_read(&sdp->sd_log_blks_needed) >= + atomic_read(&sdp->sd_log_thresh2); +} + /** * gfs2_log_commit - Commit a transaction to the log * @sdp: the filesystem @@ -1246,9 +1261,7 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { log_refund(sdp, tr); - if (atomic_read(&sdp->sd_log_pinned) > atomic_read(&sdp->sd_log_thresh1) || - ((sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free)) > - atomic_read(&sdp->sd_log_thresh2))) + if (gfs2_ail_flush_reqd(sdp) || gfs2_jrnl_flush_reqd(sdp)) wake_up(&sdp->sd_logd_waitq); } @@ -1271,24 +1284,6 @@ static void gfs2_log_shutdown(struct gfs2_sbd *sdp) gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list)); } -static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) -{ - return (atomic_read(&sdp->sd_log_pinned) + - atomic_read(&sdp->sd_log_blks_needed) >= - atomic_read(&sdp->sd_log_thresh1)); -} - -static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) -{ - unsigned int used_blocks = sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free); - - if (test_and_clear_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags)) - return 1; - - return used_blocks + atomic_read(&sdp->sd_log_blks_needed) >= - atomic_read(&sdp->sd_log_thresh2); -} - /** * gfs2_logd - Update log tail as Active Items get flushed to in-place blocks * @data: Pointer to GFS2 superblock @@ -1301,14 +1296,11 @@ int gfs2_logd(void *data) { struct gfs2_sbd *sdp = data; unsigned long t = 1; - DEFINE_WAIT(wait); while (!kthread_should_stop()) { + if (gfs2_withdrawn(sdp)) + break; - if (gfs2_withdrawn(sdp)) { - msleep_interruptible(HZ); - continue; - } /* Check for errors writing to the journal */ if (sdp->sd_log_error) { gfs2_lm(sdp, @@ -1317,7 +1309,7 @@ int gfs2_logd(void *data) "prevent further damage.\n", sdp->sd_fsname, sdp->sd_log_error); gfs2_withdraw(sdp); - continue; + break; } if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { @@ -1326,7 +1318,9 @@ int gfs2_logd(void *data) GFS2_LFC_LOGD_JFLUSH_REQD); } - if (gfs2_ail_flush_reqd(sdp)) { + if (test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) || + gfs2_ail_flush_reqd(sdp)) { + clear_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags); gfs2_ail1_start(sdp); gfs2_ail1_wait(sdp); gfs2_ail1_empty(sdp, 0); @@ -1338,17 +1332,14 @@ int gfs2_logd(void *data) try_to_freeze(); - do { - prepare_to_wait(&sdp->sd_logd_waitq, &wait, - TASK_INTERRUPTIBLE); - if (!gfs2_ail_flush_reqd(sdp) && - !gfs2_jrnl_flush_reqd(sdp) && - !kthread_should_stop()) - t = schedule_timeout(t); - } while(t && !gfs2_ail_flush_reqd(sdp) && - !gfs2_jrnl_flush_reqd(sdp) && - !kthread_should_stop()); - finish_wait(&sdp->sd_logd_waitq, &wait); + t = wait_event_interruptible_timeout(sdp->sd_logd_waitq, + test_bit(SDF_FORCE_AIL_FLUSH, &sdp->sd_flags) || + gfs2_ail_flush_reqd(sdp) || + gfs2_jrnl_flush_reqd(sdp) || + sdp->sd_log_error || + gfs2_withdrawn(sdp) || + kthread_should_stop(), + t); } return 0; diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index 251322b016314ab53d009333ef882c886747cefd..483f6980706220279ef411b97ab94b2e8ac7f154 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -456,7 +456,7 @@ static bool gfs2_jhead_pg_srch(struct gfs2_jdesc *jd, * Find the folio with 'index' in the journal's mapping. Search the folio for * the journal head if requested (cleanup == false). Release refs on the * folio so the page cache can reclaim it. We grabbed a - * reference on this folio twice, first when we did a find_or_create_page() + * reference on this folio twice, first when we did a grab_cache_page() * to obtain the folio to add it to the bio and second when we do a * filemap_get_folio() here to get the folio to wait on while I/O on it is being * completed. @@ -481,7 +481,7 @@ static void gfs2_jhead_process_page(struct gfs2_jdesc *jd, unsigned long index, if (!*done) *done = gfs2_jhead_pg_srch(jd, head, &folio->page); - /* filemap_get_folio() and the earlier find_or_create_page() */ + /* filemap_get_folio() and the earlier grab_cache_page() */ folio_put_refs(folio, 2); } @@ -535,8 +535,7 @@ int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head, for (; block < je->lblock + je->blocks; block++, dblock++) { if (!page) { - page = find_or_create_page(mapping, - block >> shift, GFP_NOFS); + page = grab_cache_page(mapping, block >> shift); if (!page) { ret = -ENOMEM; done = true; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index afcb32854f14224f1a07d977faa6c8202baeb38f..66eb98b690a277b0c0d8384aaffc87a37c64c0e4 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -152,9 +152,9 @@ static int __init init_gfs2_fs(void) goto fail_shrinker; error = -ENOMEM; - gfs_recovery_wq = alloc_workqueue("gfs_recovery", + gfs2_recovery_wq = alloc_workqueue("gfs2_recovery", WQ_MEM_RECLAIM | WQ_FREEZABLE, 0); - if (!gfs_recovery_wq) + if (!gfs2_recovery_wq) goto fail_wq1; gfs2_control_wq = alloc_workqueue("gfs2_control", @@ -162,7 +162,7 @@ static int __init init_gfs2_fs(void) if (!gfs2_control_wq) goto fail_wq2; - gfs2_freeze_wq = alloc_workqueue("freeze_workqueue", 0, 0); + gfs2_freeze_wq = alloc_workqueue("gfs2_freeze", 0, 0); if (!gfs2_freeze_wq) goto fail_wq3; @@ -194,7 +194,7 @@ static int __init init_gfs2_fs(void) fail_wq3: destroy_workqueue(gfs2_control_wq); fail_wq2: - destroy_workqueue(gfs_recovery_wq); + destroy_workqueue(gfs2_recovery_wq); fail_wq1: unregister_shrinker(&gfs2_qd_shrinker); fail_shrinker: @@ -234,7 +234,7 @@ static void __exit exit_gfs2_fs(void) gfs2_unregister_debugfs(); unregister_filesystem(&gfs2_fs_type); unregister_filesystem(&gfs2meta_fs_type); - destroy_workqueue(gfs_recovery_wq); + destroy_workqueue(gfs2_recovery_wq); destroy_workqueue(gfs2_control_wq); destroy_workqueue(gfs2_freeze_wq); list_lru_destroy(&gfs2_qd_lru); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 8a27957dbfeed943216c6d9002a5a07c27aa2f30..33ca04733e933e25d6fb1ce88d4f137eef191a9d 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -87,7 +87,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) set_bit(SDF_NOJOURNALID, &sdp->sd_flags); gfs2_tune_init(&sdp->sd_tune); - init_waitqueue_head(&sdp->sd_glock_wait); + init_waitqueue_head(&sdp->sd_kill_wait); init_waitqueue_head(&sdp->sd_async_glock_wait); atomic_set(&sdp->sd_glock_disposal, 0); init_completion(&sdp->sd_locking_init); @@ -1103,29 +1103,49 @@ static int init_threads(struct gfs2_sbd *sdp) struct task_struct *p; int error = 0; - p = kthread_run(gfs2_logd, sdp, "gfs2_logd"); + p = kthread_create(gfs2_logd, sdp, "gfs2_logd/%s", sdp->sd_fsname); if (IS_ERR(p)) { error = PTR_ERR(p); - fs_err(sdp, "can't start logd thread: %d\n", error); + fs_err(sdp, "can't create logd thread: %d\n", error); return error; } + get_task_struct(p); sdp->sd_logd_process = p; - p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad"); + p = kthread_create(gfs2_quotad, sdp, "gfs2_quotad/%s", sdp->sd_fsname); if (IS_ERR(p)) { error = PTR_ERR(p); - fs_err(sdp, "can't start quotad thread: %d\n", error); + fs_err(sdp, "can't create quotad thread: %d\n", error); goto fail; } + get_task_struct(p); sdp->sd_quotad_process = p; + + wake_up_process(sdp->sd_logd_process); + wake_up_process(sdp->sd_quotad_process); return 0; fail: kthread_stop(sdp->sd_logd_process); + put_task_struct(sdp->sd_logd_process); sdp->sd_logd_process = NULL; return error; } +void gfs2_destroy_threads(struct gfs2_sbd *sdp) +{ + if (sdp->sd_logd_process) { + kthread_stop(sdp->sd_logd_process); + put_task_struct(sdp->sd_logd_process); + sdp->sd_logd_process = NULL; + } + if (sdp->sd_quotad_process) { + kthread_stop(sdp->sd_quotad_process); + put_task_struct(sdp->sd_quotad_process); + sdp->sd_quotad_process = NULL; + } +} + /** * gfs2_fill_super - Read in superblock * @sb: The VFS superblock @@ -1276,12 +1296,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) if (error) { gfs2_freeze_unlock(&sdp->sd_freeze_gh); - if (sdp->sd_quotad_process) - kthread_stop(sdp->sd_quotad_process); - sdp->sd_quotad_process = NULL; - if (sdp->sd_logd_process) - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; + gfs2_destroy_threads(sdp); fs_err(sdp, "can't make FS RW: %d\n", error); goto fail_per_node; } @@ -1381,6 +1396,7 @@ static const struct constant_table gfs2_param_quota[] = { {"off", GFS2_QUOTA_OFF}, {"account", GFS2_QUOTA_ACCOUNT}, {"on", GFS2_QUOTA_ON}, + {"quiet", GFS2_QUOTA_QUIET}, {} }; @@ -1786,9 +1802,9 @@ static void gfs2_kill_sb(struct super_block *sb) /* * Flush and then drain the delete workqueue here (via * destroy_workqueue()) to ensure that any delete work that - * may be running will also see the SDF_DEACTIVATING flag. + * may be running will also see the SDF_KILL flag. */ - set_bit(SDF_DEACTIVATING, &sdp->sd_flags); + set_bit(SDF_KILL, &sdp->sd_flags); gfs2_flush_delete_work(sdp); destroy_workqueue(sdp->sd_delete_wq); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index aa5fd06d47bcb3874f3dbf85ab2c52b66c6d5e87..171b2713d2e5e608f41742f289c688105fb5de3c 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -109,38 +109,44 @@ static inline void spin_unlock_bucket(unsigned int hash) static void gfs2_qd_dealloc(struct rcu_head *rcu) { struct gfs2_quota_data *qd = container_of(rcu, struct gfs2_quota_data, qd_rcu); + struct gfs2_sbd *sdp = qd->qd_sbd; + kmem_cache_free(gfs2_quotad_cachep, qd); + if (atomic_dec_and_test(&sdp->sd_quota_count)) + wake_up(&sdp->sd_kill_wait); } -static void gfs2_qd_dispose(struct list_head *list) +static void gfs2_qd_dispose(struct gfs2_quota_data *qd) { - struct gfs2_quota_data *qd; - struct gfs2_sbd *sdp; - - while (!list_empty(list)) { - qd = list_first_entry(list, struct gfs2_quota_data, qd_lru); - sdp = qd->qd_gl->gl_name.ln_sbd; - - list_del(&qd->qd_lru); + struct gfs2_sbd *sdp = qd->qd_sbd; - /* Free from the filesystem-specific list */ - spin_lock(&qd_lock); - list_del(&qd->qd_list); - spin_unlock(&qd_lock); + spin_lock(&qd_lock); + list_del(&qd->qd_list); + spin_unlock(&qd_lock); - spin_lock_bucket(qd->qd_hash); - hlist_bl_del_rcu(&qd->qd_hlist); - spin_unlock_bucket(qd->qd_hash); + spin_lock_bucket(qd->qd_hash); + hlist_bl_del_rcu(&qd->qd_hlist); + spin_unlock_bucket(qd->qd_hash); + if (!gfs2_withdrawn(sdp)) { gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); + gfs2_assert_warn(sdp, !qd->qd_slot_ref); gfs2_assert_warn(sdp, !qd->qd_bh_count); + } - gfs2_glock_put(qd->qd_gl); - atomic_dec(&sdp->sd_quota_count); + gfs2_glock_put(qd->qd_gl); + call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); +} - /* Delete it from the common reclaim list */ - call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); +static void gfs2_qd_list_dispose(struct list_head *list) +{ + struct gfs2_quota_data *qd; + + while (!list_empty(list)) { + qd = list_first_entry(list, struct gfs2_quota_data, qd_lru); + list_del(&qd->qd_lru); + + gfs2_qd_dispose(qd); } } @@ -149,18 +155,22 @@ static enum lru_status gfs2_qd_isolate(struct list_head *item, struct list_lru_one *lru, spinlock_t *lru_lock, void *arg) { struct list_head *dispose = arg; - struct gfs2_quota_data *qd = list_entry(item, struct gfs2_quota_data, qd_lru); + struct gfs2_quota_data *qd = + list_entry(item, struct gfs2_quota_data, qd_lru); + enum lru_status status; if (!spin_trylock(&qd->qd_lockref.lock)) return LRU_SKIP; + status = LRU_SKIP; if (qd->qd_lockref.count == 0) { lockref_mark_dead(&qd->qd_lockref); list_lru_isolate_move(lru, &qd->qd_lru, dispose); + status = LRU_REMOVED; } spin_unlock(&qd->qd_lockref.lock); - return LRU_REMOVED; + return status; } static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, @@ -175,7 +185,7 @@ static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink, freed = list_lru_shrink_walk(&gfs2_qd_lru, sc, gfs2_qd_isolate, &dispose); - gfs2_qd_dispose(&dispose); + gfs2_qd_list_dispose(&dispose); return freed; } @@ -203,12 +213,7 @@ static u64 qd2index(struct gfs2_quota_data *qd) static u64 qd2offset(struct gfs2_quota_data *qd) { - u64 offset; - - offset = qd2index(qd); - offset *= sizeof(struct gfs2_quota); - - return offset; + return qd2index(qd) * sizeof(struct gfs2_quota); } static struct gfs2_quota_data *qd_alloc(unsigned hash, struct gfs2_sbd *sdp, struct kqid qid) @@ -221,7 +226,7 @@ static struct gfs2_quota_data *qd_alloc(unsigned hash, struct gfs2_sbd *sdp, str return NULL; qd->qd_sbd = sdp; - qd->qd_lockref.count = 1; + qd->qd_lockref.count = 0; spin_lock_init(&qd->qd_lockref.lock); qd->qd_id = qid; qd->qd_slot = -1; @@ -283,6 +288,7 @@ static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, spin_lock_bucket(hash); *qdp = qd = gfs2_qd_search_bucket(hash, sdp, qid); if (qd == NULL) { + new_qd->qd_lockref.count++; *qdp = new_qd; list_add(&new_qd->qd_list, &sdp->sd_quota_list); hlist_bl_add_head_rcu(&new_qd->qd_hlist, &qd_hash_table[hash]); @@ -302,20 +308,31 @@ static int qd_get(struct gfs2_sbd *sdp, struct kqid qid, static void qd_hold(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; gfs2_assert(sdp, !__lockref_is_dead(&qd->qd_lockref)); lockref_get(&qd->qd_lockref); } static void qd_put(struct gfs2_quota_data *qd) { + struct gfs2_sbd *sdp; + if (lockref_put_or_lock(&qd->qd_lockref)) return; + BUG_ON(__lockref_is_dead(&qd->qd_lockref)); + sdp = qd->qd_sbd; + if (unlikely(!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))) { + lockref_mark_dead(&qd->qd_lockref); + spin_unlock(&qd->qd_lockref.lock); + + gfs2_qd_dispose(qd); + return; + } + qd->qd_lockref.count = 0; list_lru_add(&gfs2_qd_lru, &qd->qd_lru); spin_unlock(&qd->qd_lockref.lock); - } static int slot_get(struct gfs2_quota_data *qd) @@ -325,20 +342,19 @@ static int slot_get(struct gfs2_quota_data *qd) int error = 0; spin_lock(&sdp->sd_bitmap_lock); - if (qd->qd_slot_count != 0) - goto out; - - error = -ENOSPC; - bit = find_first_zero_bit(sdp->sd_quota_bitmap, sdp->sd_quota_slots); - if (bit < sdp->sd_quota_slots) { + if (qd->qd_slot_ref == 0) { + bit = find_first_zero_bit(sdp->sd_quota_bitmap, + sdp->sd_quota_slots); + if (bit >= sdp->sd_quota_slots) { + error = -ENOSPC; + goto out; + } set_bit(bit, sdp->sd_quota_bitmap); qd->qd_slot = bit; - error = 0; -out: - qd->qd_slot_count++; } + qd->qd_slot_ref++; +out: spin_unlock(&sdp->sd_bitmap_lock); - return error; } @@ -347,8 +363,8 @@ static void slot_hold(struct gfs2_quota_data *qd) struct gfs2_sbd *sdp = qd->qd_sbd; spin_lock(&sdp->sd_bitmap_lock); - gfs2_assert(sdp, qd->qd_slot_count); - qd->qd_slot_count++; + gfs2_assert(sdp, qd->qd_slot_ref); + qd->qd_slot_ref++; spin_unlock(&sdp->sd_bitmap_lock); } @@ -357,8 +373,8 @@ static void slot_put(struct gfs2_quota_data *qd) struct gfs2_sbd *sdp = qd->qd_sbd; spin_lock(&sdp->sd_bitmap_lock); - gfs2_assert(sdp, qd->qd_slot_count); - if (!--qd->qd_slot_count) { + gfs2_assert(sdp, qd->qd_slot_ref); + if (!--qd->qd_slot_ref) { BUG_ON(!test_and_clear_bit(qd->qd_slot, sdp->sd_quota_bitmap)); qd->qd_slot = -1; } @@ -367,7 +383,7 @@ static void slot_put(struct gfs2_quota_data *qd) static int bh_get(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct inode *inode = sdp->sd_qc_inode; struct gfs2_inode *ip = GFS2_I(inode); unsigned int block, offset; @@ -421,7 +437,7 @@ static int bh_get(struct gfs2_quota_data *qd) static void bh_put(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; mutex_lock(&sdp->sd_quota_mutex); gfs2_assert(sdp, qd->qd_bh_count); @@ -451,6 +467,20 @@ static int qd_check_sync(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd, return 1; } +static int qd_bh_get_or_undo(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd) +{ + int error; + + error = bh_get(qd); + if (!error) + return 0; + + clear_bit(QDF_LOCKED, &qd->qd_flags); + slot_put(qd); + qd_put(qd); + return error; +} + static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) { struct gfs2_quota_data *qd = NULL, *iter; @@ -473,30 +503,29 @@ static int qd_fish(struct gfs2_sbd *sdp, struct gfs2_quota_data **qdp) spin_unlock(&qd_lock); if (qd) { - error = bh_get(qd); - if (error) { - clear_bit(QDF_LOCKED, &qd->qd_flags); - slot_put(qd); - qd_put(qd); + error = qd_bh_get_or_undo(sdp, qd); + if (error) return error; - } + *qdp = qd; } - *qdp = qd; - return 0; } -static void qd_unlock(struct gfs2_quota_data *qd) +static void qdsb_put(struct gfs2_quota_data *qd) { - gfs2_assert_warn(qd->qd_gl->gl_name.ln_sbd, - test_bit(QDF_LOCKED, &qd->qd_flags)); - clear_bit(QDF_LOCKED, &qd->qd_flags); bh_put(qd); slot_put(qd); qd_put(qd); } +static void qd_unlock(struct gfs2_quota_data *qd) +{ + gfs2_assert_warn(qd->qd_sbd, test_bit(QDF_LOCKED, &qd->qd_flags)); + clear_bit(QDF_LOCKED, &qd->qd_flags); + qdsb_put(qd); +} + static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, struct gfs2_quota_data **qdp) { @@ -523,13 +552,6 @@ static int qdsb_get(struct gfs2_sbd *sdp, struct kqid qid, return error; } -static void qdsb_put(struct gfs2_quota_data *qd) -{ - bh_put(qd); - slot_put(qd); - qd_put(qd); -} - /** * gfs2_qa_get - make sure we have a quota allocations data structure, * if necessary @@ -666,7 +688,7 @@ static int sort_qd(const void *a, const void *b) static void do_qc(struct gfs2_quota_data *qd, s64 change, int qc_type) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_qc_inode); struct gfs2_quota_change *qc = qd->qd_bh_qc; s64 x; @@ -708,30 +730,29 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change, int qc_type) mutex_unlock(&sdp->sd_quota_mutex); } -static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, +static int gfs2_write_buf_to_page(struct gfs2_sbd *sdp, unsigned long index, unsigned off, void *buf, unsigned bytes) { + struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct inode *inode = &ip->i_inode; - struct gfs2_sbd *sdp = GFS2_SB(inode); struct address_space *mapping = inode->i_mapping; struct page *page; struct buffer_head *bh; u64 blk; unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0; unsigned to_write = bytes, pg_off = off; - int done = 0; blk = index << (PAGE_SHIFT - sdp->sd_sb.sb_bsize_shift); boff = off % bsize; - page = find_or_create_page(mapping, index, GFP_NOFS); + page = grab_cache_page(mapping, index); if (!page) return -ENOMEM; if (!page_has_buffers(page)) create_empty_buffers(page, bsize, 0); bh = page_buffers(page); - while (!done) { + for(;;) { /* Find the beginning block within the page */ if (pg_off >= ((bnum * bsize) + bsize)) { bh = bh->b_this_page; @@ -751,10 +772,7 @@ static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, set_buffer_uptodate(bh); if (bh_read(bh, REQ_META | REQ_PRIO) < 0) goto unlock_out; - if (gfs2_is_jdata(ip)) - gfs2_trans_add_data(ip->i_gl, bh); - else - gfs2_ordered_add_inode(ip); + gfs2_trans_add_data(ip->i_gl, bh); /* If we need to write to the next block as well */ if (to_write > (bsize - boff)) { @@ -763,7 +781,7 @@ static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, boff = pg_off % bsize; continue; } - done = 1; + break; } /* Write to the page, now that we have setup the buffer(s) */ @@ -780,12 +798,12 @@ static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, return -EIO; } -static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, +static int gfs2_write_disk_quota(struct gfs2_sbd *sdp, struct gfs2_quota *qp, loff_t loc) { unsigned long pg_beg; unsigned pg_off, nbytes, overflow = 0; - int pg_oflow = 0, error; + int error; void *ptr; nbytes = sizeof(struct gfs2_quota); @@ -794,17 +812,15 @@ static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, pg_off = offset_in_page(loc); /* If the quota straddles a page boundary, split the write in two */ - if ((pg_off + nbytes) > PAGE_SIZE) { - pg_oflow = 1; + if ((pg_off + nbytes) > PAGE_SIZE) overflow = (pg_off + nbytes) - PAGE_SIZE; - } ptr = qp; - error = gfs2_write_buf_to_page(ip, pg_beg, pg_off, ptr, + error = gfs2_write_buf_to_page(sdp, pg_beg, pg_off, ptr, nbytes - overflow); /* If there's an overflow, write the remaining bytes to the next page */ - if (!error && pg_oflow) - error = gfs2_write_buf_to_page(ip, pg_beg + 1, 0, + if (!error && overflow) + error = gfs2_write_buf_to_page(sdp, pg_beg + 1, 0, ptr + nbytes - overflow, overflow); return error; @@ -812,7 +828,7 @@ static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, /** * gfs2_adjust_quota - adjust record of current block usage - * @ip: The quota inode + * @sdp: The superblock * @loc: Offset of the entry in the quota file * @change: The amount of usage change to record * @qd: The quota data @@ -824,12 +840,12 @@ static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, * Returns: 0 or -ve on error */ -static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, +static int gfs2_adjust_quota(struct gfs2_sbd *sdp, loff_t loc, s64 change, struct gfs2_quota_data *qd, struct qc_dqblk *fdq) { + struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct inode *inode = &ip->i_inode; - struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_quota q; int err; u64 size; @@ -846,7 +862,6 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, return err; loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */ - err = -EIO; be64_add_cpu(&q.qu_value, change); if (((s64)be64_to_cpu(q.qu_value)) < 0) q.qu_value = 0; /* Never go negative on quota usage */ @@ -866,7 +881,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, } } - err = gfs2_write_disk_quota(ip, &q, loc); + err = gfs2_write_disk_quota(sdp, &q, loc); if (!err) { size = loc + sizeof(struct gfs2_quota); if (size > inode->i_size) @@ -881,7 +896,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) { - struct gfs2_sbd *sdp = (*qda)->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = (*qda)->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct gfs2_alloc_parms ap = { .aflags = 0, }; unsigned int data_blocks, ind_blocks; @@ -893,18 +908,12 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) unsigned int nalloc = 0, blocks; int error; - error = gfs2_qa_get(ip); - if (error) - return error; - gfs2_write_calc_reserv(ip, sizeof(struct gfs2_quota), &data_blocks, &ind_blocks); ghs = kmalloc_array(num_qd, sizeof(struct gfs2_holder), GFP_NOFS); - if (!ghs) { - error = -ENOMEM; - goto out; - } + if (!ghs) + return -ENOMEM; sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL); inode_lock(&ip->i_inode); @@ -953,7 +962,8 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) for (x = 0; x < num_qd; x++) { qd = qda[x]; offset = qd2offset(qd); - error = gfs2_adjust_quota(ip, offset, qd->qd_change_sync, qd, NULL); + error = gfs2_adjust_quota(sdp, offset, qd->qd_change_sync, qd, + NULL); if (error) goto out_end_trans; @@ -961,8 +971,6 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) set_bit(QDF_REFRESH, &qd->qd_flags); } - error = 0; - out_end_trans: gfs2_trans_end(sdp); out_ipres: @@ -976,8 +984,10 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) kfree(ghs); gfs2_log_flush(ip->i_gl->gl_name.ln_sbd, ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_DO_SYNC); -out: - gfs2_qa_put(ip); + if (!error) { + for (x = 0; x < num_qd; x++) + qda[x]->qd_sync_gen = sdp->sd_quota_sync_gen; + } return error; } @@ -1009,11 +1019,12 @@ static int update_qd(struct gfs2_sbd *sdp, struct gfs2_quota_data *qd) static int do_glock(struct gfs2_quota_data *qd, int force_refresh, struct gfs2_holder *q_gh) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode); struct gfs2_holder i_gh; int error; + gfs2_assert_warn(sdp, sdp == qd->qd_gl->gl_name.ln_sbd); restart: error = gfs2_glock_nq_init(qd->qd_gl, LM_ST_SHARED, 0, q_gh); if (error) @@ -1059,9 +1070,10 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_quota_data *qd; u32 x; - int error = 0; + int error; - if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) + if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON && + sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) return 0; error = gfs2_quota_hold(ip, uid, gid); @@ -1089,16 +1101,15 @@ int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) return error; } -static int need_sync(struct gfs2_quota_data *qd) +static bool need_sync(struct gfs2_quota_data *qd) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; struct gfs2_tune *gt = &sdp->sd_tune; s64 value; unsigned int num, den; - int do_sync = 1; if (!qd->qd_qb.qb_limit) - return 0; + return false; spin_lock(&qd_lock); value = qd->qd_change; @@ -1109,26 +1120,26 @@ static int need_sync(struct gfs2_quota_data *qd) den = gt->gt_quota_scale_den; spin_unlock(>->gt_spin); - if (value < 0) - do_sync = 0; + if (value <= 0) + return false; else if ((s64)be64_to_cpu(qd->qd_qb.qb_value) >= (s64)be64_to_cpu(qd->qd_qb.qb_limit)) - do_sync = 0; + return false; else { value *= gfs2_jindex_size(sdp) * num; value = div_s64(value, den); value += (s64)be64_to_cpu(qd->qd_qb.qb_value); if (value < (s64)be64_to_cpu(qd->qd_qb.qb_limit)) - do_sync = 0; + return false; } - return do_sync; + return true; } void gfs2_quota_unlock(struct gfs2_inode *ip) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - struct gfs2_quota_data *qda[4]; + struct gfs2_quota_data *qda[2 * GFS2_MAXQUOTAS]; unsigned int count = 0; u32 x; int found; @@ -1138,7 +1149,7 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) for (x = 0; x < ip->i_qadata->qa_qd_num; x++) { struct gfs2_quota_data *qd; - int sync; + bool sync; qd = ip->i_qadata->qa_qd[x]; sync = need_sync(qd); @@ -1154,15 +1165,8 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) if (!found) continue; - gfs2_assert_warn(sdp, qd->qd_change_sync); - if (bh_get(qd)) { - clear_bit(QDF_LOCKED, &qd->qd_flags); - slot_put(qd); - qd_put(qd); - continue; - } - - qda[count++] = qd; + if (!qd_bh_get_or_undo(sdp, qd)) + qda[count++] = qd; } if (count) { @@ -1178,12 +1182,13 @@ void gfs2_quota_unlock(struct gfs2_inode *ip) static int print_message(struct gfs2_quota_data *qd, char *type) { - struct gfs2_sbd *sdp = qd->qd_gl->gl_name.ln_sbd; + struct gfs2_sbd *sdp = qd->qd_sbd; - fs_info(sdp, "quota %s for %s %u\n", - type, - (qd->qd_id.type == USRQUOTA) ? "user" : "group", - from_kqid(&init_user_ns, qd->qd_id)); + if (sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) + fs_info(sdp, "quota %s for %s %u\n", + type, + (qd->qd_id.type == USRQUOTA) ? "user" : "group", + from_kqid(&init_user_ns, qd->qd_id)); return 0; } @@ -1269,7 +1274,8 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change, u32 x; struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); - if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON || + if ((sdp->sd_args.ar_quota != GFS2_QUOTA_ON && + sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) || gfs2_assert_warn(sdp, change)) return; if (ip->i_diskflags & GFS2_DIF_SYSTEM) @@ -1288,6 +1294,24 @@ void gfs2_quota_change(struct gfs2_inode *ip, s64 change, } } +static bool qd_changed(struct gfs2_sbd *sdp) +{ + struct gfs2_quota_data *qd; + bool changed = false; + + spin_lock(&qd_lock); + list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { + if (test_bit(QDF_LOCKED, &qd->qd_flags) || + !test_bit(QDF_CHANGE, &qd->qd_flags)) + continue; + + changed = true; + break; + } + spin_unlock(&qd_lock); + return changed; +} + int gfs2_quota_sync(struct super_block *sb, int type) { struct gfs2_sbd *sdp = sb->s_fs_info; @@ -1297,6 +1321,9 @@ int gfs2_quota_sync(struct super_block *sb, int type) unsigned int x; int error = 0; + if (!qd_changed(sdp)) + return 0; + qda = kcalloc(max_qd, sizeof(struct gfs2_quota_data *), GFP_KERNEL); if (!qda) return -ENOMEM; @@ -1318,10 +1345,6 @@ int gfs2_quota_sync(struct super_block *sb, int type) if (num_qd) { if (!error) error = do_sync(num_qd, qda); - if (!error) - for (x = 0; x < num_qd; x++) - qda[x]->qd_sync_gen = - sdp->sd_quota_sync_gen; for (x = 0; x < num_qd; x++) qd_unlock(qda[x]); @@ -1423,7 +1446,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) set_bit(QDF_CHANGE, &qd->qd_flags); qd->qd_change = qc_change; qd->qd_slot = slot; - qd->qd_slot_count = 1; + qd->qd_slot_ref = 1; spin_lock(&qd_lock); BUG_ON(test_and_set_bit(slot, sdp->sd_quota_bitmap)); @@ -1455,36 +1478,35 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) void gfs2_quota_cleanup(struct gfs2_sbd *sdp) { - struct list_head *head = &sdp->sd_quota_list; struct gfs2_quota_data *qd; + LIST_HEAD(dispose); + int count; - spin_lock(&qd_lock); - while (!list_empty(head)) { - qd = list_last_entry(head, struct gfs2_quota_data, qd_list); + BUG_ON(test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)); - list_del(&qd->qd_list); + spin_lock(&qd_lock); + list_for_each_entry(qd, &sdp->sd_quota_list, qd_list) { + spin_lock(&qd->qd_lockref.lock); + if (qd->qd_lockref.count != 0) { + spin_unlock(&qd->qd_lockref.lock); + continue; + } + lockref_mark_dead(&qd->qd_lockref); + spin_unlock(&qd->qd_lockref.lock); - /* Also remove if this qd exists in the reclaim list */ list_lru_del(&gfs2_qd_lru, &qd->qd_lru); - atomic_dec(&sdp->sd_quota_count); - spin_unlock(&qd_lock); - - spin_lock_bucket(qd->qd_hash); - hlist_bl_del_rcu(&qd->qd_hlist); - spin_unlock_bucket(qd->qd_hash); - - gfs2_assert_warn(sdp, !qd->qd_change); - gfs2_assert_warn(sdp, !qd->qd_slot_count); - gfs2_assert_warn(sdp, !qd->qd_bh_count); - - gfs2_glock_put(qd->qd_gl); - call_rcu(&qd->qd_rcu, gfs2_qd_dealloc); - - spin_lock(&qd_lock); + list_add(&qd->qd_lru, &dispose); } spin_unlock(&qd_lock); - gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_quota_count)); + gfs2_qd_list_dispose(&dispose); + + wait_event_timeout(sdp->sd_kill_wait, + (count = atomic_read(&sdp->sd_quota_count)) == 0, + HZ * 60); + + if (count != 0) + fs_err(sdp, "%d left-over quota data objects\n", count); kvfree(sdp->sd_quota_bitmap); sdp->sd_quota_bitmap = NULL; @@ -1536,12 +1558,11 @@ int gfs2_quotad(void *data) unsigned long statfs_timeo = 0; unsigned long quotad_timeo = 0; unsigned long t = 0; - DEFINE_WAIT(wait); while (!kthread_should_stop()) { - if (gfs2_withdrawn(sdp)) - goto bypass; + break; + /* Update the master statfs file */ if (sdp->sd_statfs_force_sync) { int error = gfs2_statfs_sync(sdp->sd_vfs, 0); @@ -1559,15 +1580,16 @@ int gfs2_quotad(void *data) try_to_freeze(); -bypass: t = min(quotad_timeo, statfs_timeo); - prepare_to_wait(&sdp->sd_quota_wait, &wait, TASK_INTERRUPTIBLE); - if (!sdp->sd_statfs_force_sync) - t -= schedule_timeout(t); - else + t = wait_event_interruptible_timeout(sdp->sd_quota_wait, + sdp->sd_statfs_force_sync || + gfs2_withdrawn(sdp) || + kthread_should_stop(), + t); + + if (sdp->sd_statfs_force_sync) t = 0; - finish_wait(&sdp->sd_quota_wait, &wait); } return 0; @@ -1580,6 +1602,8 @@ static int gfs2_quota_get_state(struct super_block *sb, struct qc_state *state) memset(state, 0, sizeof(*state)); switch (sdp->sd_args.ar_quota) { + case GFS2_QUOTA_QUIET: + fallthrough; case GFS2_QUOTA_ON: state->s_state[USRQUOTA].flags |= QCI_LIMITS_ENFORCED; state->s_state[GRPQUOTA].flags |= QCI_LIMITS_ENFORCED; @@ -1726,7 +1750,7 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, goto out_release; /* Apply changes */ - error = gfs2_adjust_quota(ip, offset, 0, qd, fdq); + error = gfs2_adjust_quota(sdp, offset, 0, qd, fdq); if (!error) clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 21ada332d55571021bda02fb55be74da43952388..1429945215a039dad28c05de4bc419ad0ebd6b79 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -50,7 +50,8 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip, ret = gfs2_quota_lock(ip, NO_UID_QUOTA_CHANGE, NO_GID_QUOTA_CHANGE); if (ret) return ret; - if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) + if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON && + sdp->sd_args.ar_quota != GFS2_QUOTA_QUIET) return 0; ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid, ap); if (ret) diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 9c7a9f640badda89059935374062b784cc9dc581..5aae02669a4090ae62af37412c5ccd67c328c38d 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -27,7 +27,7 @@ #include "util.h" #include "dir.h" -struct workqueue_struct *gfs_recovery_wq; +struct workqueue_struct *gfs2_recovery_wq; int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk, struct buffer_head **bh) @@ -570,7 +570,7 @@ int gfs2_recover_journal(struct gfs2_jdesc *jd, bool wait) return -EBUSY; /* we have JDF_RECOVERY, queue should always succeed */ - rv = queue_work(gfs_recovery_wq, &jd->jd_work); + rv = queue_work(gfs2_recovery_wq, &jd->jd_work); BUG_ON(!rv); if (wait) diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h index 0d30f8e804f497b1170660f6611f008bbf4a1286..7a0c9d0b7503f0bd14f7bce068103d6a1063c209 100644 --- a/fs/gfs2/recovery.h +++ b/fs/gfs2/recovery.h @@ -9,7 +9,7 @@ #include "incore.h" -extern struct workqueue_struct *gfs_recovery_wq; +extern struct workqueue_struct *gfs2_recovery_wq; static inline void gfs2_replay_incr_blk(struct gfs2_jdesc *jd, u32 *blk) { diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 2f701335e8ee20405ed9d355a517db564a56fbf3..02d93da21b2b07643c6a7be334ca5b6838efb115 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -546,20 +546,10 @@ void gfs2_make_fs_ro(struct gfs2_sbd *sdp) { int log_write_allowed = test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); - if (!test_bit(SDF_DEACTIVATING, &sdp->sd_flags)) + if (!test_bit(SDF_KILL, &sdp->sd_flags)) gfs2_flush_delete_work(sdp); - if (!log_write_allowed && current == sdp->sd_quotad_process) - fs_warn(sdp, "The quotad daemon is withdrawing.\n"); - else if (sdp->sd_quotad_process) - kthread_stop(sdp->sd_quotad_process); - sdp->sd_quotad_process = NULL; - - if (!log_write_allowed && current == sdp->sd_logd_process) - fs_warn(sdp, "The logd daemon is withdrawing.\n"); - else if (sdp->sd_logd_process) - kthread_stop(sdp->sd_logd_process); - sdp->sd_logd_process = NULL; + gfs2_destroy_threads(sdp); if (log_write_allowed) { gfs2_quota_sync(sdp->sd_vfs, 0); @@ -580,15 +570,8 @@ void gfs2_make_fs_ro(struct gfs2_sbd *sdp) gfs2_log_is_empty(sdp), HZ * 5); gfs2_assert_warn(sdp, gfs2_log_is_empty(sdp)); - } else { - wait_event_timeout(sdp->sd_log_waitq, - gfs2_log_is_empty(sdp), - HZ * 5); } gfs2_quota_cleanup(sdp); - - if (!log_write_allowed) - sdp->sd_vfs->s_flags |= SB_RDONLY; } /** @@ -622,6 +605,10 @@ static void gfs2_put_super(struct super_block *sb) if (!sb_rdonly(sb)) { gfs2_make_fs_ro(sdp); } + if (gfs2_withdrawn(sdp)) { + gfs2_destroy_threads(sdp); + gfs2_quota_cleanup(sdp); + } WARN_ON(gfs2_withdrawing(sdp)); /* At this point, we're through modifying the disk */ @@ -1134,6 +1121,9 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root) case GFS2_QUOTA_ON: state = "on"; break; + case GFS2_QUOTA_QUIET: + state = "quiet"; + break; default: state = "unknown"; break; diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h index bba58629bc45800ed9bf65eccdcadf0f8143826e..ab9c83106932db17dd247d795bbf774b53899004 100644 --- a/fs/gfs2/super.h +++ b/fs/gfs2/super.h @@ -36,6 +36,7 @@ extern int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename, extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp); extern void gfs2_make_fs_ro(struct gfs2_sbd *sdp); extern void gfs2_online_uevent(struct gfs2_sbd *sdp); +extern void gfs2_destroy_threads(struct gfs2_sbd *sdp); extern int gfs2_statfs_init(struct gfs2_sbd *sdp); extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free, s64 dinodes); diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index c60bc7f628e192643e52d51f5b908778701f9666..60a0206890c54ceaebf793b18760f0afd58437bb 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -98,7 +98,10 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) "sd_log_flush_head: %d\n" "sd_log_flush_tail: %d\n" "sd_log_blks_reserved: %d\n" - "sd_log_revokes_available: %d\n", + "sd_log_revokes_available: %d\n" + "sd_log_pinned: %d\n" + "sd_log_thresh1: %d\n" + "sd_log_thresh2: %d\n", test_bit(SDF_JOURNAL_CHECKED, &f), test_bit(SDF_JOURNAL_LIVE, &f), (sdp->sd_jdesc ? sdp->sd_jdesc->jd_jid : 0), @@ -118,7 +121,7 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) test_bit(SDF_WITHDRAW_IN_PROG, &f), test_bit(SDF_REMOTE_WITHDRAW, &f), test_bit(SDF_WITHDRAW_RECOVERY, &f), - test_bit(SDF_DEACTIVATING, &f), + test_bit(SDF_KILL, &f), sdp->sd_log_error, rwsem_is_locked(&sdp->sd_log_flush_lock), sdp->sd_log_num_revoke, @@ -128,7 +131,10 @@ static ssize_t status_show(struct gfs2_sbd *sdp, char *buf) sdp->sd_log_flush_head, sdp->sd_log_flush_tail, sdp->sd_log_blks_reserved, - atomic_read(&sdp->sd_log_revokes_available)); + atomic_read(&sdp->sd_log_revokes_available), + atomic_read(&sdp->sd_log_pinned), + atomic_read(&sdp->sd_log_thresh1), + atomic_read(&sdp->sd_log_thresh2)); return s; } diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index dac22b1c1a2e1c401a1fcd8439360b5647d7ca69..da29fafb62728addbface4973f50049ef42febc4 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -150,7 +151,14 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp) if (!sb_rdonly(sdp->sd_vfs)) { bool locked = mutex_trylock(&sdp->sd_freeze_mutex); - gfs2_make_fs_ro(sdp); + wake_up(&sdp->sd_logd_waitq); + wake_up(&sdp->sd_quota_wait); + + wait_event_timeout(sdp->sd_log_waitq, + gfs2_log_is_empty(sdp), + HZ * 5); + + sdp->sd_vfs->s_flags |= SB_RDONLY; if (locked) mutex_unlock(&sdp->sd_freeze_mutex); @@ -315,19 +323,19 @@ int gfs2_withdraw(struct gfs2_sbd *sdp) struct lm_lockstruct *ls = &sdp->sd_lockstruct; const struct lm_lockops *lm = ls->ls_ops; - if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW && - test_and_set_bit(SDF_WITHDRAWN, &sdp->sd_flags)) { - if (!test_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags)) - return -1; - - wait_on_bit(&sdp->sd_flags, SDF_WITHDRAW_IN_PROG, - TASK_UNINTERRUPTIBLE); - return -1; - } - - set_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags); - if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) { + unsigned long old = READ_ONCE(sdp->sd_flags), new; + + do { + if (old & BIT(SDF_WITHDRAWN)) { + wait_on_bit(&sdp->sd_flags, + SDF_WITHDRAW_IN_PROG, + TASK_UNINTERRUPTIBLE); + return -1; + } + new = old | BIT(SDF_WITHDRAWN) | BIT(SDF_WITHDRAW_IN_PROG); + } while (unlikely(!try_cmpxchg(&sdp->sd_flags, &old, new))); + fs_err(sdp, "about to withdraw this file system\n"); BUG_ON(sdp->sd_args.ar_debug); diff --git a/fs/inode.c b/fs/inode.c index 35fd688168c553d1a270485fada1819db60c4918..84bc3c76e5ccb5d800172dfe76be3d3d80ecc2ff 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2102,52 +2102,10 @@ int file_remove_privs(struct file *file) } EXPORT_SYMBOL(file_remove_privs); -/** - * current_mgtime - Return FS time (possibly fine-grained) - * @inode: inode. - * - * Return the current time truncated to the time granularity supported by - * the fs, as suitable for a ctime/mtime change. If the ctime is flagged - * as having been QUERIED, get a fine-grained timestamp. - */ -struct timespec64 current_mgtime(struct inode *inode) -{ - struct timespec64 now, ctime; - atomic_long_t *pnsec = (atomic_long_t *)&inode->__i_ctime.tv_nsec; - long nsec = atomic_long_read(pnsec); - - if (nsec & I_CTIME_QUERIED) { - ktime_get_real_ts64(&now); - return timestamp_truncate(now, inode); - } - - ktime_get_coarse_real_ts64(&now); - now = timestamp_truncate(now, inode); - - /* - * If we've recently fetched a fine-grained timestamp - * then the coarse-grained one may still be earlier than the - * existing ctime. Just keep the existing value if so. - */ - ctime = inode_get_ctime(inode); - if (timespec64_compare(&ctime, &now) > 0) - now = ctime; - - return now; -} -EXPORT_SYMBOL(current_mgtime); - -static struct timespec64 current_ctime(struct inode *inode) -{ - if (is_mgtime(inode)) - return current_mgtime(inode); - return current_time(inode); -} - static int inode_needs_update_time(struct inode *inode) { int sync_it = 0; - struct timespec64 now = current_ctime(inode); + struct timespec64 now = current_time(inode); struct timespec64 ctime; /* First try to exhaust all avenues to not sync */ @@ -2578,43 +2536,9 @@ EXPORT_SYMBOL(current_time); */ struct timespec64 inode_set_ctime_current(struct inode *inode) { - struct timespec64 now; - struct timespec64 ctime; - - ctime.tv_nsec = READ_ONCE(inode->__i_ctime.tv_nsec); - if (!(ctime.tv_nsec & I_CTIME_QUERIED)) { - now = current_time(inode); + struct timespec64 now = current_time(inode); - /* Just copy it into place if it's not multigrain */ - if (!is_mgtime(inode)) { - inode_set_ctime_to_ts(inode, now); - return now; - } - - /* - * If we've recently updated with a fine-grained timestamp, - * then the coarse-grained one may still be earlier than the - * existing ctime. Just keep the existing value if so. - */ - ctime.tv_sec = inode->__i_ctime.tv_sec; - if (timespec64_compare(&ctime, &now) > 0) - return ctime; - - /* - * Ctime updates are usually protected by the inode_lock, but - * we can still race with someone setting the QUERIED flag. - * Try to swap the new nsec value into place. If it's changed - * in the interim, then just go with a fine-grained timestamp. - */ - if (cmpxchg(&inode->__i_ctime.tv_nsec, ctime.tv_nsec, - now.tv_nsec) != ctime.tv_nsec) - goto fine_grained; - inode->__i_ctime.tv_sec = now.tv_sec; - return now; - } -fine_grained: - ktime_get_real_ts64(&now); - inode_set_ctime_to_ts(inode, timestamp_truncate(now, inode)); + inode_set_ctime(inode, now.tv_sec, now.tv_nsec); return now; } EXPORT_SYMBOL(inode_set_ctime_current); diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c index ae8673ce08b16e4b4268fa3eaa76efee131155a8..5db54ca29a35acf39324b9354d9d02da21425eed 100644 --- a/fs/iomap/buffered-io.c +++ b/fs/iomap/buffered-io.c @@ -640,11 +640,13 @@ static int __iomap_write_begin(const struct iomap_iter *iter, loff_t pos, size_t poff, plen; /* - * If the write completely overlaps the current folio, then + * If the write or zeroing completely overlaps the current folio, then * entire folio will be dirtied so there is no need for * per-block state tracking structures to be attached to this folio. + * For the unshare case, we must read in the ondisk contents because we + * are not changing pagecache contents. */ - if (pos <= folio_pos(folio) && + if (!(iter->flags & IOMAP_UNSHARE) && pos <= folio_pos(folio) && pos + len >= folio_pos(folio) + folio_size(folio)) return 0; @@ -1047,7 +1049,7 @@ static int iomap_write_delalloc_punch(struct inode *inode, struct folio *folio, /* * Scan the data range passed to us for dirty page cache folios. If we find a - * dirty folio, punch out the preceeding range and update the offset from which + * dirty folio, punch out the preceding range and update the offset from which * the next punch will start from. * * We can punch out storage reservations under clean pages because they either @@ -1261,7 +1263,6 @@ static loff_t iomap_unshare_iter(struct iomap_iter *iter) const struct iomap *srcmap = iomap_iter_srcmap(iter); loff_t pos = iter->pos; loff_t length = iomap_length(iter); - long status = 0; loff_t written = 0; /* don't bother with blocks that are not shared to start with */ @@ -1272,28 +1273,33 @@ static loff_t iomap_unshare_iter(struct iomap_iter *iter) return length; do { - unsigned long offset = offset_in_page(pos); - unsigned long bytes = min_t(loff_t, PAGE_SIZE - offset, length); struct folio *folio; + int status; + size_t offset; + size_t bytes = min_t(u64, SIZE_MAX, length); status = iomap_write_begin(iter, pos, bytes, &folio); if (unlikely(status)) return status; - if (iter->iomap.flags & IOMAP_F_STALE) + if (iomap->flags & IOMAP_F_STALE) break; - status = iomap_write_end(iter, pos, bytes, bytes, folio); - if (WARN_ON_ONCE(status == 0)) + offset = offset_in_folio(folio, pos); + if (bytes > folio_size(folio) - offset) + bytes = folio_size(folio) - offset; + + bytes = iomap_write_end(iter, pos, bytes, bytes, folio); + if (WARN_ON_ONCE(bytes == 0)) return -EIO; cond_resched(); - pos += status; - written += status; - length -= status; + pos += bytes; + written += bytes; + length -= bytes; balance_dirty_pages_ratelimited(iter->inode->i_mapping); - } while (length); + } while (length > 0); return written; } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 1073259902a60b47a6504d9ce4b7ae4d19d81bb0..8d6f934c3d9543d3c87a77f5ec3566df7a2b2a37 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -298,14 +298,12 @@ static int journal_finish_inode_data_buffers(journal_t *journal, static __u32 jbd2_checksum_data(__u32 crc32_sum, struct buffer_head *bh) { - struct page *page = bh->b_page; char *addr; __u32 checksum; - addr = kmap_atomic(page); - checksum = crc32_be(crc32_sum, - (void *)(addr + offset_in_page(bh->b_data)), bh->b_size); - kunmap_atomic(addr); + addr = kmap_local_folio(bh->b_folio, bh_offset(bh)); + checksum = crc32_be(crc32_sum, addr, bh->b_size); + kunmap_local(addr); return checksum; } @@ -322,7 +320,6 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, struct buffer_head *bh, __u32 sequence) { journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; - struct page *page = bh->b_page; __u8 *addr; __u32 csum32; __be32 seq; @@ -331,11 +328,10 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, return; seq = cpu_to_be32(sequence); - addr = kmap_atomic(page); + addr = kmap_local_folio(bh->b_folio, bh_offset(bh)); csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq)); - csum32 = jbd2_chksum(j, csum32, addr + offset_in_page(bh->b_data), - bh->b_size); - kunmap_atomic(addr); + csum32 = jbd2_chksum(j, csum32, addr, bh->b_size); + kunmap_local(addr); if (jbd2_has_feature_csum3(j)) tag3->t_checksum = cpu_to_be32(csum32); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 768fa05bcbedeb7523a8ef76c8f1155ecaf2d7f3..30dec2bd2ecc26abb8d67a66b0d04f84e08dc718 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1601,6 +1601,8 @@ static journal_t *journal_init_common(struct block_device *bdev, err_cleanup: percpu_counter_destroy(&journal->j_checkpoint_jh_count); + if (journal->j_chksum_driver) + crypto_free_shash(journal->j_chksum_driver); kfree(journal->j_wbuf); jbd2_journal_destroy_revoke(journal); journal_fail_superblock(journal); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 4d1fda1f71439f46f85c265f93301518b55ef99e..5f08b5fd105a311584ffd1694667eb187f0e3975 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -935,19 +935,15 @@ static void warn_dirty_buffer(struct buffer_head *bh) /* Call t_frozen trigger and copy buffer data into jh->b_frozen_data. */ static void jbd2_freeze_jh_data(struct journal_head *jh) { - struct page *page; - int offset; char *source; struct buffer_head *bh = jh2bh(jh); J_EXPECT_JH(jh, buffer_uptodate(bh), "Possible IO failure.\n"); - page = bh->b_page; - offset = offset_in_page(bh->b_data); - source = kmap_atomic(page); + source = kmap_local_folio(bh->b_folio, bh_offset(bh)); /* Fire data frozen trigger just before we copy the data */ - jbd2_buffer_frozen_trigger(jh, source + offset, jh->b_triggers); - memcpy(jh->b_frozen_data, source + offset, bh->b_size); - kunmap_atomic(source); + jbd2_buffer_frozen_trigger(jh, source, jh->b_triggers); + memcpy(jh->b_frozen_data, source, bh->b_size); + kunmap_local(source); /* * Now that the frozen data is saved off, we need to store any matching diff --git a/fs/libfs.c b/fs/libfs.c index a4eb1275788627161d1a1f0d5f5b6bc3557a5df1..37f2d34ee090bd1230426045d8565bf72a82579b 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -1903,6 +1903,7 @@ ssize_t direct_write_fallback(struct kiocb *iocb, struct iov_iter *iter, * We don't know how much we wrote, so just return the number of * bytes which were direct-written */ + iocb->ki_pos -= buffered_written; if (direct_written) return direct_written; return err; diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 3404707ddbe7302201288e793287a1de5c5e24e7..2cd3ccf4c439960053e436d63792bc5bf7a914de 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -47,12 +47,14 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq) xas_for_each(&xas, folio, last_page) { loff_t pg_end; bool pg_failed = false; + bool folio_started; if (xas_retry(&xas, folio)) continue; pg_end = folio_pos(folio) + folio_size(folio) - 1; + folio_started = false; for (;;) { loff_t sreq_end; @@ -60,8 +62,10 @@ void netfs_rreq_unlock_folios(struct netfs_io_request *rreq) pg_failed = true; break; } - if (test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) + if (!folio_started && test_bit(NETFS_SREQ_COPY_TO_CACHE, &subreq->flags)) { folio_start_fscache(folio); + folio_started = true; + } pg_failed |= subreq_failed; sreq_end = subreq->start + subreq->len - 1; if (pg_end < sreq_end) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 47d892a1d363d94d13b5d7af5a529ba44136d3f9..f6c74f4246917fafce2746198622edad39022179 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -93,12 +93,10 @@ nfs_direct_handle_truncated(struct nfs_direct_req *dreq, dreq->max_count = dreq_len; if (dreq->count > dreq_len) dreq->count = dreq_len; - - if (test_bit(NFS_IOHDR_ERROR, &hdr->flags)) - dreq->error = hdr->error; - else /* Clear outstanding error if this is EOF */ - dreq->error = 0; } + + if (test_bit(NFS_IOHDR_ERROR, &hdr->flags) && !dreq->error) + dreq->error = hdr->error; } static void @@ -120,6 +118,18 @@ nfs_direct_count_bytes(struct nfs_direct_req *dreq, dreq->count = dreq_len; } +static void nfs_direct_truncate_request(struct nfs_direct_req *dreq, + struct nfs_page *req) +{ + loff_t offs = req_offset(req); + size_t req_start = (size_t)(offs - dreq->io_start); + + if (req_start < dreq->max_count) + dreq->max_count = req_start; + if (req_start < dreq->count) + dreq->count = req_start; +} + /** * nfs_swap_rw - NFS address space operation for swap I/O * @iocb: target I/O control block @@ -488,7 +498,9 @@ static void nfs_direct_add_page_head(struct list_head *list, kref_get(&head->wb_kref); } -static void nfs_direct_join_group(struct list_head *list, struct inode *inode) +static void nfs_direct_join_group(struct list_head *list, + struct nfs_commit_info *cinfo, + struct inode *inode) { struct nfs_page *req, *subreq; @@ -510,7 +522,7 @@ static void nfs_direct_join_group(struct list_head *list, struct inode *inode) nfs_release_request(subreq); } } while ((subreq = subreq->wb_this_page) != req); - nfs_join_page_group(req, inode); + nfs_join_page_group(req, cinfo, inode); } } @@ -528,20 +540,15 @@ nfs_direct_write_scan_commit_list(struct inode *inode, static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) { struct nfs_pageio_descriptor desc; - struct nfs_page *req, *tmp; + struct nfs_page *req; LIST_HEAD(reqs); struct nfs_commit_info cinfo; - LIST_HEAD(failed); nfs_init_cinfo_from_dreq(&cinfo, dreq); nfs_direct_write_scan_commit_list(dreq->inode, &reqs, &cinfo); - nfs_direct_join_group(&reqs, dreq->inode); + nfs_direct_join_group(&reqs, &cinfo, dreq->inode); - dreq->count = 0; - dreq->max_count = 0; - list_for_each_entry(req, &reqs, wb_list) - dreq->max_count += req->wb_bytes; nfs_clear_pnfs_ds_commit_verifiers(&dreq->ds_cinfo); get_dreq(dreq); @@ -549,27 +556,40 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) &nfs_direct_write_completion_ops); desc.pg_dreq = dreq; - list_for_each_entry_safe(req, tmp, &reqs, wb_list) { + while (!list_empty(&reqs)) { + req = nfs_list_entry(reqs.next); /* Bump the transmission count */ req->wb_nio++; if (!nfs_pageio_add_request(&desc, req)) { - nfs_list_move_request(req, &failed); - spin_lock(&cinfo.inode->i_lock); - dreq->flags = 0; - if (desc.pg_error < 0) + spin_lock(&dreq->lock); + if (dreq->error < 0) { + desc.pg_error = dreq->error; + } else if (desc.pg_error != -EAGAIN) { + dreq->flags = 0; + if (!desc.pg_error) + desc.pg_error = -EIO; dreq->error = desc.pg_error; - else - dreq->error = -EIO; - spin_unlock(&cinfo.inode->i_lock); + } else + dreq->flags = NFS_ODIRECT_RESCHED_WRITES; + spin_unlock(&dreq->lock); + break; } nfs_release_request(req); } nfs_pageio_complete(&desc); - while (!list_empty(&failed)) { - req = nfs_list_entry(failed.next); + while (!list_empty(&reqs)) { + req = nfs_list_entry(reqs.next); nfs_list_remove_request(req); nfs_unlock_and_release_request(req); + if (desc.pg_error == -EAGAIN) { + nfs_mark_request_commit(req, NULL, &cinfo, 0); + } else { + spin_lock(&dreq->lock); + nfs_direct_truncate_request(dreq, req); + spin_unlock(&dreq->lock); + nfs_release_request(req); + } } if (put_dreq(dreq)) @@ -589,8 +609,6 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data) if (status < 0) { /* Errors in commit are fatal */ dreq->error = status; - dreq->max_count = 0; - dreq->count = 0; dreq->flags = NFS_ODIRECT_DONE; } else { status = dreq->error; @@ -601,7 +619,12 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data) while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); - if (status >= 0 && !nfs_write_match_verf(verf, req)) { + if (status < 0) { + spin_lock(&dreq->lock); + nfs_direct_truncate_request(dreq, req); + spin_unlock(&dreq->lock); + nfs_release_request(req); + } else if (!nfs_write_match_verf(verf, req)) { dreq->flags = NFS_ODIRECT_RESCHED_WRITES; /* * Despite the reboot, the write was successful, @@ -609,7 +632,7 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data) */ req->wb_nio = 0; nfs_mark_request_commit(req, NULL, &cinfo, 0); - } else /* Error or match */ + } else nfs_release_request(req); nfs_unlock_and_release_request(req); } @@ -662,6 +685,7 @@ static void nfs_direct_write_clear_reqs(struct nfs_direct_req *dreq) while (!list_empty(&reqs)) { req = nfs_list_entry(reqs.next); nfs_list_remove_request(req); + nfs_direct_truncate_request(dreq, req); nfs_release_request(req); nfs_unlock_and_release_request(req); } @@ -711,7 +735,8 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr) } nfs_direct_count_bytes(dreq, hdr); - if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags)) { + if (test_bit(NFS_IOHDR_UNSTABLE_WRITES, &hdr->flags) && + !test_bit(NFS_IOHDR_ERROR, &hdr->flags)) { if (!dreq->flags) dreq->flags = NFS_ODIRECT_DO_COMMIT; flags = dreq->flags; @@ -755,18 +780,23 @@ static void nfs_write_sync_pgio_error(struct list_head *head, int error) static void nfs_direct_write_reschedule_io(struct nfs_pgio_header *hdr) { struct nfs_direct_req *dreq = hdr->dreq; + struct nfs_page *req; + struct nfs_commit_info cinfo; trace_nfs_direct_write_reschedule_io(dreq); + nfs_init_cinfo_from_dreq(&cinfo, dreq); spin_lock(&dreq->lock); - if (dreq->error == 0) { + if (dreq->error == 0) dreq->flags = NFS_ODIRECT_RESCHED_WRITES; - /* fake unstable write to let common nfs resend pages */ - hdr->verf.committed = NFS_UNSTABLE; - hdr->good_bytes = hdr->args.offset + hdr->args.count - - hdr->io_start; - } + set_bit(NFS_IOHDR_REDO, &hdr->flags); spin_unlock(&dreq->lock); + while (!list_empty(&hdr->pages)) { + req = nfs_list_entry(hdr->pages.next); + nfs_list_remove_request(req); + nfs_unlock_request(req); + nfs_mark_request_commit(req, NULL, &cinfo, 0); + } } static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops = { @@ -794,9 +824,11 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, { struct nfs_pageio_descriptor desc; struct inode *inode = dreq->inode; + struct nfs_commit_info cinfo; ssize_t result = 0; size_t requested_bytes = 0; size_t wsize = max_t(size_t, NFS_SERVER(inode)->wsize, PAGE_SIZE); + bool defer = false; trace_nfs_direct_write_schedule_iovec(dreq); @@ -837,17 +869,37 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq, break; } - nfs_lock_request(req); - if (!nfs_pageio_add_request(&desc, req)) { - result = desc.pg_error; - nfs_unlock_and_release_request(req); - break; - } pgbase = 0; bytes -= req_len; requested_bytes += req_len; pos += req_len; dreq->bytes_left -= req_len; + + if (defer) { + nfs_mark_request_commit(req, NULL, &cinfo, 0); + continue; + } + + nfs_lock_request(req); + if (nfs_pageio_add_request(&desc, req)) + continue; + + /* Exit on hard errors */ + if (desc.pg_error < 0 && desc.pg_error != -EAGAIN) { + result = desc.pg_error; + nfs_unlock_and_release_request(req); + break; + } + + /* If the error is soft, defer remaining requests */ + nfs_init_cinfo_from_dreq(&cinfo, dreq); + spin_lock(&dreq->lock); + dreq->flags = NFS_ODIRECT_RESCHED_WRITES; + spin_unlock(&dreq->lock); + nfs_unlock_request(req); + nfs_mark_request_commit(req, NULL, &cinfo, 0); + desc.pg_error = 0; + defer = true; } nfs_direct_release_pages(pagevec, npages); kvfree(pagevec); diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c index 7deb3cd76abe4b623c7d79554d7c9d7506491d1b..a1dc338649062de39c72230d3a1b11f8564033bb 100644 --- a/fs/nfs/flexfilelayout/flexfilelayout.c +++ b/fs/nfs/flexfilelayout/flexfilelayout.c @@ -1235,6 +1235,7 @@ static void ff_layout_io_track_ds_error(struct pnfs_layout_segment *lseg, case -EPFNOSUPPORT: case -EPROTONOSUPPORT: case -EOPNOTSUPP: + case -EINVAL: case -ECONNREFUSED: case -ECONNRESET: case -EHOSTDOWN: diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 27fb25567ce755a54e16e487379a3cd7a4f62824..11e3a285594c231c6b887dc53edbe52ef8519b23 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c @@ -417,6 +417,8 @@ static void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old) .net = old->cl_net, .servername = old->cl_hostname, }; + int max_connect = test_bit(NFS_CS_PNFS, &clp->cl_flags) ? + clp->cl_max_connect : old->cl_max_connect; if (clp->cl_proto != old->cl_proto) return; @@ -430,7 +432,7 @@ static void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old) xprt_args.addrlen = clp_salen; rpc_clnt_add_xprt(old->cl_rpcclient, &xprt_args, - rpc_clnt_test_and_add_xprt, NULL); + rpc_clnt_test_and_add_xprt, &max_connect); } /** @@ -1010,6 +1012,8 @@ struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); __set_bit(NFS_CS_DS, &cl_init.init_flags); + __set_bit(NFS_CS_PNFS, &cl_init.init_flags); + cl_init.max_connect = NFS_MAX_TRANSPORTS; /* * Set an authflavor equual to the MDS value. Use the MDS nfs_client * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 794343790ea8bbba5493f0374d5297c45f87dd4f..7016eaadf5554071cb2d1a45f60b7434ed8b1d94 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2703,8 +2703,12 @@ static int _nfs4_proc_open(struct nfs4_opendata *data, return status; } if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) { + struct nfs_fh *fh = &o_res->fh; + nfs4_sequence_free_slot(&o_res->seq_res); - nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, NULL); + if (o_arg->claim == NFS4_OPEN_CLAIM_FH) + fh = NFS_FH(d_inode(data->dentry)); + nfs4_proc_getattr(server, fh, o_res->f_attr, NULL); } return 0; } @@ -10618,7 +10622,9 @@ static void nfs4_disable_swap(struct inode *inode) */ struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; - nfs4_schedule_state_manager(clp); + set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); + clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); + wake_up_var(&clp->cl_state); } static const struct inode_operations nfs4_dir_inode_operations = { diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index e079987af4a3e41a5589e84f6f19853b5b5bc402..9a5d911a7edc77cae53cac45aa43f196b32caf20 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1209,16 +1209,26 @@ void nfs4_schedule_state_manager(struct nfs_client *clp) { struct task_struct *task; char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1]; + struct rpc_clnt *clnt = clp->cl_rpcclient; + bool swapon = false; - if (clp->cl_rpcclient->cl_shutdown) + if (clnt->cl_shutdown) return; set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state); - if (test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) != 0) { - wake_up_var(&clp->cl_state); - return; + + if (atomic_read(&clnt->cl_swapper)) { + swapon = !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, + &clp->cl_state); + if (!swapon) { + wake_up_var(&clp->cl_state); + return; + } } - set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state); + + if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) + return; + __module_get(THIS_MODULE); refcount_inc(&clp->cl_count); @@ -1235,8 +1245,9 @@ void nfs4_schedule_state_manager(struct nfs_client *clp) __func__, PTR_ERR(task)); if (!nfs_client_init_is_complete(clp)) nfs_mark_client_ready(clp, PTR_ERR(task)); + if (swapon) + clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); nfs4_clear_state_manager_bit(clp); - clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); nfs_put_client(clp); module_put(THIS_MODULE); } @@ -2703,6 +2714,13 @@ static void nfs4_state_manager(struct nfs_client *clp) nfs4_end_drain_session(clp); nfs4_clear_state_manager_bit(clp); + if (test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) && + !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, + &clp->cl_state)) { + memflags = memalloc_nofs_save(); + continue; + } + if (!test_and_set_bit(NFS4CLNT_RECALL_RUNNING, &clp->cl_state)) { if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) { nfs_client_return_marked_delegations(clp); @@ -2741,22 +2759,25 @@ static int nfs4_run_state_manager(void *ptr) allow_signal(SIGKILL); again: - set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state); nfs4_state_manager(clp); - if (atomic_read(&cl->cl_swapper)) { + + if (test_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state) && + !test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) { wait_var_event_interruptible(&clp->cl_state, test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state)); - if (atomic_read(&cl->cl_swapper) && - test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state)) + if (!atomic_read(&cl->cl_swapper)) + clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); + if (refcount_read(&clp->cl_count) > 1 && !signalled() && + !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) goto again; /* Either no longer a swapper, or were signalled */ + clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); } - clear_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state); if (refcount_read(&clp->cl_count) > 1 && !signalled() && test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state) && - !test_and_set_bit(NFS4CLNT_MANAGER_AVAILABLE, &clp->cl_state)) + !test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state)) goto again; nfs_put_client(clp); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index f4cca8f00c0c20f6906e4e2e08ea19fc1a692e00..7720b5e43014b4ecd5eccfa623a2f2c0805ee3ca 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -59,7 +59,8 @@ static const struct nfs_pgio_completion_ops nfs_async_write_completion_ops; static const struct nfs_commit_completion_ops nfs_commit_completion_ops; static const struct nfs_rw_ops nfs_rw_write_ops; static void nfs_inode_remove_request(struct nfs_page *req); -static void nfs_clear_request_commit(struct nfs_page *req); +static void nfs_clear_request_commit(struct nfs_commit_info *cinfo, + struct nfs_page *req); static void nfs_init_cinfo_from_inode(struct nfs_commit_info *cinfo, struct inode *inode); static struct nfs_page * @@ -502,8 +503,8 @@ nfs_destroy_unlinked_subrequests(struct nfs_page *destroy_list, * the (former) group. All subrequests are removed from any write or commit * lists, unlinked from the group and destroyed. */ -void -nfs_join_page_group(struct nfs_page *head, struct inode *inode) +void nfs_join_page_group(struct nfs_page *head, struct nfs_commit_info *cinfo, + struct inode *inode) { struct nfs_page *subreq; struct nfs_page *destroy_list = NULL; @@ -533,7 +534,7 @@ nfs_join_page_group(struct nfs_page *head, struct inode *inode) * Commit list removal accounting is done after locks are dropped */ subreq = head; do { - nfs_clear_request_commit(subreq); + nfs_clear_request_commit(cinfo, subreq); subreq = subreq->wb_this_page; } while (subreq != head); @@ -566,8 +567,10 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio) { struct inode *inode = folio_file_mapping(folio)->host; struct nfs_page *head; + struct nfs_commit_info cinfo; int ret; + nfs_init_cinfo_from_inode(&cinfo, inode); /* * A reference is taken only on the head request which acts as a * reference to the whole page group - the group will not be destroyed @@ -584,7 +587,7 @@ static struct nfs_page *nfs_lock_and_join_requests(struct folio *folio) return ERR_PTR(ret); } - nfs_join_page_group(head, inode); + nfs_join_page_group(head, &cinfo, inode); return head; } @@ -799,8 +802,8 @@ static void nfs_inode_remove_request(struct nfs_page *req) } if (test_and_clear_bit(PG_INODE_REF, &req->wb_flags)) { - nfs_release_request(req); atomic_long_dec(&NFS_I(nfs_page_to_inode(req))->nrequests); + nfs_release_request(req); } } @@ -955,18 +958,16 @@ static void nfs_folio_clear_commit(struct folio *folio) } /* Called holding the request lock on @req */ -static void -nfs_clear_request_commit(struct nfs_page *req) +static void nfs_clear_request_commit(struct nfs_commit_info *cinfo, + struct nfs_page *req) { if (test_bit(PG_CLEAN, &req->wb_flags)) { struct nfs_open_context *ctx = nfs_req_openctx(req); struct inode *inode = d_inode(ctx->dentry); - struct nfs_commit_info cinfo; - nfs_init_cinfo_from_inode(&cinfo, inode); mutex_lock(&NFS_I(inode)->commit_mutex); - if (!pnfs_clear_request_commit(req, &cinfo)) { - nfs_request_remove_commit_list(req, &cinfo); + if (!pnfs_clear_request_commit(req, cinfo)) { + nfs_request_remove_commit_list(req, cinfo); } mutex_unlock(&NFS_I(inode)->commit_mutex); nfs_folio_clear_commit(nfs_page_to_folio(req)); diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 5ca748309c262b1a82eb41981d7bc76e2520f65a..4199ede0583c7d903939e60cde9b3365a2e4bef1 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1058,8 +1058,8 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, rename->rn_tname, rename->rn_tnamelen); if (status) return status; - set_change_info(&rename->rn_sinfo, &cstate->current_fh); - set_change_info(&rename->rn_tinfo, &cstate->save_fh); + set_change_info(&rename->rn_sinfo, &cstate->save_fh); + set_change_info(&rename->rn_tinfo, &cstate->current_fh); return nfs_ok; } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 2e40c74d2f7278188e1ed0e124420db0428f132d..92c7dde148a4d81f3e29a4efbd480c9cffa03e3a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -4113,6 +4113,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, struct file *file, unsigned long maxcount) { struct xdr_stream *xdr = resp->xdr; + unsigned int base = xdr->buf->page_len & ~PAGE_MASK; unsigned int starting_len = xdr->buf->len; __be32 zero = xdr_zero; __be32 nfserr; @@ -4121,8 +4122,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, return nfserr_resource; nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, file, - read->rd_offset, &maxcount, - xdr->buf->page_len & ~PAGE_MASK, + read->rd_offset, &maxcount, base, &read->rd_eof); read->rd_length = maxcount; if (nfserr) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 1582af33e204a192989970b4548dd955f2d7705b..c7af1095f6b549f67e1e04b341b013bdedf6eee2 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -1082,11 +1082,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file) int nfsd_pool_stats_release(struct inode *inode, struct file *file) { + struct seq_file *seq = file->private_data; + struct svc_serv *serv = seq->private; int ret = seq_release(inode, file); - struct net *net = inode->i_sb->s_fs_info; mutex_lock(&nfsd_mutex); - nfsd_put(net); + svc_put(serv); mutex_unlock(&nfsd_mutex); return ret; } diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index 48fe71d309cb4ac381ae247d16f2cbfbf1c28bea..8beb2730929d43578ad9b22a721201bb4540237d 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -73,10 +73,8 @@ int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff, struct the_nilfs *nilfs = inode->i_sb->s_fs_info; err = nilfs_dat_translate(nilfs->ns_dat, vbn, &pbn); - if (unlikely(err)) { /* -EIO, -ENOMEM, -ENOENT */ - brelse(bh); + if (unlikely(err)) /* -EIO, -ENOMEM, -ENOENT */ goto failed; - } } lock_buffer(bh); @@ -102,6 +100,8 @@ int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff, failed: unlock_page(bh->b_page); put_page(bh->b_page); + if (unlikely(err)) + brelse(bh); return err; } diff --git a/fs/nls/Kconfig b/fs/nls/Kconfig index a0d0e2f7ec83465df4792ff71a99ab8798857324..2a601af6f3bd5e4d9fdd958536db5b3269866d53 100644 --- a/fs/nls/Kconfig +++ b/fs/nls/Kconfig @@ -618,11 +618,6 @@ config NLS_UTF8 the Unicode/ISO9646 universal character set. config NLS_UCS2_UTILS - tristate "NLS UCS-2 UTILS" - help - Set of older UCS-2 conversion utilities and tables used by some - filesystems including SMB/CIFS. This includes upper case conversion - tables. This will automatically be selected when the filesystem - that uses it is selected. + tristate endif # NLS diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 5fffddea554f1855fd993790cd2a758e15b440c6..5661a363005ee09d5cf1f453658bd5c0415dd43f 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -571,12 +571,8 @@ static void init_once(void *foo) /* * Noinline to reduce binary size. */ -static noinline void ntfs3_free_sbi(struct ntfs_sb_info *sbi) +static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi) { - kfree(sbi->new_rec); - kvfree(ntfs_put_shared(sbi->upcase)); - kfree(sbi->def_table); - wnd_close(&sbi->mft.bitmap); wnd_close(&sbi->used.bitmap); @@ -601,6 +597,13 @@ static noinline void ntfs3_free_sbi(struct ntfs_sb_info *sbi) indx_clear(&sbi->security.index_sdh); indx_clear(&sbi->reparse.index_r); indx_clear(&sbi->objid.index_o); +} + +static void ntfs3_free_sbi(struct ntfs_sb_info *sbi) +{ + kfree(sbi->new_rec); + kvfree(ntfs_put_shared(sbi->upcase)); + kfree(sbi->def_table); kfree(sbi->compress.lznt); #ifdef CONFIG_NTFS3_LZX_XPRESS xpress_free_decompressor(sbi->compress.xpress); @@ -625,6 +628,7 @@ static void ntfs_put_super(struct super_block *sb) /* Mark rw ntfs as clear, if possible. */ ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); + ntfs3_put_sbi(sbi); } static int ntfs_statfs(struct dentry *dentry, struct kstatfs *buf) @@ -1558,6 +1562,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc) put_inode_out: iput(inode); out: + ntfs3_put_sbi(sbi); kfree(boot2); return err; } @@ -1644,8 +1649,10 @@ static void ntfs_fs_free(struct fs_context *fc) struct ntfs_mount_options *opts = fc->fs_private; struct ntfs_sb_info *sbi = fc->s_fs_info; - if (sbi) + if (sbi) { + ntfs3_put_sbi(sbi); ntfs3_free_sbi(sbi); + } if (opts) put_mount_options(opts); diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index bae404a1bad48f8fa3611986a8bb77c77b2d8d32..ada3fcc9c6d5015ac65929ecf6738720b51615d6 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -337,7 +337,7 @@ static int ovl_set_timestamps(struct ovl_fs *ofs, struct dentry *upperdentry, { struct iattr attr = { .ia_valid = - ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET, + ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_CTIME, .ia_atime = stat->atime, .ia_mtime = stat->mtime, }; @@ -618,7 +618,8 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp) if (err) return err; - if (inode->i_flags & OVL_COPY_I_FLAGS_MASK) { + if (inode->i_flags & OVL_COPY_I_FLAGS_MASK && + (S_ISREG(c->stat.mode) || S_ISDIR(c->stat.mode))) { /* * Copy the fileattr inode flags that are the source of already * copied i_flags diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index c8c8588bd98caf4a29cd31d9e0e873de70fcec28..26b782c53910b538cb26d770fdb6b9416d0f19dd 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -188,7 +188,7 @@ static int ovl_check_encode_origin(struct dentry *dentry) /* Lower file handle for non-upper non-decodable */ if (!ovl_dentry_upper(dentry) && !decodable) - return 0; + return 1; /* Upper file handle for pure upper */ if (!ovl_dentry_lower(dentry)) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 3b4cc633d76375e47a9811ac75d497b3959a7a04..8be4dc050d1ed2e5508f62e84be2d5a835990d36 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -19,7 +19,6 @@ struct ovl_aio_req { struct kiocb iocb; refcount_t ref; struct kiocb *orig_iocb; - struct fd fd; }; static struct kmem_cache *ovl_aio_request_cachep; @@ -280,7 +279,7 @@ static rwf_t ovl_iocb_to_rwf(int ifl) static inline void ovl_aio_put(struct ovl_aio_req *aio_req) { if (refcount_dec_and_test(&aio_req->ref)) { - fdput(aio_req->fd); + fput(aio_req->iocb.ki_filp); kmem_cache_free(ovl_aio_request_cachep, aio_req); } } @@ -342,10 +341,8 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) if (!aio_req) goto out; - aio_req->fd = real; - real.flags = 0; aio_req->orig_iocb = iocb; - kiocb_clone(&aio_req->iocb, iocb, real.file); + kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); aio_req->iocb.ki_complete = ovl_aio_rw_complete; refcount_set(&aio_req->ref, 2); ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter); @@ -393,6 +390,12 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) if (!ovl_should_sync(OVL_FS(inode->i_sb))) ifl &= ~(IOCB_DSYNC | IOCB_SYNC); + /* + * Overlayfs doesn't support deferred completions, don't copy + * this property in case it is set by the issuer. + */ + ifl &= ~IOCB_DIO_CALLER_COMP; + old_cred = ovl_override_creds(file_inode(file)->i_sb); if (is_sync_kiocb(iocb)) { file_start_write(real.file); @@ -409,10 +412,8 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) if (!aio_req) goto out; - aio_req->fd = real; - real.flags = 0; aio_req->orig_iocb = iocb; - kiocb_clone(&aio_req->iocb, iocb, real.file); + kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); aio_req->iocb.ki_flags = ifl; aio_req->iocb.ki_complete = ovl_aio_rw_complete; refcount_set(&aio_req->ref, 2); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index e9539f98e86a4b29d0c54dc1859ea13d37cd46f2..d82d2a043da2c7b7ea055a5c7dbbe0b3c2544dbc 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -8,6 +8,7 @@ struct ovl_config { char *upperdir; char *workdir; + char **lowerdirs; bool default_permissions; int redirect_mode; int verity_mode; @@ -39,17 +40,8 @@ struct ovl_layer { int idx; /* One fsid per unique underlying sb (upper fsid == 0) */ int fsid; - char *name; }; -/* - * ovl_free_fs() relies on @mnt being the first member when unmounting - * the private mounts created for each layer. Let's check both the - * offset and type. - */ -static_assert(offsetof(struct ovl_layer, mnt) == 0); -static_assert(__same_type(typeof_member(struct ovl_layer, mnt), struct vfsmount *)); - struct ovl_path { const struct ovl_layer *layer; struct dentry *dentry; diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c index b9355bb6d75a33128c8ad84903dd0358b4edcd96..95b751507ac87f15733f2f38a05bf418e7b62093 100644 --- a/fs/overlayfs/params.c +++ b/fs/overlayfs/params.c @@ -752,12 +752,12 @@ void ovl_free_fs(struct ovl_fs *ofs) if (ofs->upperdir_locked) ovl_inuse_unlock(ovl_upper_mnt(ofs)->mnt_root); - /* Hack! Reuse ofs->layers as a vfsmount array before freeing it */ - mounts = (struct vfsmount **) ofs->layers; + /* Reuse ofs->config.lowerdirs as a vfsmount array before freeing it */ + mounts = (struct vfsmount **) ofs->config.lowerdirs; for (i = 0; i < ofs->numlayer; i++) { iput(ofs->layers[i].trap); + kfree(ofs->config.lowerdirs[i]); mounts[i] = ofs->layers[i].mnt; - kfree(ofs->layers[i].name); } kern_unmount_array(mounts, ofs->numlayer); kfree(ofs->layers); @@ -765,6 +765,7 @@ void ovl_free_fs(struct ovl_fs *ofs) free_anon_bdev(ofs->fs[i].pseudo_dev); kfree(ofs->fs); + kfree(ofs->config.lowerdirs); kfree(ofs->config.upperdir); kfree(ofs->config.workdir); if (ofs->creator_cred) @@ -949,16 +950,16 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry) struct super_block *sb = dentry->d_sb; struct ovl_fs *ofs = OVL_FS(sb); size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer; - const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower]; + char **lowerdatadirs = &ofs->config.lowerdirs[nr_merged_lower]; - /* ofs->layers[0] is the upper layer */ - seq_printf(m, ",lowerdir=%s", ofs->layers[1].name); + /* lowerdirs[] starts from offset 1 */ + seq_printf(m, ",lowerdir=%s", ofs->config.lowerdirs[1]); /* dump regular lower layers */ for (nr = 2; nr < nr_merged_lower; nr++) - seq_printf(m, ":%s", ofs->layers[nr].name); + seq_printf(m, ":%s", ofs->config.lowerdirs[nr]); /* dump data lower layers */ for (nr = 0; nr < ofs->numdatalayer; nr++) - seq_printf(m, "::%s", data_layers[nr].name); + seq_printf(m, "::%s", lowerdatadirs[nr]); if (ofs->config.upperdir) { seq_show_option(m, "upperdir", ofs->config.upperdir); seq_show_option(m, "workdir", ofs->config.workdir); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index def266b5e2a33b0aa630662286827227f952e513..3fa2416264a4e67b9bccd84700224ca6f484102f 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -104,8 +104,8 @@ static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak) static int ovl_dentry_revalidate_common(struct dentry *dentry, unsigned int flags, bool weak) { - struct ovl_entry *oe = OVL_E(dentry); - struct ovl_path *lowerstack = ovl_lowerstack(oe); + struct ovl_entry *oe; + struct ovl_path *lowerstack; struct inode *inode = d_inode_rcu(dentry); struct dentry *upper; unsigned int i; @@ -115,6 +115,8 @@ static int ovl_dentry_revalidate_common(struct dentry *dentry, if (!inode) return -ECHILD; + oe = OVL_I_E(inode); + lowerstack = ovl_lowerstack(oe); upper = ovl_i_dentry_upper(inode); if (upper) ret = ovl_revalidate_real(upper, flags, weak); @@ -167,6 +169,7 @@ static void ovl_free_inode(struct inode *inode) struct ovl_inode *oi = OVL_I(inode); kfree(oi->redirect); + kfree(oi->oe); mutex_destroy(&oi->lock); kmem_cache_free(ovl_inode_cachep, oi); } @@ -176,7 +179,7 @@ static void ovl_destroy_inode(struct inode *inode) struct ovl_inode *oi = OVL_I(inode); dput(oi->__upperdentry); - ovl_free_entry(oi->oe); + ovl_stack_put(ovl_lowerstack(oi->oe), ovl_numlower(oi->oe)); if (S_ISDIR(inode->i_mode)) ovl_dir_cache_free(inode); else @@ -569,11 +572,6 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, upper_layer->idx = 0; upper_layer->fsid = 0; - err = -ENOMEM; - upper_layer->name = kstrdup(ofs->config.upperdir, GFP_KERNEL); - if (!upper_layer->name) - goto out; - /* * Inherit SB_NOSEC flag from upperdir. * @@ -1122,7 +1120,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, layers[ofs->numlayer].idx = ofs->numlayer; layers[ofs->numlayer].fsid = fsid; layers[ofs->numlayer].fs = &ofs->fs[fsid]; - layers[ofs->numlayer].name = l->name; + /* Store for printing lowerdir=... in ovl_show_options() */ + ofs->config.lowerdirs[ofs->numlayer] = l->name; l->name = NULL; ofs->numlayer++; ofs->fs[fsid].is_lower = true; @@ -1367,8 +1366,16 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) if (!layers) goto out_err; + ofs->config.lowerdirs = kcalloc(ctx->nr + 1, sizeof(char *), GFP_KERNEL); + if (!ofs->config.lowerdirs) { + kfree(layers); + goto out_err; + } ofs->layers = layers; - /* Layer 0 is reserved for upper even if there's no upper */ + /* + * Layer 0 is reserved for upper even if there's no upper. + * For consistency, config.lowerdirs[0] is NULL. + */ ofs->numlayer = 1; sb->s_stack_depth = 0; diff --git a/fs/pipe.c b/fs/pipe.c index 6c1a9b1db9076c8c1402b7368a25ca7d9a894c70..139190165a1c2231ebb90fb364ee8bdb299b2b16 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -537,7 +537,6 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) break; } ret += copied; - buf->offset = 0; buf->len = copied; if (!iov_iter_count(from)) diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 9dda7e54b2d0d98766b940701dd47a5034f2f463..9a8f32f21ff569d0dc40e1d7c31b63b9aea293bc 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -289,9 +289,7 @@ struct proc_maps_private { struct inode *inode; struct task_struct *task; struct mm_struct *mm; -#ifdef CONFIG_MMU struct vma_iterator iter; -#endif #ifdef CONFIG_NUMA struct mempolicy *task_mempolicy; #endif diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 0f2aa36a9913b587dfeb93e5feba167f085199ce..3dd5be96691b4cc234454353ec1b8a324760e0c2 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -396,6 +397,7 @@ struct mem_size_stats { unsigned long swap; unsigned long shared_hugetlb; unsigned long private_hugetlb; + unsigned long ksm; u64 pss; u64 pss_anon; u64 pss_file; @@ -452,6 +454,9 @@ static void smaps_account(struct mem_size_stats *mss, struct page *page, mss->lazyfree += size; } + if (PageKsm(page)) + mss->ksm += size; + mss->resident += size; /* Accumulate the size in pages that have been accessed. */ if (young || page_is_young(page) || PageReferenced(page)) @@ -825,6 +830,7 @@ static void __show_smap(struct seq_file *m, const struct mem_size_stats *mss, SEQ_PUT_DEC(" kB\nPrivate_Dirty: ", mss->private_dirty); SEQ_PUT_DEC(" kB\nReferenced: ", mss->referenced); SEQ_PUT_DEC(" kB\nAnonymous: ", mss->anonymous); + SEQ_PUT_DEC(" kB\nKSM: ", mss->ksm); SEQ_PUT_DEC(" kB\nLazyFree: ", mss->lazyfree); SEQ_PUT_DEC(" kB\nAnonHugePages: ", mss->anonymous_thp); SEQ_PUT_DEC(" kB\nShmemPmdMapped: ", mss->shmem_thp); diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index a8ac0dd8041ebca2178ab245c6337a3b9db2cbe6..7cebd397cc26e1056d3533643c98f87a2ae5fd79 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -175,15 +175,28 @@ static int show_map(struct seq_file *m, void *_p) return nommu_vma_show(m, _p); } -static void *m_start(struct seq_file *m, loff_t *pos) +static struct vm_area_struct *proc_get_vma(struct proc_maps_private *priv, + loff_t *ppos) +{ + struct vm_area_struct *vma = vma_next(&priv->iter); + + if (vma) { + *ppos = vma->vm_start; + } else { + *ppos = -1UL; + } + + return vma; +} + +static void *m_start(struct seq_file *m, loff_t *ppos) { struct proc_maps_private *priv = m->private; + unsigned long last_addr = *ppos; struct mm_struct *mm; - struct vm_area_struct *vma; - unsigned long addr = *pos; - /* See m_next(). Zero at the start or after lseek. */ - if (addr == -1UL) + /* See proc_get_vma(). Zero at the start or after lseek. */ + if (last_addr == -1UL) return NULL; /* pin the task and mm whilst we play with them */ @@ -192,44 +205,41 @@ static void *m_start(struct seq_file *m, loff_t *pos) return ERR_PTR(-ESRCH); mm = priv->mm; - if (!mm || !mmget_not_zero(mm)) + if (!mm || !mmget_not_zero(mm)) { + put_task_struct(priv->task); + priv->task = NULL; return NULL; + } if (mmap_read_lock_killable(mm)) { mmput(mm); + put_task_struct(priv->task); + priv->task = NULL; return ERR_PTR(-EINTR); } - /* start the next element from addr */ - vma = find_vma(mm, addr); - if (vma) - return vma; + vma_iter_init(&priv->iter, mm, last_addr); - mmap_read_unlock(mm); - mmput(mm); - return NULL; + return proc_get_vma(priv, ppos); } -static void m_stop(struct seq_file *m, void *_vml) +static void m_stop(struct seq_file *m, void *v) { struct proc_maps_private *priv = m->private; + struct mm_struct *mm = priv->mm; - if (!IS_ERR_OR_NULL(_vml)) { - mmap_read_unlock(priv->mm); - mmput(priv->mm); - } - if (priv->task) { - put_task_struct(priv->task); - priv->task = NULL; - } + if (!priv->task) + return; + + mmap_read_unlock(mm); + mmput(mm); + put_task_struct(priv->task); + priv->task = NULL; } -static void *m_next(struct seq_file *m, void *_p, loff_t *pos) +static void *m_next(struct seq_file *m, void *_p, loff_t *ppos) { - struct vm_area_struct *vma = _p; - - *pos = vma->vm_end; - return find_vma(vma->vm_mm, vma->vm_end); + return proc_get_vma(m->private, ppos); } static const struct seq_operations proc_pid_maps_ops = { diff --git a/fs/pstore/platform.c b/fs/pstore/platform.c index 62356d542ef67f6062dc423181acecc5b6d0b32f..e5bca9a004cccd46c8dfd0e251c79aa207ce3550 100644 --- a/fs/pstore/platform.c +++ b/fs/pstore/platform.c @@ -98,7 +98,14 @@ MODULE_PARM_DESC(kmsg_bytes, "amount of kernel log to snapshot (in bytes)"); static void *compress_workspace; +/* + * Compression is only used for dmesg output, which consists of low-entropy + * ASCII text, and so we can assume worst-case 60%. + */ +#define DMESG_COMP_PERCENT 60 + static char *big_oops_buf; +static size_t max_compressed_size; void pstore_set_kmsg_bytes(int bytes) { @@ -196,6 +203,7 @@ static int pstore_compress(const void *in, void *out, static void allocate_buf_for_compression(void) { + size_t compressed_size; char *buf; /* Skip if not built-in or compression disabled. */ @@ -216,7 +224,8 @@ static void allocate_buf_for_compression(void) * uncompressed record size, since any record that would be expanded by * compression is just stored uncompressed. */ - buf = kvzalloc(psinfo->bufsize, GFP_KERNEL); + compressed_size = (psinfo->bufsize * 100) / DMESG_COMP_PERCENT; + buf = kvzalloc(compressed_size, GFP_KERNEL); if (!buf) { pr_err("Failed %zu byte compression buffer allocation for: %s\n", psinfo->bufsize, compress); @@ -233,6 +242,7 @@ static void allocate_buf_for_compression(void) /* A non-NULL big_oops_buf indicates compression is available. */ big_oops_buf = buf; + max_compressed_size = compressed_size; pr_info("Using crash dump compression: %s\n", compress); } @@ -246,6 +256,7 @@ static void free_buf_for_compression(void) kvfree(big_oops_buf); big_oops_buf = NULL; + max_compressed_size = 0; } void pstore_record_init(struct pstore_record *record, @@ -305,7 +316,7 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.buf = psinfo->buf; dst = big_oops_buf ?: psinfo->buf; - dst_size = psinfo->bufsize; + dst_size = max_compressed_size ?: psinfo->bufsize; /* Write dump header. */ header_size = snprintf(dst, dst_size, "%s#%d Part%u\n", why, @@ -326,8 +337,15 @@ static void pstore_dump(struct kmsg_dumper *dumper, record.compressed = true; record.size = zipped_len; } else { - record.size = header_size + dump_size; - memcpy(psinfo->buf, dst, record.size); + /* + * Compression failed, so the buffer is most + * likely filled with binary data that does not + * compress as well as ASCII text. Copy as much + * of the uncompressed data as possible into + * the pstore record, and discard the rest. + */ + record.size = psinfo->bufsize; + memcpy(psinfo->buf, dst, psinfo->bufsize); } } else { record.size = header_size + dump_size; @@ -560,6 +578,7 @@ static void decompress_record(struct pstore_record *record, int ret; int unzipped_len; char *unzipped, *workspace; + size_t max_uncompressed_size; if (!IS_ENABLED(CONFIG_PSTORE_COMPRESS) || !record->compressed) return; @@ -583,7 +602,8 @@ static void decompress_record(struct pstore_record *record, } /* Allocate enough space to hold max decompression and ECC. */ - workspace = kvzalloc(psinfo->bufsize + record->ecc_notice_size, + max_uncompressed_size = 3 * psinfo->bufsize; + workspace = kvzalloc(max_uncompressed_size + record->ecc_notice_size, GFP_KERNEL); if (!workspace) return; @@ -591,11 +611,11 @@ static void decompress_record(struct pstore_record *record, zstream->next_in = record->buf; zstream->avail_in = record->size; zstream->next_out = workspace; - zstream->avail_out = psinfo->bufsize; + zstream->avail_out = max_uncompressed_size; ret = zlib_inflate(zstream, Z_FINISH); if (ret != Z_STREAM_END) { - pr_err("zlib_inflate() failed, ret = %d!\n", ret); + pr_err_ratelimited("zlib_inflate() failed, ret = %d!\n", ret); kvfree(workspace); return; } diff --git a/fs/reiserfs/reiserfs.h b/fs/reiserfs/reiserfs.h index b81749492ef98e61bd22218c585864d9a9986953..7d12b8c5b2fa8c73929f7c9bd79d5085e7b1835f 100644 --- a/fs/reiserfs/reiserfs.h +++ b/fs/reiserfs/reiserfs.h @@ -2699,7 +2699,7 @@ struct reiserfs_iget_args { #define get_journal_desc_magic(bh) (bh->b_data + bh->b_size - 12) #define journal_trans_half(blocksize) \ - ((blocksize - sizeof (struct reiserfs_journal_desc) + sizeof (__u32) - 12) / sizeof (__u32)) + ((blocksize - sizeof(struct reiserfs_journal_desc) - 12) / sizeof(__u32)) /* journal.c see journal.c for all the comments here */ @@ -2711,7 +2711,7 @@ struct reiserfs_journal_desc { __le32 j_len; __le32 j_mount_id; /* mount id of this trans */ - __le32 j_realblock[1]; /* real locations for each block */ + __le32 j_realblock[]; /* real locations for each block */ }; #define get_desc_trans_id(d) le32_to_cpu((d)->j_trans_id) @@ -2726,7 +2726,7 @@ struct reiserfs_journal_desc { struct reiserfs_journal_commit { __le32 j_trans_id; /* must match j_trans_id from the desc block */ __le32 j_len; /* ditto */ - __le32 j_realblock[1]; /* real locations for each block */ + __le32 j_realblock[]; /* real locations for each block */ }; #define get_commit_trans_id(c) le32_to_cpu((c)->j_trans_id) diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index 2d5e9a9d5b8be31e7cf7db6fc34ecb69fcaf1934..e2be8aedb26e3a4c2a8655c9d020f76dffc71dd4 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -18,7 +18,8 @@ static void smb2_close_cached_fid(struct kref *ref); static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, const char *path, - bool lookup_only) + bool lookup_only, + __u32 max_cached_dirs) { struct cached_fid *cfid; @@ -43,7 +44,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, spin_unlock(&cfids->cfid_list_lock); return NULL; } - if (cfids->num_entries >= MAX_CACHED_FIDS) { + if (cfids->num_entries >= max_cached_dirs) { spin_unlock(&cfids->cfid_list_lock); return NULL; } @@ -145,7 +146,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, const char *npath; if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache || - is_smb1_server(tcon->ses->server)) + is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0)) return -EOPNOTSUPP; ses = tcon->ses; @@ -162,7 +163,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon, if (!utf16_path) return -ENOMEM; - cfid = find_or_create_cached_dir(cfids, path, lookup_only); + cfid = find_or_create_cached_dir(cfids, path, lookup_only, tcon->max_cached_dirs); if (cfid == NULL) { kfree(utf16_path); return -ENOENT; @@ -451,6 +452,9 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon) struct cached_fid *cfid, *q; LIST_HEAD(entry); + if (cfids == NULL) + return; + spin_lock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { list_move(&cfid->entry, &entry); @@ -582,7 +586,7 @@ cifs_cfids_laundromat_thread(void *p) return 0; spin_lock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &cfids->entries, entry) { - if (time_after(jiffies, cfid->time + HZ * 30)) { + if (time_after(jiffies, cfid->time + HZ * dir_cache_timeout)) { list_del(&cfid->entry); list_add(&cfid->entry, &entry); cfids->num_entries--; @@ -650,6 +654,9 @@ void free_cached_dirs(struct cached_fids *cfids) struct cached_fid *cfid, *q; LIST_HEAD(entry); + if (cfids == NULL) + return; + if (cfids->laundromat) { kthread_stop(cfids->laundromat); cfids->laundromat = NULL; diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h index facc9b154d0094cb954f79a0f199a6f80a578996..a82ff2cea789c68b1ecdaf70dc489b593ed7227a 100644 --- a/fs/smb/client/cached_dir.h +++ b/fs/smb/client/cached_dir.h @@ -49,7 +49,7 @@ struct cached_fid { struct cached_dirents dirents; }; -#define MAX_CACHED_FIDS 16 +/* default MAX_CACHED_FIDS is 16 */ struct cached_fids { /* Must be held when: * - accessing the cfids->entries list diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 73c44e097a69b27fdf2ffc6cb8f182833254caa1..22869cda13565e5838b7711c844963f1a6d87bd9 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -117,6 +117,10 @@ module_param(cifs_max_pending, uint, 0444); MODULE_PARM_DESC(cifs_max_pending, "Simultaneous requests to server for " "CIFS/SMB1 dialect (N/A for SMB3) " "Default: 32767 Range: 2 to 32767."); +unsigned int dir_cache_timeout = 30; +module_param(dir_cache_timeout, uint, 0644); +MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory contents for which we have a lease. Default: 30 " + "Range: 1 to 65000 seconds, 0 to disable caching dir contents"); #ifdef CONFIG_CIFS_STATS2 unsigned int slow_rsp_threshold = 1; module_param(slow_rsp_threshold, uint, 0644); @@ -695,6 +699,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_printf(s, ",snapshot=%llu", tcon->snapshot_time); if (tcon->handle_timeout) seq_printf(s, ",handletimeout=%u", tcon->handle_timeout); + if (tcon->max_cached_dirs != MAX_CACHED_FIDS) + seq_printf(s, ",max_cached_dirs=%u", tcon->max_cached_dirs); /* * Display file and directory attribute timeout in seconds. @@ -1679,6 +1685,12 @@ init_cifs(void) CIFS_MAX_REQ); } + /* Limit max to about 18 hours, and setting to zero disables directory entry caching */ + if (dir_cache_timeout > 65000) { + dir_cache_timeout = 65000; + cifs_dbg(VFS, "dir_cache_timeout set to max of 65000 seconds\n"); + } + cifsiod_wq = alloc_workqueue("cifsiod", WQ_FREEZABLE|WQ_MEM_RECLAIM, 0); if (!cifsiod_wq) { rc = -ENOMEM; diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 532c38fe07cd151e424b8565d3650fc06b2029dc..41daebd220ff422e5556c7555d0ed516bef737e3 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -152,6 +152,6 @@ extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ /* when changing internal version - update following two lines at same time */ -#define SMB3_PRODUCT_BUILD 44 -#define CIFS_VERSION "2.44" +#define SMB3_PRODUCT_BUILD 45 +#define CIFS_VERSION "2.45" #endif /* _CIFSFS_H */ diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 259e231f8b4f51f31d534676c4f55c8f5e0fe221..02082621d8e07a8682095516d94f440728a74f76 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1210,6 +1210,7 @@ struct cifs_tcon { __u32 max_chunks; __u32 max_bytes_chunk; __u32 max_bytes_copy; + __u32 max_cached_dirs; #ifdef CONFIG_CIFS_FSCACHE u64 resource_id; /* server resource id */ struct fscache_volume *fscache; /* cookie for share */ @@ -1806,6 +1807,7 @@ static inline bool is_retryable_error(int error) #define MID_RETRY_NEEDED 8 /* session closed while this request out */ #define MID_RESPONSE_MALFORMED 0x10 #define MID_SHUTDOWN 0x20 +#define MID_RESPONSE_READY 0x40 /* ready for other process handle the rsp */ /* Flags */ #define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */ @@ -1942,7 +1944,7 @@ require use of the stronger protocol */ * cifsInodeInfo->lock_sem cifsInodeInfo->llist cifs_init_once * ->can_cache_brlcks * cifsInodeInfo->deferred_lock cifsInodeInfo->deferred_closes cifsInodeInfo_alloc - * cached_fid->fid_mutex cifs_tcon->crfid tconInfoAlloc + * cached_fid->fid_mutex cifs_tcon->crfid tcon_info_alloc * cifsFileInfo->fh_mutex cifsFileInfo cifs_new_fileinfo * cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo * ->invalidHandle initiate_cifs_search @@ -2016,6 +2018,7 @@ extern unsigned int CIFSMaxBufSize; /* max size not including hdr */ extern unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ extern unsigned int cifs_min_small; /* min size of small buf pool */ extern unsigned int cifs_max_pending; /* MAX requests at once to server*/ +extern unsigned int dir_cache_timeout; /* max time for directory lease caching of dir */ extern bool disable_legacy_dialects; /* forbid vers=1.0 and vers=2.0 mounts */ extern atomic_t mid_count; diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h index 7d8035846680ab224ee02300d4c0ed804a197d8c..0c37eefa18a57c0ed7be7921033ca86eda86c144 100644 --- a/fs/smb/client/cifsproto.h +++ b/fs/smb/client/cifsproto.h @@ -512,7 +512,7 @@ extern int CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses); extern struct cifs_ses *sesInfoAlloc(void); extern void sesInfoFree(struct cifs_ses *); -extern struct cifs_tcon *tconInfoAlloc(void); +extern struct cifs_tcon *tcon_info_alloc(bool dir_leases_enabled); extern void tconInfoFree(struct cifs_tcon *); extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 3bd71f982170117b658a9fc3dfd98b4b5e36721c..7b923e36501b0bb09f7a1339004ab23f335510e0 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -1882,7 +1882,8 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx) } } - tcon = tconInfoAlloc(); + /* no need to setup directory caching on IPC share, so pass in false */ + tcon = tcon_info_alloc(false); if (tcon == NULL) return -ENOMEM; @@ -2473,8 +2474,9 @@ cifs_put_tcon(struct cifs_tcon *tcon) static struct cifs_tcon * cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) { - int rc, xid; struct cifs_tcon *tcon; + bool nohandlecache; + int rc, xid; tcon = cifs_find_tcon(ses, ctx); if (tcon) { @@ -2492,11 +2494,17 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) goto out_fail; } - tcon = tconInfoAlloc(); + if (ses->server->dialect >= SMB20_PROT_ID && + (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING)) + nohandlecache = ctx->nohandlecache; + else + nohandlecache = true; + tcon = tcon_info_alloc(!nohandlecache); if (tcon == NULL) { rc = -ENOMEM; goto out_fail; } + tcon->nohandlecache = nohandlecache; if (ctx->snapshot_time) { if (ses->server->vals->protocol_id == 0) { @@ -2657,10 +2665,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) tcon->retry = ctx->retry; tcon->nocase = ctx->nocase; tcon->broken_sparse_sup = ctx->no_sparse; - if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) - tcon->nohandlecache = ctx->nohandlecache; - else - tcon->nohandlecache = true; + tcon->max_cached_dirs = ctx->max_cached_dirs; tcon->nodelete = ctx->nodelete; tcon->local_lease = ctx->local_lease; INIT_LIST_HEAD(&tcon->pending_opens); @@ -2890,9 +2895,9 @@ bind_socket(struct TCP_Server_Info *server) if (server->srcaddr.ss_family != AF_UNSPEC) { /* Bind to the specified local IP address */ struct socket *socket = server->ssocket; - rc = socket->ops->bind(socket, - (struct sockaddr *) &server->srcaddr, - sizeof(server->srcaddr)); + rc = kernel_bind(socket, + (struct sockaddr *) &server->srcaddr, + sizeof(server->srcaddr)); if (rc < 0) { struct sockaddr_in *saddr4; struct sockaddr_in6 *saddr6; @@ -3041,8 +3046,8 @@ generic_ip_connect(struct TCP_Server_Info *server) socket->sk->sk_sndbuf, socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo); - rc = socket->ops->connect(socket, saddr, slen, - server->noblockcnt ? O_NONBLOCK : 0); + rc = kernel_connect(socket, saddr, slen, + server->noblockcnt ? O_NONBLOCK : 0); /* * When mounting SMB root file systems, we do not want to block in * connect. Otherwise bail out and then let cifs_reconnect() perform diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index 67e16c2ac90e6f7b2d34418cae4b0b936ccaf29f..a3493da12ad1e6cbac7249f3e8464cf7eeff542e 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -150,6 +150,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = { fsparam_u32("closetimeo", Opt_closetimeo), fsparam_u32("echo_interval", Opt_echo_interval), fsparam_u32("max_credits", Opt_max_credits), + fsparam_u32("max_cached_dirs", Opt_max_cached_dirs), fsparam_u32("handletimeout", Opt_handletimeout), fsparam_u64("snapshot", Opt_snapshot), fsparam_u32("max_channels", Opt_max_channels), @@ -1165,6 +1166,14 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, if (result.uint_32 > 1) ctx->multichannel = true; break; + case Opt_max_cached_dirs: + if (result.uint_32 < 1) { + cifs_errorf(fc, "%s: Invalid max_cached_dirs, needs to be 1 or more\n", + __func__); + goto cifs_parse_mount_err; + } + ctx->max_cached_dirs = result.uint_32; + break; case Opt_handletimeout: ctx->handle_timeout = result.uint_32; if (ctx->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) { @@ -1532,6 +1541,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, cifs_parse_mount_err: kfree_sensitive(ctx->password); + ctx->password = NULL; return -EINVAL; } @@ -1592,7 +1602,7 @@ int smb3_init_fs_context(struct fs_context *fc) ctx->acregmax = CIFS_DEF_ACTIMEO; ctx->acdirmax = CIFS_DEF_ACTIMEO; ctx->closetimeo = SMB3_DEF_DCLOSETIMEO; - + ctx->max_cached_dirs = MAX_CACHED_FIDS; /* Most clients set timeout to 0, allows server to use its default */ ctx->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */ diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index f4eaf855890222924c99a92f693f131b0c69cde7..9d8d34af021147f646cc6421fdbd210f9cbfa8f2 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -128,6 +128,7 @@ enum cifs_param { Opt_closetimeo, Opt_echo_interval, Opt_max_credits, + Opt_max_cached_dirs, Opt_snapshot, Opt_max_channels, Opt_handletimeout, @@ -261,6 +262,7 @@ struct smb3_fs_context { __u32 handle_timeout; /* persistent and durable handle timeout in ms */ unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ unsigned int max_channels; + unsigned int max_cached_dirs; __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */ bool rootfs:1; /* if it's a SMB root file system */ bool witness:1; /* use witness protocol */ @@ -287,7 +289,7 @@ extern void smb3_update_mnt_flags(struct cifs_sb_info *cifs_sb); */ #define SMB3_MAX_DCLOSETIMEO (1 << 30) #define SMB3_DEF_DCLOSETIMEO (1 * HZ) /* even 1 sec enough to help eg open/write/close/open/read */ - +#define MAX_CACHED_FIDS 16 extern char *cifs_sanitize_prepath(char *prepath, gfp_t gfp); #endif diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c index 3677525ee99311b4005224931ce4a07eef4fa2a1..e5cad149f5a2d7d3f12d53ef61f78332a828c367 100644 --- a/fs/smb/client/fscache.c +++ b/fs/smb/client/fscache.c @@ -48,7 +48,7 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) sharename = extract_sharename(tcon->tree_name); if (IS_ERR(sharename)) { cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); - return -EINVAL; + return PTR_ERR(sharename); } slen = strlen(sharename); diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index de2dfbaae821026dbdb585e2274efdc9308dfa6f..d7c302442c1ec876cb977850c74a161331ff8577 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2680,7 +2680,7 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, } cifsFileInfo_put(cfile); - return -ENOTSUPP; + return -EOPNOTSUPP; } int cifs_truncate_page(struct address_space *mapping, loff_t from) diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 366b755ca9130df5177bd92ea7d1f940c18cbf1f..35b176457bbed02d0df438e127dc2c0482e5143c 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -113,18 +113,22 @@ sesInfoFree(struct cifs_ses *buf_to_free) } struct cifs_tcon * -tconInfoAlloc(void) +tcon_info_alloc(bool dir_leases_enabled) { struct cifs_tcon *ret_buf; ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); if (!ret_buf) return NULL; - ret_buf->cfids = init_cached_dirs(); - if (!ret_buf->cfids) { - kfree(ret_buf); - return NULL; + + if (dir_leases_enabled == true) { + ret_buf->cfids = init_cached_dirs(); + if (!ret_buf->cfids) { + kfree(ret_buf); + return NULL; + } } + /* else ret_buf->cfids is already set to NULL above */ atomic_inc(&tconInfoAllocCount); ret_buf->status = TID_NEW; diff --git a/fs/smb/client/smb2inode.c b/fs/smb/client/smb2inode.c index b41e2e872b22462f2d0ee6ee14ecab0a3959654a..0b89f7008ac0f429cc1a6b004c70117e19658837 100644 --- a/fs/smb/client/smb2inode.c +++ b/fs/smb/client/smb2inode.c @@ -539,6 +539,9 @@ static int parse_create_response(struct cifs_open_info_data *data, int rc = 0; switch (rsp->hdr.Status) { + case STATUS_IO_REPARSE_TAG_NOT_HANDLED: + reparse_point = true; + break; case STATUS_STOPPED_ON_SYMLINK: rc = smb2_parse_symlink_response(cifs_sb, iov, &data->symlink_target); diff --git a/fs/smb/client/smb2maperror.c b/fs/smb/client/smb2maperror.c index 194799ddd38288847dec2b2a1b65f604ec4be322..1a90dd78b238f0de191421bd0d1838164bff9778 100644 --- a/fs/smb/client/smb2maperror.c +++ b/fs/smb/client/smb2maperror.c @@ -877,8 +877,6 @@ static const struct status_to_posix_error smb2_error_map_table[] = { "STATUS_IO_REPARSE_TAG_MISMATCH"}, {STATUS_IO_REPARSE_DATA_INVALID, -EIO, "STATUS_IO_REPARSE_DATA_INVALID"}, - {STATUS_IO_REPARSE_TAG_NOT_HANDLED, -EIO, - "STATUS_IO_REPARSE_TAG_NOT_HANDLED"}, {STATUS_REPARSE_POINT_NOT_RESOLVED, -EIO, "STATUS_REPARSE_POINT_NOT_RESOLVED"}, {STATUS_DIRECTORY_IS_A_REPARSE_POINT, -EIO, diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index e3dd698854d629a18165fd42b84084ab7b6f89ef..9aeecee6b91b356711d3f545119eba504e08c28e 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -297,7 +297,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server, cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", credits->value, new_val); - return -ENOTSUPP; + return -EOPNOTSUPP; } spin_lock(&server->req_lock); @@ -1161,7 +1161,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, /* Use a fudge factor of 256 bytes in case we collide * with a different set_EAs command. */ - if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - + if (CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < used_len + ea_name_len + ea_value_len + 1) { rc = -ENOSPC; @@ -2683,6 +2683,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, smb2_copy_fs_info_to_kstatfs(info, buf); qfs_exit: + trace_smb3_qfs_done(xid, tcon->tid, tcon->ses->Suid, tcon->tree_name, rc); free_rsp_buf(buftype, rsp_iov.iov_base); return rc; } @@ -4590,7 +4591,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, if (shdr->Command != SMB2_READ) { cifs_server_dbg(VFS, "only big read responses are supported\n"); - return -ENOTSUPP; + return -EOPNOTSUPP; } if (server->ops->is_session_expired && diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 092b0087c9dc14441d80cc197417c410a2d86f0d..c75a80bb6d9eefa8a76e8c03baea7af8b7740c25 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -89,20 +89,26 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, struct TCP_Server_Info *server) { struct smb3_hdr_req *smb3_hdr; + shdr->ProtocolId = SMB2_PROTO_NUMBER; shdr->StructureSize = cpu_to_le16(64); shdr->Command = smb2_cmd; - if (server->dialect >= SMB30_PROT_ID) { - /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ - smb3_hdr = (struct smb3_hdr_req *)shdr; - /* if primary channel is not set yet, use default channel for chan sequence num */ - if (SERVER_IS_CHAN(server)) - smb3_hdr->ChannelSequence = - cpu_to_le16(server->primary_server->channel_sequence_num); - else - smb3_hdr->ChannelSequence = cpu_to_le16(server->channel_sequence_num); - } + if (server) { + /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ + if (server->dialect >= SMB30_PROT_ID) { + smb3_hdr = (struct smb3_hdr_req *)shdr; + /* + * if primary channel is not set yet, use default + * channel for chan sequence num + */ + if (SERVER_IS_CHAN(server)) + smb3_hdr->ChannelSequence = + cpu_to_le16(server->primary_server->channel_sequence_num); + else + smb3_hdr->ChannelSequence = + cpu_to_le16(server->channel_sequence_num); + } spin_lock(&server->req_lock); /* Request up to 10 credits but don't go over the limit. */ if (server->credits >= server->max_credits) @@ -842,7 +848,7 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) iov[num].iov_base = create_posix_buf(mode); if (mode == ACL_NO_MODE) - cifs_dbg(FYI, "Invalid mode\n"); + cifs_dbg(FYI, "%s: no mode\n", __func__); if (iov[num].iov_base == NULL) return -ENOMEM; iov[num].iov_len = sizeof(struct create_posix); @@ -2234,7 +2240,7 @@ create_durable_v2_buf(struct cifs_open_parms *oparms) * (most servers default to 120 seconds) and most clients default to 0. * This can be overridden at mount ("handletimeout=") if the user wants * a different persistent (or resilient) handle timeout for all opens - * opens on a particular SMB3 mount. + * on a particular SMB3 mount. */ buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); @@ -2379,7 +2385,7 @@ add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) return 0; } -/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ +/* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ static void setup_owner_group_sids(char *buf) { struct owner_group_sids *sids = (struct owner_group_sids *)buf; @@ -3124,6 +3130,7 @@ void SMB2_ioctl_free(struct smb_rqst *rqst) { int i; + if (rqst && rqst->rq_iov) { cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ for (i = 1; i < rqst->rq_nvec; i++) @@ -3871,7 +3878,7 @@ void smb2_reconnect_server(struct work_struct *work) goto done; /* allocate a dummy tcon struct used for reconnect */ - tcon = tconInfoAlloc(); + tcon = tcon_info_alloc(false); if (!tcon) { resched = true; list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) { diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c index 2a2aec8c61129b907acc7b1418e8207548e3716b..94df9eec3d8d1c240d6b4573c7fe1884ef99fdd4 100644 --- a/fs/smb/client/smbdirect.c +++ b/fs/smb/client/smbdirect.c @@ -1401,10 +1401,13 @@ int smbd_reconnect(struct TCP_Server_Info *server) server->smbd_conn = smbd_get_connection( server, (struct sockaddr *) &server->dstaddr); - if (server->smbd_conn) + if (server->smbd_conn) { cifs_dbg(VFS, "RDMA transport re-established\n"); - - return server->smbd_conn ? 0 : -ENOENT; + trace_smb3_smbd_connect_done(server->hostname, server->conn_id, &server->dstaddr); + return 0; + } + trace_smb3_smbd_connect_err(server->hostname, server->conn_id, &server->dstaddr); + return -ENOENT; } static void destroy_caches_and_workqueue(struct smbd_connection *info) diff --git a/fs/smb/client/trace.h b/fs/smb/client/trace.h index e671bd16f00c556e97279b8ce6c066a5dd12de96..de199ec9f7263daf369639d2bda1df453f225e59 100644 --- a/fs/smb/client/trace.h +++ b/fs/smb/client/trace.h @@ -691,7 +691,7 @@ DEFINE_EVENT(smb3_tcon_class, smb3_##name, \ TP_ARGS(xid, tid, sesid, unc_name, rc)) DEFINE_SMB3_TCON_EVENT(tcon); - +DEFINE_SMB3_TCON_EVENT(qfs_done); /* * For smb2/smb3 open (including create and mkdir) calls @@ -935,6 +935,8 @@ DEFINE_EVENT(smb3_connect_class, smb3_##name, \ TP_ARGS(hostname, conn_id, addr)) DEFINE_SMB3_CONNECT_EVENT(connect_done); +DEFINE_SMB3_CONNECT_EVENT(smbd_connect_done); +DEFINE_SMB3_CONNECT_EVENT(smbd_connect_err); DECLARE_EVENT_CLASS(smb3_connect_err_class, TP_PROTO(char *hostname, __u64 conn_id, diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 1b5d9794ed5b0b5e5e0e63a4dadea8f8c66a6ddd..14710afdc2a36cf3715e940a77d79b5f02f7c999 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -35,6 +35,8 @@ void cifs_wake_up_task(struct mid_q_entry *mid) { + if (mid->mid_state == MID_RESPONSE_RECEIVED) + mid->mid_state = MID_RESPONSE_READY; wake_up_process(mid->callback_data); } @@ -87,7 +89,8 @@ static void __release_mid(struct kref *refcount) struct TCP_Server_Info *server = midEntry->server; if (midEntry->resp_buf && (midEntry->mid_flags & MID_WAIT_CANCELLED) && - midEntry->mid_state == MID_RESPONSE_RECEIVED && + (midEntry->mid_state == MID_RESPONSE_RECEIVED || + midEntry->mid_state == MID_RESPONSE_READY) && server->ops->handle_cancelled_mid) server->ops->handle_cancelled_mid(midEntry, server); @@ -737,7 +740,8 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ) int error; error = wait_event_state(server->response_q, - midQ->mid_state != MID_REQUEST_SUBMITTED, + midQ->mid_state != MID_REQUEST_SUBMITTED && + midQ->mid_state != MID_RESPONSE_RECEIVED, (TASK_KILLABLE|TASK_FREEZABLE_UNSAFE)); if (error < 0) return -ERESTARTSYS; @@ -890,7 +894,7 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server) spin_lock(&server->mid_lock); switch (mid->mid_state) { - case MID_RESPONSE_RECEIVED: + case MID_RESPONSE_READY: spin_unlock(&server->mid_lock); return rc; case MID_RETRY_NEEDED: @@ -989,6 +993,9 @@ cifs_compound_callback(struct mid_q_entry *mid) credits.instance = server->reconnect_instance; add_credits(server, &credits, mid->optype); + + if (mid->mid_state == MID_RESPONSE_RECEIVED) + mid->mid_state = MID_RESPONSE_READY; } static void @@ -1209,7 +1216,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, send_cancel(server, &rqst[i], midQ[i]); spin_lock(&server->mid_lock); midQ[i]->mid_flags |= MID_WAIT_CANCELLED; - if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { + if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED || + midQ[i]->mid_state == MID_RESPONSE_RECEIVED) { midQ[i]->callback = cifs_cancelled_callback; cancelled_mid[i] = true; credits[i].value = 0; @@ -1230,7 +1238,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, } if (!midQ[i]->resp_buf || - midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { + midQ[i]->mid_state != MID_RESPONSE_READY) { rc = -EIO; cifs_dbg(FYI, "Bad MID state?\n"); goto out; @@ -1417,7 +1425,8 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, if (rc != 0) { send_cancel(server, &rqst, midQ); spin_lock(&server->mid_lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { + if (midQ->mid_state == MID_REQUEST_SUBMITTED || + midQ->mid_state == MID_RESPONSE_RECEIVED) { /* no longer considered to be "in-flight" */ midQ->callback = release_mid; spin_unlock(&server->mid_lock); @@ -1434,7 +1443,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, } if (!midQ->resp_buf || !out_buf || - midQ->mid_state != MID_RESPONSE_RECEIVED) { + midQ->mid_state != MID_RESPONSE_READY) { rc = -EIO; cifs_server_dbg(VFS, "Bad MID state?\n"); goto out; @@ -1558,14 +1567,16 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(server->response_q, - (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || + (!(midQ->mid_state == MID_REQUEST_SUBMITTED || + midQ->mid_state == MID_RESPONSE_RECEIVED)) || ((server->tcpStatus != CifsGood) && (server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ spin_lock(&server->srv_lock); if ((rc == -ERESTARTSYS) && - (midQ->mid_state == MID_REQUEST_SUBMITTED) && + (midQ->mid_state == MID_REQUEST_SUBMITTED || + midQ->mid_state == MID_RESPONSE_RECEIVED) && ((server->tcpStatus == CifsGood) || (server->tcpStatus == CifsNew))) { spin_unlock(&server->srv_lock); @@ -1596,7 +1607,8 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, if (rc) { send_cancel(server, &rqst, midQ); spin_lock(&server->mid_lock); - if (midQ->mid_state == MID_REQUEST_SUBMITTED) { + if (midQ->mid_state == MID_REQUEST_SUBMITTED || + midQ->mid_state == MID_RESPONSE_RECEIVED) { /* no longer considered to be "in-flight" */ midQ->callback = release_mid; spin_unlock(&server->mid_lock); @@ -1616,7 +1628,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, return rc; /* rcvd frame is ok */ - if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { + if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) { rc = -EIO; cifs_tcon_dbg(VFS, "Bad MID state?\n"); goto out; diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 2680251b9aac1209f0c547776105404bf7ab3b05..319fb9ffc6a03212011007da32761e9781369ce0 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -406,7 +406,7 @@ struct smb2_tree_disconnect_rsp { /* Capabilities flags */ #define SMB2_GLOBAL_CAP_DFS 0x00000001 #define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */ -#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */ +#define SMB2_GLOBAL_CAP_LARGE_MTU 0x00000004 /* Resp only New to SMB2.1 */ #define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */ #define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */ #define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */ diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig index 793151ddd60e99d5f0c9bdc6a1fd3b99fc7a514c..cabe6a843c6a0722cc69b9d1f2dda5da9cabfb0d 100644 --- a/fs/smb/server/Kconfig +++ b/fs/smb/server/Kconfig @@ -1,5 +1,5 @@ config SMB_SERVER - tristate "SMB3 server support (EXPERIMENTAL)" + tristate "SMB3 server support" depends on INET depends on MULTIUSER depends on FILE_LOCKING diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 0d990c2f33cda76f6aeba475b5b8b9d02d6582d6..4b38c3a285f6028ea3bcdf9f7ecea957a1398e82 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -84,6 +84,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void) spin_lock_init(&conn->llist_lock); INIT_LIST_HEAD(&conn->lock_list); + init_rwsem(&conn->session_lock); + down_write(&conn_list_lock); list_add(&conn->conns_list, &conn_list); up_write(&conn_list_lock); @@ -197,6 +199,9 @@ int ksmbd_conn_write(struct ksmbd_work *work) if (work->send_no_response) return 0; + if (!work->iov_idx) + return -EINVAL; + ksmbd_conn_lock(conn); sent = conn->transport->ops->writev(conn->transport, work->iov, work->iov_cnt, diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index ab2583f030cebcc0cdc0069ed5aa6fe6f1ab5223..3c005246a32e8d2c38bde51b3ea8994e319c9c6b 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -50,6 +50,7 @@ struct ksmbd_conn { struct nls_table *local_nls; struct unicode_map *um; struct list_head conns_list; + struct rw_semaphore session_lock; /* smb session 1 per user */ struct xarray sessions; unsigned long last_active; diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c index 408cddf2f094a87a0b5ffce7855b08acd886a68d..d2c81a8a11dda10ef710bc97e1c059073fddb700 100644 --- a/fs/smb/server/mgmt/tree_connect.c +++ b/fs/smb/server/mgmt/tree_connect.c @@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, tree_conn->user = sess->user; tree_conn->share_conf = sc; + tree_conn->t_state = TREE_NEW; status.tree_conn = tree_conn; + atomic_set(&tree_conn->refcount, 1); + init_waitqueue_head(&tree_conn->refcount_q); ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, GFP_KERNEL)); @@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, return status; } +void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) +{ + /* + * Checking waitqueue to releasing tree connect on + * tree disconnect. waitqueue_active is safe because it + * uses atomic operation for condition. + */ + if (!atomic_dec_return(&tcon->refcount) && + waitqueue_active(&tcon->refcount_q)) + wake_up(&tcon->refcount_q); +} + int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *tree_conn) { int ret; + write_lock(&sess->tree_conns_lock); + xa_erase(&sess->tree_conns, tree_conn->id); + write_unlock(&sess->tree_conns_lock); + + if (!atomic_dec_and_test(&tree_conn->refcount)) + wait_event(tree_conn->refcount_q, + atomic_read(&tree_conn->refcount) == 0); + ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); ksmbd_release_tree_conn_id(sess, tree_conn->id); - xa_erase(&sess->tree_conns, tree_conn->id); ksmbd_share_config_put(tree_conn->share_conf); kfree(tree_conn); return ret; @@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, { struct ksmbd_tree_connect *tcon; + read_lock(&sess->tree_conns_lock); tcon = xa_load(&sess->tree_conns, id); if (tcon) { - if (test_bit(TREE_CONN_EXPIRE, &tcon->status)) + if (tcon->t_state != TREE_CONNECTED) + tcon = NULL; + else if (!atomic_inc_not_zero(&tcon->refcount)) tcon = NULL; } + read_unlock(&sess->tree_conns_lock); return tcon; } @@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) if (!sess) return -EINVAL; - xa_for_each(&sess->tree_conns, id, tc) + xa_for_each(&sess->tree_conns, id, tc) { + write_lock(&sess->tree_conns_lock); + if (tc->t_state == TREE_DISCONNECTED) { + write_unlock(&sess->tree_conns_lock); + ret = -ENOENT; + continue; + } + tc->t_state = TREE_DISCONNECTED; + write_unlock(&sess->tree_conns_lock); + ret |= ksmbd_tree_conn_disconnect(sess, tc); + } xa_destroy(&sess->tree_conns); return ret; } diff --git a/fs/smb/server/mgmt/tree_connect.h b/fs/smb/server/mgmt/tree_connect.h index 562d647ad9fad46e49e6746f396bce223a73c742..6377a70b811c8993575d96889a8c2ef5f2fada2c 100644 --- a/fs/smb/server/mgmt/tree_connect.h +++ b/fs/smb/server/mgmt/tree_connect.h @@ -14,7 +14,11 @@ struct ksmbd_share_config; struct ksmbd_user; struct ksmbd_conn; -#define TREE_CONN_EXPIRE 1 +enum { + TREE_NEW = 0, + TREE_CONNECTED, + TREE_DISCONNECTED +}; struct ksmbd_tree_connect { int id; @@ -27,7 +31,9 @@ struct ksmbd_tree_connect { int maximal_access; bool posix_extensions; - unsigned long status; + atomic_t refcount; + wait_queue_head_t refcount_q; + unsigned int t_state; }; struct ksmbd_tree_conn_status { @@ -46,6 +52,7 @@ struct ksmbd_session; struct ksmbd_tree_conn_status ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess, const char *share_name); +void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon); int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, struct ksmbd_tree_connect *tree_conn); diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index 8a5dcab05614f196652999d33ed233ef3069ee8b..15f68ee0508946843983b7d773dc6837f12b3bf0 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -174,7 +174,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) unsigned long id; struct ksmbd_session *sess; - down_write(&sessions_table_lock); + down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { if (sess->state != SMB2_SESSION_VALID || time_after(jiffies, @@ -185,7 +185,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn) continue; } } - up_write(&sessions_table_lock); + up_write(&conn->session_lock); } int ksmbd_session_register(struct ksmbd_conn *conn, @@ -227,7 +227,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) } } } + up_write(&sessions_table_lock); + down_write(&conn->session_lock); xa_for_each(&conn->sessions, id, sess) { unsigned long chann_id; struct channel *chann; @@ -244,7 +246,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn) ksmbd_session_destroy(sess); } } - up_write(&sessions_table_lock); + up_write(&conn->session_lock); } struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, @@ -252,9 +254,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, { struct ksmbd_session *sess; + down_read(&conn->session_lock); sess = xa_load(&conn->sessions, id); if (sess) sess->last_active = jiffies; + up_read(&conn->session_lock); return sess; } @@ -351,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol) xa_init(&sess->ksmbd_chann_list); xa_init(&sess->rpc_handle_list); sess->sequence_number = 1; + rwlock_init(&sess->tree_conns_lock); ret = __init_smb2_session(sess); if (ret) diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index f99d475b28db45f96ae559c7368220a5dcd1a69e..63cb08fffde84c4a5f03b936577fe02288885b6e 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -60,6 +60,7 @@ struct ksmbd_session { struct ksmbd_file_table file_table; unsigned long last_active; + rwlock_t tree_conns_lock; }; static inline int test_session_flag(struct ksmbd_session *sess, int bit) diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 801cd0929209c6817ddc0282e2576a14282161a0..3079e607c5fe6db71e3cc70a76bcb26d90f813c3 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, if (check_conn_state(work)) return SERVER_HANDLER_CONTINUE; - if (ksmbd_verify_smb_message(work)) + if (ksmbd_verify_smb_message(work)) { + conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); return SERVER_HANDLER_ABORT; + } command = conn->ops->get_cmd_val(work); *cmd = command; @@ -239,6 +241,8 @@ static void __handle_ksmbd_work(struct ksmbd_work *work, } while (is_chained == true); send: + if (work->tcon) + ksmbd_tree_connect_put(work->tcon); smb3_preauth_hash_rsp(work); if (work->sess && work->sess->enc && work->encrypted && conn->ops->encrypt_resp) { @@ -590,8 +594,6 @@ static int __init ksmbd_server_init(void) if (ret) goto err_crypto_destroy; - pr_warn_once("The ksmbd server is experimental\n"); - return 0; err_crypto_destroy: diff --git a/fs/smb/server/smb2misc.c b/fs/smb/server/smb2misc.c index e881df1d10cbd58ba1be08556a15c13c3d1a06db..23bd3d1209dfa53feb4f094989b2f9c5f3a67603 100644 --- a/fs/smb/server/smb2misc.c +++ b/fs/smb/server/smb2misc.c @@ -440,10 +440,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work) validate_credit: if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) && - smb2_validate_credit_charge(work->conn, hdr)) { - work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); + smb2_validate_credit_charge(work->conn, hdr)) return 1; - } return 0; } diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 749660110878fdbd75c2820a14eaf7ba543496ce..898860adf9290d51cafcffb02ea4f12f20ed7805 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1993,6 +1993,9 @@ int smb2_tree_connect(struct ksmbd_work *work) if (conn->posix_ext_supported) status.tree_conn->posix_extensions = true; + write_lock(&sess->tree_conns_lock); + status.tree_conn->t_state = TREE_CONNECTED; + write_unlock(&sess->tree_conns_lock); rsp->StructureSize = cpu_to_le16(16); out_err1: rsp->Capabilities = 0; @@ -2122,27 +2125,50 @@ int smb2_tree_disconnect(struct ksmbd_work *work) ksmbd_debug(SMB, "request\n"); + if (!tcon) { + ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + err = -ENOENT; + goto err_out; + } + + ksmbd_close_tree_conn_fds(work); + + write_lock(&sess->tree_conns_lock); + if (tcon->t_state == TREE_DISCONNECTED) { + write_unlock(&sess->tree_conns_lock); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + err = -ENOENT; + goto err_out; + } + + WARN_ON_ONCE(atomic_dec_and_test(&tcon->refcount)); + tcon->t_state = TREE_DISCONNECTED; + write_unlock(&sess->tree_conns_lock); + + err = ksmbd_tree_conn_disconnect(sess, tcon); + if (err) { + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; + goto err_out; + } + + work->tcon = NULL; + rsp->StructureSize = cpu_to_le16(4); err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_disconnect_rsp)); if (err) { rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; - smb2_set_err_rsp(work); - return err; + goto err_out; } - if (!tcon || test_and_set_bit(TREE_CONN_EXPIRE, &tcon->status)) { - ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId); + return 0; - rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; - smb2_set_err_rsp(work); - return -ENOENT; - } +err_out: + smb2_set_err_rsp(work); + return err; - ksmbd_close_tree_conn_fds(work); - ksmbd_tree_conn_disconnect(sess, tcon); - work->tcon = NULL; - return 0; } /** @@ -2164,17 +2190,17 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_debug(SMB, "request\n"); - sess_id = le64_to_cpu(req->hdr.SessionId); - - rsp->StructureSize = cpu_to_le16(4); - err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); - if (err) { - rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + ksmbd_conn_lock(conn); + if (!ksmbd_conn_good(conn)) { + ksmbd_conn_unlock(conn); + rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; smb2_set_err_rsp(work); - return err; + return -ENOENT; } - + sess_id = le64_to_cpu(req->hdr.SessionId); ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_RECONNECT); + ksmbd_conn_unlock(conn); + ksmbd_close_session_fds(work); ksmbd_conn_wait_idle(conn, sess_id); @@ -2196,6 +2222,14 @@ int smb2_session_logoff(struct ksmbd_work *work) ksmbd_free_user(sess->user); sess->user = NULL; ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE); + + rsp->StructureSize = cpu_to_le16(4); + err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp)); + if (err) { + rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; + smb2_set_err_rsp(work); + return err; + } return 0; } @@ -3370,8 +3404,10 @@ int smb2_open(struct ksmbd_work *work) } ksmbd_revert_fsids(work); err_out1: - if (!rc) + if (!rc) { + ksmbd_update_fstate(&work->sess->file_table, fp, FP_INITED); rc = ksmbd_iov_pin_rsp(work, (void *)rsp, iov_len); + } if (rc) { if (rc == -EINVAL) rsp->hdr.Status = STATUS_INVALID_PARAMETER; @@ -6312,7 +6348,7 @@ int smb2_read(struct ksmbd_work *work) aux_payload_buf, nbytes); kvfree(aux_payload_buf); - + aux_payload_buf = NULL; nbytes = 0; if (remain_bytes < 0) { err = (int)remain_bytes; @@ -7028,10 +7064,6 @@ int smb2_lock(struct ksmbd_work *work) ksmbd_debug(SMB, "would have to wait for getting lock\n"); - spin_lock(&work->conn->llist_lock); - list_add_tail(&smb_lock->clist, - &work->conn->lock_list); - spin_unlock(&work->conn->llist_lock); list_add(&smb_lock->llist, &rollback_list); argv = kmalloc(sizeof(void *), GFP_KERNEL); @@ -7062,9 +7094,6 @@ int smb2_lock(struct ksmbd_work *work) if (work->state != KSMBD_WORK_ACTIVE) { list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); locks_free_lock(flock); if (work->state == KSMBD_WORK_CANCELLED) { @@ -7084,19 +7113,16 @@ int smb2_lock(struct ksmbd_work *work) } list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); release_async_work(work); goto retry; } else if (!rc) { + list_add(&smb_lock->llist, &rollback_list); spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); list_add_tail(&smb_lock->flist, &fp->lock_list); spin_unlock(&work->conn->llist_lock); - list_add(&smb_lock->llist, &rollback_list); ksmbd_debug(SMB, "successful in taking lock\n"); } else { goto out; @@ -8036,10 +8062,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) goto err_out; } - opinfo_put(opinfo); - ksmbd_fd_put(work, fp); opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); + opinfo_put(opinfo); + ksmbd_fd_put(work, fp); rsp->StructureSize = cpu_to_le16(24); rsp->OplockLevel = rsp_oplevel; diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index e5e438bf54996510da5800ea38ac50f60e36b36c..6c0305be895e56fb11464c5fe17245afc4354447 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1420,7 +1420,6 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, out: posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); - mark_inode_dirty(inode); return rc; } diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index f41f8d6108ce92c07e870e9043f5bf9533117e4e..c4b80ab7df74319af74fe16f9ea40e8240dd0049 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -333,6 +333,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) { + if (fp->f_state != FP_INITED) + return NULL; + if (!atomic_inc_not_zero(&fp->refcount)) return NULL; return fp; @@ -382,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) return 0; ft = &work->sess->file_table; - read_lock(&ft->lock); + write_lock(&ft->lock); fp = idr_find(ft->idr, id); if (fp) { set_close_state_blocked_works(fp); - if (!atomic_dec_and_test(&fp->refcount)) + if (fp->f_state != FP_INITED) fp = NULL; + else { + fp->f_state = FP_CLOSED; + if (!atomic_dec_and_test(&fp->refcount)) + fp = NULL; + } } - read_unlock(&ft->lock); + write_unlock(&ft->lock); if (!fp) return -EINVAL; @@ -570,6 +578,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) fp->tcon = work->tcon; fp->volatile_id = KSMBD_NO_FID; fp->persistent_id = KSMBD_NO_FID; + fp->f_state = FP_NEW; fp->f_ci = ksmbd_inode_get(fp); if (!fp->f_ci) { @@ -591,6 +600,14 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp) return ERR_PTR(ret); } +void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state) +{ + write_lock(&ft->lock); + fp->f_state = state; + write_unlock(&ft->lock); +} + static int __close_file_table_ids(struct ksmbd_file_table *ft, struct ksmbd_tree_connect *tcon, diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index fcb13413fa8d9366c12e5a0d506412d3e8159060..03d0bf941216f8f5157e1d9f1dca76897a3c51c3 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -60,6 +60,12 @@ struct ksmbd_inode { __le32 m_fattr; }; +enum { + FP_NEW = 0, + FP_INITED, + FP_CLOSED +}; + struct ksmbd_file { struct file *filp; u64 persistent_id; @@ -98,6 +104,7 @@ struct ksmbd_file { /* if ls is happening on directory, below is valid*/ struct ksmbd_readdir_data readdir_data; int dot_dotdot[2]; + unsigned int f_state; }; static inline void set_ctx_actor(struct dir_context *ctx, @@ -142,6 +149,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode); int ksmbd_init_global_file_table(void); void ksmbd_free_global_file_table(void); void ksmbd_set_fd_limit(unsigned long limit); +void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp, + unsigned int state); /* * INODE hash diff --git a/fs/stat.c b/fs/stat.c index 136711ae72fb3290000ec6e66f8b82a3abf461e0..d43a5cc1bfa46b142197c991343a295d375af60f 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -26,37 +26,6 @@ #include "internal.h" #include "mount.h" -/** - * fill_mg_cmtime - Fill in the mtime and ctime and flag ctime as QUERIED - * @stat: where to store the resulting values - * @request_mask: STATX_* values requested - * @inode: inode from which to grab the c/mtime - * - * Given @inode, grab the ctime and mtime out if it and store the result - * in @stat. When fetching the value, flag it as queried so the next write - * will use a fine-grained timestamp. - */ -void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode) -{ - atomic_long_t *pnsec = (atomic_long_t *)&inode->__i_ctime.tv_nsec; - - /* If neither time was requested, then don't report them */ - if (!(request_mask & (STATX_CTIME|STATX_MTIME))) { - stat->result_mask &= ~(STATX_CTIME|STATX_MTIME); - return; - } - - stat->mtime = inode->i_mtime; - stat->ctime.tv_sec = inode->__i_ctime.tv_sec; - /* - * Atomically set the QUERIED flag and fetch the new value with - * the flag masked off. - */ - stat->ctime.tv_nsec = atomic_long_fetch_or(I_CTIME_QUERIED, pnsec) & - ~I_CTIME_QUERIED; -} -EXPORT_SYMBOL(fill_mg_cmtime); - /** * generic_fillattr - Fill in the basic attributes from the inode struct * @idmap: idmap of the mount the inode was found from @@ -89,14 +58,8 @@ void generic_fillattr(struct mnt_idmap *idmap, u32 request_mask, stat->rdev = inode->i_rdev; stat->size = i_size_read(inode); stat->atime = inode->i_atime; - - if (is_mgtime(inode)) { - fill_mg_cmtime(stat, request_mask, inode); - } else { - stat->mtime = inode->i_mtime; - stat->ctime = inode_get_ctime(inode); - } - + stat->mtime = inode->i_mtime; + stat->ctime = inode_get_ctime(inode); stat->blksize = i_blocksize(inode); stat->blocks = inode->i_blocks; @@ -311,6 +274,23 @@ int vfs_fstatat(int dfd, const char __user *filename, int statx_flags = flags | AT_NO_AUTOMOUNT; struct filename *name; + /* + * Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH) + * + * If AT_EMPTY_PATH is set, we expect the common case to be that + * empty path, and avoid doing all the extra pathname work. + */ + if (dfd >= 0 && flags == AT_EMPTY_PATH) { + char c; + + ret = get_user(c, filename); + if (unlikely(ret)) + return ret; + + if (likely(!c)) + return vfs_fstat(dfd, stat); + } + name = getname_flags(filename, getname_statx_lookup_flags(statx_flags), NULL); ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS); putname(name); @@ -402,12 +382,6 @@ SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, stat #ifdef __ARCH_WANT_NEW_STAT -#if BITS_PER_LONG == 32 -# define choose_32_64(a,b) a -#else -# define choose_32_64(a,b) b -#endif - #ifndef INIT_STRUCT_STAT_PADDING # define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) #endif diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile index 7c35a282b484c8d9fad08e35ddce2475b3d6082b..73c56da8e284e3724f4418614cf5a591e57a91a5 100644 --- a/fs/tracefs/Makefile +++ b/fs/tracefs/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only tracefs-objs := inode.o +tracefs-objs += event_inode.o obj-$(CONFIG_TRACING) += tracefs.o diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c new file mode 100644 index 0000000000000000000000000000000000000000..8c8d64e76103e130aeab0a8f913822abf1b0b5af --- /dev/null +++ b/fs/tracefs/event_inode.c @@ -0,0 +1,905 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * event_inode.c - part of tracefs, a pseudo file system for activating tracing + * + * Copyright (C) 2020-23 VMware Inc, author: Steven Rostedt (VMware) + * Copyright (C) 2020-23 VMware Inc, author: Ajay Kaher + * + * eventfs is used to dynamically create inodes and dentries based on the + * meta data provided by the tracing system. + * + * eventfs stores the meta-data of files/dirs and holds off on creating + * inodes/dentries of the files. When accessed, the eventfs will create the + * inodes/dentries in a just-in-time (JIT) manner. The eventfs will clean up + * and delete the inodes/dentries when they are no longer referenced. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +struct eventfs_inode { + struct list_head e_top_files; +}; + +/* + * struct eventfs_file - hold the properties of the eventfs files and + * directories. + * @name: the name of the file or directory to create + * @d_parent: holds parent's dentry + * @dentry: once accessed holds dentry + * @list: file or directory to be added to parent directory + * @ei: list of files and directories within directory + * @fop: file_operations for file or directory + * @iop: inode_operations for file or directory + * @data: something that the caller will want to get to later on + * @mode: the permission that the file or directory should have + */ +struct eventfs_file { + const char *name; + struct dentry *d_parent; + struct dentry *dentry; + struct list_head list; + struct eventfs_inode *ei; + const struct file_operations *fop; + const struct inode_operations *iop; + /* + * Union - used for deletion + * @del_list: list of eventfs_file to delete + * @rcu: eventfs_file to delete in RCU + * @is_freed: node is freed if one of the above is set + */ + union { + struct list_head del_list; + struct rcu_head rcu; + unsigned long is_freed; + }; + void *data; + umode_t mode; +}; + +static DEFINE_MUTEX(eventfs_mutex); +DEFINE_STATIC_SRCU(eventfs_srcu); + +static struct dentry *eventfs_root_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags); +static int dcache_dir_open_wrapper(struct inode *inode, struct file *file); +static int dcache_readdir_wrapper(struct file *file, struct dir_context *ctx); +static int eventfs_release(struct inode *inode, struct file *file); + +static const struct inode_operations eventfs_root_dir_inode_operations = { + .lookup = eventfs_root_lookup, +}; + +static const struct file_operations eventfs_file_operations = { + .open = dcache_dir_open_wrapper, + .read = generic_read_dir, + .iterate_shared = dcache_readdir_wrapper, + .llseek = generic_file_llseek, + .release = eventfs_release, +}; + +/** + * create_file - create a file in the tracefs filesystem + * @name: the name of the file to create. + * @mode: the permission that the file should have. + * @parent: parent dentry for this file. + * @data: something that the caller will want to get to later on. + * @fop: struct file_operations that should be used for this file. + * + * This is the basic "create a file" function for tracefs. It allows for a + * wide range of flexibility in creating a file. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If tracefs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +static struct dentry *create_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fop) +{ + struct tracefs_inode *ti; + struct dentry *dentry; + struct inode *inode; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + + if (WARN_ON_ONCE(!S_ISREG(mode))) + return NULL; + + dentry = eventfs_start_creating(name, parent); + + if (IS_ERR(dentry)) + return dentry; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return eventfs_failed_creating(dentry); + + inode->i_mode = mode; + inode->i_fop = fop; + inode->i_private = data; + + ti = get_tracefs(inode); + ti->flags |= TRACEFS_EVENT_INODE; + d_instantiate(dentry, inode); + fsnotify_create(dentry->d_parent->d_inode, dentry); + return eventfs_end_creating(dentry); +}; + +/** + * create_dir - create a dir in the tracefs filesystem + * @name: the name of the file to create. + * @parent: parent dentry for this file. + * @data: something that the caller will want to get to later on. + * + * This is the basic "create a dir" function for eventfs. It allows for a + * wide range of flexibility in creating a dir. + * + * This function will return a pointer to a dentry if it succeeds. This + * pointer must be passed to the tracefs_remove() function when the file is + * to be removed (no automatic cleanup happens if your module is unloaded, + * you are responsible here.) If an error occurs, %NULL will be returned. + * + * If tracefs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +static struct dentry *create_dir(const char *name, struct dentry *parent, void *data) +{ + struct tracefs_inode *ti; + struct dentry *dentry; + struct inode *inode; + + dentry = eventfs_start_creating(name, parent); + if (IS_ERR(dentry)) + return dentry; + + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) + return eventfs_failed_creating(dentry); + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_fop = &eventfs_file_operations; + inode->i_private = data; + + ti = get_tracefs(inode); + ti->flags |= TRACEFS_EVENT_INODE; + + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return eventfs_end_creating(dentry); +} + +/** + * eventfs_set_ef_status_free - set the ef->status to free + * @ti: the tracefs_inode of the dentry + * @dentry: dentry who's status to be freed + * + * eventfs_set_ef_status_free will be called if no more + * references remain + */ +void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry) +{ + struct tracefs_inode *ti_parent; + struct eventfs_inode *ei; + struct eventfs_file *ef, *tmp; + + /* The top level events directory may be freed by this */ + if (unlikely(ti->flags & TRACEFS_EVENT_TOP_INODE)) { + LIST_HEAD(ef_del_list); + + mutex_lock(&eventfs_mutex); + + ei = ti->private; + + /* Record all the top level files */ + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + lockdep_is_held(&eventfs_mutex)) { + list_add_tail(&ef->del_list, &ef_del_list); + } + + /* Nothing should access this, but just in case! */ + ti->private = NULL; + + mutex_unlock(&eventfs_mutex); + + /* Now safely free the top level files and their children */ + list_for_each_entry_safe(ef, tmp, &ef_del_list, del_list) { + list_del(&ef->del_list); + eventfs_remove(ef); + } + + kfree(ei); + return; + } + + mutex_lock(&eventfs_mutex); + + ti_parent = get_tracefs(dentry->d_parent->d_inode); + if (!ti_parent || !(ti_parent->flags & TRACEFS_EVENT_INODE)) + goto out; + + ef = dentry->d_fsdata; + if (!ef) + goto out; + + /* + * If ef was freed, then the LSB bit is set for d_fsdata. + * But this should not happen, as it should still have a + * ref count that prevents it. Warn in case it does. + */ + if (WARN_ON_ONCE((unsigned long)ef & 1)) + goto out; + + dentry->d_fsdata = NULL; + ef->dentry = NULL; +out: + mutex_unlock(&eventfs_mutex); +} + +/** + * eventfs_post_create_dir - post create dir routine + * @ef: eventfs_file of recently created dir + * + * Map the meta-data of files within an eventfs dir to their parent dentry + */ +static void eventfs_post_create_dir(struct eventfs_file *ef) +{ + struct eventfs_file *ef_child; + struct tracefs_inode *ti; + + /* srcu lock already held */ + /* fill parent-child relation */ + list_for_each_entry_srcu(ef_child, &ef->ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { + ef_child->d_parent = ef->dentry; + } + + ti = get_tracefs(ef->dentry->d_inode); + ti->private = ef->ei; +} + +/** + * create_dentry - helper function to create dentry + * @ef: eventfs_file of file or directory to create + * @parent: parent dentry + * @lookup: true if called from lookup routine + * + * Used to create a dentry for file/dir, executes post dentry creation routine + */ +static struct dentry * +create_dentry(struct eventfs_file *ef, struct dentry *parent, bool lookup) +{ + bool invalidate = false; + struct dentry *dentry; + + mutex_lock(&eventfs_mutex); + if (ef->is_freed) { + mutex_unlock(&eventfs_mutex); + return NULL; + } + if (ef->dentry) { + dentry = ef->dentry; + /* On dir open, up the ref count */ + if (!lookup) + dget(dentry); + mutex_unlock(&eventfs_mutex); + return dentry; + } + mutex_unlock(&eventfs_mutex); + + if (!lookup) + inode_lock(parent->d_inode); + + if (ef->ei) + dentry = create_dir(ef->name, parent, ef->data); + else + dentry = create_file(ef->name, ef->mode, parent, + ef->data, ef->fop); + + if (!lookup) + inode_unlock(parent->d_inode); + + mutex_lock(&eventfs_mutex); + if (IS_ERR_OR_NULL(dentry)) { + /* If the ef was already updated get it */ + dentry = ef->dentry; + if (dentry && !lookup) + dget(dentry); + mutex_unlock(&eventfs_mutex); + return dentry; + } + + if (!ef->dentry && !ef->is_freed) { + ef->dentry = dentry; + if (ef->ei) + eventfs_post_create_dir(ef); + dentry->d_fsdata = ef; + } else { + /* A race here, should try again (unless freed) */ + invalidate = true; + + /* + * Should never happen unless we get here due to being freed. + * Otherwise it means two dentries exist with the same name. + */ + WARN_ON_ONCE(!ef->is_freed); + } + mutex_unlock(&eventfs_mutex); + if (invalidate) + d_invalidate(dentry); + + if (lookup || invalidate) + dput(dentry); + + return invalidate ? NULL : dentry; +} + +static bool match_event_file(struct eventfs_file *ef, const char *name) +{ + bool ret; + + mutex_lock(&eventfs_mutex); + ret = !ef->is_freed && strcmp(ef->name, name) == 0; + mutex_unlock(&eventfs_mutex); + + return ret; +} + +/** + * eventfs_root_lookup - lookup routine to create file/dir + * @dir: in which a lookup is being done + * @dentry: file/dir dentry + * @flags: to pass as flags parameter to simple lookup + * + * Used to create a dynamic file/dir within @dir. Use the eventfs_inode + * list of meta data to find the information needed to create the file/dir. + */ +static struct dentry *eventfs_root_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + struct dentry *ret = NULL; + int idx; + + ti = get_tracefs(dir); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return NULL; + + ei = ti->private; + idx = srcu_read_lock(&eventfs_srcu); + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { + if (!match_event_file(ef, dentry->d_name.name)) + continue; + ret = simple_lookup(dir, dentry, flags); + create_dentry(ef, ef->d_parent, true); + break; + } + srcu_read_unlock(&eventfs_srcu, idx); + return ret; +} + +struct dentry_list { + void *cursor; + struct dentry **dentries; +}; + +/** + * eventfs_release - called to release eventfs file/dir + * @inode: inode to be released + * @file: file to be released (not used) + */ +static int eventfs_release(struct inode *inode, struct file *file) +{ + struct tracefs_inode *ti; + struct dentry_list *dlist = file->private_data; + void *cursor; + int i; + + ti = get_tracefs(inode); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return -EINVAL; + + if (WARN_ON_ONCE(!dlist)) + return -EINVAL; + + for (i = 0; dlist->dentries && dlist->dentries[i]; i++) { + dput(dlist->dentries[i]); + } + + cursor = dlist->cursor; + kfree(dlist->dentries); + kfree(dlist); + file->private_data = cursor; + return dcache_dir_close(inode, file); +} + +/** + * dcache_dir_open_wrapper - eventfs open wrapper + * @inode: not used + * @file: dir to be opened (to create its child) + * + * Used to dynamically create the file/dir within @file. @file is really a + * directory and all the files/dirs of the children within @file will be + * created. If any of the files/dirs have already been created, their + * reference count will be incremented. + */ +static int dcache_dir_open_wrapper(struct inode *inode, struct file *file) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + struct dentry_list *dlist; + struct dentry **dentries = NULL; + struct dentry *dentry = file_dentry(file); + struct dentry *d; + struct inode *f_inode = file_inode(file); + int cnt = 0; + int idx; + int ret; + + ti = get_tracefs(f_inode); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return -EINVAL; + + if (WARN_ON_ONCE(file->private_data)) + return -EINVAL; + + dlist = kmalloc(sizeof(*dlist), GFP_KERNEL); + if (!dlist) + return -ENOMEM; + + ei = ti->private; + idx = srcu_read_lock(&eventfs_srcu); + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { + d = create_dentry(ef, dentry, false); + if (d) { + struct dentry **tmp; + + tmp = krealloc(dentries, sizeof(d) * (cnt + 2), GFP_KERNEL); + if (!tmp) + break; + tmp[cnt] = d; + tmp[cnt + 1] = NULL; + cnt++; + dentries = tmp; + } + } + srcu_read_unlock(&eventfs_srcu, idx); + ret = dcache_dir_open(inode, file); + + /* + * dcache_dir_open() sets file->private_data to a dentry cursor. + * Need to save that but also save all the dentries that were + * opened by this function. + */ + dlist->cursor = file->private_data; + dlist->dentries = dentries; + file->private_data = dlist; + return ret; +} + +/* + * This just sets the file->private_data back to the cursor and back. + */ +static int dcache_readdir_wrapper(struct file *file, struct dir_context *ctx) +{ + struct dentry_list *dlist = file->private_data; + int ret; + + file->private_data = dlist->cursor; + ret = dcache_readdir(file, ctx); + dlist->cursor = file->private_data; + file->private_data = dlist; + return ret; +} + +/** + * eventfs_prepare_ef - helper function to prepare eventfs_file + * @name: the name of the file/directory to create. + * @mode: the permission that the file should have. + * @fop: struct file_operations that should be used for this file/directory. + * @iop: struct inode_operations that should be used for this file/directory. + * @data: something that the caller will want to get to later on. The + * inode.i_private pointer will point to this value on the open() call. + * + * This function allocates and fills the eventfs_file structure. + */ +static struct eventfs_file *eventfs_prepare_ef(const char *name, umode_t mode, + const struct file_operations *fop, + const struct inode_operations *iop, + void *data) +{ + struct eventfs_file *ef; + + ef = kzalloc(sizeof(*ef), GFP_KERNEL); + if (!ef) + return ERR_PTR(-ENOMEM); + + ef->name = kstrdup(name, GFP_KERNEL); + if (!ef->name) { + kfree(ef); + return ERR_PTR(-ENOMEM); + } + + if (S_ISDIR(mode)) { + ef->ei = kzalloc(sizeof(*ef->ei), GFP_KERNEL); + if (!ef->ei) { + kfree(ef->name); + kfree(ef); + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&ef->ei->e_top_files); + } else { + ef->ei = NULL; + } + + ef->iop = iop; + ef->fop = fop; + ef->mode = mode; + ef->data = data; + return ef; +} + +/** + * eventfs_create_events_dir - create the trace event structure + * @name: the name of the directory to create. + * @parent: parent dentry for this file. This should be a directory dentry + * if set. If this parameter is NULL, then the directory will be + * created in the root of the tracefs filesystem. + * + * This function creates the top of the trace event directory. + */ +struct dentry *eventfs_create_events_dir(const char *name, + struct dentry *parent) +{ + struct dentry *dentry = tracefs_start_creating(name, parent); + struct eventfs_inode *ei; + struct tracefs_inode *ti; + struct inode *inode; + + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + + if (IS_ERR(dentry)) + return dentry; + + ei = kzalloc(sizeof(*ei), GFP_KERNEL); + if (!ei) + return ERR_PTR(-ENOMEM); + inode = tracefs_get_inode(dentry->d_sb); + if (unlikely(!inode)) { + kfree(ei); + tracefs_failed_creating(dentry); + return ERR_PTR(-ENOMEM); + } + + INIT_LIST_HEAD(&ei->e_top_files); + + ti = get_tracefs(inode); + ti->flags |= TRACEFS_EVENT_INODE | TRACEFS_EVENT_TOP_INODE; + ti->private = ei; + + inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + inode->i_op = &eventfs_root_dir_inode_operations; + inode->i_fop = &eventfs_file_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + d_instantiate(dentry, inode); + inc_nlink(dentry->d_parent->d_inode); + fsnotify_mkdir(dentry->d_parent->d_inode, dentry); + return tracefs_end_creating(dentry); +} + +/** + * eventfs_add_subsystem_dir - add eventfs subsystem_dir to list to create later + * @name: the name of the file to create. + * @parent: parent dentry for this dir. + * + * This function adds eventfs subsystem dir to list. + * And all these dirs are created on the fly when they are looked up, + * and the dentry and inodes will be removed when they are done. + */ +struct eventfs_file *eventfs_add_subsystem_dir(const char *name, + struct dentry *parent) +{ + struct tracefs_inode *ti_parent; + struct eventfs_inode *ei_parent; + struct eventfs_file *ef; + + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + + if (!parent) + return ERR_PTR(-EINVAL); + + ti_parent = get_tracefs(parent->d_inode); + ei_parent = ti_parent->private; + + ef = eventfs_prepare_ef(name, S_IFDIR, NULL, NULL, NULL); + if (IS_ERR(ef)) + return ef; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ei_parent->e_top_files); + ef->d_parent = parent; + mutex_unlock(&eventfs_mutex); + return ef; +} + +/** + * eventfs_add_dir - add eventfs dir to list to create later + * @name: the name of the file to create. + * @ef_parent: parent eventfs_file for this dir. + * + * This function adds eventfs dir to list. + * And all these dirs are created on the fly when they are looked up, + * and the dentry and inodes will be removed when they are done. + */ +struct eventfs_file *eventfs_add_dir(const char *name, + struct eventfs_file *ef_parent) +{ + struct eventfs_file *ef; + + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + + if (!ef_parent) + return ERR_PTR(-EINVAL); + + ef = eventfs_prepare_ef(name, S_IFDIR, NULL, NULL, NULL); + if (IS_ERR(ef)) + return ef; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ef_parent->ei->e_top_files); + ef->d_parent = ef_parent->dentry; + mutex_unlock(&eventfs_mutex); + return ef; +} + +/** + * eventfs_add_events_file - add the data needed to create a file for later reference + * @name: the name of the file to create. + * @mode: the permission that the file should have. + * @parent: parent dentry for this file. + * @data: something that the caller will want to get to later on. + * @fop: struct file_operations that should be used for this file. + * + * This function is used to add the information needed to create a + * dentry/inode within the top level events directory. The file created + * will have the @mode permissions. The @data will be used to fill the + * inode.i_private when the open() call is done. The dentry and inodes are + * all created when they are referenced, and removed when they are no + * longer referenced. + */ +int eventfs_add_events_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fop) +{ + struct tracefs_inode *ti; + struct eventfs_inode *ei; + struct eventfs_file *ef; + + if (security_locked_down(LOCKDOWN_TRACEFS)) + return -ENODEV; + + if (!parent) + return -EINVAL; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + + if (!parent->d_inode) + return -EINVAL; + + ti = get_tracefs(parent->d_inode); + if (!(ti->flags & TRACEFS_EVENT_INODE)) + return -EINVAL; + + ei = ti->private; + ef = eventfs_prepare_ef(name, mode, fop, NULL, data); + + if (IS_ERR(ef)) + return -ENOMEM; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ei->e_top_files); + ef->d_parent = parent; + mutex_unlock(&eventfs_mutex); + return 0; +} + +/** + * eventfs_add_file - add eventfs file to list to create later + * @name: the name of the file to create. + * @mode: the permission that the file should have. + * @ef_parent: parent eventfs_file for this file. + * @data: something that the caller will want to get to later on. + * @fop: struct file_operations that should be used for this file. + * + * This function is used to add the information needed to create a + * file within a subdirectory of the events directory. The file created + * will have the @mode permissions. The @data will be used to fill the + * inode.i_private when the open() call is done. The dentry and inodes are + * all created when they are referenced, and removed when they are no + * longer referenced. + */ +int eventfs_add_file(const char *name, umode_t mode, + struct eventfs_file *ef_parent, + void *data, + const struct file_operations *fop) +{ + struct eventfs_file *ef; + + if (security_locked_down(LOCKDOWN_TRACEFS)) + return -ENODEV; + + if (!ef_parent) + return -EINVAL; + + if (!(mode & S_IFMT)) + mode |= S_IFREG; + + ef = eventfs_prepare_ef(name, mode, fop, NULL, data); + if (IS_ERR(ef)) + return -ENOMEM; + + mutex_lock(&eventfs_mutex); + list_add_tail(&ef->list, &ef_parent->ei->e_top_files); + ef->d_parent = ef_parent->dentry; + mutex_unlock(&eventfs_mutex); + return 0; +} + +static void free_ef(struct rcu_head *head) +{ + struct eventfs_file *ef = container_of(head, struct eventfs_file, rcu); + + kfree(ef->name); + kfree(ef->ei); + kfree(ef); +} + +/** + * eventfs_remove_rec - remove eventfs dir or file from list + * @ef: eventfs_file to be removed. + * @head: to create list of eventfs_file to be deleted + * @level: to check recursion depth + * + * The helper function eventfs_remove_rec() is used to clean up and free the + * associated data from eventfs for both of the added functions. + */ +static void eventfs_remove_rec(struct eventfs_file *ef, struct list_head *head, int level) +{ + struct eventfs_file *ef_child; + + if (!ef) + return; + /* + * Check recursion depth. It should never be greater than 3: + * 0 - events/ + * 1 - events/group/ + * 2 - events/group/event/ + * 3 - events/group/event/file + */ + if (WARN_ON_ONCE(level > 3)) + return; + + if (ef->ei) { + /* search for nested folders or files */ + list_for_each_entry_srcu(ef_child, &ef->ei->e_top_files, list, + lockdep_is_held(&eventfs_mutex)) { + eventfs_remove_rec(ef_child, head, level + 1); + } + } + + list_del_rcu(&ef->list); + list_add_tail(&ef->del_list, head); +} + +/** + * eventfs_remove - remove eventfs dir or file from list + * @ef: eventfs_file to be removed. + * + * This function acquire the eventfs_mutex lock and call eventfs_remove_rec() + */ +void eventfs_remove(struct eventfs_file *ef) +{ + struct eventfs_file *tmp; + LIST_HEAD(ef_del_list); + struct dentry *dentry_list = NULL; + struct dentry *dentry; + + if (!ef) + return; + + mutex_lock(&eventfs_mutex); + eventfs_remove_rec(ef, &ef_del_list, 0); + list_for_each_entry_safe(ef, tmp, &ef_del_list, del_list) { + if (ef->dentry) { + unsigned long ptr = (unsigned long)dentry_list; + + /* Keep the dentry from being freed yet */ + dget(ef->dentry); + + /* + * Paranoid: The dget() above should prevent the dentry + * from being freed and calling eventfs_set_ef_status_free(). + * But just in case, set the link list LSB pointer to 1 + * and have eventfs_set_ef_status_free() check that to + * make sure that if it does happen, it will not think + * the d_fsdata is an event_file. + * + * For this to work, no event_file should be allocated + * on a odd space, as the ef should always be allocated + * to be at least word aligned. Check for that too. + */ + WARN_ON_ONCE(ptr & 1); + + ef->dentry->d_fsdata = (void *)(ptr | 1); + dentry_list = ef->dentry; + ef->dentry = NULL; + } + call_srcu(&eventfs_srcu, &ef->rcu, free_ef); + } + mutex_unlock(&eventfs_mutex); + + while (dentry_list) { + unsigned long ptr; + + dentry = dentry_list; + ptr = (unsigned long)dentry->d_fsdata & ~1UL; + dentry_list = (struct dentry *)ptr; + dentry->d_fsdata = NULL; + d_invalidate(dentry); + mutex_lock(&eventfs_mutex); + /* dentry should now have at least a single reference */ + WARN_ONCE((int)d_count(dentry) < 1, + "dentry %p less than one reference (%d) after invalidate\n", + dentry, d_count(dentry)); + mutex_unlock(&eventfs_mutex); + dput(dentry); + } +} + +/** + * eventfs_remove_events_dir - remove eventfs dir or file from list + * @dentry: events's dentry to be removed. + * + * This function remove events main directory + */ +void eventfs_remove_events_dir(struct dentry *dentry) +{ + struct tracefs_inode *ti; + + if (!dentry || !dentry->d_inode) + return; + + ti = get_tracefs(dentry->d_inode); + if (!ti || !(ti->flags & TRACEFS_EVENT_INODE)) + return; + + d_invalidate(dentry); + dput(dentry); +} diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 2feb6c58648ca70f7f5683272ab13546f415a330..891653ba9cf35863f5cc83638b2a2a9a88a03444 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -21,13 +21,33 @@ #include #include #include +#include "internal.h" #define TRACEFS_DEFAULT_MODE 0700 +static struct kmem_cache *tracefs_inode_cachep __ro_after_init; static struct vfsmount *tracefs_mount; static int tracefs_mount_count; static bool tracefs_registered; +static struct inode *tracefs_alloc_inode(struct super_block *sb) +{ + struct tracefs_inode *ti; + + ti = kmem_cache_alloc(tracefs_inode_cachep, GFP_KERNEL); + if (!ti) + return NULL; + + ti->flags = 0; + + return &ti->vfs_inode; +} + +static void tracefs_free_inode(struct inode *inode) +{ + kmem_cache_free(tracefs_inode_cachep, get_tracefs(inode)); +} + static ssize_t default_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -127,7 +147,7 @@ static const struct inode_operations tracefs_dir_inode_operations = { .rmdir = tracefs_syscall_rmdir, }; -static struct inode *tracefs_get_inode(struct super_block *sb) +struct inode *tracefs_get_inode(struct super_block *sb) { struct inode *inode = new_inode(sb); if (inode) { @@ -290,6 +310,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) struct tracefs_fs_info *fsi = sb->s_fs_info; struct inode *inode = d_inode(sb->s_root); struct tracefs_mount_opts *opts = &fsi->mount_opts; + umode_t tmp_mode; /* * On remount, only reset mode/uid/gid if they were provided as mount @@ -297,8 +318,9 @@ static int tracefs_apply_options(struct super_block *sb, bool remount) */ if (!remount || opts->opts & BIT(Opt_mode)) { - inode->i_mode &= ~S_IALLUGO; - inode->i_mode |= opts->mode; + tmp_mode = READ_ONCE(inode->i_mode) & ~S_IALLUGO; + tmp_mode |= opts->mode; + WRITE_ONCE(inode->i_mode, tmp_mode); } if (!remount || opts->opts & BIT(Opt_uid)) @@ -346,11 +368,31 @@ static int tracefs_show_options(struct seq_file *m, struct dentry *root) } static const struct super_operations tracefs_super_operations = { + .alloc_inode = tracefs_alloc_inode, + .free_inode = tracefs_free_inode, + .drop_inode = generic_delete_inode, .statfs = simple_statfs, .remount_fs = tracefs_remount, .show_options = tracefs_show_options, }; +static void tracefs_dentry_iput(struct dentry *dentry, struct inode *inode) +{ + struct tracefs_inode *ti; + + if (!dentry || !inode) + return; + + ti = get_tracefs(inode); + if (ti && ti->flags & TRACEFS_EVENT_INODE) + eventfs_set_ef_status_free(ti, dentry); + iput(inode); +} + +static const struct dentry_operations tracefs_dentry_operations = { + .d_iput = tracefs_dentry_iput, +}; + static int trace_fill_super(struct super_block *sb, void *data, int silent) { static const struct tree_descr trace_files[] = {{""}}; @@ -373,6 +415,7 @@ static int trace_fill_super(struct super_block *sb, void *data, int silent) goto fail; sb->s_op = &tracefs_super_operations; + sb->s_d_op = &tracefs_dentry_operations; tracefs_apply_options(sb, false); @@ -399,7 +442,7 @@ static struct file_system_type trace_fs_type = { }; MODULE_ALIAS_FS("tracefs"); -static struct dentry *start_creating(const char *name, struct dentry *parent) +struct dentry *tracefs_start_creating(const char *name, struct dentry *parent) { struct dentry *dentry; int error; @@ -437,7 +480,7 @@ static struct dentry *start_creating(const char *name, struct dentry *parent) return dentry; } -static struct dentry *failed_creating(struct dentry *dentry) +struct dentry *tracefs_failed_creating(struct dentry *dentry) { inode_unlock(d_inode(dentry->d_parent)); dput(dentry); @@ -445,12 +488,86 @@ static struct dentry *failed_creating(struct dentry *dentry) return NULL; } -static struct dentry *end_creating(struct dentry *dentry) +struct dentry *tracefs_end_creating(struct dentry *dentry) { inode_unlock(d_inode(dentry->d_parent)); return dentry; } +/** + * eventfs_start_creating - start the process of creating a dentry + * @name: Name of the file created for the dentry + * @parent: The parent dentry where this dentry will be created + * + * This is a simple helper function for the dynamically created eventfs + * files. When the directory of the eventfs files are accessed, their + * dentries are created on the fly. This function is used to start that + * process. + */ +struct dentry *eventfs_start_creating(const char *name, struct dentry *parent) +{ + struct dentry *dentry; + int error; + + error = simple_pin_fs(&trace_fs_type, &tracefs_mount, + &tracefs_mount_count); + if (error) + return ERR_PTR(error); + + /* + * If the parent is not specified, we create it in the root. + * We need the root dentry to do this, which is in the super + * block. A pointer to that is in the struct vfsmount that we + * have around. + */ + if (!parent) + parent = tracefs_mount->mnt_root; + + if (unlikely(IS_DEADDIR(parent->d_inode))) + dentry = ERR_PTR(-ENOENT); + else + dentry = lookup_one_len(name, parent, strlen(name)); + + if (!IS_ERR(dentry) && dentry->d_inode) { + dput(dentry); + dentry = ERR_PTR(-EEXIST); + } + + if (IS_ERR(dentry)) + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + + return dentry; +} + +/** + * eventfs_failed_creating - clean up a failed eventfs dentry creation + * @dentry: The dentry to clean up + * + * If after calling eventfs_start_creating(), a failure is detected, the + * resources created by eventfs_start_creating() needs to be cleaned up. In + * that case, this function should be called to perform that clean up. + */ +struct dentry *eventfs_failed_creating(struct dentry *dentry) +{ + dput(dentry); + simple_release_fs(&tracefs_mount, &tracefs_mount_count); + return NULL; +} + +/** + * eventfs_end_creating - Finish the process of creating a eventfs dentry + * @dentry: The dentry that has successfully been created. + * + * This function is currently just a place holder to match + * eventfs_start_creating(). In case any synchronization needs to be added, + * this function will be used to implement that without having to modify + * the callers of eventfs_start_creating(). + */ +struct dentry *eventfs_end_creating(struct dentry *dentry) +{ + return dentry; +} + /** * tracefs_create_file - create a file in the tracefs filesystem * @name: a pointer to a string containing the name of the file to create. @@ -490,14 +607,14 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, if (!(mode & S_IFMT)) mode |= S_IFREG; BUG_ON(!S_ISREG(mode)); - dentry = start_creating(name, parent); + dentry = tracefs_start_creating(name, parent); if (IS_ERR(dentry)) return NULL; inode = tracefs_get_inode(dentry->d_sb); if (unlikely(!inode)) - return failed_creating(dentry); + return tracefs_failed_creating(dentry); inode->i_mode = mode; inode->i_fop = fops ? fops : &tracefs_file_operations; @@ -506,13 +623,13 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode, inode->i_gid = d_inode(dentry->d_parent)->i_gid; d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return tracefs_end_creating(dentry); } static struct dentry *__create_dir(const char *name, struct dentry *parent, const struct inode_operations *ops) { - struct dentry *dentry = start_creating(name, parent); + struct dentry *dentry = tracefs_start_creating(name, parent); struct inode *inode; if (IS_ERR(dentry)) @@ -520,7 +637,7 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, inode = tracefs_get_inode(dentry->d_sb); if (unlikely(!inode)) - return failed_creating(dentry); + return tracefs_failed_creating(dentry); /* Do not set bits for OTH */ inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; @@ -534,7 +651,7 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, d_instantiate(dentry, inode); inc_nlink(d_inode(dentry->d_parent)); fsnotify_mkdir(d_inode(dentry->d_parent), dentry); - return end_creating(dentry); + return tracefs_end_creating(dentry); } /** @@ -556,6 +673,9 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, */ struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) { + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + return __create_dir(name, parent, &simple_dir_inode_operations); } @@ -628,10 +748,26 @@ bool tracefs_initialized(void) return tracefs_registered; } +static void init_once(void *foo) +{ + struct tracefs_inode *ti = (struct tracefs_inode *) foo; + + inode_init_once(&ti->vfs_inode); +} + static int __init tracefs_init(void) { int retval; + tracefs_inode_cachep = kmem_cache_create("tracefs_inode_cache", + sizeof(struct tracefs_inode), + 0, (SLAB_RECLAIM_ACCOUNT| + SLAB_MEM_SPREAD| + SLAB_ACCOUNT), + init_once); + if (!tracefs_inode_cachep) + return -ENOMEM; + retval = sysfs_create_mount_point(kernel_kobj, "tracing"); if (retval) return -EINVAL; diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h new file mode 100644 index 0000000000000000000000000000000000000000..4f2e49e2197b14e70dcaf771ea131835e833a6ee --- /dev/null +++ b/fs/tracefs/internal.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _TRACEFS_INTERNAL_H +#define _TRACEFS_INTERNAL_H + +enum { + TRACEFS_EVENT_INODE = BIT(1), + TRACEFS_EVENT_TOP_INODE = BIT(2), +}; + +struct tracefs_inode { + unsigned long flags; + void *private; + struct inode vfs_inode; +}; + +static inline struct tracefs_inode *get_tracefs(const struct inode *inode) +{ + return container_of(inode, struct tracefs_inode, vfs_inode); +} + +struct dentry *tracefs_start_creating(const char *name, struct dentry *parent); +struct dentry *tracefs_end_creating(struct dentry *dentry); +struct dentry *tracefs_failed_creating(struct dentry *dentry); +struct inode *tracefs_get_inode(struct super_block *sb); +struct dentry *eventfs_start_creating(const char *name, struct dentry *parent); +struct dentry *eventfs_failed_creating(struct dentry *dentry); +struct dentry *eventfs_end_creating(struct dentry *dentry); +void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry); + +#endif /* _TRACEFS_INTERNAL_H */ diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig index c9d653168ad03bbe95e9bc738523ec0ae6e676ac..ed0bc8cbc703d9c345f121f1226e13f419b511d4 100644 --- a/fs/xfs/Kconfig +++ b/fs/xfs/Kconfig @@ -147,7 +147,7 @@ config XFS_ONLINE_SCRUB_STATS bool "XFS online metadata check usage data collection" default y depends on XFS_ONLINE_SCRUB - select FS_DEBUG + select XFS_DEBUG help If you say Y here, the kernel will gather usage data about the online metadata check subsystem. This includes the number diff --git a/fs/xfs/libxfs/xfs_log_recover.h b/fs/xfs/libxfs/xfs_log_recover.h index 2420865f300719d8974c316934c4d4013c9f42d9..a5100a11faf9cd26943a21410f6f7c0d9fb34e96 100644 --- a/fs/xfs/libxfs/xfs_log_recover.h +++ b/fs/xfs/libxfs/xfs_log_recover.h @@ -131,4 +131,26 @@ void xlog_check_buf_cancel_table(struct xlog *log); #define xlog_check_buf_cancel_table(log) do { } while (0) #endif +/* + * Transform a regular reservation into one suitable for recovery of a log + * intent item. + * + * Intent recovery only runs a single step of the transaction chain and defers + * the rest to a separate transaction. Therefore, we reduce logcount to 1 here + * to avoid livelocks if the log grant space is nearly exhausted due to the + * recovered intent pinning the tail. Keep the same logflags to avoid tripping + * asserts elsewhere. Struct copies abound below. + */ +static inline struct xfs_trans_res +xlog_recover_resv(const struct xfs_trans_res *r) +{ + struct xfs_trans_res ret = { + .tr_logres = r->tr_logres, + .tr_logcount = 1, + .tr_logflags = r->tr_logflags, + }; + + return ret; +} + #endif /* __XFS_LOG_RECOVER_H__ */ diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index 5e174685a77c5ee4f52984e71530a585bf011aff..6264daaab37b06151bd12abc2ef9f41f61fcc675 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -266,7 +266,8 @@ xfs_validate_sb_write( return -EFSCORRUPTED; } - if (xfs_sb_has_ro_compat_feature(sbp, XFS_SB_FEAT_RO_COMPAT_UNKNOWN)) { + if (!xfs_is_readonly(mp) && + xfs_sb_has_ro_compat_feature(sbp, XFS_SB_FEAT_RO_COMPAT_UNKNOWN)) { xfs_alert(mp, "Corruption detected in superblock read-only compatible features (0x%x)!", (sbp->sb_features_ro_compat & diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c index ad22656376d3f88521f98c2c5d9f573e7cd2aeca..6b2296ff248a4385c737c943064c9234dfef402f 100644 --- a/fs/xfs/libxfs/xfs_trans_inode.c +++ b/fs/xfs/libxfs/xfs_trans_inode.c @@ -62,12 +62,12 @@ xfs_trans_ichgtime( ASSERT(tp); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); - /* If the mtime changes, then ctime must also change */ - ASSERT(flags & XFS_ICHGTIME_CHG); + tv = current_time(inode); - tv = inode_set_ctime_current(inode); if (flags & XFS_ICHGTIME_MOD) inode->i_mtime = tv; + if (flags & XFS_ICHGTIME_CHG) + inode_set_ctime_to_ts(inode, tv); if (flags & XFS_ICHGTIME_CREATE) ip->i_crtime = tv; } diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c index 7d3aa14d81b55e9f0b867fb569da2ea11af61375..4849efcaa33aea500372195b1027976e6d2b0e58 100644 --- a/fs/xfs/scrub/scrub.c +++ b/fs/xfs/scrub/scrub.c @@ -588,6 +588,8 @@ xfs_scrub_metadata( out_teardown: error = xchk_teardown(sc, error); out_sc: + if (error != -ENOENT) + xchk_stats_merge(mp, sm, &run); kfree(sc); out: trace_xchk_done(XFS_I(file_inode(file)), sm, error); @@ -595,8 +597,6 @@ xfs_scrub_metadata( sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; error = 0; } - if (error != -ENOENT) - xchk_stats_merge(mp, sm, &run); return error; need_drain: error = xchk_teardown(sc, 0); diff --git a/fs/xfs/scrub/stats.c b/fs/xfs/scrub/stats.c index aeb92624176b9d1636ce10f14790efc480544478..cd91db4a554896fdcdd61f4824ae4047cab93017 100644 --- a/fs/xfs/scrub/stats.c +++ b/fs/xfs/scrub/stats.c @@ -185,7 +185,10 @@ xchk_stats_merge_one( { struct xchk_scrub_stats *css; - ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR); + if (sm->sm_type >= XFS_SCRUB_TYPE_NR) { + ASSERT(sm->sm_type < XFS_SCRUB_TYPE_NR); + return; + } css = &cs->cs_stats[sm->sm_type]; spin_lock(&css->css_lock); diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c index 5db87b34fb6e2067442e50d7e586605978e27b0a..89c7a9f4f93054d20b3504a8192fe16deee0dbda 100644 --- a/fs/xfs/xfs_attr_inactive.c +++ b/fs/xfs/xfs_attr_inactive.c @@ -333,7 +333,6 @@ xfs_attr_inactive( int error = 0; mp = dp->i_mount; - ASSERT(! XFS_NOT_DQATTACHED(mp, dp)); xfs_ilock(dp, lock_mode); if (!xfs_inode_has_attr_fork(dp)) diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c index 2788a6f2edcdb82b1765af1a7a939ac2420e8c71..36fe2abb16e6e35563a0f7d5ca8ed895c4ff2130 100644 --- a/fs/xfs/xfs_attr_item.c +++ b/fs/xfs/xfs_attr_item.c @@ -547,7 +547,7 @@ xfs_attri_item_recover( struct xfs_inode *ip; struct xfs_da_args *args; struct xfs_trans *tp; - struct xfs_trans_res tres; + struct xfs_trans_res resv; struct xfs_attri_log_format *attrp; struct xfs_attri_log_nameval *nv = attrip->attri_nameval; int error; @@ -618,8 +618,9 @@ xfs_attri_item_recover( goto out; } - xfs_init_attr_trans(args, &tres, &total); - error = xfs_trans_alloc(mp, &tres, total, 0, XFS_TRANS_RESERVE, &tp); + xfs_init_attr_trans(args, &resv, &total); + resv = xlog_recover_resv(&resv); + error = xfs_trans_alloc(mp, &resv, total, 0, XFS_TRANS_RESERVE, &tp); if (error) goto out; diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c index 7551c3ec4ea5d68eaba0d233ac7c4446d02614b8..e736a0844c89d655e717fec39f96b939e764dc73 100644 --- a/fs/xfs/xfs_bmap_item.c +++ b/fs/xfs/xfs_bmap_item.c @@ -490,6 +490,7 @@ xfs_bui_item_recover( struct list_head *capture_list) { struct xfs_bmap_intent fake = { }; + struct xfs_trans_res resv; struct xfs_bui_log_item *buip = BUI_ITEM(lip); struct xfs_trans *tp; struct xfs_inode *ip = NULL; @@ -515,7 +516,8 @@ xfs_bui_item_recover( return error; /* Allocate transaction and do the work. */ - error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); + error = xfs_trans_alloc(mp, &resv, XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK), 0, 0, &tp); if (error) goto err_rele; diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index afc4c78b9eed648ccfcb0611a3657eaca083f886..d5787991bb5b46477419074e1015ba1135945c7b 100644 --- a/fs/xfs/xfs_discard.c +++ b/fs/xfs/xfs_discard.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010, 2023 Red Hat, Inc. * All Rights Reserved. */ #include "xfs.h" @@ -19,21 +19,147 @@ #include "xfs_log.h" #include "xfs_ag.h" -STATIC int -xfs_trim_extents( +/* + * Notes on an efficient, low latency fstrim algorithm + * + * We need to walk the filesystem free space and issue discards on the free + * space that meet the search criteria (size and location). We cannot issue + * discards on extents that might be in use, or are so recently in use they are + * still marked as busy. To serialise against extent state changes whilst we are + * gathering extents to trim, we must hold the AGF lock to lock out other + * allocations and extent free operations that might change extent state. + * + * However, we cannot just hold the AGF for the entire AG free space walk whilst + * we issue discards on each free space that is found. Storage devices can have + * extremely slow discard implementations (e.g. ceph RBD) and so walking a + * couple of million free extents and issuing synchronous discards on each + * extent can take a *long* time. Whilst we are doing this walk, nothing else + * can access the AGF, and we can stall transactions and hence the log whilst + * modifications wait for the AGF lock to be released. This can lead hung tasks + * kicking the hung task timer and rebooting the system. This is bad. + * + * Hence we need to take a leaf from the bulkstat playbook. It takes the AGI + * lock, gathers a range of inode cluster buffers that are allocated, drops the + * AGI lock and then reads all the inode cluster buffers and processes them. It + * loops doing this, using a cursor to keep track of where it is up to in the AG + * for each iteration to restart the INOBT lookup from. + * + * We can't do this exactly with free space - once we drop the AGF lock, the + * state of the free extent is out of our control and we cannot run a discard + * safely on it in this situation. Unless, of course, we've marked the free + * extent as busy and undergoing a discard operation whilst we held the AGF + * locked. + * + * This is exactly how online discard works - free extents are marked busy when + * they are freed, and once the extent free has been committed to the journal, + * the busy extent record is marked as "undergoing discard" and the discard is + * then issued on the free extent. Once the discard completes, the busy extent + * record is removed and the extent is able to be allocated again. + * + * In the context of fstrim, if we find a free extent we need to discard, we + * don't have to discard it immediately. All we need to do it record that free + * extent as being busy and under discard, and all the allocation routines will + * now avoid trying to allocate it. Hence if we mark the extent as busy under + * the AGF lock, we can safely discard it without holding the AGF lock because + * nothing will attempt to allocate that free space until the discard completes. + * + * This also allows us to issue discards asynchronously like we do with online + * discard, and so for fast devices fstrim will run much faster as we can have + * multiple discard operations in flight at once, as well as pipeline the free + * extent search so that it overlaps in flight discard IO. + */ + +struct workqueue_struct *xfs_discard_wq; + +static void +xfs_discard_endio_work( + struct work_struct *work) +{ + struct xfs_busy_extents *extents = + container_of(work, struct xfs_busy_extents, endio_work); + + xfs_extent_busy_clear(extents->mount, &extents->extent_list, false); + kmem_free(extents->owner); +} + +/* + * Queue up the actual completion to a thread to avoid IRQ-safe locking for + * pagb_lock. + */ +static void +xfs_discard_endio( + struct bio *bio) +{ + struct xfs_busy_extents *extents = bio->bi_private; + + INIT_WORK(&extents->endio_work, xfs_discard_endio_work); + queue_work(xfs_discard_wq, &extents->endio_work); + bio_put(bio); +} + +/* + * Walk the discard list and issue discards on all the busy extents in the + * list. We plug and chain the bios so that we only need a single completion + * call to clear all the busy extents once the discards are complete. + */ +int +xfs_discard_extents( + struct xfs_mount *mp, + struct xfs_busy_extents *extents) +{ + struct xfs_extent_busy *busyp; + struct bio *bio = NULL; + struct blk_plug plug; + int error = 0; + + blk_start_plug(&plug); + list_for_each_entry(busyp, &extents->extent_list, list) { + trace_xfs_discard_extent(mp, busyp->agno, busyp->bno, + busyp->length); + + error = __blkdev_issue_discard(mp->m_ddev_targp->bt_bdev, + XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno), + XFS_FSB_TO_BB(mp, busyp->length), + GFP_NOFS, &bio); + if (error && error != -EOPNOTSUPP) { + xfs_info(mp, + "discard failed for extent [0x%llx,%u], error %d", + (unsigned long long)busyp->bno, + busyp->length, + error); + break; + } + } + + if (bio) { + bio->bi_private = extents; + bio->bi_end_io = xfs_discard_endio; + submit_bio(bio); + } else { + xfs_discard_endio_work(&extents->endio_work); + } + blk_finish_plug(&plug); + + return error; +} + + +static int +xfs_trim_gather_extents( struct xfs_perag *pag, xfs_daddr_t start, xfs_daddr_t end, xfs_daddr_t minlen, + struct xfs_alloc_rec_incore *tcur, + struct xfs_busy_extents *extents, uint64_t *blocks_trimmed) { struct xfs_mount *mp = pag->pag_mount; - struct block_device *bdev = mp->m_ddev_targp->bt_bdev; struct xfs_btree_cur *cur; struct xfs_buf *agbp; - struct xfs_agf *agf; int error; int i; + int batch = 100; /* * Force out the log. This means any transactions that might have freed @@ -45,20 +171,28 @@ xfs_trim_extents( error = xfs_alloc_read_agf(pag, NULL, 0, &agbp); if (error) return error; - agf = agbp->b_addr; cur = xfs_allocbt_init_cursor(mp, NULL, agbp, pag, XFS_BTNUM_CNT); /* - * Look up the longest btree in the AGF and start with it. + * Look up the extent length requested in the AGF and start with it. */ - error = xfs_alloc_lookup_ge(cur, 0, be32_to_cpu(agf->agf_longest), &i); + if (tcur->ar_startblock == NULLAGBLOCK) + error = xfs_alloc_lookup_ge(cur, 0, tcur->ar_blockcount, &i); + else + error = xfs_alloc_lookup_le(cur, tcur->ar_startblock, + tcur->ar_blockcount, &i); if (error) goto out_del_cursor; + if (i == 0) { + /* nothing of that length left in the AG, we are done */ + tcur->ar_blockcount = 0; + goto out_del_cursor; + } /* * Loop until we are done with all extents that are large - * enough to be worth discarding. + * enough to be worth discarding or we hit batch limits. */ while (i) { xfs_agblock_t fbno; @@ -73,7 +207,16 @@ xfs_trim_extents( error = -EFSCORRUPTED; break; } - ASSERT(flen <= be32_to_cpu(agf->agf_longest)); + + if (--batch <= 0) { + /* + * Update the cursor to point at this extent so we + * restart the next batch from this extent. + */ + tcur->ar_startblock = fbno; + tcur->ar_blockcount = flen; + break; + } /* * use daddr format for all range/len calculations as that is @@ -88,6 +231,7 @@ xfs_trim_extents( */ if (dlen < minlen) { trace_xfs_discard_toosmall(mp, pag->pag_agno, fbno, flen); + tcur->ar_blockcount = 0; break; } @@ -110,29 +254,103 @@ xfs_trim_extents( goto next_extent; } - trace_xfs_discard_extent(mp, pag->pag_agno, fbno, flen); - error = blkdev_issue_discard(bdev, dbno, dlen, GFP_NOFS); - if (error) - break; + xfs_extent_busy_insert_discard(pag, fbno, flen, + &extents->extent_list); *blocks_trimmed += flen; - next_extent: error = xfs_btree_decrement(cur, 0, &i); if (error) break; - if (fatal_signal_pending(current)) { - error = -ERESTARTSYS; - break; - } + /* + * If there's no more records in the tree, we are done. Set the + * cursor block count to 0 to indicate to the caller that there + * is no more extents to search. + */ + if (i == 0) + tcur->ar_blockcount = 0; } + /* + * If there was an error, release all the gathered busy extents because + * we aren't going to issue a discard on them any more. + */ + if (error) + xfs_extent_busy_clear(mp, &extents->extent_list, false); out_del_cursor: xfs_btree_del_cursor(cur, error); xfs_buf_relse(agbp); return error; } +static bool +xfs_trim_should_stop(void) +{ + return fatal_signal_pending(current) || freezing(current); +} + +/* + * Iterate the free list gathering extents and discarding them. We need a cursor + * for the repeated iteration of gather/discard loop, so use the longest extent + * we found in the last batch as the key to start the next. + */ +static int +xfs_trim_extents( + struct xfs_perag *pag, + xfs_daddr_t start, + xfs_daddr_t end, + xfs_daddr_t minlen, + uint64_t *blocks_trimmed) +{ + struct xfs_alloc_rec_incore tcur = { + .ar_blockcount = pag->pagf_longest, + .ar_startblock = NULLAGBLOCK, + }; + int error = 0; + + do { + struct xfs_busy_extents *extents; + + extents = kzalloc(sizeof(*extents), GFP_KERNEL); + if (!extents) { + error = -ENOMEM; + break; + } + + extents->mount = pag->pag_mount; + extents->owner = extents; + INIT_LIST_HEAD(&extents->extent_list); + + error = xfs_trim_gather_extents(pag, start, end, minlen, + &tcur, extents, blocks_trimmed); + if (error) { + kfree(extents); + break; + } + + /* + * We hand the extent list to the discard function here so the + * discarded extents can be removed from the busy extent list. + * This allows the discards to run asynchronously with gathering + * the next round of extents to discard. + * + * However, we must ensure that we do not reference the extent + * list after this function call, as it may have been freed by + * the time control returns to us. + */ + error = xfs_discard_extents(pag->pag_mount, extents); + if (error) + break; + + if (xfs_trim_should_stop()) + break; + + } while (tcur.ar_blockcount != 0); + + return error; + +} + /* * trim a range of the filesystem. * @@ -195,12 +413,12 @@ xfs_ioc_trim( for_each_perag_range(mp, agno, xfs_daddr_to_agno(mp, end), pag) { error = xfs_trim_extents(pag, start, end, minlen, &blocks_trimmed); - if (error) { + if (error) last_error = error; - if (error == -ERESTARTSYS) { - xfs_perag_rele(pag); - break; - } + + if (xfs_trim_should_stop()) { + xfs_perag_rele(pag); + break; } } diff --git a/fs/xfs/xfs_discard.h b/fs/xfs/xfs_discard.h index de92d9cc958ff353b2e5304025c98a0e614e4127..2b1a85223a56c6d37e9711c1025ce63bdb6b048d 100644 --- a/fs/xfs/xfs_discard.h +++ b/fs/xfs/xfs_discard.h @@ -3,8 +3,10 @@ #define XFS_DISCARD_H 1 struct fstrim_range; -struct list_head; +struct xfs_mount; +struct xfs_busy_extents; -extern int xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *); +int xfs_discard_extents(struct xfs_mount *mp, struct xfs_busy_extents *busy); +int xfs_ioc_trim(struct xfs_mount *mp, struct fstrim_range __user *fstrim); #endif /* XFS_DISCARD_H */ diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c index 1064c2342876807837bf400a87ac9d296bfec9fa..7cd09c3a82cb5013ced681bc389266d21da90a60 100644 --- a/fs/xfs/xfs_export.c +++ b/fs/xfs/xfs_export.c @@ -146,6 +146,20 @@ xfs_nfs_get_inode( return ERR_PTR(error); } + /* + * Reload the incore unlinked list to avoid failure in inodegc. + * Use an unlocked check here because unrecovered unlinked inodes + * should be somewhat rare. + */ + if (xfs_inode_unlinked_incomplete(ip)) { + error = xfs_inode_reload_unlinked(ip); + if (error) { + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + xfs_irele(ip); + return ERR_PTR(error); + } + } + if (VFS_I(ip)->i_generation != generation) { xfs_irele(ip); return ERR_PTR(-ESTALE); diff --git a/fs/xfs/xfs_extent_busy.c b/fs/xfs/xfs_extent_busy.c index 7c2fdc71e42d448ee87938270232bfbdc1377734..746814815b1da7fb8b1092f6772a6d6b26c99f62 100644 --- a/fs/xfs/xfs_extent_busy.c +++ b/fs/xfs/xfs_extent_busy.c @@ -19,13 +19,13 @@ #include "xfs_log.h" #include "xfs_ag.h" -void -xfs_extent_busy_insert( - struct xfs_trans *tp, +static void +xfs_extent_busy_insert_list( struct xfs_perag *pag, xfs_agblock_t bno, xfs_extlen_t len, - unsigned int flags) + unsigned int flags, + struct list_head *busy_list) { struct xfs_extent_busy *new; struct xfs_extent_busy *busyp; @@ -40,7 +40,7 @@ xfs_extent_busy_insert( new->flags = flags; /* trace before insert to be able to see failed inserts */ - trace_xfs_extent_busy(tp->t_mountp, pag->pag_agno, bno, len); + trace_xfs_extent_busy(pag->pag_mount, pag->pag_agno, bno, len); spin_lock(&pag->pagb_lock); rbp = &pag->pagb_tree.rb_node; @@ -62,10 +62,32 @@ xfs_extent_busy_insert( rb_link_node(&new->rb_node, parent, rbp); rb_insert_color(&new->rb_node, &pag->pagb_tree); - list_add(&new->list, &tp->t_busy); + list_add(&new->list, busy_list); spin_unlock(&pag->pagb_lock); } +void +xfs_extent_busy_insert( + struct xfs_trans *tp, + struct xfs_perag *pag, + xfs_agblock_t bno, + xfs_extlen_t len, + unsigned int flags) +{ + xfs_extent_busy_insert_list(pag, bno, len, flags, &tp->t_busy); +} + +void +xfs_extent_busy_insert_discard( + struct xfs_perag *pag, + xfs_agblock_t bno, + xfs_extlen_t len, + struct list_head *busy_list) +{ + xfs_extent_busy_insert_list(pag, bno, len, XFS_EXTENT_BUSY_DISCARDED, + busy_list); +} + /* * Search for a busy extent within the range of the extent we are about to * allocate. You need to be holding the busy extent tree lock when calling diff --git a/fs/xfs/xfs_extent_busy.h b/fs/xfs/xfs_extent_busy.h index c37bf87e6781b6aa09b794627a4d5b9cf30ed760..0639aab336f3f71c129713f3710e05115b7d38c3 100644 --- a/fs/xfs/xfs_extent_busy.h +++ b/fs/xfs/xfs_extent_busy.h @@ -16,9 +16,6 @@ struct xfs_alloc_arg; /* * Busy block/extent entry. Indexed by a rbtree in perag to mark blocks that * have been freed but whose transactions aren't committed to disk yet. - * - * Note that we use the transaction ID to record the transaction, not the - * transaction structure itself. See xfs_extent_busy_insert() for details. */ struct xfs_extent_busy { struct rb_node rb_node; /* ag by-bno indexed search tree */ @@ -31,10 +28,31 @@ struct xfs_extent_busy { #define XFS_EXTENT_BUSY_SKIP_DISCARD 0x02 /* do not discard */ }; +/* + * List used to track groups of related busy extents all the way through + * to discard completion. + */ +struct xfs_busy_extents { + struct xfs_mount *mount; + struct list_head extent_list; + struct work_struct endio_work; + + /* + * Owner is the object containing the struct xfs_busy_extents to free + * once the busy extents have been processed. If only the + * xfs_busy_extents object needs freeing, then point this at itself. + */ + void *owner; +}; + void xfs_extent_busy_insert(struct xfs_trans *tp, struct xfs_perag *pag, xfs_agblock_t bno, xfs_extlen_t len, unsigned int flags); +void +xfs_extent_busy_insert_discard(struct xfs_perag *pag, xfs_agblock_t bno, + xfs_extlen_t len, struct list_head *busy_list); + void xfs_extent_busy_clear(struct xfs_mount *mp, struct list_head *list, bool do_discard); diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index f1a5ecf099aabf6779a69a85a64b34207c2c8781..3fa8789820ad9d4e5014aa4e00b1e4c3dc3ee874 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -660,6 +660,7 @@ xfs_efi_item_recover( struct xfs_log_item *lip, struct list_head *capture_list) { + struct xfs_trans_res resv; struct xfs_efi_log_item *efip = EFI_ITEM(lip); struct xfs_mount *mp = lip->li_log->l_mp; struct xfs_efd_log_item *efdp; @@ -683,7 +684,8 @@ xfs_efi_item_recover( } } - error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp); + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); + error = xfs_trans_alloc(mp, &resv, 0, 0, 0, &tp); if (error) return error; efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents); diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c index 10403ba9b58f7d31d42ee2f0a80993f692557beb..736e5545f584025ec204dfc6059e83fc06e99bdb 100644 --- a/fs/xfs/xfs_fsmap.c +++ b/fs/xfs/xfs_fsmap.c @@ -565,6 +565,19 @@ xfs_getfsmap_rtdev_rtbitmap( } #endif /* CONFIG_XFS_RT */ +static inline bool +rmap_not_shareable(struct xfs_mount *mp, const struct xfs_rmap_irec *r) +{ + if (!xfs_has_reflink(mp)) + return true; + if (XFS_RMAP_NON_INODE_OWNER(r->rm_owner)) + return true; + if (r->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK | + XFS_RMAP_UNWRITTEN)) + return true; + return false; +} + /* Execute a getfsmap query against the regular data device. */ STATIC int __xfs_getfsmap_datadev( @@ -598,7 +611,6 @@ __xfs_getfsmap_datadev( * low to the fsmap low key and max out the high key to the end * of the AG. */ - info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb); info->low.rm_offset = XFS_BB_TO_FSBT(mp, keys[0].fmr_offset); error = xfs_fsmap_owner_to_rmap(&info->low, &keys[0]); if (error) @@ -608,12 +620,9 @@ __xfs_getfsmap_datadev( /* Adjust the low key if we are continuing from where we left off. */ if (info->low.rm_blockcount == 0) { - /* empty */ - } else if (XFS_RMAP_NON_INODE_OWNER(info->low.rm_owner) || - (info->low.rm_flags & (XFS_RMAP_ATTR_FORK | - XFS_RMAP_BMBT_BLOCK | - XFS_RMAP_UNWRITTEN))) { - info->low.rm_startblock += info->low.rm_blockcount; + /* No previous record from which to continue */ + } else if (rmap_not_shareable(mp, &info->low)) { + /* Last record seen was an unshareable extent */ info->low.rm_owner = 0; info->low.rm_offset = 0; @@ -621,8 +630,10 @@ __xfs_getfsmap_datadev( if (XFS_FSB_TO_DADDR(mp, start_fsb) >= eofs) return 0; } else { + /* Last record seen was a shareable file data extent */ info->low.rm_offset += info->low.rm_blockcount; } + info->low.rm_startblock = XFS_FSB_TO_AGBNO(mp, start_fsb); info->high.rm_startblock = -1U; info->high.rm_owner = ULLONG_MAX; diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index e541f5c0bc251c4a1e2d74b3094e32f30b6ccd94..3c210ac8371368bc00a77fb9df52315b710d17ce 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -113,7 +113,7 @@ xfs_inode_alloc( INIT_LIST_HEAD(&ip->i_ioend_list); spin_lock_init(&ip->i_ioend_lock); ip->i_next_unlinked = NULLAGINO; - ip->i_prev_unlinked = NULLAGINO; + ip->i_prev_unlinked = 0; return ip; } @@ -443,7 +443,7 @@ xfs_inodegc_queue_all( int cpu; bool ret = false; - for_each_online_cpu(cpu) { + for_each_cpu(cpu, &mp->m_inodegc_cpumask) { gc = per_cpu_ptr(mp->m_inodegc, cpu); if (!llist_empty(&gc->list)) { mod_delayed_work_on(cpu, mp->m_inodegc_wq, &gc->work, 0); @@ -463,7 +463,7 @@ xfs_inodegc_wait_all( int error = 0; flush_workqueue(mp->m_inodegc_wq); - for_each_online_cpu(cpu) { + for_each_cpu(cpu, &mp->m_inodegc_cpumask) { struct xfs_inodegc *gc; gc = per_cpu_ptr(mp->m_inodegc, cpu); @@ -1845,9 +1845,17 @@ xfs_inodegc_worker( struct xfs_inodegc, work); struct llist_node *node = llist_del_all(&gc->list); struct xfs_inode *ip, *n; + struct xfs_mount *mp = gc->mp; unsigned int nofs_flag; - ASSERT(gc->cpu == smp_processor_id()); + /* + * Clear the cpu mask bit and ensure that we have seen the latest + * update of the gc structure associated with this CPU. This matches + * with the release semantics used when setting the cpumask bit in + * xfs_inodegc_queue. + */ + cpumask_clear_cpu(gc->cpu, &mp->m_inodegc_cpumask); + smp_mb__after_atomic(); WRITE_ONCE(gc->items, 0); @@ -1862,7 +1870,7 @@ xfs_inodegc_worker( nofs_flag = memalloc_nofs_save(); ip = llist_entry(node, struct xfs_inode, i_gclist); - trace_xfs_inodegc_worker(ip->i_mount, READ_ONCE(gc->shrinker_hits)); + trace_xfs_inodegc_worker(mp, READ_ONCE(gc->shrinker_hits)); WRITE_ONCE(gc->shrinker_hits, 0); llist_for_each_entry_safe(ip, n, node, i_gclist) { @@ -2057,6 +2065,7 @@ xfs_inodegc_queue( struct xfs_inodegc *gc; int items; unsigned int shrinker_hits; + unsigned int cpu_nr; unsigned long queue_delay = 1; trace_xfs_inode_set_need_inactive(ip); @@ -2064,18 +2073,28 @@ xfs_inodegc_queue( ip->i_flags |= XFS_NEED_INACTIVE; spin_unlock(&ip->i_flags_lock); - gc = get_cpu_ptr(mp->m_inodegc); + cpu_nr = get_cpu(); + gc = this_cpu_ptr(mp->m_inodegc); llist_add(&ip->i_gclist, &gc->list); items = READ_ONCE(gc->items); WRITE_ONCE(gc->items, items + 1); shrinker_hits = READ_ONCE(gc->shrinker_hits); + /* + * Ensure the list add is always seen by anyone who finds the cpumask + * bit set. This effectively gives the cpumask bit set operation + * release ordering semantics. + */ + smp_mb__before_atomic(); + if (!cpumask_test_cpu(cpu_nr, &mp->m_inodegc_cpumask)) + cpumask_test_and_set_cpu(cpu_nr, &mp->m_inodegc_cpumask); + /* * We queue the work while holding the current CPU so that the work * is scheduled to run on this CPU. */ if (!xfs_is_inodegc_enabled(mp)) { - put_cpu_ptr(gc); + put_cpu(); return; } @@ -2085,7 +2104,7 @@ xfs_inodegc_queue( trace_xfs_inodegc_queue(mp, __return_address); mod_delayed_work_on(current_cpu(), mp->m_inodegc_wq, &gc->work, queue_delay); - put_cpu_ptr(gc); + put_cpu(); if (xfs_inodegc_want_flush_work(ip, items, shrinker_hits)) { trace_xfs_inodegc_throttle(mp, __return_address); @@ -2093,47 +2112,6 @@ xfs_inodegc_queue( } } -/* - * Fold the dead CPU inodegc queue into the current CPUs queue. - */ -void -xfs_inodegc_cpu_dead( - struct xfs_mount *mp, - unsigned int dead_cpu) -{ - struct xfs_inodegc *dead_gc, *gc; - struct llist_node *first, *last; - unsigned int count = 0; - - dead_gc = per_cpu_ptr(mp->m_inodegc, dead_cpu); - cancel_delayed_work_sync(&dead_gc->work); - - if (llist_empty(&dead_gc->list)) - return; - - first = dead_gc->list.first; - last = first; - while (last->next) { - last = last->next; - count++; - } - dead_gc->list.first = NULL; - dead_gc->items = 0; - - /* Add pending work to current CPU */ - gc = get_cpu_ptr(mp->m_inodegc); - llist_add_batch(first, last, &gc->list); - count += READ_ONCE(gc->items); - WRITE_ONCE(gc->items, count); - - if (xfs_is_inodegc_enabled(mp)) { - trace_xfs_inodegc_queue(mp, __return_address); - mod_delayed_work_on(current_cpu(), mp->m_inodegc_wq, &gc->work, - 0); - } - put_cpu_ptr(gc); -} - /* * We set the inode flag atomically with the radix tree tag. Once we get tag * lookups on the radix tree, this inode flag can go away. @@ -2195,7 +2173,7 @@ xfs_inodegc_shrinker_count( if (!xfs_is_inodegc_enabled(mp)) return 0; - for_each_online_cpu(cpu) { + for_each_cpu(cpu, &mp->m_inodegc_cpumask) { gc = per_cpu_ptr(mp->m_inodegc, cpu); if (!llist_empty(&gc->list)) return XFS_INODEGC_SHRINKER_COUNT; @@ -2220,7 +2198,7 @@ xfs_inodegc_shrinker_scan( trace_xfs_inodegc_shrinker_scan(mp, sc, __return_address); - for_each_online_cpu(cpu) { + for_each_cpu(cpu, &mp->m_inodegc_cpumask) { gc = per_cpu_ptr(mp->m_inodegc, cpu); if (!llist_empty(&gc->list)) { unsigned int h = READ_ONCE(gc->shrinker_hits); diff --git a/fs/xfs/xfs_icache.h b/fs/xfs/xfs_icache.h index 2fa6f2e09d078a2a884bf1bc88bec960eeea92aa..905944dafbe539245bc30fe2df2d8681af0c5a04 100644 --- a/fs/xfs/xfs_icache.h +++ b/fs/xfs/xfs_icache.h @@ -79,7 +79,6 @@ void xfs_inodegc_push(struct xfs_mount *mp); int xfs_inodegc_flush(struct xfs_mount *mp); void xfs_inodegc_stop(struct xfs_mount *mp); void xfs_inodegc_start(struct xfs_mount *mp); -void xfs_inodegc_cpu_dead(struct xfs_mount *mp, unsigned int cpu); int xfs_inodegc_register_shrinker(struct xfs_mount *mp); #endif diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 360fe83a334fc5dcf061d22628bee58d0baa3619..4d55f58d99b7ac0333b725fa7baf6f1f62b7fc98 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1642,8 +1642,11 @@ xfs_inode_needs_inactive( if (VFS_I(ip)->i_mode == 0) return false; - /* If this is a read-only mount, don't do this (would generate I/O) */ - if (xfs_is_readonly(mp)) + /* + * If this is a read-only mount, don't do this (would generate I/O) + * unless we're in log recovery and cleaning the iunlinked list. + */ + if (xfs_is_readonly(mp) && !xlog_recovery_needed(mp->m_log)) return false; /* If the log isn't running, push inodes straight to reclaim. */ @@ -1703,8 +1706,11 @@ xfs_inactive( mp = ip->i_mount; ASSERT(!xfs_iflags_test(ip, XFS_IRECOVERY)); - /* If this is a read-only mount, don't do this (would generate I/O) */ - if (xfs_is_readonly(mp)) + /* + * If this is a read-only mount, don't do this (would generate I/O) + * unless we're in log recovery and cleaning the iunlinked list. + */ + if (xfs_is_readonly(mp) && !xlog_recovery_needed(mp->m_log)) goto out; /* Metadata inodes require explicit resource cleanup. */ @@ -1736,9 +1742,21 @@ xfs_inactive( ip->i_df.if_nextents > 0 || ip->i_delayed_blks > 0)) truncate = 1; - error = xfs_qm_dqattach(ip); - if (error) - goto out; + if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) { + /* + * If this inode is being inactivated during a quotacheck and + * has not yet been scanned by quotacheck, we /must/ remove + * the dquots from the inode before inactivation changes the + * block and inode counts. Most probably this is a result of + * reloading the incore iunlinked list to purge unrecovered + * unlinked inodes. + */ + xfs_qm_dqdetach(ip); + } else { + error = xfs_qm_dqattach(ip); + if (error) + goto out; + } if (S_ISLNK(VFS_I(ip)->i_mode)) error = xfs_inactive_symlink(ip); @@ -1822,12 +1840,17 @@ xfs_iunlink_lookup( rcu_read_lock(); ip = radix_tree_lookup(&pag->pag_ici_root, agino); + if (!ip) { + /* Caller can handle inode not being in memory. */ + rcu_read_unlock(); + return NULL; + } /* - * Inode not in memory or in RCU freeing limbo should not happen. - * Warn about this and let the caller handle the failure. + * Inode in RCU freeing limbo should not happen. Warn about this and + * let the caller handle the failure. */ - if (WARN_ON_ONCE(!ip || !ip->i_ino)) { + if (WARN_ON_ONCE(!ip->i_ino)) { rcu_read_unlock(); return NULL; } @@ -1836,7 +1859,10 @@ xfs_iunlink_lookup( return ip; } -/* Update the prev pointer of the next agino. */ +/* + * Update the prev pointer of the next agino. Returns -ENOLINK if the inode + * is not in cache. + */ static int xfs_iunlink_update_backref( struct xfs_perag *pag, @@ -1851,7 +1877,8 @@ xfs_iunlink_update_backref( ip = xfs_iunlink_lookup(pag, next_agino); if (!ip) - return -EFSCORRUPTED; + return -ENOLINK; + ip->i_prev_unlinked = prev_agino; return 0; } @@ -1895,6 +1922,64 @@ xfs_iunlink_update_bucket( return 0; } +/* + * Load the inode @next_agino into the cache and set its prev_unlinked pointer + * to @prev_agino. Caller must hold the AGI to synchronize with other changes + * to the unlinked list. + */ +STATIC int +xfs_iunlink_reload_next( + struct xfs_trans *tp, + struct xfs_buf *agibp, + xfs_agino_t prev_agino, + xfs_agino_t next_agino) +{ + struct xfs_perag *pag = agibp->b_pag; + struct xfs_mount *mp = pag->pag_mount; + struct xfs_inode *next_ip = NULL; + xfs_ino_t ino; + int error; + + ASSERT(next_agino != NULLAGINO); + +#ifdef DEBUG + rcu_read_lock(); + next_ip = radix_tree_lookup(&pag->pag_ici_root, next_agino); + ASSERT(next_ip == NULL); + rcu_read_unlock(); +#endif + + xfs_info_ratelimited(mp, + "Found unrecovered unlinked inode 0x%x in AG 0x%x. Initiating recovery.", + next_agino, pag->pag_agno); + + /* + * Use an untrusted lookup just to be cautious in case the AGI has been + * corrupted and now points at a free inode. That shouldn't happen, + * but we'd rather shut down now since we're already running in a weird + * situation. + */ + ino = XFS_AGINO_TO_INO(mp, pag->pag_agno, next_agino); + error = xfs_iget(mp, tp, ino, XFS_IGET_UNTRUSTED, 0, &next_ip); + if (error) + return error; + + /* If this is not an unlinked inode, something is very wrong. */ + if (VFS_I(next_ip)->i_nlink != 0) { + error = -EFSCORRUPTED; + goto rele; + } + + next_ip->i_prev_unlinked = prev_agino; + trace_xfs_iunlink_reload_next(next_ip); +rele: + ASSERT(!(VFS_I(next_ip)->i_state & I_DONTCACHE)); + if (xfs_is_quotacheck_running(mp) && next_ip) + xfs_iflags_set(next_ip, XFS_IQUOTAUNCHECKED); + xfs_irele(next_ip); + return error; +} + static int xfs_iunlink_insert_inode( struct xfs_trans *tp, @@ -1926,6 +2011,8 @@ xfs_iunlink_insert_inode( * inode. */ error = xfs_iunlink_update_backref(pag, agino, next_agino); + if (error == -ENOLINK) + error = xfs_iunlink_reload_next(tp, agibp, agino, next_agino); if (error) return error; @@ -1941,6 +2028,7 @@ xfs_iunlink_insert_inode( } /* Point the head of the list to point to this inode. */ + ip->i_prev_unlinked = NULLAGINO; return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino); } @@ -2020,6 +2108,9 @@ xfs_iunlink_remove_inode( */ error = xfs_iunlink_update_backref(pag, ip->i_prev_unlinked, ip->i_next_unlinked); + if (error == -ENOLINK) + error = xfs_iunlink_reload_next(tp, agibp, ip->i_prev_unlinked, + ip->i_next_unlinked); if (error) return error; @@ -2040,7 +2131,7 @@ xfs_iunlink_remove_inode( } ip->i_next_unlinked = NULLAGINO; - ip->i_prev_unlinked = NULLAGINO; + ip->i_prev_unlinked = 0; return error; } @@ -3529,3 +3620,117 @@ xfs_iunlock2_io_mmap( if (ip1 != ip2) inode_unlock(VFS_I(ip1)); } + +/* + * Reload the incore inode list for this inode. Caller should ensure that + * the link count cannot change, either by taking ILOCK_SHARED or otherwise + * preventing other threads from executing. + */ +int +xfs_inode_reload_unlinked_bucket( + struct xfs_trans *tp, + struct xfs_inode *ip) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_buf *agibp; + struct xfs_agi *agi; + struct xfs_perag *pag; + xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino); + xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); + xfs_agino_t prev_agino, next_agino; + unsigned int bucket; + bool foundit = false; + int error; + + /* Grab the first inode in the list */ + pag = xfs_perag_get(mp, agno); + error = xfs_ialloc_read_agi(pag, tp, &agibp); + xfs_perag_put(pag); + if (error) + return error; + + /* + * We've taken ILOCK_SHARED and the AGI buffer lock to stabilize the + * incore unlinked list pointers for this inode. Check once more to + * see if we raced with anyone else to reload the unlinked list. + */ + if (!xfs_inode_unlinked_incomplete(ip)) { + foundit = true; + goto out_agibp; + } + + bucket = agino % XFS_AGI_UNLINKED_BUCKETS; + agi = agibp->b_addr; + + trace_xfs_inode_reload_unlinked_bucket(ip); + + xfs_info_ratelimited(mp, + "Found unrecovered unlinked inode 0x%x in AG 0x%x. Initiating list recovery.", + agino, agno); + + prev_agino = NULLAGINO; + next_agino = be32_to_cpu(agi->agi_unlinked[bucket]); + while (next_agino != NULLAGINO) { + struct xfs_inode *next_ip = NULL; + + /* Found this caller's inode, set its backlink. */ + if (next_agino == agino) { + next_ip = ip; + next_ip->i_prev_unlinked = prev_agino; + foundit = true; + goto next_inode; + } + + /* Try in-memory lookup first. */ + next_ip = xfs_iunlink_lookup(pag, next_agino); + if (next_ip) + goto next_inode; + + /* Inode not in memory, try reloading it. */ + error = xfs_iunlink_reload_next(tp, agibp, prev_agino, + next_agino); + if (error) + break; + + /* Grab the reloaded inode. */ + next_ip = xfs_iunlink_lookup(pag, next_agino); + if (!next_ip) { + /* No incore inode at all? We reloaded it... */ + ASSERT(next_ip != NULL); + error = -EFSCORRUPTED; + break; + } + +next_inode: + prev_agino = next_agino; + next_agino = next_ip->i_next_unlinked; + } + +out_agibp: + xfs_trans_brelse(tp, agibp); + /* Should have found this inode somewhere in the iunlinked bucket. */ + if (!error && !foundit) + error = -EFSCORRUPTED; + return error; +} + +/* Decide if this inode is missing its unlinked list and reload it. */ +int +xfs_inode_reload_unlinked( + struct xfs_inode *ip) +{ + struct xfs_trans *tp; + int error; + + error = xfs_trans_alloc_empty(ip->i_mount, &tp); + if (error) + return error; + + xfs_ilock(ip, XFS_ILOCK_SHARED); + if (xfs_inode_unlinked_incomplete(ip)) + error = xfs_inode_reload_unlinked_bucket(tp, ip); + xfs_iunlock(ip, XFS_ILOCK_SHARED); + xfs_trans_cancel(tp); + + return error; +} diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 7547caf2f2abd525010aa046d8b1e93e29d599dd..0c5bdb91152e1cf920cfb5cb76268d0b025640fc 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -68,8 +68,21 @@ typedef struct xfs_inode { uint64_t i_diflags2; /* XFS_DIFLAG2_... */ struct timespec64 i_crtime; /* time created */ - /* unlinked list pointers */ + /* + * Unlinked list pointers. These point to the next and previous inodes + * in the AGI unlinked bucket list, respectively. These fields can + * only be updated with the AGI locked. + * + * i_next_unlinked caches di_next_unlinked. + */ xfs_agino_t i_next_unlinked; + + /* + * If the inode is not on an unlinked list, this field is zero. If the + * inode is the first element in an unlinked list, this field is + * NULLAGINO. Otherwise, i_prev_unlinked points to the previous inode + * in the unlinked list. + */ xfs_agino_t i_prev_unlinked; /* VFS inode */ @@ -81,6 +94,11 @@ typedef struct xfs_inode { struct list_head i_ioend_list; } xfs_inode_t; +static inline bool xfs_inode_on_unlinked_list(const struct xfs_inode *ip) +{ + return ip->i_prev_unlinked != 0; +} + static inline bool xfs_inode_has_attr_fork(struct xfs_inode *ip) { return ip->i_forkoff > 0; @@ -326,6 +344,9 @@ static inline bool xfs_inode_has_large_extent_counts(struct xfs_inode *ip) */ #define XFS_INACTIVATING (1 << 13) +/* Quotacheck is running but inode has not been added to quota counts. */ +#define XFS_IQUOTAUNCHECKED (1 << 14) + /* All inode state flags related to inode reclaim. */ #define XFS_ALL_IRECLAIM_FLAGS (XFS_IRECLAIMABLE | \ XFS_IRECLAIM | \ @@ -340,7 +361,7 @@ static inline bool xfs_inode_has_large_extent_counts(struct xfs_inode *ip) #define XFS_IRECLAIM_RESET_FLAGS \ (XFS_IRECLAIMABLE | XFS_IRECLAIM | \ XFS_IDIRTY_RELEASE | XFS_ITRUNCATED | XFS_NEED_INACTIVE | \ - XFS_INACTIVATING) + XFS_INACTIVATING | XFS_IQUOTAUNCHECKED) /* * Flags for inode locking. @@ -575,4 +596,13 @@ void xfs_end_io(struct work_struct *work); int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2); void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2); +static inline bool +xfs_inode_unlinked_incomplete( + struct xfs_inode *ip) +{ + return VFS_I(ip)->i_nlink == 0 && !xfs_inode_on_unlinked_list(ip); +} +int xfs_inode_reload_unlinked_bucket(struct xfs_trans *tp, struct xfs_inode *ip); +int xfs_inode_reload_unlinked(struct xfs_inode *ip); + #endif /* __XFS_INODE_H__ */ diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 2ededd3f6b8ce861489a54038fa4d700637c845c..1c1e6171209def64e5bafb0d06c0906eff60344d 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -573,10 +573,10 @@ xfs_vn_getattr( stat->gid = vfsgid_into_kgid(vfsgid); stat->ino = ip->i_ino; stat->atime = inode->i_atime; + stat->mtime = inode->i_mtime; + stat->ctime = inode_get_ctime(inode); stat->blocks = XFS_FSB_TO_BB(mp, ip->i_nblocks + ip->i_delayed_blks); - fill_mg_cmtime(stat, request_mask, inode); - if (xfs_has_v3inodes(mp)) { if (request_mask & STATX_BTIME) { stat->result_mask |= STATX_BTIME; @@ -917,7 +917,7 @@ xfs_setattr_size( if (newsize != oldsize && !(iattr->ia_valid & (ATTR_CTIME | ATTR_MTIME))) { iattr->ia_ctime = iattr->ia_mtime = - current_mgtime(inode); + current_time(inode); iattr->ia_valid |= ATTR_CTIME | ATTR_MTIME; } diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index c2093cb56092bedb2058e3fdd9df13cbd099068b..f5377ba5967a7a30398b893dec0424c1caa96a2e 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -80,6 +80,17 @@ xfs_bulkstat_one_int( if (error) goto out; + /* Reload the incore unlinked list to avoid failure in inodegc. */ + if (xfs_inode_unlinked_incomplete(ip)) { + error = xfs_inode_reload_unlinked_bucket(tp, ip); + if (error) { + xfs_iunlock(ip, XFS_ILOCK_SHARED); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + xfs_irele(ip); + return error; + } + } + ASSERT(ip != NULL); ASSERT(ip->i_imap.im_blkno != 0); inode = VFS_I(ip); diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 79004d193e54dc2b06c796cef994d0bb1ba786eb..51c100c861770f619776a8e3efb921d0b2e55bd8 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -715,15 +715,7 @@ xfs_log_mount( * just worked. */ if (!xfs_has_norecovery(mp)) { - /* - * log recovery ignores readonly state and so we need to clear - * mount-based read only state so it can write to disk. - */ - bool readonly = test_and_clear_bit(XFS_OPSTATE_READONLY, - &mp->m_opstate); error = xlog_recover(log); - if (readonly) - set_bit(XFS_OPSTATE_READONLY, &mp->m_opstate); if (error) { xfs_warn(mp, "log mount/recovery failed: error %d", error); @@ -772,7 +764,6 @@ xfs_log_mount_finish( struct xfs_mount *mp) { struct xlog *log = mp->m_log; - bool readonly; int error = 0; if (xfs_has_norecovery(mp)) { @@ -780,12 +771,6 @@ xfs_log_mount_finish( return 0; } - /* - * log recovery ignores readonly state and so we need to clear - * mount-based read only state so it can write to disk. - */ - readonly = test_and_clear_bit(XFS_OPSTATE_READONLY, &mp->m_opstate); - /* * During the second phase of log recovery, we need iget and * iput to behave like they do for an active filesystem. @@ -835,8 +820,6 @@ xfs_log_mount_finish( xfs_buftarg_drain(mp->m_ddev_targp); clear_bit(XLOG_RECOVERY_NEEDED, &log->l_opstate); - if (readonly) - set_bit(XFS_OPSTATE_READONLY, &mp->m_opstate); /* Make sure the log is dead if we're returning failure. */ ASSERT(!error || xlog_is_shutdown(log)); diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index eccbfb99e89486cca1f1f185d2875d9421d44764..67a99d94701e5b08da7849ddc29a361a71e12f12 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -16,8 +16,7 @@ #include "xfs_log.h" #include "xfs_log_priv.h" #include "xfs_trace.h" - -struct workqueue_struct *xfs_discard_wq; +#include "xfs_discard.h" /* * Allocate a new ticket. Failing to get a new ticket makes it really hard to @@ -103,7 +102,7 @@ xlog_cil_ctx_alloc(void) ctx = kmem_zalloc(sizeof(*ctx), KM_NOFS); INIT_LIST_HEAD(&ctx->committing); - INIT_LIST_HEAD(&ctx->busy_extents); + INIT_LIST_HEAD(&ctx->busy_extents.extent_list); INIT_LIST_HEAD(&ctx->log_items); INIT_LIST_HEAD(&ctx->lv_chain); INIT_WORK(&ctx->push_work, xlog_cil_push_work); @@ -124,7 +123,7 @@ xlog_cil_push_pcp_aggregate( struct xlog_cil_pcp *cilpcp; int cpu; - for_each_online_cpu(cpu) { + for_each_cpu(cpu, &ctx->cil_pcpmask) { cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); ctx->ticket->t_curr_res += cilpcp->space_reserved; @@ -132,7 +131,7 @@ xlog_cil_push_pcp_aggregate( if (!list_empty(&cilpcp->busy_extents)) { list_splice_init(&cilpcp->busy_extents, - &ctx->busy_extents); + &ctx->busy_extents.extent_list); } if (!list_empty(&cilpcp->log_items)) list_splice_init(&cilpcp->log_items, &ctx->log_items); @@ -165,7 +164,13 @@ xlog_cil_insert_pcp_aggregate( if (!test_and_clear_bit(XLOG_CIL_PCP_SPACE, &cil->xc_flags)) return; - for_each_online_cpu(cpu) { + /* + * We can race with other cpus setting cil_pcpmask. However, we've + * atomically cleared PCP_SPACE which forces other threads to add to + * the global space used count. cil_pcpmask is a superset of cilpcp + * structures that could have a nonzero space_used. + */ + for_each_cpu(cpu, &ctx->cil_pcpmask) { int old, prev; cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); @@ -554,6 +559,7 @@ xlog_cil_insert_items( int iovhdr_res = 0, split_res = 0, ctx_res = 0; int space_used; int order; + unsigned int cpu_nr; struct xlog_cil_pcp *cilpcp; ASSERT(tp); @@ -577,7 +583,12 @@ xlog_cil_insert_items( * can't be scheduled away between split sample/update operations that * are done without outside locking to serialise them. */ - cilpcp = get_cpu_ptr(cil->xc_pcp); + cpu_nr = get_cpu(); + cilpcp = this_cpu_ptr(cil->xc_pcp); + + /* Tell the future push that there was work added by this CPU. */ + if (!cpumask_test_cpu(cpu_nr, &ctx->cil_pcpmask)) + cpumask_test_and_set_cpu(cpu_nr, &ctx->cil_pcpmask); /* * We need to take the CIL checkpoint unit reservation on the first @@ -663,7 +674,7 @@ xlog_cil_insert_items( continue; list_add_tail(&lip->li_cil, &cilpcp->log_items); } - put_cpu_ptr(cilpcp); + put_cpu(); /* * If we've overrun the reservation, dump the tx details before we move @@ -696,76 +707,6 @@ xlog_cil_free_logvec( } } -static void -xlog_discard_endio_work( - struct work_struct *work) -{ - struct xfs_cil_ctx *ctx = - container_of(work, struct xfs_cil_ctx, discard_endio_work); - struct xfs_mount *mp = ctx->cil->xc_log->l_mp; - - xfs_extent_busy_clear(mp, &ctx->busy_extents, false); - kmem_free(ctx); -} - -/* - * Queue up the actual completion to a thread to avoid IRQ-safe locking for - * pagb_lock. Note that we need a unbounded workqueue, otherwise we might - * get the execution delayed up to 30 seconds for weird reasons. - */ -static void -xlog_discard_endio( - struct bio *bio) -{ - struct xfs_cil_ctx *ctx = bio->bi_private; - - INIT_WORK(&ctx->discard_endio_work, xlog_discard_endio_work); - queue_work(xfs_discard_wq, &ctx->discard_endio_work); - bio_put(bio); -} - -static void -xlog_discard_busy_extents( - struct xfs_mount *mp, - struct xfs_cil_ctx *ctx) -{ - struct list_head *list = &ctx->busy_extents; - struct xfs_extent_busy *busyp; - struct bio *bio = NULL; - struct blk_plug plug; - int error = 0; - - ASSERT(xfs_has_discard(mp)); - - blk_start_plug(&plug); - list_for_each_entry(busyp, list, list) { - trace_xfs_discard_extent(mp, busyp->agno, busyp->bno, - busyp->length); - - error = __blkdev_issue_discard(mp->m_ddev_targp->bt_bdev, - XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno), - XFS_FSB_TO_BB(mp, busyp->length), - GFP_NOFS, &bio); - if (error && error != -EOPNOTSUPP) { - xfs_info(mp, - "discard failed for extent [0x%llx,%u], error %d", - (unsigned long long)busyp->bno, - busyp->length, - error); - break; - } - } - - if (bio) { - bio->bi_private = ctx; - bio->bi_end_io = xlog_discard_endio; - submit_bio(bio); - } else { - xlog_discard_endio_work(&ctx->discard_endio_work); - } - blk_finish_plug(&plug); -} - /* * Mark all items committed and clear busy extents. We free the log vector * chains in a separate pass so that we unpin the log items as quickly as @@ -795,8 +736,8 @@ xlog_cil_committed( xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, &ctx->lv_chain, ctx->start_lsn, abort); - xfs_extent_busy_sort(&ctx->busy_extents); - xfs_extent_busy_clear(mp, &ctx->busy_extents, + xfs_extent_busy_sort(&ctx->busy_extents.extent_list); + xfs_extent_busy_clear(mp, &ctx->busy_extents.extent_list, xfs_has_discard(mp) && !abort); spin_lock(&ctx->cil->xc_push_lock); @@ -805,10 +746,14 @@ xlog_cil_committed( xlog_cil_free_logvec(&ctx->lv_chain); - if (!list_empty(&ctx->busy_extents)) - xlog_discard_busy_extents(mp, ctx); - else - kmem_free(ctx); + if (!list_empty(&ctx->busy_extents.extent_list)) { + ctx->busy_extents.mount = mp; + ctx->busy_extents.owner = ctx; + xfs_discard_extents(mp, &ctx->busy_extents); + return; + } + + kmem_free(ctx); } void @@ -1790,38 +1735,6 @@ xlog_cil_force_seq( return 0; } -/* - * Move dead percpu state to the relevant CIL context structures. - * - * We have to lock the CIL context here to ensure that nothing is modifying - * the percpu state, either addition or removal. Both of these are done under - * the CIL context lock, so grabbing that exclusively here will ensure we can - * safely drain the cilpcp for the CPU that is dying. - */ -void -xlog_cil_pcp_dead( - struct xlog *log, - unsigned int cpu) -{ - struct xfs_cil *cil = log->l_cilp; - struct xlog_cil_pcp *cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); - struct xfs_cil_ctx *ctx; - - down_write(&cil->xc_ctx_lock); - ctx = cil->xc_ctx; - if (ctx->ticket) - ctx->ticket->t_curr_res += cilpcp->space_reserved; - cilpcp->space_reserved = 0; - - if (!list_empty(&cilpcp->log_items)) - list_splice_init(&cilpcp->log_items, &ctx->log_items); - if (!list_empty(&cilpcp->busy_extents)) - list_splice_init(&cilpcp->busy_extents, &ctx->busy_extents); - atomic_add(cilpcp->space_used, &ctx->space_used); - cilpcp->space_used = 0; - up_write(&cil->xc_ctx_lock); -} - /* * Perform initial CIL structure initialisation. */ diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index 1bd2963e8fbd10c0adcc02887382030e79ff37ae..fa3ad1d7b31c85b1196fc1b93ee416f21ab5e98b 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -6,6 +6,8 @@ #ifndef __XFS_LOG_PRIV_H__ #define __XFS_LOG_PRIV_H__ +#include "xfs_extent_busy.h" /* for struct xfs_busy_extents */ + struct xfs_buf; struct xlog; struct xlog_ticket; @@ -223,14 +225,19 @@ struct xfs_cil_ctx { struct xlog_in_core *commit_iclog; struct xlog_ticket *ticket; /* chkpt ticket */ atomic_t space_used; /* aggregate size of regions */ - struct list_head busy_extents; /* busy extents in chkpt */ + struct xfs_busy_extents busy_extents; struct list_head log_items; /* log items in chkpt */ struct list_head lv_chain; /* logvecs being pushed */ struct list_head iclog_entry; struct list_head committing; /* ctx committing list */ - struct work_struct discard_endio_work; struct work_struct push_work; atomic_t order_id; + + /* + * CPUs that could have added items to the percpu CIL data. Access is + * coordinated with xc_ctx_lock. + */ + struct cpumask cil_pcpmask; }; /* @@ -278,9 +285,6 @@ struct xfs_cil { wait_queue_head_t xc_push_wait; /* background push throttle */ void __percpu *xc_pcp; /* percpu CIL structures */ -#ifdef CONFIG_HOTPLUG_CPU - struct list_head xc_pcp_list; -#endif } ____cacheline_aligned_in_smp; /* xc_flags bit values */ @@ -705,9 +709,4 @@ xlog_kvmalloc( return p; } -/* - * CIL CPU dead notifier - */ -void xlog_cil_pcp_dead(struct xlog *log, unsigned int cpu); - #endif /* __XFS_LOG_PRIV_H__ */ diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 82c81d20459d147ce6f18574037b2850863d0176..13b94d2e605bd9ddb852f76eeeea0bd956a595e0 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -329,7 +329,7 @@ xlog_find_verify_cycle( * try a smaller size. We need to be able to read at least * a log sector, or we're out of luck. */ - bufblks = 1 << ffs(nbblks); + bufblks = roundup_pow_of_two(nbblks); while (bufblks > log->l_logBBsize) bufblks >>= 1; while (!(buffer = xlog_alloc_buffer(log, bufblks))) { @@ -1528,7 +1528,7 @@ xlog_write_log_records( * a smaller size. We need to be able to write at least a * log sector, or we're out of luck. */ - bufblks = 1 << ffs(blocks); + bufblks = roundup_pow_of_two(blocks); while (bufblks > log->l_logBBsize) bufblks >>= 1; while (!(buffer = xlog_alloc_buffer(log, bufblks))) { diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index a25eece3be2b9b53c1370c112957011046310d7a..d19cca099bc3a701786c3e5e9871f086ac3f290c 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -60,6 +60,7 @@ struct xfs_error_cfg { * Per-cpu deferred inode inactivation GC lists. */ struct xfs_inodegc { + struct xfs_mount *mp; struct llist_head list; struct delayed_work work; int error; @@ -67,9 +68,7 @@ struct xfs_inodegc { /* approximate count of inodes in the list */ unsigned int items; unsigned int shrinker_hits; -#if defined(DEBUG) || defined(XFS_WARN) unsigned int cpu; -#endif }; /* @@ -98,7 +97,6 @@ typedef struct xfs_mount { xfs_buftarg_t *m_ddev_targp; /* saves taking the address */ xfs_buftarg_t *m_logdev_targp;/* ptr to log device */ xfs_buftarg_t *m_rtdev_targp; /* ptr to rt device */ - struct list_head m_mount_list; /* global mount list */ void __percpu *m_inodegc; /* percpu inodegc structures */ /* @@ -249,6 +247,9 @@ typedef struct xfs_mount { unsigned int *m_errortag; struct xfs_kobj m_errortag_kobj; #endif + + /* cpus that have inodes queued for inactivation */ + struct cpumask m_inodegc_cpumask; } xfs_mount_t; #define M_IGEO(mp) (&(mp)->m_ino_geo) @@ -404,6 +405,8 @@ __XFS_HAS_FEAT(nouuid, NOUUID) #define XFS_OPSTATE_WARNED_SHRINK 8 /* Kernel has logged a warning about logged xattr updates being used. */ #define XFS_OPSTATE_WARNED_LARP 9 +/* Mount time quotacheck is running */ +#define XFS_OPSTATE_QUOTACHECK_RUNNING 10 #define __XFS_IS_OPSTATE(name, NAME) \ static inline bool xfs_is_ ## name (struct xfs_mount *mp) \ @@ -426,6 +429,11 @@ __XFS_IS_OPSTATE(inode32, INODE32) __XFS_IS_OPSTATE(readonly, READONLY) __XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED) __XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED) +#ifdef CONFIG_XFS_QUOTA +__XFS_IS_OPSTATE(quotacheck_running, QUOTACHECK_RUNNING) +#else +# define xfs_is_quotacheck_running(mp) (false) +#endif static inline bool xfs_should_warn(struct xfs_mount *mp, long nr) @@ -443,7 +451,8 @@ xfs_should_warn(struct xfs_mount *mp, long nr) { (1UL << XFS_OPSTATE_BLOCKGC_ENABLED), "blockgc" }, \ { (1UL << XFS_OPSTATE_WARNED_SCRUB), "wscrub" }, \ { (1UL << XFS_OPSTATE_WARNED_SHRINK), "wshrink" }, \ - { (1UL << XFS_OPSTATE_WARNED_LARP), "wlarp" } + { (1UL << XFS_OPSTATE_WARNED_LARP), "wlarp" }, \ + { (1UL << XFS_OPSTATE_QUOTACHECK_RUNNING), "quotacheck" } /* * Max and min values for mount-option defined I/O diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 6abcc34fafd8c2e807784908be3004c7e58ffda4..086e78a6143aa151c706eeaaf394fb7844875059 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1160,6 +1160,19 @@ xfs_qm_dqusage_adjust( if (error) return error; + /* + * Reload the incore unlinked list to avoid failure in inodegc. + * Use an unlocked check here because unrecovered unlinked inodes + * should be somewhat rare. + */ + if (xfs_inode_unlinked_incomplete(ip)) { + error = xfs_inode_reload_unlinked(ip); + if (error) { + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + goto error0; + } + } + ASSERT(ip->i_delayed_blks == 0); if (XFS_IS_REALTIME_INODE(ip)) { @@ -1173,6 +1186,7 @@ xfs_qm_dqusage_adjust( } nblks = (xfs_qcnt_t)ip->i_nblocks - rtblks; + xfs_iflags_clear(ip, XFS_IQUOTAUNCHECKED); /* * Add the (disk blocks and inode) resources occupied by this @@ -1319,8 +1333,10 @@ xfs_qm_quotacheck( flags |= XFS_PQUOTA_CHKD; } + xfs_set_quotacheck_running(mp); error = xfs_iwalk_threaded(mp, 0, 0, xfs_qm_dqusage_adjust, 0, true, NULL); + xfs_clear_quotacheck_running(mp); /* * On error, the inode walk may have partially populated the dquot diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c index edd8587658d5577a6c7a8d37a292808ebac94b55..2d4444d61e98766e0b49f1187634b470f66334c9 100644 --- a/fs/xfs/xfs_refcount_item.c +++ b/fs/xfs/xfs_refcount_item.c @@ -477,6 +477,7 @@ xfs_cui_item_recover( struct xfs_log_item *lip, struct list_head *capture_list) { + struct xfs_trans_res resv; struct xfs_cui_log_item *cuip = CUI_ITEM(lip); struct xfs_cud_log_item *cudp; struct xfs_trans *tp; @@ -514,8 +515,9 @@ xfs_cui_item_recover( * doesn't fit. We need to reserve enough blocks to handle a * full btree split on either end of the refcount range. */ - error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, - mp->m_refc_maxlevels * 2, 0, XFS_TRANS_RESERVE, &tp); + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); + error = xfs_trans_alloc(mp, &resv, mp->m_refc_maxlevels * 2, 0, + XFS_TRANS_RESERVE, &tp); if (error) return error; diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c index 520c7ebdfed8d1ab2b1b91a5e45dba19b2ac4907..0e0e747028da2e8638114b3268ee87490eed5f97 100644 --- a/fs/xfs/xfs_rmap_item.c +++ b/fs/xfs/xfs_rmap_item.c @@ -507,6 +507,7 @@ xfs_rui_item_recover( struct xfs_log_item *lip, struct list_head *capture_list) { + struct xfs_trans_res resv; struct xfs_rui_log_item *ruip = RUI_ITEM(lip); struct xfs_rud_log_item *rudp; struct xfs_trans *tp; @@ -530,8 +531,9 @@ xfs_rui_item_recover( } } - error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, - mp->m_rmap_maxlevels, 0, XFS_TRANS_RESERVE, &tp); + resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate); + error = xfs_trans_alloc(mp, &resv, mp->m_rmap_maxlevels, 0, + XFS_TRANS_RESERVE, &tp); if (error) return error; rudp = xfs_trans_get_rud(tp, ruip); diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index 1f77014c6e1abd136753807d37ac5cdf12099b76..819a3568b28f46fa9036d888ca0b8284d5f8fed0 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -56,28 +56,6 @@ static struct kset *xfs_kset; /* top-level xfs sysfs dir */ static struct xfs_kobj xfs_dbg_kobj; /* global debug sysfs attrs */ #endif -#ifdef CONFIG_HOTPLUG_CPU -static LIST_HEAD(xfs_mount_list); -static DEFINE_SPINLOCK(xfs_mount_list_lock); - -static inline void xfs_mount_list_add(struct xfs_mount *mp) -{ - spin_lock(&xfs_mount_list_lock); - list_add(&mp->m_mount_list, &xfs_mount_list); - spin_unlock(&xfs_mount_list_lock); -} - -static inline void xfs_mount_list_del(struct xfs_mount *mp) -{ - spin_lock(&xfs_mount_list_lock); - list_del(&mp->m_mount_list); - spin_unlock(&xfs_mount_list_lock); -} -#else /* !CONFIG_HOTPLUG_CPU */ -static inline void xfs_mount_list_add(struct xfs_mount *mp) {} -static inline void xfs_mount_list_del(struct xfs_mount *mp) {} -#endif - enum xfs_dax_mode { XFS_DAX_INODE = 0, XFS_DAX_ALWAYS = 1, @@ -1135,9 +1113,8 @@ xfs_inodegc_init_percpu( for_each_possible_cpu(cpu) { gc = per_cpu_ptr(mp->m_inodegc, cpu); -#if defined(DEBUG) || defined(XFS_WARN) gc->cpu = cpu; -#endif + gc->mp = mp; init_llist_head(&gc->list); gc->items = 0; gc->error = 0; @@ -1168,7 +1145,6 @@ xfs_fs_put_super( xfs_freesb(mp); xchk_mount_stats_free(mp); free_percpu(mp->m_stats.xs_stats); - xfs_mount_list_del(mp); xfs_inodegc_free_percpu(mp); xfs_destroy_percpu_counters(mp); xfs_destroy_mount_workqueues(mp); @@ -1577,13 +1553,6 @@ xfs_fs_fill_super( if (error) goto out_destroy_counters; - /* - * All percpu data structures requiring cleanup when a cpu goes offline - * must be allocated before adding this @mp to the cpu-dead handler's - * mount list. - */ - xfs_mount_list_add(mp); - /* Allocate stats memory before we do operations that might use it */ mp->m_stats.xs_stats = alloc_percpu(struct xfsstats); if (!mp->m_stats.xs_stats) { @@ -1781,7 +1750,6 @@ xfs_fs_fill_super( out_free_stats: free_percpu(mp->m_stats.xs_stats); out_destroy_inodegc: - xfs_mount_list_del(mp); xfs_inodegc_free_percpu(mp); out_destroy_counters: xfs_destroy_percpu_counters(mp); @@ -2065,7 +2033,7 @@ static struct file_system_type xfs_fs_type = { .init_fs_context = xfs_init_fs_context, .parameters = xfs_fs_parameters, .kill_sb = xfs_kill_sb, - .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP | FS_MGTIME, + .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, }; MODULE_ALIAS_FS("xfs"); @@ -2326,49 +2294,6 @@ xfs_destroy_workqueues(void) destroy_workqueue(xfs_alloc_wq); } -#ifdef CONFIG_HOTPLUG_CPU -static int -xfs_cpu_dead( - unsigned int cpu) -{ - struct xfs_mount *mp, *n; - - spin_lock(&xfs_mount_list_lock); - list_for_each_entry_safe(mp, n, &xfs_mount_list, m_mount_list) { - spin_unlock(&xfs_mount_list_lock); - xfs_inodegc_cpu_dead(mp, cpu); - xlog_cil_pcp_dead(mp->m_log, cpu); - spin_lock(&xfs_mount_list_lock); - } - spin_unlock(&xfs_mount_list_lock); - return 0; -} - -static int __init -xfs_cpu_hotplug_init(void) -{ - int error; - - error = cpuhp_setup_state_nocalls(CPUHP_XFS_DEAD, "xfs:dead", NULL, - xfs_cpu_dead); - if (error < 0) - xfs_alert(NULL, -"Failed to initialise CPU hotplug, error %d. XFS is non-functional.", - error); - return error; -} - -static void -xfs_cpu_hotplug_destroy(void) -{ - cpuhp_remove_state_nocalls(CPUHP_XFS_DEAD); -} - -#else /* !CONFIG_HOTPLUG_CPU */ -static inline int xfs_cpu_hotplug_init(void) { return 0; } -static inline void xfs_cpu_hotplug_destroy(void) {} -#endif - STATIC int __init init_xfs_fs(void) { @@ -2385,13 +2310,9 @@ init_xfs_fs(void) xfs_dir_startup(); - error = xfs_cpu_hotplug_init(); - if (error) - goto out; - error = xfs_init_caches(); if (error) - goto out_destroy_hp; + goto out; error = xfs_init_workqueues(); if (error) @@ -2475,8 +2396,6 @@ init_xfs_fs(void) xfs_destroy_workqueues(); out_destroy_caches: xfs_destroy_caches(); - out_destroy_hp: - xfs_cpu_hotplug_destroy(); out: return error; } @@ -2500,7 +2419,6 @@ exit_xfs_fs(void) xfs_destroy_workqueues(); xfs_destroy_caches(); xfs_uuid_table_free(); - xfs_cpu_hotplug_destroy(); } module_init(init_xfs_fs); diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 902c7f67a117f4831016d093773d73e5297e9a98..3926cf7f2a6ed189ccbf2d76f7b8e02db9e02294 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -3824,6 +3824,51 @@ TRACE_EVENT(xfs_iunlink_update_dinode, __entry->new_ptr) ); +TRACE_EVENT(xfs_iunlink_reload_next, + TP_PROTO(struct xfs_inode *ip), + TP_ARGS(ip), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_agnumber_t, agno) + __field(xfs_agino_t, agino) + __field(xfs_agino_t, prev_agino) + __field(xfs_agino_t, next_agino) + ), + TP_fast_assign( + __entry->dev = ip->i_mount->m_super->s_dev; + __entry->agno = XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino); + __entry->agino = XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino); + __entry->prev_agino = ip->i_prev_unlinked; + __entry->next_agino = ip->i_next_unlinked; + ), + TP_printk("dev %d:%d agno 0x%x agino 0x%x prev_unlinked 0x%x next_unlinked 0x%x", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->agno, + __entry->agino, + __entry->prev_agino, + __entry->next_agino) +); + +TRACE_EVENT(xfs_inode_reload_unlinked_bucket, + TP_PROTO(struct xfs_inode *ip), + TP_ARGS(ip), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_agnumber_t, agno) + __field(xfs_agino_t, agino) + ), + TP_fast_assign( + __entry->dev = ip->i_mount->m_super->s_dev; + __entry->agno = XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino); + __entry->agino = XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino); + ), + TP_printk("dev %d:%d agno 0x%x agino 0x%x bucket %u", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->agno, + __entry->agino, + __entry->agino % XFS_AGI_UNLINKED_BUCKETS) +); + DECLARE_EVENT_CLASS(xfs_ag_inode_class, TP_PROTO(struct xfs_inode *ip), TP_ARGS(ip), diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c index 43e5c219aaed568ffb5a711b4583b2cdf1723d9c..a3975f325f4ea83dde12540f7d854ab384d4fd4c 100644 --- a/fs/xfs/xfs_xattr.c +++ b/fs/xfs/xfs_xattr.c @@ -46,6 +46,17 @@ xfs_attr_grab_log_assist( if (xfs_sb_version_haslogxattrs(&mp->m_sb)) return 0; + /* + * Check if the filesystem featureset is new enough to set this log + * incompat feature bit. Strictly speaking, the minimum requirement is + * a V5 filesystem for the superblock field, but we'll require rmap + * or reflink to avoid having to deal with really old kernels. + */ + if (!xfs_has_reflink(mp) && !xfs_has_rmapbt(mp)) { + error = -EOPNOTSUPP; + goto drop_incompat; + } + /* Enable log-assisted xattrs. */ error = xfs_add_incompat_log_feature(mp, XFS_SB_FEAT_INCOMPAT_LOG_XATTRS); diff --git a/include/asm-generic/current.h b/include/asm-generic/current.h index 3a2e224b9fa09386bba7bfd7bb49de5bcede1669..9c2aeecbd05a66371b7473c7d46319a6b5eb4e0b 100644 --- a/include/asm-generic/current.h +++ b/include/asm-generic/current.h @@ -2,9 +2,11 @@ #ifndef __ASM_GENERIC_CURRENT_H #define __ASM_GENERIC_CURRENT_H +#ifndef __ASSEMBLY__ #include #define get_current() (current_thread_info()->task) #define current get_current() +#endif #endif /* __ASM_GENERIC_CURRENT_H */ diff --git a/include/asm-generic/hugetlb.h b/include/asm-generic/hugetlb.h index 4da02798a00bb6cbc8d5491ce89e72eb22616788..6dcf4d576970c4b43ce8be9e0373bc39d10e3518 100644 --- a/include/asm-generic/hugetlb.h +++ b/include/asm-generic/hugetlb.h @@ -76,7 +76,7 @@ static inline void hugetlb_free_pgd_range(struct mmu_gather *tlb, #ifndef __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pte) + pte_t *ptep, pte_t pte, unsigned long sz) { set_pte_at(mm, addr, ptep, pte); } diff --git a/include/asm-generic/hyperv-tlfs.h b/include/asm-generic/hyperv-tlfs.h index f4e4cc4f965f2a7707f0ff80ccc404e69d6d5993..fdac4a1714ec092647116c090db0e033a30aa5c8 100644 --- a/include/asm-generic/hyperv-tlfs.h +++ b/include/asm-generic/hyperv-tlfs.h @@ -223,6 +223,7 @@ enum HV_GENERIC_SET_FORMAT { #define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 +#define HV_STATUS_TIME_OUT 120 #define HV_STATUS_VTL_ALREADY_ENABLED 134 /* diff --git a/include/asm-generic/ide_iops.h b/include/asm-generic/ide_iops.h deleted file mode 100644 index 81dfa3ee5e0697b7e0880a85f80cc42f7315b497..0000000000000000000000000000000000000000 --- a/include/asm-generic/ide_iops.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Generic I/O and MEMIO string operations. */ - -#define __ide_insw insw -#define __ide_insl insl -#define __ide_outsw outsw -#define __ide_outsl outsl - -static __inline__ void __ide_mm_insw(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - *(u16 *)addr = readw(port); - addr += 2; - } -} - -static __inline__ void __ide_mm_insl(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - *(u32 *)addr = readl(port); - addr += 4; - } -} - -static __inline__ void __ide_mm_outsw(void __iomem *port, void *addr, u32 count) -{ - while (count--) { - writew(*(u16 *)addr, port); - addr += 2; - } -} - -static __inline__ void __ide_mm_outsl(void __iomem * port, void *addr, u32 count) -{ - while (count--) { - writel(*(u32 *)addr, port); - addr += 4; - } -} diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index a8f4b653ef4ee161abd5e3ef7fbe2de951332952..430f0ae0dde2d0046dd37067ed4403c213a2029a 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -36,15 +36,22 @@ struct ms_hyperv_info { u32 nested_features; u32 max_vp_index; u32 max_lp_index; - u32 isolation_config_a; + u8 vtl; + union { + u32 isolation_config_a; + struct { + u32 paravisor_present : 1; + u32 reserved_a1 : 31; + }; + }; union { u32 isolation_config_b; struct { u32 cvm_type : 4; - u32 reserved1 : 1; + u32 reserved_b1 : 1; u32 shared_gpa_boundary_active : 1; u32 shared_gpa_boundary_bits : 6; - u32 reserved2 : 20; + u32 reserved_b2 : 20; }; }; u64 shared_gpa_boundary; @@ -57,7 +64,8 @@ extern void * __percpu *hyperv_pcpu_output_arg; extern u64 hv_do_hypercall(u64 control, void *inputaddr, void *outputaddr); extern u64 hv_do_fast_hypercall8(u16 control, u64 input8); -extern bool hv_isolation_type_snp(void); +bool hv_isolation_type_snp(void); +bool hv_isolation_type_tdx(void); /* Helper functions that provide a consistent pattern for checking Hyper-V hypercall status. */ static inline int hv_result(u64 status) @@ -274,6 +282,7 @@ enum hv_isolation_type hv_get_isolation_type(void); bool hv_is_isolation_supported(void); bool hv_isolation_type_snp(void); u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size); +u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2); void hyperv_cleanup(void); bool hv_query_ext_cap(u64 cap_query); void hv_setup_dma_ops(struct device *dev, bool coherent); diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 9c59409104f69830fe229ef8c32df24cb9c03e07..67d8dd2f1bdec12a0d4a919641d9397aebcf85d3 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -138,13 +138,6 @@ * are handled as text/data or they can be discarded (which * often happens at runtime) */ -#ifdef CONFIG_HOTPLUG_CPU -#define CPU_KEEP(sec) *(.cpu##sec) -#define CPU_DISCARD(sec) -#else -#define CPU_KEEP(sec) -#define CPU_DISCARD(sec) *(.cpu##sec) -#endif #if defined(CONFIG_MEMORY_HOTPLUG) #define MEM_KEEP(sec) *(.mem##sec) diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index e0462361adf9ede869e4adea49dc8ad24c41094e..b5bf0b6da791c14c68062e9fb755643ac4db1c54 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -51,6 +51,20 @@ struct drm_exec { struct drm_gem_object *prelocked; }; +/** + * drm_exec_obj() - Return the object for a give drm_exec index + * @exec: Pointer to the drm_exec context + * @index: The index. + * + * Return: Pointer to the locked object corresponding to @index if + * index is within the number of locked objects. NULL otherwise. + */ +static inline struct drm_gem_object * +drm_exec_obj(struct drm_exec *exec, unsigned long index) +{ + return index < exec->num_objects ? exec->objects[index] : NULL; +} + /** * drm_exec_for_each_locked_object - iterate over all the locked objects * @exec: drm_exec object @@ -59,10 +73,23 @@ struct drm_exec { * * Iterate over all the locked GEM objects inside the drm_exec object. */ -#define drm_exec_for_each_locked_object(exec, index, obj) \ - for (index = 0, obj = (exec)->objects[0]; \ - index < (exec)->num_objects; \ - ++index, obj = (exec)->objects[index]) +#define drm_exec_for_each_locked_object(exec, index, obj) \ + for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index)) + +/** + * drm_exec_for_each_locked_object_reverse - iterate over all the locked + * objects in reverse locking order + * @exec: drm_exec object + * @index: unsigned long index for the iteration + * @obj: the current GEM object + * + * Iterate over all the locked GEM objects inside the drm_exec object in + * reverse locking order. Note that @index may go below zero and wrap, + * but that will be caught by drm_exec_obj(), returning a NULL object. + */ +#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \ + for ((index) = (exec)->num_objects - 1; \ + ((obj) = drm_exec_obj(exec, index)); --(index)) /** * drm_exec_until_all_locked - loop until all GEM objects are locked diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 514c8a7a32f0f03d043cee7f16c9391752187795..ba483c87f0e7bd049b056c029b231e62ca535843 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -3,6 +3,8 @@ #ifndef DRM_KUNIT_HELPERS_H_ #define DRM_KUNIT_HELPERS_H_ +#include + #include struct drm_device; @@ -51,7 +53,7 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, { struct drm_driver *driver; - driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); + driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, driver); driver->driver_features = features; diff --git a/include/dt-bindings/memory/mediatek,mt8188-memory-port.h b/include/dt-bindings/memory/mediatek,mt8188-memory-port.h new file mode 100644 index 0000000000000000000000000000000000000000..337ab11262af95b2465f1cb7f000e651c53b9253 --- /dev/null +++ b/include/dt-bindings/memory/mediatek,mt8188-memory-port.h @@ -0,0 +1,489 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Chengci Xu + */ +#ifndef _DT_BINDINGS_MEMORY_MEDIATEK_MT8188_LARB_PORT_H_ +#define _DT_BINDINGS_MEMORY_MEDIATEK_MT8188_LARB_PORT_H_ + +#include + +/* + * MM IOMMU larbs: + * From below, for example larb11 has larb11a/larb11b/larb11c, + * the index of larb is not in order. So we reindexed these larbs from a + * software view. + */ +#define SMI_L0_ID 0 +#define SMI_L1_ID 1 +#define SMI_L2_ID 2 +#define SMI_L3_ID 3 +#define SMI_L4_ID 4 +#define SMI_L5_ID 5 +#define SMI_L6_ID 6 +#define SMI_L7_ID 7 +#define SMI_L9_ID 8 +#define SMI_L10_ID 9 +#define SMI_L11A_ID 10 +#define SMI_L11B_ID 11 +#define SMI_L11C_ID 12 +#define SMI_L12_ID 13 +#define SMI_L13_ID 14 +#define SMI_L14_ID 15 +#define SMI_L15_ID 16 +#define SMI_L16A_ID 17 +#define SMI_L16B_ID 18 +#define SMI_L17A_ID 19 +#define SMI_L17B_ID 20 +#define SMI_L19_ID 21 +#define SMI_L21_ID 22 +#define SMI_L23_ID 23 +#define SMI_L27_ID 24 +#define SMI_L28_ID 25 + +/* + * MM IOMMU supports 16GB dma address. We separate it to four ranges: + * 0 ~ 4G; 4G ~ 8G; 8G ~ 12G; 12G ~ 16G, we could adjust these masters + * locate in anyone region. BUT: + * a) Make sure all the ports inside a larb are in one range. + * b) The iova of any master can NOT cross the 4G/8G/12G boundary. + * + * This is the suggested mapping in this SoC: + * + * modules dma-address-region larbs-ports + * disp 0 ~ 4G larb0/1/2/3 + * vcodec 4G ~ 8G larb19(21)[1]/21(22)/23 + * cam/mdp 8G ~ 12G the other larbs. + * N/A 12G ~ 16G + * CCU0 0x24000_0000 ~ 0x243ff_ffff larb27(24): port 0/1 + * CCU1 0x24400_0000 ~ 0x247ff_ffff larb27(24): port 2/3 + * + * This SoC have two MM IOMMU HWs, this is the connected information: + * iommu-vdo: larb0/2/5/9/10/11A/11C/13/16B/17B/19/21 + * iommu-vpp: larb1/3/4/6/7/11B/12/14/15/16A/17A/23/27 + * + * [1]: This is larb19, but the index is 21 from the SW view. + */ + +/* MM IOMMU ports */ +/* LARB 0 -- VDO-0 */ +#define M4U_PORT_L0_DISP_RDMA1 MTK_M4U_ID(SMI_L0_ID, 0) +#define M4U_PORT_L0_DISP_WDMA0 MTK_M4U_ID(SMI_L0_ID, 1) +#define M4U_PORT_L0_DISP_OVL0_RDMA0 MTK_M4U_ID(SMI_L0_ID, 2) +#define M4U_PORT_L0_DISP_OVL0_RDMA1 MTK_M4U_ID(SMI_L0_ID, 3) +#define M4U_PORT_L0_DISP_OVL0_HDR MTK_M4U_ID(SMI_L0_ID, 4) +#define M4U_PORT_L0_DISP_POSTMASK0 MTK_M4U_ID(SMI_L0_ID, 5) +#define M4U_PORT_L0_DISP_FAKE_ENG0 MTK_M4U_ID(SMI_L0_ID, 6) + +/* LARB 1 -- VD0-0 */ +#define M4U_PORT_L1_DISP_RDMA0 MTK_M4U_ID(SMI_L1_ID, 0) +#define M4U_PORT_L1_DISP_WDMA1 MTK_M4U_ID(SMI_L1_ID, 1) +#define M4U_PORT_L1_DISP_OVL1_RDMA0 MTK_M4U_ID(SMI_L1_ID, 2) +#define M4U_PORT_L1_DISP_OVL1_RDMA1 MTK_M4U_ID(SMI_L1_ID, 3) +#define M4U_PORT_L1_DISP_OVL1_HDR MTK_M4U_ID(SMI_L1_ID, 4) +#define M4U_PORT_L1_DISP_WROT0 MTK_M4U_ID(SMI_L1_ID, 5) +#define M4U_PORT_L1_DISP_FAKE_ENG1 MTK_M4U_ID(SMI_L1_ID, 6) + +/* LARB 2 -- VDO-1 */ +#define M4U_PORT_L2_MDP_RDMA0 MTK_M4U_ID(SMI_L2_ID, 0) +#define M4U_PORT_L2_MDP_RDMA2 MTK_M4U_ID(SMI_L2_ID, 1) +#define M4U_PORT_L2_MDP_RDMA4 MTK_M4U_ID(SMI_L2_ID, 2) +#define M4U_PORT_L2_MDP_RDMA6 MTK_M4U_ID(SMI_L2_ID, 3) +#define M4U_PORT_L2_DISP_FAKE1 MTK_M4U_ID(SMI_L2_ID, 4) + +/* LARB 3 -- VDO-1 */ +#define M4U_PORT_L3_MDP_RDMA1 MTK_M4U_ID(SMI_L3_ID, 0) +#define M4U_PORT_L3_MDP_RDMA3 MTK_M4U_ID(SMI_L3_ID, 1) +#define M4U_PORT_L3_MDP_RDMA5 MTK_M4U_ID(SMI_L3_ID, 2) +#define M4U_PORT_L3_MDP_RDMA7 MTK_M4U_ID(SMI_L3_ID, 3) +#define M4U_PORT_L3_HDR_DS_SMI MTK_M4U_ID(SMI_L3_ID, 4) +#define M4U_PORT_L3_HDR_ADL_SMI MTK_M4U_ID(SMI_L3_ID, 5) +#define M4U_PORT_L3_DISP_FAKE1 MTK_M4U_ID(SMI_L3_ID, 6) + +/* LARB 4 -- VPP-0 */ +#define M4U_PORT_L4_MDP_RDMA MTK_M4U_ID(SMI_L4_ID, 0) +#define M4U_PORT_L4_MDP_FG MTK_M4U_ID(SMI_L4_ID, 1) +#define M4U_PORT_L4_MDP_OVL MTK_M4U_ID(SMI_L4_ID, 2) +#define M4U_PORT_L4_MDP_WROT MTK_M4U_ID(SMI_L4_ID, 3) +#define M4U_PORT_L4_FAKE_ENG MTK_M4U_ID(SMI_L4_ID, 4) +#define M4U_PORT_L4_DISP_RDMA MTK_M4U_ID(SMI_L4_ID, 5) +#define M4U_PORT_L4_DISP_WDMA MTK_M4U_ID(SMI_L4_ID, 6) + +/* LARB 5 -- VPP-1 */ +#define M4U_PORT_L5_SVPP1_MDP_RDMA MTK_M4U_ID(SMI_L5_ID, 0) +#define M4U_PORT_L5_SVPP1_MDP_FG MTK_M4U_ID(SMI_L5_ID, 1) +#define M4U_PORT_L5_SVPP1_MDP_OVL MTK_M4U_ID(SMI_L5_ID, 2) +#define M4U_PORT_L5_SVPP1_MDP_WROT MTK_M4U_ID(SMI_L5_ID, 3) +#define M4U_PORT_L5_SVPP2_MDP_RDMA MTK_M4U_ID(SMI_L5_ID, 4) +#define M4U_PORT_L5_SVPP2_MDP_FG MTK_M4U_ID(SMI_L5_ID, 5) +#define M4U_PORT_L5_SVPP2_MDP_WROT MTK_M4U_ID(SMI_L5_ID, 6) +#define M4U_PORT_L5_LARB5_FAKE_ENG MTK_M4U_ID(SMI_L5_ID, 7) + +/* LARB 6 -- VPP-1 */ +#define M4U_PORT_L6_SVPP3_MDP_RDMA MTK_M4U_ID(SMI_L6_ID, 0) +#define M4U_PORT_L6_SVPP3_MDP_FG MTK_M4U_ID(SMI_L6_ID, 1) +#define M4U_PORT_L6_SVPP3_MDP_WROT MTK_M4U_ID(SMI_L6_ID, 2) +#define M4U_PORT_L6_LARB6_FAKE_ENG MTK_M4U_ID(SMI_L6_ID, 3) + +/* LARB 7 -- WPE */ +#define M4U_PORT_L7_WPE_RDMA_0 MTK_M4U_ID(SMI_L7_ID, 0) +#define M4U_PORT_L7_WPE_RDMA_1 MTK_M4U_ID(SMI_L7_ID, 1) +#define M4U_PORT_L7_WPE_WDMA_0 MTK_M4U_ID(SMI_L7_ID, 2) + +/* LARB 9 -- IMG-M */ +#define M4U_PORT_L9_IMGI_T1_A MTK_M4U_ID(SMI_L9_ID, 0) +#define M4U_PORT_L9_UFDI_T1_A MTK_M4U_ID(SMI_L9_ID, 1) +#define M4U_PORT_L9_IMGBI_T1_A MTK_M4U_ID(SMI_L9_ID, 2) +#define M4U_PORT_L9_IMGCI_T1_A MTK_M4U_ID(SMI_L9_ID, 3) +#define M4U_PORT_L9_SMTI_T1_A MTK_M4U_ID(SMI_L9_ID, 4) +#define M4U_PORT_L9_SMTI_T4_A MTK_M4U_ID(SMI_L9_ID, 5) +#define M4U_PORT_L9_TNCSTI_T1_A MTK_M4U_ID(SMI_L9_ID, 6) +#define M4U_PORT_L9_TNCSTI_T4_A MTK_M4U_ID(SMI_L9_ID, 7) +#define M4U_PORT_L9_YUVO_T1_A MTK_M4U_ID(SMI_L9_ID, 8) +#define M4U_PORT_L9_YUVBO_T1_A MTK_M4U_ID(SMI_L9_ID, 9) +#define M4U_PORT_L9_YUVCO_T1_A MTK_M4U_ID(SMI_L9_ID, 10) +#define M4U_PORT_L9_TIMGO_T1_A MTK_M4U_ID(SMI_L9_ID, 11) +#define M4U_PORT_L9_YUVO_T2_A MTK_M4U_ID(SMI_L9_ID, 12) +#define M4U_PORT_L9_YUVO_T5_A MTK_M4U_ID(SMI_L9_ID, 13) +#define M4U_PORT_L9_IMGI_T1_B MTK_M4U_ID(SMI_L9_ID, 14) +#define M4U_PORT_L9_IMGBI_T1_B MTK_M4U_ID(SMI_L9_ID, 15) +#define M4U_PORT_L9_IMGCI_T1_B MTK_M4U_ID(SMI_L9_ID, 16) +#define M4U_PORT_L9_SMTI_T4_B MTK_M4U_ID(SMI_L9_ID, 17) +#define M4U_PORT_L9_TNCSO_T1_A MTK_M4U_ID(SMI_L9_ID, 18) +#define M4U_PORT_L9_SMTO_T1_A MTK_M4U_ID(SMI_L9_ID, 19) +#define M4U_PORT_L9_SMTO_T4_A MTK_M4U_ID(SMI_L9_ID, 20) +#define M4U_PORT_L9_TNCSTO_T1_A MTK_M4U_ID(SMI_L9_ID, 21) +#define M4U_PORT_L9_YUVO_T2_B MTK_M4U_ID(SMI_L9_ID, 22) +#define M4U_PORT_L9_YUVO_T5_B MTK_M4U_ID(SMI_L9_ID, 23) +#define M4U_PORT_L9_SMTO_T4_B MTK_M4U_ID(SMI_L9_ID, 24) + +/* LARB 10 -- IMG-D */ +#define M4U_PORT_L10_IMGI_D1 MTK_M4U_ID(SMI_L10_ID, 0) +#define M4U_PORT_L10_IMGBI_D1 MTK_M4U_ID(SMI_L10_ID, 1) +#define M4U_PORT_L10_IMGCI_D1 MTK_M4U_ID(SMI_L10_ID, 2) +#define M4U_PORT_L10_IMGDI_D1 MTK_M4U_ID(SMI_L10_ID, 3) +#define M4U_PORT_L10_DEPI_D1 MTK_M4U_ID(SMI_L10_ID, 4) +#define M4U_PORT_L10_DMGI_D1 MTK_M4U_ID(SMI_L10_ID, 5) +#define M4U_PORT_L10_SMTI_D1 MTK_M4U_ID(SMI_L10_ID, 6) +#define M4U_PORT_L10_RECI_D1 MTK_M4U_ID(SMI_L10_ID, 7) +#define M4U_PORT_L10_RECI_D1_N MTK_M4U_ID(SMI_L10_ID, 8) +#define M4U_PORT_L10_TNRWI_D1 MTK_M4U_ID(SMI_L10_ID, 9) +#define M4U_PORT_L10_TNRCI_D1 MTK_M4U_ID(SMI_L10_ID, 10) +#define M4U_PORT_L10_TNRCI_D1_N MTK_M4U_ID(SMI_L10_ID, 11) +#define M4U_PORT_L10_IMG4O_D1 MTK_M4U_ID(SMI_L10_ID, 12) +#define M4U_PORT_L10_IMG4BO_D1 MTK_M4U_ID(SMI_L10_ID, 13) +#define M4U_PORT_L10_SMTI_D8 MTK_M4U_ID(SMI_L10_ID, 14) +#define M4U_PORT_L10_SMTO_D1 MTK_M4U_ID(SMI_L10_ID, 15) +#define M4U_PORT_L10_TNRMO_D1 MTK_M4U_ID(SMI_L10_ID, 16) +#define M4U_PORT_L10_TNRMO_D1_N MTK_M4U_ID(SMI_L10_ID, 17) +#define M4U_PORT_L10_SMTO_D8 MTK_M4U_ID(SMI_L10_ID, 18) +#define M4U_PORT_L10_DBGO_D1 MTK_M4U_ID(SMI_L10_ID, 19) + +/* LARB 11A -- IMG-D */ +#define M4U_PORT_L11A_WPE_RDMA_0 MTK_M4U_ID(SMI_L11A_ID, 0) +#define M4U_PORT_L11A_WPE_RDMA_1 MTK_M4U_ID(SMI_L11A_ID, 1) +#define M4U_PORT_L11A_WPE_RDMA_4P_0 MTK_M4U_ID(SMI_L11A_ID, 2) +#define M4U_PORT_L11A_WPE_RDMA_4P_1 MTK_M4U_ID(SMI_L11A_ID, 3) +#define M4U_PORT_L11A_WPE_CQ0 MTK_M4U_ID(SMI_L11A_ID, 4) +#define M4U_PORT_L11A_WPE_CQ1 MTK_M4U_ID(SMI_L11A_ID, 5) +#define M4U_PORT_L11A_PIMGI_P1 MTK_M4U_ID(SMI_L11A_ID, 6) +#define M4U_PORT_L11A_PIMGBI_P1 MTK_M4U_ID(SMI_L11A_ID, 7) +#define M4U_PORT_L11A_PIMGCI_P1 MTK_M4U_ID(SMI_L11A_ID, 8) +#define M4U_PORT_L11A_IMGI_T1_C MTK_M4U_ID(SMI_L11A_ID, 9) +#define M4U_PORT_L11A_IMGBI_T1_C MTK_M4U_ID(SMI_L11A_ID, 10) +#define M4U_PORT_L11A_IMGCI_T1_C MTK_M4U_ID(SMI_L11A_ID, 11) +#define M4U_PORT_L11A_SMTI_T1_C MTK_M4U_ID(SMI_L11A_ID, 12) +#define M4U_PORT_L11A_SMTI_T4_C MTK_M4U_ID(SMI_L11A_ID, 13) +#define M4U_PORT_L11A_SMTI_T6_C MTK_M4U_ID(SMI_L11A_ID, 14) +#define M4U_PORT_L11A_YUVO_T1_C MTK_M4U_ID(SMI_L11A_ID, 15) +#define M4U_PORT_L11A_YUVBO_T1_C MTK_M4U_ID(SMI_L11A_ID, 16) +#define M4U_PORT_L11A_YUVCO_T1_C MTK_M4U_ID(SMI_L11A_ID, 17) +#define M4U_PORT_L11A_WPE_WDMA_0 MTK_M4U_ID(SMI_L11A_ID, 18) +#define M4U_PORT_L11A_WPE_WDMA_4P_0 MTK_M4U_ID(SMI_L11A_ID, 19) +#define M4U_PORT_L11A_WROT_P1 MTK_M4U_ID(SMI_L11A_ID, 20) +#define M4U_PORT_L11A_TCCSO_P1 MTK_M4U_ID(SMI_L11A_ID, 21) +#define M4U_PORT_L11A_TCCSI_P1 MTK_M4U_ID(SMI_L11A_ID, 22) +#define M4U_PORT_L11A_TIMGO_T1_C MTK_M4U_ID(SMI_L11A_ID, 23) +#define M4U_PORT_L11A_YUVO_T2_C MTK_M4U_ID(SMI_L11A_ID, 24) +#define M4U_PORT_L11A_YUVO_T5_C MTK_M4U_ID(SMI_L11A_ID, 25) +#define M4U_PORT_L11A_SMTO_T1_C MTK_M4U_ID(SMI_L11A_ID, 26) +#define M4U_PORT_L11A_SMTO_T4_C MTK_M4U_ID(SMI_L11A_ID, 27) +#define M4U_PORT_L11A_SMTO_T6_C MTK_M4U_ID(SMI_L11A_ID, 28) +#define M4U_PORT_L11A_DBGO_T1_C MTK_M4U_ID(SMI_L11A_ID, 29) + +/* LARB 11B -- IMG-D */ +#define M4U_PORT_L11B_WPE_RDMA_0 MTK_M4U_ID(SMI_L11B_ID, 0) +#define M4U_PORT_L11B_WPE_RDMA_1 MTK_M4U_ID(SMI_L11B_ID, 1) +#define M4U_PORT_L11B_WPE_RDMA_4P_0 MTK_M4U_ID(SMI_L11B_ID, 2) +#define M4U_PORT_L11B_WPE_RDMA_4P_1 MTK_M4U_ID(SMI_L11B_ID, 3) +#define M4U_PORT_L11B_WPE_CQ0 MTK_M4U_ID(SMI_L11B_ID, 4) +#define M4U_PORT_L11B_WPE_CQ1 MTK_M4U_ID(SMI_L11B_ID, 5) +#define M4U_PORT_L11B_PIMGI_P1 MTK_M4U_ID(SMI_L11B_ID, 6) +#define M4U_PORT_L11B_PIMGBI_P1 MTK_M4U_ID(SMI_L11B_ID, 7) +#define M4U_PORT_L11B_PIMGCI_P1 MTK_M4U_ID(SMI_L11B_ID, 8) +#define M4U_PORT_L11B_IMGI_T1_C MTK_M4U_ID(SMI_L11B_ID, 9) +#define M4U_PORT_L11B_IMGBI_T1_C MTK_M4U_ID(SMI_L11B_ID, 10) +#define M4U_PORT_L11B_IMGCI_T1_C MTK_M4U_ID(SMI_L11B_ID, 11) +#define M4U_PORT_L11B_SMTI_T1_C MTK_M4U_ID(SMI_L11B_ID, 12) +#define M4U_PORT_L11B_SMTI_T4_C MTK_M4U_ID(SMI_L11B_ID, 13) +#define M4U_PORT_L11B_SMTI_T6_C MTK_M4U_ID(SMI_L11B_ID, 14) +#define M4U_PORT_L11B_YUVO_T1_C MTK_M4U_ID(SMI_L11B_ID, 15) +#define M4U_PORT_L11B_YUVBO_T1_C MTK_M4U_ID(SMI_L11B_ID, 16) +#define M4U_PORT_L11B_YUVCO_T1_C MTK_M4U_ID(SMI_L11B_ID, 17) +#define M4U_PORT_L11B_WPE_WDMA_0 MTK_M4U_ID(SMI_L11B_ID, 18) +#define M4U_PORT_L11B_WPE_WDMA_4P_0 MTK_M4U_ID(SMI_L11B_ID, 19) +#define M4U_PORT_L11B_WROT_P1 MTK_M4U_ID(SMI_L11B_ID, 20) +#define M4U_PORT_L11B_TCCSO_P1 MTK_M4U_ID(SMI_L11B_ID, 21) +#define M4U_PORT_L11B_TCCSI_P1 MTK_M4U_ID(SMI_L11B_ID, 22) +#define M4U_PORT_L11B_TIMGO_T1_C MTK_M4U_ID(SMI_L11B_ID, 23) +#define M4U_PORT_L11B_YUVO_T2_C MTK_M4U_ID(SMI_L11B_ID, 24) +#define M4U_PORT_L11B_YUVO_T5_C MTK_M4U_ID(SMI_L11B_ID, 25) +#define M4U_PORT_L11B_SMTO_T1_C MTK_M4U_ID(SMI_L11B_ID, 26) +#define M4U_PORT_L11B_SMTO_T4_C MTK_M4U_ID(SMI_L11B_ID, 27) +#define M4U_PORT_L11B_SMTO_T6_C MTK_M4U_ID(SMI_L11B_ID, 28) +#define M4U_PORT_L11B_DBGO_T1_C MTK_M4U_ID(SMI_L11B_ID, 29) + +/* LARB 11C -- IMG-D */ +#define M4U_PORT_L11C_WPE_RDMA_0 MTK_M4U_ID(SMI_L11C_ID, 0) +#define M4U_PORT_L11C_WPE_RDMA_1 MTK_M4U_ID(SMI_L11C_ID, 1) +#define M4U_PORT_L11C_WPE_RDMA_4P_0 MTK_M4U_ID(SMI_L11C_ID, 2) +#define M4U_PORT_L11C_WPE_RDMA_4P_1 MTK_M4U_ID(SMI_L11C_ID, 3) +#define M4U_PORT_L11C_WPE_CQ0 MTK_M4U_ID(SMI_L11C_ID, 4) +#define M4U_PORT_L11C_WPE_CQ1 MTK_M4U_ID(SMI_L11C_ID, 5) +#define M4U_PORT_L11C_PIMGI_P1 MTK_M4U_ID(SMI_L11C_ID, 6) +#define M4U_PORT_L11C_PIMGBI_P1 MTK_M4U_ID(SMI_L11C_ID, 7) +#define M4U_PORT_L11C_PIMGCI_P1 MTK_M4U_ID(SMI_L11C_ID, 8) +#define M4U_PORT_L11C_IMGI_T1_C MTK_M4U_ID(SMI_L11C_ID, 9) +#define M4U_PORT_L11C_IMGBI_T1_C MTK_M4U_ID(SMI_L11C_ID, 10) +#define M4U_PORT_L11C_IMGCI_T1_C MTK_M4U_ID(SMI_L11C_ID, 11) +#define M4U_PORT_L11C_SMTI_T1_C MTK_M4U_ID(SMI_L11C_ID, 12) +#define M4U_PORT_L11C_SMTI_T4_C MTK_M4U_ID(SMI_L11C_ID, 13) +#define M4U_PORT_L11C_SMTI_T6_C MTK_M4U_ID(SMI_L11C_ID, 14) +#define M4U_PORT_L11C_YUVO_T1_C MTK_M4U_ID(SMI_L11C_ID, 15) +#define M4U_PORT_L11C_YUVBO_T1_C MTK_M4U_ID(SMI_L11C_ID, 16) +#define M4U_PORT_L11C_YUVCO_T1_C MTK_M4U_ID(SMI_L11C_ID, 17) +#define M4U_PORT_L11C_WPE_WDMA_0 MTK_M4U_ID(SMI_L11C_ID, 18) +#define M4U_PORT_L11C_WPE_WDMA_4P_0 MTK_M4U_ID(SMI_L11C_ID, 19) +#define M4U_PORT_L11C_WROT_P1 MTK_M4U_ID(SMI_L11C_ID, 20) +#define M4U_PORT_L11C_TCCSO_P1 MTK_M4U_ID(SMI_L11C_ID, 21) +#define M4U_PORT_L11C_TCCSI_P1 MTK_M4U_ID(SMI_L11C_ID, 22) +#define M4U_PORT_L11C_TIMGO_T1_C MTK_M4U_ID(SMI_L11C_ID, 23) +#define M4U_PORT_L11C_YUVO_T2_C MTK_M4U_ID(SMI_L11C_ID, 24) +#define M4U_PORT_L11C_YUVO_T5_C MTK_M4U_ID(SMI_L11C_ID, 25) +#define M4U_PORT_L11C_SMTO_T1_C MTK_M4U_ID(SMI_L11C_ID, 26) +#define M4U_PORT_L11C_SMTO_T4_C MTK_M4U_ID(SMI_L11C_ID, 27) +#define M4U_PORT_L11C_SMTO_T6_C MTK_M4U_ID(SMI_L11C_ID, 28) +#define M4U_PORT_L11C_DBGO_T1_C MTK_M4U_ID(SMI_L11C_ID, 29) + +/* LARB 12 -- IPE */ +#define M4U_PORT_L12_FDVT_RDA_0 MTK_M4U_ID(SMI_L12_ID, 0) +#define M4U_PORT_L12_FDVT_RDB_0 MTK_M4U_ID(SMI_L12_ID, 1) +#define M4U_PORT_L12_FDVT_WRA_0 MTK_M4U_ID(SMI_L12_ID, 2) +#define M4U_PORT_L12_FDVT_WRB_0 MTK_M4U_ID(SMI_L12_ID, 3) +#define M4U_PORT_L12_ME_RDMA MTK_M4U_ID(SMI_L12_ID, 4) +#define M4U_PORT_L12_ME_WDMA MTK_M4U_ID(SMI_L12_ID, 5) +#define M4U_PORT_L12_DVS_RDMA MTK_M4U_ID(SMI_L12_ID, 6) +#define M4U_PORT_L12_DVS_WDMA MTK_M4U_ID(SMI_L12_ID, 7) +#define M4U_PORT_L12_DVP_RDMA MTK_M4U_ID(SMI_L12_ID, 8) +#define M4U_PORT_L12_DVP_WDMA MTK_M4U_ID(SMI_L12_ID, 9) +#define M4U_PORT_L12_FDVT_2ND_RDA_0 MTK_M4U_ID(SMI_L12_ID, 10) +#define M4U_PORT_L12_FDVT_2ND_RDB_0 MTK_M4U_ID(SMI_L12_ID, 11) +#define M4U_PORT_L12_FDVT_2ND_WRA_0 MTK_M4U_ID(SMI_L12_ID, 12) +#define M4U_PORT_L12_FDVT_2ND_WRB_0 MTK_M4U_ID(SMI_L12_ID, 13) +#define M4U_PORT_L12_DHZEI_E1 MTK_M4U_ID(SMI_L12_ID, 14) +#define M4U_PORT_L12_DHZEO_E1 MTK_M4U_ID(SMI_L12_ID, 15) + +/* LARB 13 -- CAM-1 */ +#define M4U_PORT_L13_CAMSV_CQI_E1 MTK_M4U_ID(SMI_L13_ID, 0) +#define M4U_PORT_L13_CAMSV_CQI_E2 MTK_M4U_ID(SMI_L13_ID, 1) +#define M4U_PORT_L13_GCAMSV_A_IMGO_1 MTK_M4U_ID(SMI_L13_ID, 2) +#define M4U_PORT_L13_GCAMSV_C_IMGO_1 MTK_M4U_ID(SMI_L13_ID, 3) +#define M4U_PORT_L13_GCAMSV_A_IMGO_2 MTK_M4U_ID(SMI_L13_ID, 4) +#define M4U_PORT_L13_GCAMSV_C_IMGO_2 MTK_M4U_ID(SMI_L13_ID, 5) +#define M4U_PORT_L13_PDAI_A_0 MTK_M4U_ID(SMI_L13_ID, 6) +#define M4U_PORT_L13_PDAI_A_1 MTK_M4U_ID(SMI_L13_ID, 7) +#define M4U_PORT_L13_CAMSV_CQI_B_E1 MTK_M4U_ID(SMI_L13_ID, 8) +#define M4U_PORT_L13_CAMSV_CQI_B_E2 MTK_M4U_ID(SMI_L13_ID, 9) +#define M4U_PORT_L13_CAMSV_CQI_C_E1 MTK_M4U_ID(SMI_L13_ID, 10) +#define M4U_PORT_L13_CAMSV_CQI_C_E2 MTK_M4U_ID(SMI_L13_ID, 11) +#define M4U_PORT_L13_GCAMSV_E_IMGO_1 MTK_M4U_ID(SMI_L13_ID, 12) +#define M4U_PORT_L13_GCAMSV_E_IMGO_2 MTK_M4U_ID(SMI_L13_ID, 13) +#define M4U_PORT_L13_GCAMSV_A_UFEO_1 MTK_M4U_ID(SMI_L13_ID, 14) +#define M4U_PORT_L13_GCAMSV_C_UFEO_1 MTK_M4U_ID(SMI_L13_ID, 15) +#define M4U_PORT_L13_GCAMSV_A_UFEO_2 MTK_M4U_ID(SMI_L13_ID, 16) +#define M4U_PORT_L13_GCAMSV_C_UFEO_2 MTK_M4U_ID(SMI_L13_ID, 17) +#define M4U_PORT_L13_GCAMSV_E_UFEO_1 MTK_M4U_ID(SMI_L13_ID, 18) +#define M4U_PORT_L13_GCAMSV_E_UFEO_2 MTK_M4U_ID(SMI_L13_ID, 19) +#define M4U_PORT_L13_GCAMSV_G_IMGO_1 MTK_M4U_ID(SMI_L13_ID, 20) +#define M4U_PORT_L13_GCAMSV_G_IMGO_2 MTK_M4U_ID(SMI_L13_ID, 21) +#define M4U_PORT_L13_PDAO_A MTK_M4U_ID(SMI_L13_ID, 22) +#define M4U_PORT_L13_PDAO_C MTK_M4U_ID(SMI_L13_ID, 23) + +/* LARB 14 -- CAM-1 */ +#define M4U_PORT_L14_GCAMSV_B_IMGO_1 MTK_M4U_ID(SMI_L14_ID, 0) +#define M4U_PORT_L14_GCAMSV_B_IMGO_2 MTK_M4U_ID(SMI_L14_ID, 1) +#define M4U_PORT_L14_SCAMSV_A_IMGO_1 MTK_M4U_ID(SMI_L14_ID, 2) +#define M4U_PORT_L14_SCAMSV_A_IMGO_2 MTK_M4U_ID(SMI_L14_ID, 3) +#define M4U_PORT_L14_SCAMSV_B_IMGO_1 MTK_M4U_ID(SMI_L14_ID, 4) +#define M4U_PORT_L14_SCAMSV_B_IMGO_2 MTK_M4U_ID(SMI_L14_ID, 5) +#define M4U_PORT_L14_PDAI_B_0 MTK_M4U_ID(SMI_L14_ID, 6) +#define M4U_PORT_L14_PDAI_B_1 MTK_M4U_ID(SMI_L14_ID, 7) +#define M4U_PORT_L14_GCAMSV_D_IMGO_1 MTK_M4U_ID(SMI_L14_ID, 8) +#define M4U_PORT_L14_GCAMSV_D_IMGO_2 MTK_M4U_ID(SMI_L14_ID, 9) +#define M4U_PORT_L14_GCAMSV_F_IMGO_1 MTK_M4U_ID(SMI_L14_ID, 10) +#define M4U_PORT_L14_GCAMSV_F_IMGO_2 MTK_M4U_ID(SMI_L14_ID, 11) +#define M4U_PORT_L14_GCAMSV_H_IMGO_1 MTK_M4U_ID(SMI_L14_ID, 12) +#define M4U_PORT_L14_GCAMSV_H_IMGO_2 MTK_M4U_ID(SMI_L14_ID, 13) +#define M4U_PORT_L14_GCAMSV_B_UFEO_1 MTK_M4U_ID(SMI_L14_ID, 14) +#define M4U_PORT_L14_GCAMSV_B_UFEO_2 MTK_M4U_ID(SMI_L14_ID, 15) +#define M4U_PORT_L14_GCAMSV_D_UFEO_1 MTK_M4U_ID(SMI_L14_ID, 16) +#define M4U_PORT_L14_GCAMSV_D_UFEO_2 MTK_M4U_ID(SMI_L14_ID, 17) +#define M4U_PORT_L14_PDAO_B MTK_M4U_ID(SMI_L14_ID, 18) +#define M4U_PORT_L14_IPUI MTK_M4U_ID(SMI_L14_ID, 19) +#define M4U_PORT_L14_IPUO MTK_M4U_ID(SMI_L14_ID, 20) +#define M4U_PORT_L14_IPU3O MTK_M4U_ID(SMI_L14_ID, 21) +#define M4U_PORT_L14_FAKE MTK_M4U_ID(SMI_L14_ID, 22) + +/* LARB 15 -- IMG-D */ +#define M4U_PORT_L15_VIPI_D1 MTK_M4U_ID(SMI_L15_ID, 0) +#define M4U_PORT_L15_VIPBI_D1 MTK_M4U_ID(SMI_L15_ID, 1) +#define M4U_PORT_L15_SMTI_D6 MTK_M4U_ID(SMI_L15_ID, 2) +#define M4U_PORT_L15_TNCSTI_D1 MTK_M4U_ID(SMI_L15_ID, 3) +#define M4U_PORT_L15_TNCSTI_D4 MTK_M4U_ID(SMI_L15_ID, 4) +#define M4U_PORT_L15_SMTI_D4 MTK_M4U_ID(SMI_L15_ID, 5) +#define M4U_PORT_L15_IMG3O_D1 MTK_M4U_ID(SMI_L15_ID, 6) +#define M4U_PORT_L15_IMG3BO_D1 MTK_M4U_ID(SMI_L15_ID, 7) +#define M4U_PORT_L15_IMG3CO_D1 MTK_M4U_ID(SMI_L15_ID, 8) +#define M4U_PORT_L15_IMG2O_D1 MTK_M4U_ID(SMI_L15_ID, 9) +#define M4U_PORT_L15_SMTI_D9 MTK_M4U_ID(SMI_L15_ID, 10) +#define M4U_PORT_L15_SMTO_D4 MTK_M4U_ID(SMI_L15_ID, 11) +#define M4U_PORT_L15_FEO_D1 MTK_M4U_ID(SMI_L15_ID, 12) +#define M4U_PORT_L15_TNCSO_D1 MTK_M4U_ID(SMI_L15_ID, 13) +#define M4U_PORT_L15_TNCSTO_D1 MTK_M4U_ID(SMI_L15_ID, 14) +#define M4U_PORT_L15_SMTO_D6 MTK_M4U_ID(SMI_L15_ID, 15) +#define M4U_PORT_L15_SMTO_D9 MTK_M4U_ID(SMI_L15_ID, 16) +#define M4U_PORT_L15_TNCO_D1 MTK_M4U_ID(SMI_L15_ID, 17) +#define M4U_PORT_L15_TNCO_D1_N MTK_M4U_ID(SMI_L15_ID, 18) + +/* LARB 16A -- CAM */ +#define M4U_PORT_L16A_IMGO_R1 MTK_M4U_ID(SMI_L16A_ID, 0) +#define M4U_PORT_L16A_CQI_R1 MTK_M4U_ID(SMI_L16A_ID, 1) +#define M4U_PORT_L16A_CQI_R2 MTK_M4U_ID(SMI_L16A_ID, 2) +#define M4U_PORT_L16A_BPCI_R1 MTK_M4U_ID(SMI_L16A_ID, 3) +#define M4U_PORT_L16A_LSCI_R1 MTK_M4U_ID(SMI_L16A_ID, 4) +#define M4U_PORT_L16A_RAWI_R2 MTK_M4U_ID(SMI_L16A_ID, 5) +#define M4U_PORT_L16A_RAWI_R3 MTK_M4U_ID(SMI_L16A_ID, 6) +#define M4U_PORT_L16A_UFDI_R2 MTK_M4U_ID(SMI_L16A_ID, 7) +#define M4U_PORT_L16A_UFDI_R3 MTK_M4U_ID(SMI_L16A_ID, 8) +#define M4U_PORT_L16A_RAWI_R4 MTK_M4U_ID(SMI_L16A_ID, 9) +#define M4U_PORT_L16A_RAWI_R5 MTK_M4U_ID(SMI_L16A_ID, 10) +#define M4U_PORT_L16A_AAI_R1 MTK_M4U_ID(SMI_L16A_ID, 11) +#define M4U_PORT_L16A_UFDI_R5 MTK_M4U_ID(SMI_L16A_ID, 12) +#define M4U_PORT_L16A_FHO_R1 MTK_M4U_ID(SMI_L16A_ID, 13) +#define M4U_PORT_L16A_AAO_R1 MTK_M4U_ID(SMI_L16A_ID, 14) +#define M4U_PORT_L16A_TSFSO_R1 MTK_M4U_ID(SMI_L16A_ID, 15) +#define M4U_PORT_L16A_FLKO_R1 MTK_M4U_ID(SMI_L16A_ID, 16) + +/* LARB 16B -- CAM */ +#define M4U_PORT_L16B_IMGO_R1 MTK_M4U_ID(SMI_L16B_ID, 0) +#define M4U_PORT_L16B_CQI_R1 MTK_M4U_ID(SMI_L16B_ID, 1) +#define M4U_PORT_L16B_CQI_R2 MTK_M4U_ID(SMI_L16B_ID, 2) +#define M4U_PORT_L16B_BPCI_R1 MTK_M4U_ID(SMI_L16B_ID, 3) +#define M4U_PORT_L16B_LSCI_R1 MTK_M4U_ID(SMI_L16B_ID, 4) +#define M4U_PORT_L16B_RAWI_R2 MTK_M4U_ID(SMI_L16B_ID, 5) +#define M4U_PORT_L16B_RAWI_R3 MTK_M4U_ID(SMI_L16B_ID, 6) +#define M4U_PORT_L16B_UFDI_R2 MTK_M4U_ID(SMI_L16B_ID, 7) +#define M4U_PORT_L16B_UFDI_R3 MTK_M4U_ID(SMI_L16B_ID, 8) +#define M4U_PORT_L16B_RAWI_R4 MTK_M4U_ID(SMI_L16B_ID, 9) +#define M4U_PORT_L16B_RAWI_R5 MTK_M4U_ID(SMI_L16B_ID, 10) +#define M4U_PORT_L16B_AAI_R1 MTK_M4U_ID(SMI_L16B_ID, 11) +#define M4U_PORT_L16B_UFDI_R5 MTK_M4U_ID(SMI_L16B_ID, 12) +#define M4U_PORT_L16B_FHO_R1 MTK_M4U_ID(SMI_L16B_ID, 13) +#define M4U_PORT_L16B_AAO_R1 MTK_M4U_ID(SMI_L16B_ID, 14) +#define M4U_PORT_L16B_TSFSO_R1 MTK_M4U_ID(SMI_L16B_ID, 15) +#define M4U_PORT_L16B_FLKO_R1 MTK_M4U_ID(SMI_L16B_ID, 16) + +/* LARB 17A -- CAM */ +#define M4U_PORT_L17A_YUVO_R1 MTK_M4U_ID(SMI_L17A_ID, 0) +#define M4U_PORT_L17A_YUVO_R3 MTK_M4U_ID(SMI_L17A_ID, 1) +#define M4U_PORT_L17A_YUVCO_R1 MTK_M4U_ID(SMI_L17A_ID, 2) +#define M4U_PORT_L17A_YUVO_R2 MTK_M4U_ID(SMI_L17A_ID, 3) +#define M4U_PORT_L17A_RZH1N2TO_R1 MTK_M4U_ID(SMI_L17A_ID, 4) +#define M4U_PORT_L17A_DRZS4NO_R1 MTK_M4U_ID(SMI_L17A_ID, 5) +#define M4U_PORT_L17A_TNCSO_R1 MTK_M4U_ID(SMI_L17A_ID, 6) + +/* LARB 17B -- CAM */ +#define M4U_PORT_L17B_YUVO_R1 MTK_M4U_ID(SMI_L17B_ID, 0) +#define M4U_PORT_L17B_YUVO_R3 MTK_M4U_ID(SMI_L17B_ID, 1) +#define M4U_PORT_L17B_YUVCO_R1 MTK_M4U_ID(SMI_L17B_ID, 2) +#define M4U_PORT_L17B_YUVO_R2 MTK_M4U_ID(SMI_L17B_ID, 3) +#define M4U_PORT_L17B_RZH1N2TO_R1 MTK_M4U_ID(SMI_L17B_ID, 4) +#define M4U_PORT_L17B_DRZS4NO_R1 MTK_M4U_ID(SMI_L17B_ID, 5) +#define M4U_PORT_L17B_TNCSO_R1 MTK_M4U_ID(SMI_L17B_ID, 6) + +/* LARB 19 -- VENC */ +#define M4U_PORT_L19_VENC_RCPU MTK_M4U_ID(SMI_L19_ID, 0) +#define M4U_PORT_L19_VENC_REC MTK_M4U_ID(SMI_L19_ID, 1) +#define M4U_PORT_L19_VENC_BSDMA MTK_M4U_ID(SMI_L19_ID, 2) +#define M4U_PORT_L19_VENC_SV_COMV MTK_M4U_ID(SMI_L19_ID, 3) +#define M4U_PORT_L19_VENC_RD_COMV MTK_M4U_ID(SMI_L19_ID, 4) +#define M4U_PORT_L19_VENC_NBM_RDMA MTK_M4U_ID(SMI_L19_ID, 5) +#define M4U_PORT_L19_VENC_NBM_RDMA_LITE MTK_M4U_ID(SMI_L19_ID, 6) +#define M4U_PORT_L19_JPGENC_Y_RDMA MTK_M4U_ID(SMI_L19_ID, 7) +#define M4U_PORT_L19_JPGENC_C_RDMA MTK_M4U_ID(SMI_L19_ID, 8) +#define M4U_PORT_L19_JPGENC_Q_TABLE MTK_M4U_ID(SMI_L19_ID, 9) +#define M4U_PORT_L19_VENC_SUB_W_LUMA MTK_M4U_ID(SMI_L19_ID, 10) +#define M4U_PORT_L19_VENC_FCS_NBM_RDMA MTK_M4U_ID(SMI_L19_ID, 11) +#define M4U_PORT_L19_JPGENC_BSDMA MTK_M4U_ID(SMI_L19_ID, 12) +#define M4U_PORT_L19_JPGDEC_WDMA_0 MTK_M4U_ID(SMI_L19_ID, 13) +#define M4U_PORT_L19_JPGDEC_BSDMA_0 MTK_M4U_ID(SMI_L19_ID, 14) +#define M4U_PORT_L19_VENC_NBM_WDMA MTK_M4U_ID(SMI_L19_ID, 15) +#define M4U_PORT_L19_VENC_NBM_WDMA_LITE MTK_M4U_ID(SMI_L19_ID, 16) +#define M4U_PORT_L19_VENC_FCS_NBM_WDMA MTK_M4U_ID(SMI_L19_ID, 17) +#define M4U_PORT_L19_JPGDEC_WDMA_1 MTK_M4U_ID(SMI_L19_ID, 18) +#define M4U_PORT_L19_JPGDEC_BSDMA_1 MTK_M4U_ID(SMI_L19_ID, 19) +#define M4U_PORT_L19_JPGDEC_HUFF_OFFSET_1 MTK_M4U_ID(SMI_L19_ID, 20) +#define M4U_PORT_L19_JPGDEC_HUFF_OFFSET_0 MTK_M4U_ID(SMI_L19_ID, 21) +#define M4U_PORT_L19_VENC_CUR_LUMA MTK_M4U_ID(SMI_L19_ID, 22) +#define M4U_PORT_L19_VENC_CUR_CHROMA MTK_M4U_ID(SMI_L19_ID, 23) +#define M4U_PORT_L19_VENC_REF_LUMA MTK_M4U_ID(SMI_L19_ID, 24) +#define M4U_PORT_L19_VENC_REF_CHROMA MTK_M4U_ID(SMI_L19_ID, 25) +#define M4U_PORT_L19_VENC_SUB_R_LUMA MTK_M4U_ID(SMI_L19_ID, 26) + +/* LARB 21 -- VDEC-CORE0 */ +#define M4U_PORT_L21_HW_VDEC_MC_EXT MTK_M4U_ID(SMI_L21_ID, 0) +#define M4U_PORT_L21_HW_VDEC_UFO_EXT MTK_M4U_ID(SMI_L21_ID, 1) +#define M4U_PORT_L21_HW_VDEC_PP_EXT MTK_M4U_ID(SMI_L21_ID, 2) +#define M4U_PORT_L21_HW_VDEC_PRED_RD_EXT MTK_M4U_ID(SMI_L21_ID, 3) +#define M4U_PORT_L21_HW_VDEC_PRED_WR_EXT MTK_M4U_ID(SMI_L21_ID, 4) +#define M4U_PORT_L21_HW_VDEC_PPWRAP_EXT MTK_M4U_ID(SMI_L21_ID, 5) +#define M4U_PORT_L21_HW_VDEC_TILE_EXT MTK_M4U_ID(SMI_L21_ID, 6) +#define M4U_PORT_L21_HW_VDEC_VLD_EXT MTK_M4U_ID(SMI_L21_ID, 7) +#define M4U_PORT_L21_HW_VDEC_VLD2_EXT MTK_M4U_ID(SMI_L21_ID, 8) +#define M4U_PORT_L21_HW_VDEC_AVC_MV_EXT MTK_M4U_ID(SMI_L21_ID, 9) +#define M4U_PORT_L21_HW_VDEC_UFO_EXT_C MTK_M4U_ID(SMI_L21_ID, 10) + +/* LARB 23 -- VDEC-SOC */ +#define M4U_PORT_L23_HW_VDEC_LAT0_VLD_EXT MTK_M4U_ID(SMI_L23_ID, 0) +#define M4U_PORT_L23_HW_VDEC_LAT0_VLD2_EXT MTK_M4U_ID(SMI_L23_ID, 1) +#define M4U_PORT_L23_HW_VDEC_LAT0_AVC_MV_EXT MTK_M4U_ID(SMI_L23_ID, 2) +#define M4U_PORT_L23_HW_VDEC_LAT0_PRED_RD_EXT MTK_M4U_ID(SMI_L23_ID, 3) +#define M4U_PORT_L23_HW_VDEC_LAT0_TILE_EXT MTK_M4U_ID(SMI_L23_ID, 4) +#define M4U_PORT_L23_HW_VDEC_LAT0_WDMA_EXT MTK_M4U_ID(SMI_L23_ID, 5) +#define M4U_PORT_L23_HW_VDEC_UFO_ENC_EXT MTK_M4U_ID(SMI_L23_ID, 6) +#define M4U_PORT_L23_HW_VDEC_UFO_ENC_EXT_C MTK_M4U_ID(SMI_L23_ID, 7) +#define M4U_PORT_L23_HW_VDEC_MC_EXT_C MTK_M4U_ID(SMI_L23_ID, 8) + +/* LARB 27 -- CCU */ +#define M4U_PORT_L27_CCUI MTK_M4U_ID(SMI_L27_ID, 0) +#define M4U_PORT_L27_CCUO MTK_M4U_ID(SMI_L27_ID, 1) +#define M4U_PORT_L27_CCUI2 MTK_M4U_ID(SMI_L27_ID, 2) +#define M4U_PORT_L27_CCUO2 MTK_M4U_ID(SMI_L27_ID, 3) + +/* LARB 28 -- AXI-CCU */ +#define M4U_PORT_L28_CCU_AXI_0 MTK_M4U_ID(SMI_L28_ID, 0) + +/* infra/peri */ +#define IFR_IOMMU_PORT_PCIE_0 MTK_IFAIOMMU_PERI_ID(0) + +#endif diff --git a/include/kvm/arm_pmu.h b/include/kvm/arm_pmu.h index 847da6fc271394c6c2516ae060a43b5d0f84cef6..31029f4f7be851d684c9500c243b59e979e6df67 100644 --- a/include/kvm/arm_pmu.h +++ b/include/kvm/arm_pmu.h @@ -12,7 +12,7 @@ #define ARMV8_PMU_CYCLE_IDX (ARMV8_PMU_MAX_COUNTERS - 1) -#ifdef CONFIG_HW_PERF_EVENTS +#if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM) struct kvm_pmc { u8 idx; /* index into the pmu->pmc array */ @@ -74,6 +74,7 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu); struct kvm_pmu_events *kvm_get_pmu_events(void); void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu); void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu); +void kvm_vcpu_pmu_resync_el0(void); #define kvm_vcpu_has_pmu(vcpu) \ (test_bit(KVM_ARM_VCPU_PMU_V3, (vcpu)->arch.features)) @@ -171,6 +172,7 @@ static inline u8 kvm_arm_pmu_get_pmuver_limit(void) { return 0; } +static inline void kvm_vcpu_pmu_resync_el0(void) {} #endif diff --git a/include/linux/acpi.h b/include/linux/acpi.h index a73246c3c35e083541b2e33638c575b6910497ba..afd94c9b8b8afd100c54fb07b5f1e8dcce08e92c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1480,6 +1480,15 @@ static inline int lpit_read_residency_count_address(u64 *address) } #endif +#ifdef CONFIG_ACPI_PROCESSOR_IDLE +#ifndef arch_get_idle_state_flags +static inline unsigned int arch_get_idle_state_flags(u32 arch_flags) +{ + return 0; +} +#endif +#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ + #ifdef CONFIG_ACPI_PPTT int acpi_pptt_cpu_is_thread(unsigned int cpu); int find_acpi_cpu_topology(unsigned int cpu, int level); diff --git a/include/linux/aer.h b/include/linux/aer.h index 2dd175f5debd080f4cc20f7090b2ad57ce03df33..29cc10220952dbe05497875b64cd19f00a81362b 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -42,11 +42,13 @@ struct aer_capability_regs { #if defined(CONFIG_PCIEAER) int pci_aer_clear_nonfatal_status(struct pci_dev *dev); +int pcie_aer_is_native(struct pci_dev *dev); #else static inline int pci_aer_clear_nonfatal_status(struct pci_dev *dev) { return -EINVAL; } +static inline int pcie_aer_is_native(struct pci_dev *dev) { return 0; } #endif void cper_print_aer(struct pci_dev *dev, int aer_severity, diff --git a/include/linux/amd-iommu.h b/include/linux/amd-iommu.h index 953e6f12fa1c306d290519d7a0e76e51ef0cc752..99a5201d9e625796f762b35beb32b1b523497019 100644 --- a/include/linux/amd-iommu.h +++ b/include/linux/amd-iommu.h @@ -32,7 +32,6 @@ struct task_struct; struct pci_dev; extern int amd_iommu_detect(void); -extern int amd_iommu_init_hardware(void); /** * amd_iommu_init_device() - Init device for use with IOMMUv2 driver diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 7c67c17321d4ca4e221045d172b6a62e989a0e27..083f8565371616269fa0050036ec8a3631c44357 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -67,6 +67,8 @@ #define ARM_SMCCC_VERSION_1_3 0x10003 #define ARM_SMCCC_1_3_SVE_HINT 0x10000 +#define ARM_SMCCC_CALL_HINTS ARM_SMCCC_1_3_SVE_HINT + #define ARM_SMCCC_VERSION_FUNC_ID \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ diff --git a/include/linux/atomic/atomic-arch-fallback.h b/include/linux/atomic/atomic-arch-fallback.h index 18f5744dfb5d89f09ff3191bdfc517a0839580d6..b83ef19da13de210e3ae8ce42f8dc022b51d8ef1 100644 --- a/include/linux/atomic/atomic-arch-fallback.h +++ b/include/linux/atomic/atomic-arch-fallback.h @@ -459,8 +459,6 @@ raw_atomic_read_acquire(const atomic_t *v) { #if defined(arch_atomic_read_acquire) return arch_atomic_read_acquire(v); -#elif defined(arch_atomic_read) - return arch_atomic_read(v); #else int ret; @@ -508,8 +506,6 @@ raw_atomic_set_release(atomic_t *v, int i) { #if defined(arch_atomic_set_release) arch_atomic_set_release(v, i); -#elif defined(arch_atomic_set) - arch_atomic_set(v, i); #else if (__native_word(atomic_t)) { smp_store_release(&(v)->counter, i); @@ -2575,8 +2571,6 @@ raw_atomic64_read_acquire(const atomic64_t *v) { #if defined(arch_atomic64_read_acquire) return arch_atomic64_read_acquire(v); -#elif defined(arch_atomic64_read) - return arch_atomic64_read(v); #else s64 ret; @@ -2624,8 +2618,6 @@ raw_atomic64_set_release(atomic64_t *v, s64 i) { #if defined(arch_atomic64_set_release) arch_atomic64_set_release(v, i); -#elif defined(arch_atomic64_set) - arch_atomic64_set(v, i); #else if (__native_word(atomic64_t)) { smp_store_release(&(v)->counter, i); @@ -4657,4 +4649,4 @@ raw_atomic64_dec_if_positive(atomic64_t *v) } #endif /* _LINUX_ATOMIC_FALLBACK_H */ -// 202b45c7db600ce36198eb1f1fc2c2d5268ace2d +// 2fdd6702823fa842f9cea57a002e6e4476ae780c diff --git a/include/linux/audit.h b/include/linux/audit.h index 6a3a9e122bb5ef005f7c50f98185a44b289de844..51b1b7054a233a167fa29a6461cce4bc5202a75e 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -117,6 +117,8 @@ enum audit_nfcfgop { AUDIT_NFT_OP_OBJ_RESET, AUDIT_NFT_OP_FLOWTABLE_REGISTER, AUDIT_NFT_OP_FLOWTABLE_UNREGISTER, + AUDIT_NFT_OP_SETELEM_RESET, + AUDIT_NFT_OP_RULE_RESET, AUDIT_NFT_OP_INVALID, }; diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 12596af59c0049a04039d117f3c98bb269413d6b..49f8b691496c4563b9cf4a9dc51b2b9c8133487d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -438,7 +438,7 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size) size /= sizeof(long); while (size--) - *ldst++ = *lsrc++; + data_race(*ldst++ = *lsrc++); } /* copy everything but bpf_spin_lock, bpf_timer, and kptrs. There could be one of each. */ @@ -1307,7 +1307,7 @@ static inline int bpf_trampoline_unlink_prog(struct bpf_tramp_link *link, static inline struct bpf_trampoline *bpf_trampoline_get(u64 key, struct bpf_attach_target_info *tgt_info) { - return ERR_PTR(-EOPNOTSUPP); + return NULL; } static inline void bpf_trampoline_put(struct bpf_trampoline *tr) {} #define DEFINE_BPF_DISPATCHER(name) diff --git a/include/linux/btf.h b/include/linux/btf.h index df64cc642074162e94123e8caf507def4f0dacbf..928113a80a95278e44161ed8a7c559716e237651 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -209,6 +209,7 @@ struct btf_record *btf_parse_fields(const struct btf *btf, const struct btf_type int btf_check_and_fixup_fields(const struct btf *btf, struct btf_record *rec); bool btf_type_is_void(const struct btf_type *t); s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind); +s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p); const struct btf_type *btf_type_skip_modifiers(const struct btf *btf, u32 id, u32 *res_id); const struct btf_type *btf_type_resolve_ptr(const struct btf *btf, diff --git a/include/linux/btf_ids.h b/include/linux/btf_ids.h index a3462a9b8e18547d521d9dc318074f6a69afe5ed..a9cb10b0e2e9bf45c33f4ad39e7a95d8d9b6b1b5 100644 --- a/include/linux/btf_ids.h +++ b/include/linux/btf_ids.h @@ -49,7 +49,7 @@ word \ ____BTF_ID(symbol, word) #define __ID(prefix) \ - __PASTE(prefix, __COUNTER__) + __PASTE(__PASTE(prefix, __COUNTER__), __LINE__) /* * The BTF_ID defines unique symbol for each ID pointing diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 4ede47649a811801f07a8aba4a392a35e1593c8e..44e9de51eedfb634aa2a44420a0fc32ee61ff924 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -171,7 +171,10 @@ static __always_inline int buffer_uptodate(const struct buffer_head *bh) return test_bit_acquire(BH_Uptodate, &bh->b_state); } -#define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) +static inline unsigned long bh_offset(const struct buffer_head *bh) +{ + return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1); +} /* If we *know* page->private refers to buffer_heads */ #define page_buffers(page) \ diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index 49586ff2615204e91761bf93690f4be379f443c8..f3b3593254b975004409b9dec8a8c88eec8bb0b0 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -359,14 +359,19 @@ enum { extern const char *ceph_mds_op_name(int op); - -#define CEPH_SETATTR_MODE 1 -#define CEPH_SETATTR_UID 2 -#define CEPH_SETATTR_GID 4 -#define CEPH_SETATTR_MTIME 8 -#define CEPH_SETATTR_ATIME 16 -#define CEPH_SETATTR_SIZE 32 -#define CEPH_SETATTR_CTIME 64 +#define CEPH_SETATTR_MODE (1 << 0) +#define CEPH_SETATTR_UID (1 << 1) +#define CEPH_SETATTR_GID (1 << 2) +#define CEPH_SETATTR_MTIME (1 << 3) +#define CEPH_SETATTR_ATIME (1 << 4) +#define CEPH_SETATTR_SIZE (1 << 5) +#define CEPH_SETATTR_CTIME (1 << 6) +#define CEPH_SETATTR_MTIME_NOW (1 << 7) +#define CEPH_SETATTR_ATIME_NOW (1 << 8) +#define CEPH_SETATTR_BTIME (1 << 9) +#define CEPH_SETATTR_KILL_SGUID (1 << 10) +#define CEPH_SETATTR_FSCRYPT_AUTH (1 << 11) +#define CEPH_SETATTR_FSCRYPT_FILE (1 << 12) /* * Ceph setxattr request flags. @@ -479,7 +484,7 @@ union ceph_mds_request_args_ext { #define CEPH_MDS_FLAG_WANT_DENTRY 2 /* want dentry in reply */ #define CEPH_MDS_FLAG_ASYNC 4 /* request is asynchronous */ -struct ceph_mds_request_head_old { +struct ceph_mds_request_head_legacy { __le64 oldest_client_tid; __le32 mdsmap_epoch; /* on client */ __le32 flags; /* CEPH_MDS_FLAG_* */ @@ -492,9 +497,9 @@ struct ceph_mds_request_head_old { union ceph_mds_request_args args; } __attribute__ ((packed)); -#define CEPH_MDS_REQUEST_HEAD_VERSION 1 +#define CEPH_MDS_REQUEST_HEAD_VERSION 2 -struct ceph_mds_request_head { +struct ceph_mds_request_head_old { __le16 version; /* struct version */ __le64 oldest_client_tid; __le32 mdsmap_epoch; /* on client */ @@ -508,6 +513,23 @@ struct ceph_mds_request_head { union ceph_mds_request_args_ext args; } __attribute__ ((packed)); +struct ceph_mds_request_head { + __le16 version; /* struct version */ + __le64 oldest_client_tid; + __le32 mdsmap_epoch; /* on client */ + __le32 flags; /* CEPH_MDS_FLAG_* */ + __u8 num_retry, num_fwd; /* legacy count retry and fwd attempts */ + __le16 num_releases; /* # include cap/lease release records */ + __le32 op; /* mds op code */ + __le32 caller_uid, caller_gid; + __le64 ino; /* use this ino for openc, mkdir, mknod, + etc. (if replaying) */ + union ceph_mds_request_args_ext args; + + __le32 ext_num_retry; /* new count retry attempts */ + __le32 ext_num_fwd; /* new count fwd attempts */ +} __attribute__ ((packed)); + /* cap/lease release record */ struct ceph_mds_request_release { __le64 ino, cap_id; /* ino and unique cap id */ diff --git a/include/linux/ceph/messenger.h b/include/linux/ceph/messenger.h index 99c1726be6ee7e92f6f183614c4b5bc3ab35d6e0..2eaaabbe98cb64d3f64a698a6f527e244649ca4e 100644 --- a/include/linux/ceph/messenger.h +++ b/include/linux/ceph/messenger.h @@ -17,6 +17,7 @@ struct ceph_msg; struct ceph_connection; +struct ceph_msg_data_cursor; /* * Ceph defines these callbacks for handling connection events. @@ -70,6 +71,30 @@ struct ceph_connection_operations { int used_proto, int result, const int *allowed_protos, int proto_cnt, const int *allowed_modes, int mode_cnt); + + /** + * sparse_read: read sparse data + * @con: connection we're reading from + * @cursor: data cursor for reading extents + * @buf: optional buffer to read into + * + * This should be called more than once, each time setting up to + * receive an extent into the current cursor position, and zeroing + * the holes between them. + * + * Returns amount of data to be read (in bytes), 0 if reading is + * complete, or -errno if there was an error. + * + * If @buf is set on a >0 return, then the data should be read into + * the provided buffer. Otherwise, it should be read into the cursor. + * + * The sparse read operation is expected to initialize the cursor + * with a length covering up to the end of the last extent. + */ + int (*sparse_read)(struct ceph_connection *con, + struct ceph_msg_data_cursor *cursor, + char **buf); + }; /* use format string %s%lld */ @@ -98,6 +123,7 @@ enum ceph_msg_data_type { CEPH_MSG_DATA_BIO, /* data source/destination is a bio list */ #endif /* CONFIG_BLOCK */ CEPH_MSG_DATA_BVECS, /* data source/destination is a bio_vec array */ + CEPH_MSG_DATA_ITER, /* data source/destination is an iov_iter */ }; #ifdef CONFIG_BLOCK @@ -199,6 +225,7 @@ struct ceph_msg_data { bool own_pages; }; struct ceph_pagelist *pagelist; + struct iov_iter iter; }; }; @@ -207,6 +234,7 @@ struct ceph_msg_data_cursor { struct ceph_msg_data *data; /* current data item */ size_t resid; /* bytes not yet consumed */ + int sr_resid; /* residual sparse_read len */ bool need_crc; /* crc update needed */ union { #ifdef CONFIG_BLOCK @@ -222,6 +250,10 @@ struct ceph_msg_data_cursor { struct page *page; /* page from list */ size_t offset; /* bytes from list */ }; + struct { + struct iov_iter iov_iter; + unsigned int lastlen; + }; }; }; @@ -251,6 +283,7 @@ struct ceph_msg { struct kref kref; bool more_to_follow; bool needs_out_seq; + bool sparse_read; int front_alloc_len; struct ceph_msgpool *pool; @@ -309,6 +342,10 @@ struct ceph_connection_v1_info { int in_base_pos; /* bytes read */ + /* sparse reads */ + struct kvec in_sr_kvec; /* current location to receive into */ + u64 in_sr_len; /* amount of data in this extent */ + /* message in temps */ u8 in_tag; /* protocol control byte */ struct ceph_msg_header in_hdr; @@ -395,6 +432,7 @@ struct ceph_connection_v2_info { void *conn_bufs[16]; int conn_buf_cnt; + int data_len_remain; struct kvec in_sign_kvecs[8]; struct kvec out_sign_kvecs[8]; @@ -573,6 +611,8 @@ void ceph_msg_data_add_bio(struct ceph_msg *msg, struct ceph_bio_iter *bio_pos, #endif /* CONFIG_BLOCK */ void ceph_msg_data_add_bvecs(struct ceph_msg *msg, struct ceph_bvec_iter *bvec_pos); +void ceph_msg_data_add_iter(struct ceph_msg *msg, + struct iov_iter *iter); struct ceph_msg *ceph_msg_new2(int type, int front_len, int max_data_items, gfp_t flags, bool can_fail); diff --git a/include/linux/ceph/osd_client.h b/include/linux/ceph/osd_client.h index fb6be72104df4ec9ee06e26b8275bf06fcde9639..bf9823956758c48cb23ce688bd2cfcb17e713bcb 100644 --- a/include/linux/ceph/osd_client.h +++ b/include/linux/ceph/osd_client.h @@ -29,14 +29,62 @@ typedef void (*ceph_osdc_callback_t)(struct ceph_osd_request *); #define CEPH_HOMELESS_OSD -1 -/* a given osd we're communicating with */ +/* + * A single extent in a SPARSE_READ reply. + * + * Note that these come from the OSD as little-endian values. On BE arches, + * we convert them in-place after receipt. + */ +struct ceph_sparse_extent { + u64 off; + u64 len; +} __packed; + +/* Sparse read state machine state values */ +enum ceph_sparse_read_state { + CEPH_SPARSE_READ_HDR = 0, + CEPH_SPARSE_READ_EXTENTS, + CEPH_SPARSE_READ_DATA_LEN, + CEPH_SPARSE_READ_DATA, +}; + +/* + * A SPARSE_READ reply is a 32-bit count of extents, followed by an array of + * 64-bit offset/length pairs, and then all of the actual file data + * concatenated after it (sans holes). + * + * Unfortunately, we don't know how long the extent array is until we've + * started reading the data section of the reply. The caller should send down + * a destination buffer for the array, but we'll alloc one if it's too small + * or if the caller doesn't. + */ +struct ceph_sparse_read { + enum ceph_sparse_read_state sr_state; /* state machine state */ + u64 sr_req_off; /* orig request offset */ + u64 sr_req_len; /* orig request length */ + u64 sr_pos; /* current pos in buffer */ + int sr_index; /* current extent index */ + __le32 sr_datalen; /* length of actual data */ + u32 sr_count; /* extent count in reply */ + int sr_ext_len; /* length of extent array */ + struct ceph_sparse_extent *sr_extent; /* extent array */ +}; + +/* + * A given osd we're communicating with. + * + * Note that the o_requests tree can be searched while holding the "lock" mutex + * or the "o_requests_lock" spinlock. Insertion or removal requires both! + */ struct ceph_osd { refcount_t o_ref; + int o_sparse_op_idx; struct ceph_osd_client *o_osdc; int o_osd; int o_incarnation; struct rb_node o_node; struct ceph_connection o_con; + spinlock_t o_requests_lock; struct rb_root o_requests; struct rb_root o_linger_requests; struct rb_root o_backoff_mappings; @@ -46,6 +94,7 @@ struct ceph_osd { unsigned long lru_ttl; struct list_head o_keepalive_item; struct mutex lock; + struct ceph_sparse_read o_sparse_read; }; #define CEPH_OSD_SLAB_OPS 2 @@ -59,6 +108,7 @@ enum ceph_osd_data_type { CEPH_OSD_DATA_TYPE_BIO, #endif /* CONFIG_BLOCK */ CEPH_OSD_DATA_TYPE_BVECS, + CEPH_OSD_DATA_TYPE_ITER, }; struct ceph_osd_data { @@ -82,6 +132,7 @@ struct ceph_osd_data { struct ceph_bvec_iter bvec_pos; u32 num_bvecs; }; + struct iov_iter iter; }; }; @@ -98,6 +149,8 @@ struct ceph_osd_req_op { u64 offset, length; u64 truncate_size; u32 truncate_seq; + int sparse_ext_cnt; + struct ceph_sparse_extent *sparse_ext; struct ceph_osd_data osd_data; } extent; struct { @@ -145,6 +198,9 @@ struct ceph_osd_req_op { u32 src_fadvise_flags; struct ceph_osd_data osd_data; } copy_from; + struct { + u64 ver; + } assert_ver; }; }; @@ -199,6 +255,7 @@ struct ceph_osd_request { struct ceph_osd_client *r_osdc; struct kref r_kref; bool r_mempool; + bool r_linger; /* don't resend on failure */ struct completion r_completion; /* private to osd_client.c */ ceph_osdc_callback_t r_callback; @@ -211,9 +268,9 @@ struct ceph_osd_request { struct ceph_snap_context *r_snapc; /* for writes */ struct timespec64 r_mtime; /* ditto */ u64 r_data_offset; /* ditto */ - bool r_linger; /* don't resend on failure */ /* internal */ + u64 r_version; /* data version sent in reply */ unsigned long r_stamp; /* jiffies, send or check time */ unsigned long r_start_stamp; /* jiffies */ ktime_t r_start_latency; /* ktime_t */ @@ -450,6 +507,8 @@ void osd_req_op_extent_osd_data_bvecs(struct ceph_osd_request *osd_req, void osd_req_op_extent_osd_data_bvec_pos(struct ceph_osd_request *osd_req, unsigned int which, struct ceph_bvec_iter *bvec_pos); +void osd_req_op_extent_osd_iter(struct ceph_osd_request *osd_req, + unsigned int which, struct iov_iter *iter); extern void osd_req_op_cls_request_data_pagelist(struct ceph_osd_request *, unsigned int which, @@ -504,6 +563,20 @@ extern struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *, u32 truncate_seq, u64 truncate_size, bool use_mempool); +int __ceph_alloc_sparse_ext_map(struct ceph_osd_req_op *op, int cnt); + +/* + * How big an extent array should we preallocate for a sparse read? This is + * just a starting value. If we get more than this back from the OSD, the + * receiver will reallocate. + */ +#define CEPH_SPARSE_EXT_ARRAY_INITIAL 16 + +static inline int ceph_alloc_sparse_ext_map(struct ceph_osd_req_op *op) +{ + return __ceph_alloc_sparse_ext_map(op, CEPH_SPARSE_EXT_ARRAY_INITIAL); +} + extern void ceph_osdc_get_request(struct ceph_osd_request *req); extern void ceph_osdc_put_request(struct ceph_osd_request *req); @@ -558,5 +631,19 @@ int ceph_osdc_list_watchers(struct ceph_osd_client *osdc, struct ceph_object_locator *oloc, struct ceph_watch_item **watchers, u32 *num_watchers); -#endif +/* Find offset into the buffer of the end of the extent map */ +static inline u64 ceph_sparse_ext_map_end(struct ceph_osd_req_op *op) +{ + struct ceph_sparse_extent *ext; + + /* No extents? No data */ + if (op->extent.sparse_ext_cnt == 0) + return 0; + + ext = &op->extent.sparse_ext[op->extent.sparse_ext_cnt - 1]; + + return ext->off + ext->len - op->extent.offset; +} + +#endif diff --git a/include/linux/ceph/rados.h b/include/linux/ceph/rados.h index 43a7a1573b51dce0088f0da59dee344b1de7798e..73c3efbec36cb152d9a99d49e01557451dfb8b75 100644 --- a/include/linux/ceph/rados.h +++ b/include/linux/ceph/rados.h @@ -523,6 +523,10 @@ struct ceph_osd_op { struct { __le64 cookie; } __attribute__ ((packed)) notify; + struct { + __le64 unused; + __le64 ver; + } __attribute__ ((packed)) assert_ver; struct { __le64 offset, length; __le64 src_offset; diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index ae20dbb885d66e3d470952f702c14210cbc39c4d..f1b3151ac30bf0e13b1762f3730695e726237c82 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -341,6 +341,20 @@ struct cgroup_rstat_cpu { */ struct cgroup_base_stat last_bstat; + /* + * This field is used to record the cumulative per-cpu time of + * the cgroup and its descendants. Currently it can be read via + * eBPF/drgn etc, and we are still trying to determine how to + * expose it in the cgroupfs interface. + */ + struct cgroup_base_stat subtree_bstat; + + /* + * Snapshots at the last reading. These are used to calculate the + * deltas to propagate to the per-cpu subtree_bstat. + */ + struct cgroup_base_stat last_subtree_bstat; + /* * Child cgroups with stat updates on this cpu since the last read * are linked on the parent's ->updated_children through diff --git a/include/linux/console.h b/include/linux/console.h index d3195664baa5a4288660698f62ed10ff318a7a47..7de11c763eb35deed528272727bf2f2bf4d18701 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -154,6 +154,8 @@ static inline int con_debug_leave(void) * receiving the printk spam for obvious reasons. * @CON_EXTENDED: The console supports the extended output format of * /dev/kmesg which requires a larger output buffer. + * @CON_SUSPENDED: Indicates if a console is suspended. If true, the + * printing callbacks must not be called. */ enum cons_flags { CON_PRINTBUFFER = BIT(0), @@ -163,6 +165,7 @@ enum cons_flags { CON_ANYTIME = BIT(4), CON_BRL = BIT(5), CON_EXTENDED = BIT(6), + CON_SUSPENDED = BIT(7), }; /** diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 43b363a99215702597b72dc5c0a22afa529d7d0c..71d186d6933a5e5f700335e4bd29eb47d59ee1f8 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -141,6 +141,9 @@ struct cpufreq_policy { */ bool dvfs_possible_from_any_cpu; + /* Per policy boost enabled flag. */ + bool boost_enabled; + /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ unsigned int cached_target_freq; unsigned int cached_resolved_idx; diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 06dda85f042445bb0d4b017b7e2aecd5b0587651..068f7738be22ab17b9958d01d46f92bec0d903f7 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -90,7 +90,6 @@ enum cpuhp_state { CPUHP_FS_BUFF_DEAD, CPUHP_PRINTK_DEAD, CPUHP_MM_MEMCQ_DEAD, - CPUHP_XFS_DEAD, CPUHP_PERCPU_CNT_DEAD, CPUHP_RADIX_DEAD, CPUHP_PAGE_ALLOC, diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 27dbd4c64860bfb28ce164d235e65eab286788d6..e34b601b71fd25c4b68c6e6c44075fea16275cd0 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -106,8 +106,6 @@ static inline bool dmar_rcu_check(void) extern int dmar_table_init(void); extern int dmar_dev_scope_init(void); extern void dmar_register_bus_notifier(void); -extern int dmar_parse_dev_scope(void *start, void *end, int *cnt, - struct dmar_dev_scope **devices, u16 segment); extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt); extern void dmar_free_dev_scope(struct dmar_dev_scope **devices, int *cnt); extern int dmar_insert_dev_scope(struct dmar_pci_notify_info *info, diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h index 1c849db953a51a71809c9b428f5b092507473149..45fca09b231943ea38f56f5079ba7e431074f8a9 100644 --- a/include/linux/export-internal.h +++ b/include/linux/export-internal.h @@ -52,6 +52,8 @@ #ifdef CONFIG_IA64 #define KSYM_FUNC(name) @fptr(name) +#elif defined(CONFIG_PARISC) && defined(CONFIG_64BIT) +#define KSYM_FUNC(name) P%name #else #define KSYM_FUNC(name) name #endif diff --git a/include/linux/export.h b/include/linux/export.h index beed8387e0a44018f0ef9c5ec046e6cb5fbfc43a..9911508a9604fb048c2587730520331ac621fd80 100644 --- a/include/linux/export.h +++ b/include/linux/export.h @@ -50,7 +50,7 @@ extern struct module __this_module; __EXPORT_SYMBOL_REF(sym) ASM_NL \ .previous -#if !defined(CONFIG_MODULES) || defined(__DISABLE_EXPORTS) +#if defined(__DISABLE_EXPORTS) /* * Allow symbol exports to be disabled completely so that C code may @@ -75,7 +75,7 @@ extern struct module __this_module; __ADDRESSABLE(sym) \ asm(__stringify(___EXPORT_SYMBOL(sym, license, ns))) -#endif /* CONFIG_MODULES */ +#endif #ifdef DEFAULT_SYMBOL_NAMESPACE #define _EXPORT_SYMBOL(sym, license) __EXPORT_SYMBOL(sym, license, __stringify(DEFAULT_SYMBOL_NAMESPACE)) diff --git a/include/linux/fs.h b/include/linux/fs.h index 4aeb3fa1192771cdd8e1e1ebdf18f9b08c983166..b528f063e8ffaa32a45e1b14398f448445771aa6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1508,47 +1508,18 @@ static inline bool fsuidgid_has_mapping(struct super_block *sb, kgid_has_mapping(fs_userns, kgid); } -struct timespec64 current_mgtime(struct inode *inode); struct timespec64 current_time(struct inode *inode); struct timespec64 inode_set_ctime_current(struct inode *inode); -/* - * Multigrain timestamps - * - * Conditionally use fine-grained ctime and mtime timestamps when there - * are users actively observing them via getattr. The primary use-case - * for this is NFS clients that use the ctime to distinguish between - * different states of the file, and that are often fooled by multiple - * operations that occur in the same coarse-grained timer tick. - * - * The kernel always keeps normalized struct timespec64 values in the ctime, - * which means that only the first 30 bits of the value are used. Use the - * 31st bit of the ctime's tv_nsec field as a flag to indicate that the value - * has been queried since it was last updated. - */ -#define I_CTIME_QUERIED (1L<<30) - /** * inode_get_ctime - fetch the current ctime from the inode * @inode: inode from which to fetch ctime * - * Grab the current ctime tv_nsec field from the inode, mask off the - * I_CTIME_QUERIED flag and return it. This is mostly intended for use by - * internal consumers of the ctime that aren't concerned with ensuring a - * fine-grained update on the next change (e.g. when preparing to store - * the value in the backing store for later retrieval). - * - * This is safe to call regardless of whether the underlying filesystem - * is using multigrain timestamps. + * Grab the current ctime from the inode and return it. */ static inline struct timespec64 inode_get_ctime(const struct inode *inode) { - struct timespec64 ctime; - - ctime.tv_sec = inode->__i_ctime.tv_sec; - ctime.tv_nsec = inode->__i_ctime.tv_nsec & ~I_CTIME_QUERIED; - - return ctime; + return inode->__i_ctime; } /** @@ -2334,7 +2305,6 @@ struct file_system_type { #define FS_USERNS_MOUNT 8 /* Can be mounted by userns root */ #define FS_DISALLOW_NOTIFY_PERM 16 /* Disable fanotify permission events */ #define FS_ALLOW_IDMAP 32 /* FS has been updated to handle vfs idmappings. */ -#define FS_MGTIME 64 /* FS uses multigrain timestamps */ #define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */ int (*init_fs_context)(struct fs_context *); const struct fs_parameter_spec *parameters; @@ -2358,17 +2328,6 @@ struct file_system_type { #define MODULE_ALIAS_FS(NAME) MODULE_ALIAS("fs-" NAME) -/** - * is_mgtime: is this inode using multigrain timestamps - * @inode: inode to test for multigrain timestamps - * - * Return true if the inode uses multigrain timestamps, false otherwise. - */ -static inline bool is_mgtime(const struct inode *inode) -{ - return inode->i_sb->s_type->fs_flags & FS_MGTIME; -} - extern struct dentry *mount_bdev(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, int (*fill_super)(struct super_block *, void *, int)); @@ -3054,7 +3013,6 @@ extern void page_put_link(void *); extern int page_symlink(struct inode *inode, const char *symname, int len); extern const struct inode_operations page_symlink_inode_operations; extern void kfree_link(void *); -void fill_mg_cmtime(struct kstat *stat, u32 request_mask, struct inode *inode); void generic_fillattr(struct mnt_idmap *, u32, struct inode *, struct kstat *); void generic_fill_statx_attr(struct inode *inode, struct kstat *stat); extern int vfs_getattr_nosec(const struct path *, struct kstat *, u32, unsigned int); diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index aad9cf8876b550119c1c3157953086a6c5a20743..e8921871ef9aaa4ff9a8c74359bf49ebe95d8b3e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -862,13 +862,8 @@ extern int skip_trace(unsigned long ip); extern void ftrace_module_init(struct module *mod); extern void ftrace_module_enable(struct module *mod); extern void ftrace_release_mod(struct module *mod); - -extern void ftrace_disable_daemon(void); -extern void ftrace_enable_daemon(void); #else /* CONFIG_DYNAMIC_FTRACE */ static inline int skip_trace(unsigned long ip) { return 0; } -static inline void ftrace_disable_daemon(void) { } -static inline void ftrace_enable_daemon(void) { } static inline void ftrace_module_init(struct module *mod) { } static inline void ftrace_module_enable(struct module *mod) { } static inline void ftrace_release_mod(struct module *mod) { } diff --git a/include/linux/gameport.h b/include/linux/gameport.h index 0a221e768ea4f6c92ff6bef47a5c4c3f85fe12ad..07e370113b2b6558faf293c11bebe5ac7ca85ea6 100644 --- a/include/linux/gameport.h +++ b/include/linux/gameport.h @@ -63,7 +63,7 @@ struct gameport_driver { int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode); void gameport_close(struct gameport *gameport); -#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) +#if IS_REACHABLE(CONFIG_GAMEPORT) void __gameport_register_port(struct gameport *gameport, struct module *owner); /* use a define to avoid include chaining to get THIS_MODULE */ diff --git a/include/linux/hid.h b/include/linux/hid.h index 964ca1f15e3f6a417987e5c0c7df405841e22327..5a8387a4a7126303adcee9553cce3cfb7599e1b2 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -833,11 +833,11 @@ struct hid_driver { void (*feature_mapping)(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage); -#ifdef CONFIG_PM + int (*suspend)(struct hid_device *hdev, pm_message_t message); int (*resume)(struct hid_device *hdev); int (*reset_resume)(struct hid_device *hdev); -#endif + /* private: */ struct device_driver driver; }; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 5b2626063f4fddd521c831eb17c1aae8a0e9d41e..a30686e649f7ac971835c215bb7675cc84cb4b6b 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -984,7 +984,9 @@ static inline void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep, pte_t old_pte, pte_t pte) { - set_huge_pte_at(vma->vm_mm, addr, ptep, pte); + unsigned long psize = huge_page_size(hstate_vma(vma)); + + set_huge_pte_at(vma->vm_mm, addr, ptep, pte, psize); } #endif @@ -1173,7 +1175,7 @@ static inline pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, } static inline void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, - pte_t *ptep, pte_t pte) + pte_t *ptep, pte_t pte, unsigned long sz) { } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 3ac3974b3c7819053b07e7aa423d9153dfbbe186..2b00faf98017cc9c2a99da9f2cc29c45ecbaf2ec 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -348,7 +348,7 @@ struct vmtransfer_page_packet_header { u8 sender_owns_set; u8 reserved; u32 range_cnt; - struct vmtransfer_page_range ranges[1]; + struct vmtransfer_page_range ranges[]; } __packed; struct vmgpadl_packet_header { @@ -665,8 +665,8 @@ struct vmbus_channel_initiate_contact { u64 interrupt_page; struct { u8 msg_sint; - u8 padding1[3]; - u32 padding2; + u8 msg_vtl; + u8 reserved[6]; }; }; u64 monitor_page1; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 3430cc2b05a696eb0444c8e88db0740ebb5ee34e..0dae9db275380b16bada4327f8f02e53ff8db30f 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -237,7 +237,6 @@ enum i2c_driver_flags { * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) * @probe: Callback for device binding - * @probe_new: Transitional callback for device binding - do not use * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown * @alert: Alert callback, for example for the SMBus alert protocol @@ -272,16 +271,8 @@ enum i2c_driver_flags { struct i2c_driver { unsigned int class; - union { /* Standard driver model interfaces */ - int (*probe)(struct i2c_client *client); - /* - * Legacy callback that was part of a conversion of .probe(). - * Today it has the same semantic as .probe(). Don't use for new - * code. - */ - int (*probe_new)(struct i2c_client *client); - }; + int (*probe)(struct i2c_client *client); void (*remove)(struct i2c_client *client); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 1b9b15a492fae417f87b946fe343f7ee29ec890b..cdc684e04a2fb6fcabda07a3427270af79b7eca8 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -189,6 +189,8 @@ struct team { struct net_device *dev; /* associated netdevice */ struct team_pcpu_stats __percpu *pcpu_stats; + const struct header_ops *header_ops_cache; + struct mutex lock; /* used for overall locking, e.g. port lists write */ /* diff --git a/include/linux/instruction_pointer.h b/include/linux/instruction_pointer.h index cda1f706eaeb1186972eab1ca88e5d8584fcd9e1..aa0b3ffea93536c7b423742f09efbaee50c7f56e 100644 --- a/include/linux/instruction_pointer.h +++ b/include/linux/instruction_pointer.h @@ -2,7 +2,12 @@ #ifndef _LINUX_INSTRUCTION_POINTER_H #define _LINUX_INSTRUCTION_POINTER_H +#include + #define _RET_IP_ (unsigned long)__builtin_return_address(0) + +#ifndef _THIS_IP_ #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) +#endif #endif /* _LINUX_INSTRUCTION_POINTER_H */ diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index a92bce40b04b3a0352534f76334faec42d85fbb1..4a1dc88ddbff9a6bfec51b1b62f3cd2cc3758463 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -569,8 +569,12 @@ enum * 2) rcu_report_dead() reports the final quiescent states. * * _ IRQ_POLL: irq_poll_cpu_dead() migrates the queue + * + * _ (HR)TIMER_SOFTIRQ: (hr)timers_dead_cpu() migrates the queue */ -#define SOFTIRQ_HOTPLUG_SAFE_MASK (BIT(RCU_SOFTIRQ) | BIT(IRQ_POLL_SOFTIRQ)) +#define SOFTIRQ_HOTPLUG_SAFE_MASK (BIT(TIMER_SOFTIRQ) | BIT(IRQ_POLL_SOFTIRQ) |\ + BIT(HRTIMER_SOFTIRQ) | BIT(RCU_SOFTIRQ)) + /* map softirq index to softirq name. update 'softirq_to_name' in * kernel/softirq.c when adding a new softirq. diff --git a/include/linux/iommu.h b/include/linux/iommu.h index bd6a1110b294504eb379f1f814b263e31c994a6e..c50a769d569a60f2922a40ad7b813de4b29513d0 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -196,6 +196,8 @@ enum iommu_dev_features { IOMMU_DEV_FEAT_IOPF, }; +#define IOMMU_NO_PASID (0U) /* Reserved for DMA w/o PASID */ +#define IOMMU_FIRST_GLOBAL_PASID (1U) /*starting range for allocation */ #define IOMMU_PASID_INVALID (-1U) typedef unsigned int ioasid_t; @@ -414,6 +416,8 @@ struct iommu_fault_param { * @priv: IOMMU Driver private data * @max_pasids: number of PASIDs this device can consume * @attach_deferred: the dma domain attachment is deferred + * @pci_32bit_workaround: Limit DMA allocations to 32-bit IOVAs + * @require_direct: device requires IOMMU_RESV_DIRECT regions * * TODO: migrate other per device data pointers under iommu_dev_data, e.g. * struct iommu_group *iommu_group; @@ -427,6 +431,8 @@ struct dev_iommu { void *priv; u32 max_pasids; u32 attach_deferred:1; + u32 pci_32bit_workaround:1; + u32 require_direct:1; }; int iommu_device_register(struct iommu_device *iommu, @@ -721,6 +727,8 @@ void iommu_detach_device_pasid(struct iommu_domain *domain, struct iommu_domain * iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid, unsigned int type); +ioasid_t iommu_alloc_global_pasid(struct device *dev); +void iommu_free_global_pasid(ioasid_t pasid); #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; @@ -1082,6 +1090,13 @@ iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid, { return NULL; } + +static inline ioasid_t iommu_alloc_global_pasid(struct device *dev) +{ + return IOMMU_PASID_INVALID; +} + +static inline void iommu_free_global_pasid(ioasid_t pasid) {} #endif /* CONFIG_IOMMU_API */ /** diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 5883551b1ee874b8c217bbefa5cb5feeb8e894c1..af8a771a053c51eed297516f927a5fd003315ef4 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -147,6 +147,7 @@ struct inet6_skb_parm { #define IP6SKB_JUMBOGRAM 128 #define IP6SKB_SEG6 256 #define IP6SKB_FAKEJUMBO 512 +#define IP6SKB_MULTIPATH 1024 }; #if defined(CONFIG_NET_L3_MASTER_DEV) diff --git a/include/linux/kasan.h b/include/linux/kasan.h index 819b6bc8ac0886d8d671a117ac9d9c694baebe95..842623d708c2e0625e23d7439071eae31c2c6cc0 100644 --- a/include/linux/kasan.h +++ b/include/linux/kasan.h @@ -54,11 +54,13 @@ extern p4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D]; int kasan_populate_early_shadow(const void *shadow_start, const void *shadow_end); +#ifndef kasan_mem_to_shadow static inline void *kasan_mem_to_shadow(const void *addr) { return (void *)((unsigned long)addr >> KASAN_SHADOW_SCALE_SHIFT) + KASAN_SHADOW_OFFSET; } +#endif int kasan_add_zero_shadow(void *start, unsigned long size); void kasan_remove_zero_shadow(void *start, unsigned long size); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 9d3ac7720da9f46a2de764bbd0508da040355f59..fb6c6109fdcad69f81cd38edf52dce7dc3d7a4e9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -190,8 +190,6 @@ bool kvm_make_vcpus_request_mask(struct kvm *kvm, unsigned int req, bool kvm_make_all_cpus_request(struct kvm *kvm, unsigned int req); bool kvm_make_all_cpus_request_except(struct kvm *kvm, unsigned int req, struct kvm_vcpu *except); -bool kvm_make_cpus_request_mask(struct kvm *kvm, unsigned int req, - unsigned long *vcpu_bitmap); #define KVM_USERSPACE_IRQ_SOURCE_ID 0 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 @@ -256,11 +254,15 @@ int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu); #endif #ifdef KVM_ARCH_WANT_MMU_NOTIFIER +union kvm_mmu_notifier_arg { + pte_t pte; +}; + struct kvm_gfn_range { struct kvm_memory_slot *slot; gfn_t start; gfn_t end; - pte_t pte; + union kvm_mmu_notifier_arg arg; bool may_block; }; bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range); @@ -865,6 +867,25 @@ static inline void kvm_vm_bugged(struct kvm *kvm) unlikely(__ret); \ }) +/* + * Note, "data corruption" refers to corruption of host kernel data structures, + * not guest data. Guest data corruption, suspected or confirmed, that is tied + * and contained to a single VM should *never* BUG() and potentially panic the + * host, i.e. use this variant of KVM_BUG() if and only if a KVM data structure + * is corrupted and that corruption can have a cascading effect to other parts + * of the hosts and/or to other VMs. + */ +#define KVM_BUG_ON_DATA_CORRUPTION(cond, kvm) \ +({ \ + bool __ret = !!(cond); \ + \ + if (IS_ENABLED(CONFIG_BUG_ON_DATA_CORRUPTION)) \ + BUG_ON(__ret); \ + else if (WARN_ON_ONCE(__ret && !(kvm)->vm_bugged)) \ + kvm_vm_bugged(kvm); \ + unlikely(__ret); \ +}) + static inline void kvm_vcpu_srcu_read_lock(struct kvm_vcpu *vcpu) { #ifdef CONFIG_PROVE_RCU @@ -1359,6 +1380,9 @@ int kvm_vcpu_yield_to(struct kvm_vcpu *target); void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu, bool yield_to_kernel_mode); void kvm_flush_remote_tlbs(struct kvm *kvm); +void kvm_flush_remote_tlbs_range(struct kvm *kvm, gfn_t gfn, u64 nr_pages); +void kvm_flush_remote_tlbs_memslot(struct kvm *kvm, + const struct kvm_memory_slot *memslot); #ifdef KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min); @@ -1387,10 +1411,7 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, unsigned long mask); void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot); -#ifdef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT -void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, - const struct kvm_memory_slot *memslot); -#else /* !CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT */ +#ifndef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log); int kvm_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log, int *is_dirty, struct kvm_memory_slot **memslot); @@ -1479,11 +1500,23 @@ static inline void kvm_arch_free_vm(struct kvm *kvm) } #endif -#ifndef __KVM_HAVE_ARCH_FLUSH_REMOTE_TLB -static inline int kvm_arch_flush_remote_tlb(struct kvm *kvm) +#ifndef __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS +static inline int kvm_arch_flush_remote_tlbs(struct kvm *kvm) { return -ENOTSUPP; } +#else +int kvm_arch_flush_remote_tlbs(struct kvm *kvm); +#endif + +#ifndef __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE +static inline int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, + gfn_t gfn, u64 nr_pages) +{ + return -EOPNOTSUPP; +} +#else +int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, gfn_t gfn, u64 nr_pages); #endif #ifdef __KVM_HAVE_ARCH_NONCOHERENT_DMA @@ -2148,8 +2181,6 @@ struct kvm_device_ops { int (*mmap)(struct kvm_device *dev, struct vm_area_struct *vma); }; -void kvm_device_get(struct kvm_device *dev); -void kvm_device_put(struct kvm_device *dev); struct kvm_device *kvm_device_from_filp(struct file *filp); int kvm_register_device_ops(const struct kvm_device_ops *ops, u32 type); void kvm_unregister_device_ops(u32 type); diff --git a/include/linux/leds.h b/include/linux/leds.h index 7d428100b42b5cc631b01ff250838fe11588f2cf..aa16dc2a8230fa50ebcb6c8005f8806922e0d958 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -100,6 +100,7 @@ struct led_classdev { const char *name; unsigned int brightness; unsigned int max_brightness; + unsigned int color; int flags; /* Lower 16 bits reflect status */ @@ -313,6 +314,8 @@ extern struct led_classdev *of_led_get(struct device_node *np, int index); extern void led_put(struct led_classdev *led_cdev); struct led_classdev *__must_check devm_of_led_get(struct device *dev, int index); +struct led_classdev *__must_check devm_of_led_get_optional(struct device *dev, + int index); /** * led_blink_set - set blinking with software fallback diff --git a/include/linux/libata.h b/include/linux/libata.h index 820f7a3a2749bedf8f46cc2684cda1d336b7cfa4..2a7d2af0ed80a860717a27303e7da6b8ade51e3d 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -192,6 +192,7 @@ enum { ATA_PFLAG_UNLOADING = (1 << 9), /* driver is being unloaded */ ATA_PFLAG_UNLOADED = (1 << 10), /* driver is unloaded */ + ATA_PFLAG_RESUMING = (1 << 16), /* port is being resumed */ ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ ATA_PFLAG_INIT_GTM_VALID = (1 << 19), /* initial gtm data valid */ @@ -222,6 +223,10 @@ enum { ATA_HOST_PARALLEL_SCAN = (1 << 2), /* Ports on this host can be scanned in parallel */ ATA_HOST_IGNORE_ATA = (1 << 3), /* Ignore ATA devices on this host. */ + ATA_HOST_NO_PART = (1 << 4), /* Host does not support partial */ + ATA_HOST_NO_SSC = (1 << 5), /* Host does not support slumber */ + ATA_HOST_NO_DEVSLP = (1 << 6), /* Host does not support devslp */ + /* bits 24:31 of host->flags are reserved for LLD specific flags */ /* various lengths of time */ @@ -255,7 +260,7 @@ enum { * advised to wait only for the following duration before * doing SRST. */ - ATA_TMOUT_PMP_SRST_WAIT = 5000, + ATA_TMOUT_PMP_SRST_WAIT = 10000, /* When the LPM policy is set to ATA_LPM_MAX_POWER, there might * be a spurious PHY event, so ignore the first PHY event that @@ -314,9 +319,10 @@ enum { ATA_EH_ENABLE_LINK = (1 << 3), ATA_EH_PARK = (1 << 5), /* unload heads and stop I/O */ ATA_EH_GET_SUCCESS_SENSE = (1 << 6), /* Get sense data for successful cmd */ + ATA_EH_SET_ACTIVE = (1 << 7), /* Set a device to active power mode */ ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE | ATA_EH_PARK | - ATA_EH_GET_SUCCESS_SENSE, + ATA_EH_GET_SUCCESS_SENSE | ATA_EH_SET_ACTIVE, ATA_EH_ALL_ACTIONS = ATA_EH_REVALIDATE | ATA_EH_RESET | ATA_EH_ENABLE_LINK, @@ -344,7 +350,6 @@ enum { ATA_LINK_RESUME_TRIES = 5, /* how hard are we gonna try to probe/recover devices */ - ATA_PROBE_MAX_TRIES = 3, ATA_EH_DEV_TRIES = 3, ATA_EH_PMP_TRIES = 5, ATA_EH_PMP_LINK_TRIES = 3, @@ -354,7 +359,7 @@ enum { /* This should match the actual table size of * ata_eh_cmd_timeout_table in libata-eh.c. */ - ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 7, + ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8, /* Horkage types. May be set by libata or controller on drives (some horkage may be drive/controller pair dependent */ @@ -976,12 +981,6 @@ struct ata_port_operations { ssize_t (*transmit_led_message)(struct ata_port *ap, u32 state, ssize_t size); - /* - * Obsolete - */ - void (*phy_reset)(struct ata_port *ap); - void (*eng_timeout)(struct ata_port *ap); - /* * ->inherits must be the last field and all the preceding * fields must be pointers. @@ -1116,7 +1115,7 @@ static inline void ata_sas_port_resume(struct ata_port *ap) extern int ata_ratelimit(void); extern void ata_msleep(struct ata_port *ap, unsigned int msecs); extern u32 ata_wait_register(struct ata_port *ap, void __iomem *reg, u32 mask, - u32 val, unsigned long interval, unsigned long timeout); + u32 val, unsigned int interval, unsigned int timeout); extern int atapi_cmd_type(u8 opcode); extern unsigned int ata_pack_xfermask(unsigned int pio_mask, unsigned int mwdma_mask, @@ -1151,6 +1150,7 @@ extern int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]); extern void ata_scsi_unlock_native_capacity(struct scsi_device *sdev); +extern int ata_scsi_slave_alloc(struct scsi_device *sdev); extern int ata_scsi_slave_config(struct scsi_device *sdev); extern void ata_scsi_slave_destroy(struct scsi_device *sdev); extern int ata_scsi_change_queue_depth(struct scsi_device *sdev, @@ -1166,11 +1166,11 @@ extern void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port * * SATA specific code - drivers/ata/libata-sata.c */ #ifdef CONFIG_SATA_HOST -extern const unsigned long sata_deb_timing_normal[]; -extern const unsigned long sata_deb_timing_hotplug[]; -extern const unsigned long sata_deb_timing_long[]; +extern const unsigned int sata_deb_timing_normal[]; +extern const unsigned int sata_deb_timing_hotplug[]; +extern const unsigned int sata_deb_timing_long[]; -static inline const unsigned long * +static inline const unsigned int * sata_ehc_deb_timing(struct ata_eh_context *ehc) { if (ehc->i.flags & ATA_EHI_HOTPLUGGED) @@ -1185,14 +1185,14 @@ extern int sata_scr_write(struct ata_link *link, int reg, u32 val); extern int sata_scr_write_flush(struct ata_link *link, int reg, u32 val); extern int sata_set_spd(struct ata_link *link); extern int sata_link_hardreset(struct ata_link *link, - const unsigned long *timing, unsigned long deadline, + const unsigned int *timing, unsigned long deadline, bool *online, int (*check_ready)(struct ata_link *)); -extern int sata_link_resume(struct ata_link *link, const unsigned long *params, +extern int sata_link_resume(struct ata_link *link, const unsigned int *params, unsigned long deadline); extern int ata_eh_read_sense_success_ncq_log(struct ata_link *link); extern void ata_eh_analyze_ncq_error(struct ata_link *link); #else -static inline const unsigned long * +static inline const unsigned int * sata_ehc_deb_timing(struct ata_eh_context *ehc) { return NULL; @@ -1212,7 +1212,7 @@ static inline int sata_scr_write_flush(struct ata_link *link, int reg, u32 val) } static inline int sata_set_spd(struct ata_link *link) { return -EOPNOTSUPP; } static inline int sata_link_hardreset(struct ata_link *link, - const unsigned long *timing, + const unsigned int *timing, unsigned long deadline, bool *online, int (*check_ready)(struct ata_link *)) @@ -1222,7 +1222,7 @@ static inline int sata_link_hardreset(struct ata_link *link, return -EOPNOTSUPP; } static inline int sata_link_resume(struct ata_link *link, - const unsigned long *params, + const unsigned int *params, unsigned long deadline) { return -EOPNOTSUPP; @@ -1234,20 +1234,15 @@ static inline int ata_eh_read_sense_success_ncq_log(struct ata_link *link) static inline void ata_eh_analyze_ncq_error(struct ata_link *link) { } #endif extern int sata_link_debounce(struct ata_link *link, - const unsigned long *params, unsigned long deadline); + const unsigned int *params, unsigned long deadline); extern int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, bool spm_wakeup); extern int ata_slave_link_init(struct ata_port *ap); -extern void ata_sas_port_destroy(struct ata_port *); extern struct ata_port *ata_sas_port_alloc(struct ata_host *, struct ata_port_info *, struct Scsi_Host *); -extern void ata_sas_async_probe(struct ata_port *ap); -extern int ata_sas_sync_probe(struct ata_port *ap); -extern int ata_sas_port_init(struct ata_port *); -extern int ata_sas_port_start(struct ata_port *ap); +extern void ata_port_probe(struct ata_port *ap); extern int ata_sas_tport_add(struct device *parent, struct ata_port *ap); extern void ata_sas_tport_delete(struct ata_port *ap); -extern void ata_sas_port_stop(struct ata_port *ap); extern int ata_sas_slave_configure(struct scsi_device *, struct ata_port *); extern int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap); extern void ata_tf_to_fis(const struct ata_taskfile *tf, @@ -1404,6 +1399,7 @@ extern const struct attribute_group *ata_common_sdev_groups[]; .this_id = ATA_SHT_THIS_ID, \ .emulated = ATA_SHT_EMULATED, \ .proc_name = drv_name, \ + .slave_alloc = ata_scsi_slave_alloc, \ .slave_destroy = ata_scsi_slave_destroy, \ .bios_param = ata_std_bios_param, \ .unlock_native_capacity = ata_scsi_unlock_native_capacity,\ @@ -1785,7 +1781,7 @@ static inline struct ata_queued_cmd *ata_qc_from_tag(struct ata_port *ap, { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); - if (unlikely(!qc) || !ap->ops->error_handler) + if (unlikely(!qc)) return qc; if ((qc->flags & (ATA_QCFLAG_ACTIVE | @@ -1876,7 +1872,7 @@ static inline int ata_check_ready(u8 status) } static inline unsigned long ata_deadline(unsigned long from_jiffies, - unsigned long timeout_msecs) + unsigned int timeout_msecs) { return from_jiffies + msecs_to_jiffies(timeout_msecs); } diff --git a/include/linux/maple_tree.h b/include/linux/maple_tree.h index e41c70ac7744e4310b2e69259c700b8c1be22493..d01e850b570fd41a42f1d778c2242bb3dc58db6a 100644 --- a/include/linux/maple_tree.h +++ b/include/linux/maple_tree.h @@ -428,6 +428,8 @@ struct ma_wr_state { #define MAS_ROOT ((struct maple_enode *)5UL) #define MAS_NONE ((struct maple_enode *)9UL) #define MAS_PAUSE ((struct maple_enode *)17UL) +#define MAS_OVERFLOW ((struct maple_enode *)33UL) +#define MAS_UNDERFLOW ((struct maple_enode *)65UL) #define MA_ERROR(err) \ ((struct maple_enode *)(((unsigned long)err << 2) | 2UL)) @@ -511,6 +513,15 @@ static inline bool mas_is_paused(const struct ma_state *mas) return mas->node == MAS_PAUSE; } +/* Check if the mas is pointing to a node or not */ +static inline bool mas_is_active(struct ma_state *mas) +{ + if ((unsigned long)mas->node >= MAPLE_RESERVED_RANGE) + return true; + + return false; +} + /** * mas_reset() - Reset a Maple Tree operation state. * @mas: Maple Tree operation state. diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index ab94ad4597d07373b813f110092ebaa3cf5527c5..e4e24da16d2c398493e197cfb97f25be9940e3d0 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -920,7 +920,7 @@ unsigned long mem_cgroup_get_zone_lru_size(struct lruvec *lruvec, return READ_ONCE(mz->lru_zone_size[zone_idx][lru]); } -void mem_cgroup_handle_over_high(void); +void mem_cgroup_handle_over_high(gfp_t gfp_mask); unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg); @@ -1458,7 +1458,7 @@ static inline void mem_cgroup_unlock_pages(void) rcu_read_unlock(); } -static inline void mem_cgroup_handle_over_high(void) +static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask) { } diff --git a/include/linux/mfd/88pm860x.h b/include/linux/mfd/88pm860x.h index 473545a2c425b81bc4b7d5c096a74a8526236818..6fa21791fc85e81b88b26275c86138fbda660772 100644 --- a/include/linux/mfd/88pm860x.h +++ b/include/linux/mfd/88pm860x.h @@ -472,13 +472,7 @@ extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *); extern int pm860x_bulk_write(struct i2c_client *, int, int, unsigned char *); extern int pm860x_set_bits(struct i2c_client *, int, unsigned char, unsigned char); -extern int pm860x_page_reg_read(struct i2c_client *, int); extern int pm860x_page_reg_write(struct i2c_client *, int, unsigned char); extern int pm860x_page_bulk_read(struct i2c_client *, int, int, unsigned char *); -extern int pm860x_page_bulk_write(struct i2c_client *, int, int, - unsigned char *); -extern int pm860x_page_set_bits(struct i2c_client *, int, unsigned char, - unsigned char); - #endif /* __LINUX_MFD_88PM860X_H */ diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h index 302a330c5c84cd18e3606d59719a0b4b1f414c59..09fb3c56e7d7bca1b0719b1139ebca176bee5951 100644 --- a/include/linux/mfd/abx500/ab8500.h +++ b/include/linux/mfd/abx500/ab8500.h @@ -382,10 +382,6 @@ struct ab8500_platform_data { struct ab8500_sysctrl_platform_data *sysctrl; }; -extern int ab8500_init(struct ab8500 *ab8500, - enum ab8500_version version); -extern int ab8500_exit(struct ab8500 *ab8500); - extern int ab8500_suspend(struct ab8500 *ab8500); static inline int is_ab8500(struct ab8500 *ab) diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index e7a7e70fdb385d8e6009de4d45654fd8d25470e5..dd0fc891b2289e544f2fec7e94c8ca6d30f1fd91 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -556,16 +556,6 @@ static inline void prcmu_clear(unsigned int reg, u32 bits) #define PRCMU_QOS_ARM_OPP 3 #define PRCMU_QOS_DEFAULT_VALUE -1 -static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void) -{ - return 0; -} - -static inline int prcmu_qos_requirement(int prcmu_qos_class) -{ - return 0; -} - static inline int prcmu_qos_add_requirement(int prcmu_qos_class, char *name, s32 value) { @@ -582,15 +572,4 @@ static inline void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name) { } -static inline int prcmu_qos_add_notifier(int prcmu_qos_class, - struct notifier_block *notifier) -{ - return 0; -} -static inline int prcmu_qos_remove_notifier(int prcmu_qos_class, - struct notifier_block *notifier) -{ - return 0; -} - #endif /* __MACH_PRCMU_H */ diff --git a/include/linux/mfd/hi655x-pmic.h b/include/linux/mfd/hi655x-pmic.h index 6a012784dd1b9b72e09adfef58c169c48a66e007..194556851ccf7759bdfa8b1052e6fb455c03e108 100644 --- a/include/linux/mfd/hi655x-pmic.h +++ b/include/linux/mfd/hi655x-pmic.h @@ -52,7 +52,6 @@ #define OTMP_D1R_INT_MASK BIT(OTMP_D1R_INT) struct hi655x_pmic { - struct resource *res; struct device *dev; struct regmap *regmap; struct gpio_desc *gpio; diff --git a/include/linux/mfd/max77686-private.h b/include/linux/mfd/max77686-private.h index 3acceeedbaba61437ec459fda1355ad1f88492e2..ea635d12a741bac202156e75419eb3f4010eb912 100644 --- a/include/linux/mfd/max77686-private.h +++ b/include/linux/mfd/max77686-private.h @@ -441,8 +441,4 @@ enum max77686_types { TYPE_MAX77802, }; -extern int max77686_irq_init(struct max77686_dev *max77686); -extern void max77686_irq_exit(struct max77686_dev *max77686); -extern int max77686_irq_resume(struct max77686_dev *max77686); - #endif /* __LINUX_MFD_MAX77686_PRIV_H */ diff --git a/include/linux/mfd/rz-mtu3.h b/include/linux/mfd/rz-mtu3.h index c5173bc062701b001b06a70ba25608b5a5ae041f..8421d49500bf4da9e98f75ee84488e4cfe5348fc 100644 --- a/include/linux/mfd/rz-mtu3.h +++ b/include/linux/mfd/rz-mtu3.h @@ -151,7 +151,6 @@ struct rz_mtu3 { void *priv_data; }; -#if IS_ENABLED(CONFIG_RZ_MTU3) static inline bool rz_mtu3_request_channel(struct rz_mtu3_channel *ch) { mutex_lock(&ch->lock); @@ -188,70 +187,5 @@ void rz_mtu3_32bit_ch_write(struct rz_mtu3_channel *ch, u16 off, u32 val); void rz_mtu3_shared_reg_write(struct rz_mtu3_channel *ch, u16 off, u16 val); void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, u16 off, u16 pos, u8 val); -#else -static inline bool rz_mtu3_request_channel(struct rz_mtu3_channel *ch) -{ - return false; -} - -static inline void rz_mtu3_release_channel(struct rz_mtu3_channel *ch) -{ -} - -static inline bool rz_mtu3_is_enabled(struct rz_mtu3_channel *ch) -{ - return false; -} - -static inline void rz_mtu3_disable(struct rz_mtu3_channel *ch) -{ -} - -static inline int rz_mtu3_enable(struct rz_mtu3_channel *ch) -{ - return 0; -} - -static inline u8 rz_mtu3_8bit_ch_read(struct rz_mtu3_channel *ch, u16 off) -{ - return 0; -} - -static inline u16 rz_mtu3_16bit_ch_read(struct rz_mtu3_channel *ch, u16 off) -{ - return 0; -} - -static inline u32 rz_mtu3_32bit_ch_read(struct rz_mtu3_channel *ch, u16 off) -{ - return 0; -} - -static inline u16 rz_mtu3_shared_reg_read(struct rz_mtu3_channel *ch, u16 off) -{ - return 0; -} - -static inline void rz_mtu3_8bit_ch_write(struct rz_mtu3_channel *ch, u16 off, u8 val) -{ -} - -static inline void rz_mtu3_16bit_ch_write(struct rz_mtu3_channel *ch, u16 off, u16 val) -{ -} - -static inline void rz_mtu3_32bit_ch_write(struct rz_mtu3_channel *ch, u16 off, u32 val) -{ -} - -static inline void rz_mtu3_shared_reg_write(struct rz_mtu3_channel *ch, u16 off, u16 val) -{ -} - -static inline void rz_mtu3_shared_reg_update_bit(struct rz_mtu3_channel *ch, - u16 off, u16 pos, u8 val) -{ -} -#endif #endif /* __MFD_RZ_MTU3_H__ */ diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h index 8bef1ab62bba3778ead0711bfb292fa0d6f74e9c..4e27ca7c49defec2668884e52dc4a76da5afb8d6 100644 --- a/include/linux/micrel_phy.h +++ b/include/linux/micrel_phy.h @@ -41,9 +41,10 @@ #define PHY_ID_KSZ9477 0x00221631 /* struct phy_device dev_flags definitions */ -#define MICREL_PHY_50MHZ_CLK 0x00000001 -#define MICREL_PHY_FXEN 0x00000002 -#define MICREL_KSZ8_P1_ERRATA 0x00000003 +#define MICREL_PHY_50MHZ_CLK BIT(0) +#define MICREL_PHY_FXEN BIT(1) +#define MICREL_KSZ8_P1_ERRATA BIT(2) +#define MICREL_NO_EEE BIT(3) #define MICREL_KSZ9021_EXTREG_CTRL 0xB #define MICREL_KSZ9021_EXTREG_DATA_WRITE 0xC diff --git a/include/linux/misc_cgroup.h b/include/linux/misc_cgroup.h index c238207d1615698a14d477246d6bbb135af79a50..e799b1f8d05bb37745af7cc3e31f2b0f8a5f441c 100644 --- a/include/linux/misc_cgroup.h +++ b/include/linux/misc_cgroup.h @@ -31,17 +31,18 @@ struct misc_cg; * struct misc_res: Per cgroup per misc type resource * @max: Maximum limit on the resource. * @usage: Current usage of the resource. - * @failed: True if charged failed for the resource in a cgroup. + * @events: Number of times, the resource limit exceeded. */ struct misc_res { - unsigned long max; - atomic_long_t usage; - atomic_long_t events; + u64 max; + atomic64_t usage; + atomic64_t events; }; /** * struct misc_cg - Miscellaneous controller's cgroup structure. * @css: cgroup subsys state object. + * @events_file: Handle for the misc resources events file. * @res: Array of misc resources usage in the cgroup. */ struct misc_cg { @@ -53,12 +54,10 @@ struct misc_cg { struct misc_res res[MISC_CG_RES_TYPES]; }; -unsigned long misc_cg_res_total_usage(enum misc_res_type type); -int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity); -int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount); -void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount); +u64 misc_cg_res_total_usage(enum misc_res_type type); +int misc_cg_set_capacity(enum misc_res_type type, u64 capacity); +int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount); +void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount); /** * css_misc() - Get misc cgroup from the css. @@ -99,27 +98,26 @@ static inline void put_misc_cg(struct misc_cg *cg) #else /* !CONFIG_CGROUP_MISC */ -static inline unsigned long misc_cg_res_total_usage(enum misc_res_type type) +static inline u64 misc_cg_res_total_usage(enum misc_res_type type) { return 0; } -static inline int misc_cg_set_capacity(enum misc_res_type type, - unsigned long capacity) +static inline int misc_cg_set_capacity(enum misc_res_type type, u64 capacity) { return 0; } static inline int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) + u64 amount) { return 0; } static inline void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) + u64 amount) { } diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 7c58c44662b87e363cea26e045f9b636f6ea503c..914a9f974baaae5010d473d0e5d8c6e79f88e191 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -379,7 +379,7 @@ struct mtd_info { struct module *owner; struct device dev; - int usecount; + struct kref refcnt; struct mtd_debug_info dbg; struct nvmem_device *nvmem; struct nvmem_device *otp_user_nvmem; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 5159d692f9ce5fc3c7b6979073b4e713d2a60b49..90a141ba2a5acc18eb347baeb31dd8dc52d24f19 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1540,6 +1540,7 @@ int nand_reset_op(struct nand_chip *chip); int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf, unsigned int len); int nand_status_op(struct nand_chip *chip, u8 *status); +int nand_exit_status_op(struct nand_chip *chip); int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock); int nand_read_page_op(struct nand_chip *chip, unsigned int page, unsigned int offset_in_page, void *buf, unsigned int len); diff --git a/include/linux/netfilter/nf_conntrack_sctp.h b/include/linux/netfilter/nf_conntrack_sctp.h index 625f491b95de87904892dc52e6e5bf5bba8a57fa..fb31312825ae530f95577f3866e8fbd235dadd2a 100644 --- a/include/linux/netfilter/nf_conntrack_sctp.h +++ b/include/linux/netfilter/nf_conntrack_sctp.h @@ -9,6 +9,7 @@ struct ip_ct_sctp { enum sctp_conntrack state; __be32 vtag[IP_CT_DIR_MAX]; + u8 init[IP_CT_DIR_MAX]; u8 last_dir; u8 flags; }; diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 20eeba8b009df1100a3c6080f84c93277aaba4dc..cd628c4b011e54fa807674daa8a3361c97c20a5f 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -48,6 +48,7 @@ struct nfs_client { #define NFS_CS_NOPING 6 /* - don't ping on connect */ #define NFS_CS_DS 7 /* - Server is a DS */ #define NFS_CS_REUSEPORT 8 /* - reuse src port on reconnect */ +#define NFS_CS_PNFS 9 /* - Server used for pnfs */ struct sockaddr_storage cl_addr; /* server identifier */ size_t cl_addrlen; char * cl_hostname; /* hostname of server */ diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index aa9f4c6ebe261d40fc3dc21389a97458e1722178..1c315f854ea8013bef87fb820dd0186aefb4a3cf 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -157,7 +157,9 @@ extern void nfs_unlock_request(struct nfs_page *req); extern void nfs_unlock_and_release_request(struct nfs_page *); extern struct nfs_page *nfs_page_group_lock_head(struct nfs_page *req); extern int nfs_page_group_lock_subrequests(struct nfs_page *head); -extern void nfs_join_page_group(struct nfs_page *head, struct inode *inode); +extern void nfs_join_page_group(struct nfs_page *head, + struct nfs_commit_info *cinfo, + struct inode *inode); extern int nfs_page_group_lock(struct nfs_page *); extern void nfs_page_group_unlock(struct nfs_page *); extern bool nfs_page_group_sync_on_bit(struct nfs_page *, unsigned int); diff --git a/include/linux/of.h b/include/linux/of.h index ed679819c27976699f699dccd6f0c8bda7ec824a..6a9ddf20e79abdf0341f0cf6210e5991231eeab8 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1676,8 +1676,8 @@ int of_overlay_notifier_unregister(struct notifier_block *nb); #else -static inline int of_overlay_fdt_apply(void *overlay_fdt, u32 overlay_fdt_size, - int *ovcs_id) +static inline int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, + int *ovcs_id, struct device_node *target_base) { return -ENOTSUPP; } diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 0f4a8903922a2e7a009b25857cfdf79d7b09a5f2..f86a08ba0207ee1dd1bb704b6380d55b2d3feb1f 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -67,6 +67,7 @@ enum OID { OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ OID_ntlmssp, /* 1.3.6.1.4.1.311.2.2.10 */ + OID_negoex, /* 1.3.6.1.4.1.311.2.2.30 */ OID_spnego, /* 1.3.6.1.5.5.2 */ diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 75b73c83bc9d0d1074c9e0f67ad39776b12c5eb1..d01351b1526f6123199eb54e58c0ead87246877b 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -30,17 +30,28 @@ struct percpu_counter { extern int percpu_counter_batch; -int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp, - struct lock_class_key *key); +int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount, + gfp_t gfp, u32 nr_counters, + struct lock_class_key *key); -#define percpu_counter_init(fbc, value, gfp) \ +#define percpu_counter_init_many(fbc, value, gfp, nr_counters) \ ({ \ static struct lock_class_key __key; \ \ - __percpu_counter_init(fbc, value, gfp, &__key); \ + __percpu_counter_init_many(fbc, value, gfp, nr_counters,\ + &__key); \ }) -void percpu_counter_destroy(struct percpu_counter *fbc); + +#define percpu_counter_init(fbc, value, gfp) \ + percpu_counter_init_many(fbc, value, gfp, 1) + +void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters); +static inline void percpu_counter_destroy(struct percpu_counter *fbc) +{ + percpu_counter_destroy_many(fbc, 1); +} + void percpu_counter_set(struct percpu_counter *fbc, s64 amount); void percpu_counter_add_batch(struct percpu_counter *fbc, s64 amount, s32 batch); @@ -116,11 +127,27 @@ struct percpu_counter { s64 count; }; +static inline int percpu_counter_init_many(struct percpu_counter *fbc, + s64 amount, gfp_t gfp, + u32 nr_counters) +{ + u32 i; + + for (i = 0; i < nr_counters; i++) + fbc[i].count = amount; + + return 0; +} + static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp) { - fbc->count = amount; - return 0; + return percpu_counter_init_many(fbc, amount, gfp, 1); +} + +static inline void percpu_counter_destroy_many(struct percpu_counter *fbc, + u32 nr_counters) +{ } static inline void percpu_counter_destroy(struct percpu_counter *fbc) diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h index 1fba072b3dac3dd613270fc8ec86922e2a3f4627..af7639c3b0a3a4919c60b70d496519407734ad11 100644 --- a/include/linux/pgtable.h +++ b/include/linux/pgtable.h @@ -206,6 +206,14 @@ static inline int pmd_young(pmd_t pmd) #endif #ifndef set_ptes + +#ifndef pte_next_pfn +static inline pte_t pte_next_pfn(pte_t pte) +{ + return __pte(pte_val(pte) + (1UL << PFN_PTE_SHIFT)); +} +#endif + /** * set_ptes - Map consecutive pages to a contiguous range of addresses. * @mm: Address space to map the pages into. @@ -231,7 +239,7 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr, if (--nr == 0) break; ptep++; - pte = __pte(pte_val(pte) + (1UL << PFN_PTE_SHIFT)); + pte = pte_next_pfn(pte); } arch_leave_lazy_mmu_mode(); } diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 7d07f8736431349cd946c00fb9a79e8f0c972c4f..2b886ea654bb36ede4e06accf13a6f257fd19f42 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -600,7 +600,7 @@ void pcs_get_state(struct phylink_pcs *pcs, * * The %neg_mode argument should be tested via the phylink_mode_*() family of * functions, or for PCS that set pcs->neg_mode true, should be tested - * against the %PHYLINK_PCS_NEG_* definitions. + * against the PHYLINK_PCS_NEG_* definitions. */ int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, const unsigned long *advertising, @@ -630,7 +630,7 @@ void pcs_an_restart(struct phylink_pcs *pcs); * * The %mode argument should be tested via the phylink_mode_*() family of * functions, or for PCS that set pcs->neg_mode true, should be tested - * against the %PHYLINK_PCS_NEG_* definitions. + * against the PHYLINK_PCS_NEG_* definitions. */ void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, int speed, int duplex); diff --git a/include/linux/platform_data/rtc-ds2404.h b/include/linux/platform_data/rtc-ds2404.h deleted file mode 100644 index 22c53825528f0995c9943954a8325e4fd18e22a0..0000000000000000000000000000000000000000 --- a/include/linux/platform_data/rtc-ds2404.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * ds2404.h - platform data structure for the DS2404 RTC. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2012 Sven Schnelle - */ - -#ifndef __LINUX_DS2404_H -#define __LINUX_DS2404_H - -struct ds2404_platform_data { - - unsigned int gpio_rst; - unsigned int gpio_clk; - unsigned int gpio_dq; -}; -#endif diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 04ae1d9073a74dba6516158cc58b17647d9e5fe5..d2f9f690a9c1452d189c5f638a61a02300d1200c 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -298,7 +298,7 @@ struct pwm_chip { int base; unsigned int npwm; - struct pwm_device * (*of_xlate)(struct pwm_chip *pc, + struct pwm_device * (*of_xlate)(struct pwm_chip *chip, const struct of_phandle_args *args); unsigned int of_pwm_n_cells; @@ -395,9 +395,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); -struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, +struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args); -struct pwm_device *of_pwm_single_xlate(struct pwm_chip *pc, +struct pwm_device *of_pwm_single_xlate(struct pwm_chip *chip, const struct of_phandle_args *args); struct pwm_device *pwm_get(struct device *dev, const char *con_id); diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h index f29aaaf2eb21e9bdab1943c50309919bec16effe..006e18decfad0796a9b8d14cc09170c074493c27 100644 --- a/include/linux/raid/pq.h +++ b/include/linux/raid/pq.h @@ -108,6 +108,8 @@ extern const struct raid6_calls raid6_vpermxor1; extern const struct raid6_calls raid6_vpermxor2; extern const struct raid6_calls raid6_vpermxor4; extern const struct raid6_calls raid6_vpermxor8; +extern const struct raid6_calls raid6_lsx; +extern const struct raid6_calls raid6_lasx; struct raid6_recov_calls { void (*data2)(int, size_t, int, int, void **); @@ -123,6 +125,8 @@ extern const struct raid6_recov_calls raid6_recov_avx2; extern const struct raid6_recov_calls raid6_recov_avx512; extern const struct raid6_recov_calls raid6_recov_s390xc; extern const struct raid6_recov_calls raid6_recov_neon; +extern const struct raid6_recov_calls raid6_recov_lsx; +extern const struct raid6_recov_calls raid6_recov_lasx; extern const struct raid6_calls raid6_neonx1; extern const struct raid6_calls raid6_neonx2; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index fe8978eb69f1c7688c85ba4ecd67cf5e8f306a4e..b4795698d8c2a4e80ccafbe632436c4dfb636a1e 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -690,6 +690,10 @@ int rproc_detach(struct rproc *rproc); int rproc_set_firmware(struct rproc *rproc, const char *fw_name); void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem); + +/* from remoteproc_coredump.c */ +void rproc_coredump_cleanup(struct rproc *rproc); +void rproc_coredump(struct rproc *rproc); void rproc_coredump_using_sections(struct rproc *rproc); int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size); int rproc_coredump_add_custom_segment(struct rproc *rproc, diff --git a/include/linux/resume_user_mode.h b/include/linux/resume_user_mode.h index 28518945444968fed623838f4028dca96736a9f7..f8f3e958e9cf2fbf0777fdbf5e3fd993c889cee3 100644 --- a/include/linux/resume_user_mode.h +++ b/include/linux/resume_user_mode.h @@ -55,7 +55,7 @@ static inline void resume_user_mode_work(struct pt_regs *regs) } #endif - mem_cgroup_handle_over_high(); + mem_cgroup_handle_over_high(GFP_KERNEL); blkcg_maybe_throttle_current(); rseq_handle_notify_resume(NULL, regs); diff --git a/include/linux/rmap.h b/include/linux/rmap.h index a3825ce81102cfd0e2d2097d8841c1ea73011737..51cc21ebb568b0bcbc9ead33666a1bc03efa08d4 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -479,7 +479,6 @@ struct anon_vma *folio_lock_anon_vma_read(struct folio *folio, #define anon_vma_init() do {} while (0) #define anon_vma_prepare(vma) (0) -#define anon_vma_link(vma) do {} while (0) static inline int folio_referenced(struct folio *folio, int is_locked, struct mem_cgroup *memcg, diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h index 523c98b96cb41ad3b46140c2b9e60c7bf7dc20ef..90d8e4475f80ed9ed8df40c81a3a8c4aba7e7aca 100644 --- a/include/linux/rpmsg.h +++ b/include/linux/rpmsg.h @@ -64,12 +64,14 @@ struct rpmsg_device { }; typedef int (*rpmsg_rx_cb_t)(struct rpmsg_device *, void *, int, void *, u32); +typedef int (*rpmsg_flowcontrol_cb_t)(struct rpmsg_device *, void *, bool); /** * struct rpmsg_endpoint - binds a local rpmsg address to its user * @rpdev: rpmsg channel device * @refcount: when this drops to zero, the ept is deallocated * @cb: rx callback handler + * @flow_cb: remote flow control callback handler * @cb_lock: must be taken before accessing/changing @cb * @addr: local rpmsg address * @priv: private data for the driver's use @@ -92,6 +94,7 @@ struct rpmsg_endpoint { struct rpmsg_device *rpdev; struct kref refcount; rpmsg_rx_cb_t cb; + rpmsg_flowcontrol_cb_t flow_cb; struct mutex cb_lock; u32 addr; void *priv; @@ -106,6 +109,7 @@ struct rpmsg_endpoint { * @probe: invoked when a matching rpmsg channel (i.e. device) is found * @remove: invoked when the rpmsg channel is removed * @callback: invoked when an inbound message is received on the channel + * @flowcontrol: invoked when remote side flow control request is received */ struct rpmsg_driver { struct device_driver drv; @@ -113,6 +117,7 @@ struct rpmsg_driver { int (*probe)(struct rpmsg_device *dev); void (*remove)(struct rpmsg_device *dev); int (*callback)(struct rpmsg_device *, void *, int, void *, u32); + int (*flowcontrol)(struct rpmsg_device *, void *, bool); }; static inline u16 rpmsg16_to_cpu(struct rpmsg_device *rpdev, __rpmsg16 val) @@ -192,6 +197,8 @@ __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, ssize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept); +int rpmsg_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst); + #else static inline int rpmsg_register_device_override(struct rpmsg_device *rpdev, @@ -316,6 +323,14 @@ static inline ssize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept) return -ENXIO; } +static inline int rpmsg_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return -ENXIO; +} + #endif /* IS_ENABLED(CONFIG_RPMSG) */ /* use a macro to avoid include chaining to get THIS_MODULE */ diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 1fd9c6a21ebe85d8474867ba344c3451eb99bc99..4c0bcbeb1f00cf378fc2567a9431d808f0a5ab02 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -146,6 +146,7 @@ struct rtc_device { time64_t range_min; timeu64_t range_max; + timeu64_t alarm_offset_max; time64_t start_secs; time64_t offset_secs; bool set_start_time; diff --git a/include/linux/sched.h b/include/linux/sched.h index 177b3f3676ef84c3a2a40a2e69f39e6c0f2f2a33..77f01ac385f7a57695c2e9af5b826cb4f9b39f6a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1671,7 +1671,7 @@ static inline unsigned int __task_state_index(unsigned int tsk_state, BUILD_BUG_ON_NOT_POWER_OF_2(TASK_REPORT_MAX); - if (tsk_state == TASK_IDLE) + if ((tsk_state & TASK_IDLE) == TASK_IDLE) state = TASK_REPORT_IDLE; /* @@ -1679,7 +1679,7 @@ static inline unsigned int __task_state_index(unsigned int tsk_state, * to userspace, we can make this appear as if the task has gone through * a regular rt_mutex_lock() call. */ - if (tsk_state == TASK_RTLOCK_WAIT) + if (tsk_state & TASK_RTLOCK_WAIT) state = TASK_UNINTERRUPTIBLE; return fls(state); @@ -1858,7 +1858,17 @@ extern int task_can_attach(struct task_struct *p); extern int dl_bw_alloc(int cpu, u64 dl_bw); extern void dl_bw_free(int cpu, u64 dl_bw); #ifdef CONFIG_SMP + +/* do_set_cpus_allowed() - consider using set_cpus_allowed_ptr() instead */ extern void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask); + +/** + * set_cpus_allowed_ptr - set CPU affinity mask of a task + * @p: the task + * @new_mask: CPU affinity mask + * + * Return: zero if successful, or a negative error code + */ extern int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask); extern int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src, int node); extern void release_user_cpus_ptr(struct task_struct *p); diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 987a59d977c566ae5eb0f306e4ed79f4e5428204..e9bd2f65d7f4e40d10cfe11b4fe5d433be4273db 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -512,8 +512,8 @@ do { \ static inline void do_write_seqcount_begin_nested(seqcount_t *s, int subclass) { - do_raw_write_seqcount_begin(s); seqcount_acquire(&s->dep_map, subclass, 0, _RET_IP_); + do_raw_write_seqcount_begin(s); } /** diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a388160f4be0d6f174b883981286bd09969e5b7a..4f3d14bb15385a2860e4e1ee54738f60eb7c1b06 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -858,6 +858,8 @@ struct sdw_defer { * @post_bank_switch: Callback for post bank switch * @read_ping_status: Read status from PING frames, reported with two bits per Device. * Bits 31:24 are reserved. + * @get_device_num: Callback for vendor-specific device_number allocation + * @put_device_num: Callback for vendor-specific device_number release * @new_peripheral_assigned: Callback to handle enumeration of new peripheral. */ struct sdw_master_ops { @@ -873,7 +875,11 @@ struct sdw_master_ops { int (*pre_bank_switch)(struct sdw_bus *bus); int (*post_bank_switch)(struct sdw_bus *bus); u32 (*read_ping_status)(struct sdw_bus *bus); - void (*new_peripheral_assigned)(struct sdw_bus *bus, int dev_num); + int (*get_device_num)(struct sdw_bus *bus, struct sdw_slave *slave); + void (*put_device_num)(struct sdw_bus *bus, struct sdw_slave *slave); + void (*new_peripheral_assigned)(struct sdw_bus *bus, + struct sdw_slave *slave, + int dev_num); }; /** @@ -908,9 +914,6 @@ struct sdw_master_ops { * meaningful if multi_link is set. If set to 1, hardware-based * synchronization will be used even if a stream only uses a single * SoundWire segment. - * @dev_num_ida_min: if set, defines the minimum values for the IDA - * used to allocate system-unique device numbers. This value needs to be - * identical across all SoundWire bus in the system. */ struct sdw_bus { struct device *dev; @@ -939,7 +942,6 @@ struct sdw_bus { u32 bank_switch_timeout; bool multi_link; int hw_sync_min_links; - int dev_num_ida_min; }; int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, diff --git a/include/linux/soundwire/sdw_intel.h b/include/linux/soundwire/sdw_intel.h index fa67fad4ef51085f6f63895b55ba79c36028dd4e..00bb22d96ae56744009c8de258d9be6b557986a3 100644 --- a/include/linux/soundwire/sdw_intel.h +++ b/include/linux/soundwire/sdw_intel.h @@ -428,4 +428,11 @@ struct sdw_intel_hw_ops { extern const struct sdw_intel_hw_ops sdw_intel_cnl_hw_ops; extern const struct sdw_intel_hw_ops sdw_intel_lnl_hw_ops; +/* + * IDA min selected to allow for 5 unconstrained devices per link, + * and 6 system-unique Device Numbers for wake-capable devices. + */ + +#define SDW_INTEL_DEV_NUM_IDA_MIN 6 + #endif diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 5b4fb3c791bc267042a9af1b7384e606ae0b043f..2f8dc47f1eb07504903318682a58ec8735ac4c5d 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -779,7 +779,9 @@ xdr_stream_decode_uint32_array(struct xdr_stream *xdr, if (unlikely(xdr_stream_decode_u32(xdr, &len) < 0)) return -EBADMSG; - p = xdr_inline_decode(xdr, size_mul(len, sizeof(*p))); + if (U32_MAX >= SIZE_MAX / sizeof(*p) && len > SIZE_MAX / sizeof(*p)) + return -EBADMSG; + p = xdr_inline_decode(xdr, len * sizeof(*p)); if (unlikely(!p)) return -EBADMSG; if (array == NULL) diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h index b4536626f8ff35c018a8f8dada7010c7f11f0c26..ecde0312dd5203a125e21846235ab5b1acae6cb7 100644 --- a/include/linux/swiotlb.h +++ b/include/linux/swiotlb.h @@ -172,14 +172,23 @@ static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr) if (!mem) return false; - if (IS_ENABLED(CONFIG_SWIOTLB_DYNAMIC)) { - /* Pairs with smp_wmb() in swiotlb_find_slots() and - * swiotlb_dyn_alloc(), which modify the RCU lists. - */ - smp_rmb(); - return swiotlb_find_pool(dev, paddr); - } +#ifdef CONFIG_SWIOTLB_DYNAMIC + /* + * All SWIOTLB buffer addresses must have been returned by + * swiotlb_tbl_map_single() and passed to a device driver. + * If a SWIOTLB address is checked on another CPU, then it was + * presumably loaded by the device driver from an unspecified private + * data structure. Make sure that this load is ordered before reading + * dev->dma_uses_io_tlb here and mem->pools in swiotlb_find_pool(). + * + * This barrier pairs with smp_mb() in swiotlb_find_slots(). + */ + smp_rmb(); + return READ_ONCE(dev->dma_uses_io_tlb) && + swiotlb_find_pool(dev, paddr); +#else return paddr >= mem->defpool.start && paddr < mem->defpool.end; +#endif } static inline bool is_swiotlb_force_bounce(struct device *dev) diff --git a/include/linux/tca6416_keypad.h b/include/linux/tca6416_keypad.h index b0d36a9934ccd403bd586954694fdb67311e833f..5cf6f6f82aa700a794564dc420b1cf1348d56e5f 100644 --- a/include/linux/tca6416_keypad.h +++ b/include/linux/tca6416_keypad.h @@ -25,7 +25,6 @@ struct tca6416_keys_platform_data { unsigned int rep:1; /* enable input subsystem auto repeat */ uint16_t pinmask; uint16_t invert; - int irq_is_gpio; int use_polling; /* use polling if Interrupt is not connected*/ }; #endif diff --git a/include/linux/thermal.h b/include/linux/thermal.h index b449a46766f5a34535fc121d40afdd5a72c19ea0..a5ae4af955ff926f9b9d49c30cab61a4f0f9f45e 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -53,6 +53,20 @@ enum thermal_notify_event { THERMAL_EVENT_KEEP_ALIVE, /* Request for user space handler to respond */ }; +/** + * struct thermal_trip - representation of a point in temperature domain + * @temperature: temperature value in miliCelsius + * @hysteresis: relative hysteresis in miliCelsius + * @type: trip point type + * @priv: pointer to driver data associated with this trip + */ +struct thermal_trip { + int temperature; + int hysteresis; + enum thermal_trip_type type; + void *priv; +}; + struct thermal_zone_device_ops { int (*bind) (struct thermal_zone_device *, struct thermal_cooling_device *); @@ -62,34 +76,16 @@ struct thermal_zone_device_ops { int (*set_trips) (struct thermal_zone_device *, int, int); int (*change_mode) (struct thermal_zone_device *, enum thermal_device_mode); - int (*get_trip_type) (struct thermal_zone_device *, int, - enum thermal_trip_type *); - int (*get_trip_temp) (struct thermal_zone_device *, int, int *); int (*set_trip_temp) (struct thermal_zone_device *, int, int); - int (*get_trip_hyst) (struct thermal_zone_device *, int, int *); int (*set_trip_hyst) (struct thermal_zone_device *, int, int); int (*get_crit_temp) (struct thermal_zone_device *, int *); int (*set_emul_temp) (struct thermal_zone_device *, int); - int (*get_trend) (struct thermal_zone_device *, int, - enum thermal_trend *); + int (*get_trend) (struct thermal_zone_device *, + const struct thermal_trip *, enum thermal_trend *); void (*hot)(struct thermal_zone_device *); void (*critical)(struct thermal_zone_device *); }; -/** - * struct thermal_trip - representation of a point in temperature domain - * @temperature: temperature value in miliCelsius - * @hysteresis: relative hysteresis in miliCelsius - * @type: trip point type - * @priv: pointer to driver data associated with this trip - */ -struct thermal_trip { - int temperature; - int hysteresis; - enum thermal_trip_type type; - void *priv; -}; - struct thermal_cooling_device_ops { int (*get_max_state) (struct thermal_cooling_device *, unsigned long *); int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *); @@ -304,16 +300,22 @@ int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp); #endif #ifdef CONFIG_THERMAL -struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, - void *, struct thermal_zone_device_ops *, - const struct thermal_zone_params *, int, int); - -void thermal_zone_device_unregister(struct thermal_zone_device *); - -struct thermal_zone_device * -thermal_zone_device_register_with_trips(const char *, struct thermal_trip *, int, int, - void *, struct thermal_zone_device_ops *, - const struct thermal_zone_params *, int, int); +struct thermal_zone_device *thermal_zone_device_register_with_trips( + const char *type, + struct thermal_trip *trips, + int num_trips, int mask, + void *devdata, + struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp, + int passive_delay, int polling_delay); + +struct thermal_zone_device *thermal_tripless_zone_device_register( + const char *type, + void *devdata, + struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp); + +void thermal_zone_device_unregister(struct thermal_zone_device *tz); void *thermal_zone_device_priv(struct thermal_zone_device *tzd); const char *thermal_zone_device_type(struct thermal_zone_device *tzd); @@ -354,15 +356,26 @@ int thermal_zone_device_enable(struct thermal_zone_device *tz); int thermal_zone_device_disable(struct thermal_zone_device *tz); void thermal_zone_device_critical(struct thermal_zone_device *tz); #else -static inline struct thermal_zone_device *thermal_zone_device_register( - const char *type, int trips, int mask, void *devdata, - struct thermal_zone_device_ops *ops, - const struct thermal_zone_params *tzp, - int passive_delay, int polling_delay) +static inline struct thermal_zone_device *thermal_zone_device_register_with_trips( + const char *type, + struct thermal_trip *trips, + int num_trips, int mask, + void *devdata, + struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp, + int passive_delay, int polling_delay) { return ERR_PTR(-ENODEV); } -static inline void thermal_zone_device_unregister( - struct thermal_zone_device *tz) + +static inline struct thermal_zone_device *thermal_tripless_zone_device_register( + const char *type, + void *devdata, + struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp) +{ return ERR_PTR(-ENODEV); } + +static inline void thermal_zone_device_unregister(struct thermal_zone_device *tz) { } + static inline struct thermal_cooling_device * thermal_cooling_device_register(const char *type, void *devdata, const struct thermal_cooling_device_ops *ops) diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index c1a0a19d80fb936b44250ea589fabb3f405645bd..21ae37e49319a13287ef895c51e1362ed20aeca7 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -62,13 +62,13 @@ void trace_event_printf(struct trace_iterator *iter, const char *fmt, ...); /* Used to find the offset and length of dynamic fields in trace events */ struct trace_dynamic_info { #ifdef CONFIG_CPU_BIG_ENDIAN - u16 offset; u16 len; + u16 offset; #else - u16 len; u16 offset; + u16 len; #endif -}; +} __packed; /* * The trace entry - the most basic unit of tracing. This is what @@ -649,7 +649,7 @@ struct trace_event_file { struct list_head list; struct trace_event_call *event_call; struct event_filter __rcu *filter; - struct dentry *dir; + struct eventfs_file *ef; struct trace_array *tr; struct trace_subsystem_dir *system; struct list_head triggers; @@ -824,6 +824,7 @@ enum { FILTER_RDYN_STRING, FILTER_PTR_STRING, FILTER_TRACE_FN, + FILTER_CPUMASK, FILTER_COMM, FILTER_CPU, FILTER_STACKTRACE, diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h index 99912445974ce68938dc2777c0bef335f97c20db..009072792fa36a607d31d7b9778dd1f5ac2ac87e 100644 --- a/include/linux/tracefs.h +++ b/include/linux/tracefs.h @@ -21,6 +21,29 @@ struct file_operations; #ifdef CONFIG_TRACING +struct eventfs_file; + +struct dentry *eventfs_create_events_dir(const char *name, + struct dentry *parent); + +struct eventfs_file *eventfs_add_subsystem_dir(const char *name, + struct dentry *parent); + +struct eventfs_file *eventfs_add_dir(const char *name, + struct eventfs_file *ef_parent); + +int eventfs_add_file(const char *name, umode_t mode, + struct eventfs_file *ef_parent, void *data, + const struct file_operations *fops); + +int eventfs_add_events_file(const char *name, umode_t mode, + struct dentry *parent, void *data, + const struct file_operations *fops); + +void eventfs_remove(struct eventfs_file *ef); + +void eventfs_remove_events_dir(struct dentry *dentry); + struct dentry *tracefs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops); diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index db1b0eaef4eb7ac68decc95e728783a09de3e39d..0e652026b776f81b697f7fa93c3183b67ee46203 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -208,6 +208,9 @@ struct vdpa_map_file { * @vdev: vdpa device * Returns the virtio features support by the * device + * @get_backend_features: Get parent-specific backend features (optional) + * Returns the vdpa features supported by the + * device. * @set_driver_features: Set virtio features supported by the driver * @vdev: vdpa device * @features: feature support by the driver @@ -358,6 +361,7 @@ struct vdpa_config_ops { u32 (*get_vq_align)(struct vdpa_device *vdev); u32 (*get_vq_group)(struct vdpa_device *vdev, u16 idx); u64 (*get_device_features)(struct vdpa_device *vdev); + u64 (*get_backend_features)(const struct vdpa_device *vdev); int (*set_driver_features)(struct vdpa_device *vdev, u64 features); u64 (*get_driver_features)(struct vdpa_device *vdev); void (*set_config_cb)(struct vdpa_device *vdev, diff --git a/include/linux/virtio.h b/include/linux/virtio.h index de6041deee372e822b77db13a28d759f759f4320..4cc614a38376593787d7fd2842a7b3649979ead8 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -9,6 +9,7 @@ #include #include #include +#include /** * struct virtqueue - a queue to register buffers for sending or receiving. @@ -61,6 +62,8 @@ int virtqueue_add_sgs(struct virtqueue *vq, void *data, gfp_t gfp); +struct device *virtqueue_dma_dev(struct virtqueue *vq); + bool virtqueue_kick(struct virtqueue *vq); bool virtqueue_kick_prepare(struct virtqueue *vq); @@ -78,6 +81,8 @@ bool virtqueue_enable_cb(struct virtqueue *vq); unsigned virtqueue_enable_cb_prepare(struct virtqueue *vq); +int virtqueue_set_dma_premapped(struct virtqueue *_vq); + bool virtqueue_poll(struct virtqueue *vq, unsigned); bool virtqueue_enable_cb_delayed(struct virtqueue *vq); @@ -95,6 +100,8 @@ dma_addr_t virtqueue_get_used_addr(const struct virtqueue *vq); int virtqueue_resize(struct virtqueue *vq, u32 num, void (*recycle)(struct virtqueue *vq, void *buf)); +int virtqueue_reset(struct virtqueue *vq, + void (*recycle)(struct virtqueue *vq, void *buf)); /** * struct virtio_device - representation of a device using virtio @@ -184,10 +191,8 @@ struct virtio_driver { void (*scan)(struct virtio_device *dev); void (*remove)(struct virtio_device *dev); void (*config_changed)(struct virtio_device *dev); -#ifdef CONFIG_PM int (*freeze)(struct virtio_device *dev); int (*restore)(struct virtio_device *dev); -#endif }; static inline struct virtio_driver *drv_to_virtio(struct device_driver *drv) @@ -206,4 +211,19 @@ void unregister_virtio_driver(struct virtio_driver *drv); #define module_virtio_driver(__virtio_driver) \ module_driver(__virtio_driver, register_virtio_driver, \ unregister_virtio_driver) + +dma_addr_t virtqueue_dma_map_single_attrs(struct virtqueue *_vq, void *ptr, size_t size, + enum dma_data_direction dir, unsigned long attrs); +void virtqueue_dma_unmap_single_attrs(struct virtqueue *_vq, dma_addr_t addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs); +int virtqueue_dma_mapping_error(struct virtqueue *_vq, dma_addr_t addr); + +bool virtqueue_dma_need_sync(struct virtqueue *_vq, dma_addr_t addr); +void virtqueue_dma_sync_single_range_for_cpu(struct virtqueue *_vq, dma_addr_t addr, + unsigned long offset, size_t size, + enum dma_data_direction dir); +void virtqueue_dma_sync_single_range_for_device(struct virtqueue *_vq, dma_addr_t addr, + unsigned long offset, size_t size, + enum dma_data_direction dir); #endif /* _LINUX_VIRTIO_H */ diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 683efe29fa698e96b9ee57a95351b431b62408e6..1c1d06804d45099f8a3e59ac615e11f65f822370 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -125,6 +125,17 @@ struct rcu_work { struct workqueue_struct *wq; }; +enum wq_affn_scope { + WQ_AFFN_DFL, /* use system default */ + WQ_AFFN_CPU, /* one pod per CPU */ + WQ_AFFN_SMT, /* one pod poer SMT */ + WQ_AFFN_CACHE, /* one pod per LLC */ + WQ_AFFN_NUMA, /* one pod per NUMA node */ + WQ_AFFN_SYSTEM, /* one pod across the whole system */ + + WQ_AFFN_NR_TYPES, +}; + /** * struct workqueue_attrs - A struct for workqueue attributes. * @@ -138,17 +149,58 @@ struct workqueue_attrs { /** * @cpumask: allowed CPUs + * + * Work items in this workqueue are affine to these CPUs and not allowed + * to execute on other CPUs. A pool serving a workqueue must have the + * same @cpumask. */ cpumask_var_t cpumask; /** - * @no_numa: disable NUMA affinity + * @__pod_cpumask: internal attribute used to create per-pod pools + * + * Internal use only. + * + * Per-pod unbound worker pools are used to improve locality. Always a + * subset of ->cpumask. A workqueue can be associated with multiple + * worker pools with disjoint @__pod_cpumask's. Whether the enforcement + * of a pool's @__pod_cpumask is strict depends on @affn_strict. + */ + cpumask_var_t __pod_cpumask; + + /** + * @affn_strict: affinity scope is strict + * + * If clear, workqueue will make a best-effort attempt at starting the + * worker inside @__pod_cpumask but the scheduler is free to migrate it + * outside. * - * Unlike other fields, ``no_numa`` isn't a property of a worker_pool. It - * only modifies how :c:func:`apply_workqueue_attrs` select pools and thus - * doesn't participate in pool hash calculations or equality comparisons. + * If set, workers are only allowed to run inside @__pod_cpumask. + */ + bool affn_strict; + + /* + * Below fields aren't properties of a worker_pool. They only modify how + * :c:func:`apply_workqueue_attrs` select pools and thus don't + * participate in pool hash calculations or equality comparisons. + */ + + /** + * @affn_scope: unbound CPU affinity scope + * + * CPU pods are used to improve execution locality of unbound work + * items. There are multiple pod types, one for each wq_affn_scope, and + * every CPU in the system belongs to one pod in every pod type. CPUs + * that belong to the same pod share the worker pool. For example, + * selecting %WQ_AFFN_NUMA makes the workqueue use a separate worker + * pool for each NUMA node. + */ + enum wq_affn_scope affn_scope; + + /** + * @ordered: work items must be executed one by one in queueing order */ - bool no_numa; + bool ordered; }; static inline struct delayed_work *to_delayed_work(struct work_struct *work) @@ -343,14 +395,10 @@ enum { __WQ_ORDERED_EXPLICIT = 1 << 19, /* internal: alloc_ordered_workqueue() */ WQ_MAX_ACTIVE = 512, /* I like 512, better ideas? */ - WQ_MAX_UNBOUND_PER_CPU = 4, /* 4 * #cpus for unbound wq */ + WQ_UNBOUND_MAX_ACTIVE = WQ_MAX_ACTIVE, WQ_DFL_ACTIVE = WQ_MAX_ACTIVE / 2, }; -/* unbound wq's aren't per-cpu, scale max_active according to #cpus */ -#define WQ_UNBOUND_MAX_ACTIVE \ - max_t(int, WQ_MAX_ACTIVE, num_possible_cpus() * WQ_MAX_UNBOUND_PER_CPU) - /* * System-wide workqueues which are always present. * @@ -391,7 +439,7 @@ extern struct workqueue_struct *system_freezable_power_efficient_wq; * alloc_workqueue - allocate a workqueue * @fmt: printf format for the name of the workqueue * @flags: WQ_* flags - * @max_active: max in-flight work items, 0 for default + * @max_active: max in-flight work items per CPU, 0 for default * remaining args: args for @fmt * * Allocate a workqueue with the specified parameters. For detailed @@ -569,6 +617,7 @@ static inline bool schedule_work(struct work_struct *work) /* * Detect attempt to flush system-wide workqueues at compile time when possible. + * Warn attempt to flush system-wide workqueues at runtime. * * See https://lkml.kernel.org/r/49925af7-78a8-a3dd-bce6-cfc02e1a9236@I-love.SAKURA.ne.jp * for reasons and steps for converting system-wide workqueues into local workqueues. @@ -576,52 +625,13 @@ static inline bool schedule_work(struct work_struct *work) extern void __warn_flushing_systemwide_wq(void) __compiletime_warning("Please avoid flushing system-wide workqueues."); -/** - * flush_scheduled_work - ensure that any scheduled work has run to completion. - * - * Forces execution of the kernel-global workqueue and blocks until its - * completion. - * - * It's very easy to get into trouble if you don't take great care. - * Either of the following situations will lead to deadlock: - * - * One of the work items currently on the workqueue needs to acquire - * a lock held by your code or its caller. - * - * Your code is running in the context of a work routine. - * - * They will be detected by lockdep when they occur, but the first might not - * occur very often. It depends on what work items are on the workqueue and - * what locks they need, which you have no control over. - * - * In most situations flushing the entire workqueue is overkill; you merely - * need to know that a particular work item isn't queued and isn't running. - * In such cases you should use cancel_delayed_work_sync() or - * cancel_work_sync() instead. - * - * Please stop calling this function! A conversion to stop flushing system-wide - * workqueues is in progress. This function will be removed after all in-tree - * users stopped calling this function. - */ -/* - * The background of commit 771c035372a036f8 ("deprecate the - * '__deprecated' attribute warnings entirely and for good") is that, - * since Linus builds all modules between every single pull he does, - * the standard kernel build needs to be _clean_ in order to be able to - * notice when new problems happen. Therefore, don't emit warning while - * there are in-tree users. - */ +/* Please stop using this function, for this function will be removed in near future. */ #define flush_scheduled_work() \ ({ \ - if (0) \ - __warn_flushing_systemwide_wq(); \ + __warn_flushing_systemwide_wq(); \ __flush_workqueue(system_wq); \ }) -/* - * Although there is no longer in-tree caller, for now just emit warning - * in order to give out-of-tree callers time to update. - */ #define flush_workqueue(wq) \ ({ \ struct workqueue_struct *_wq = (wq); \ @@ -714,5 +724,6 @@ int workqueue_offline_cpu(unsigned int cpu); void __init workqueue_init_early(void); void __init workqueue_init(void); +void __init workqueue_init_topology(void); #endif diff --git a/include/linux/xarray.h b/include/linux/xarray.h index 741703b45f61a8f082873b6a02cb4fd18452ab58..cb571dfcf4b167ca1fbbe2edeeb4a96919955b9c 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -856,6 +856,9 @@ static inline int __must_check xa_insert_irq(struct xarray *xa, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or @@ -886,6 +889,9 @@ static inline __must_check int xa_alloc(struct xarray *xa, u32 *id, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or @@ -916,6 +922,9 @@ static inline int __must_check xa_alloc_bh(struct xarray *xa, u32 *id, * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or @@ -949,6 +958,9 @@ static inline int __must_check xa_alloc_irq(struct xarray *xa, u32 *id, * The search for an empty entry will start at @next and will wrap * around if necessary. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Any context. Takes and releases the xa_lock. May sleep if * the @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the @@ -983,6 +995,9 @@ static inline int xa_alloc_cyclic(struct xarray *xa, u32 *id, void *entry, * The search for an empty entry will start at @next and will wrap * around if necessary. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Any context. Takes and releases the xa_lock while * disabling softirqs. May sleep if the @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the @@ -1017,6 +1032,9 @@ static inline int xa_alloc_cyclic_bh(struct xarray *xa, u32 *id, void *entry, * The search for an empty entry will start at @next and will wrap * around if necessary. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Process context. Takes and releases the xa_lock while * disabling interrupts. May sleep if the @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e6359f7346f1fb4a2703e3de6bae8db475972cdb..c33348ba1657e34e888119cc78ec1d245f4539f2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -350,7 +350,7 @@ struct hci_dev { struct list_head list; struct mutex lock; - char name[8]; + const char *name; unsigned long flags; __u16 id; __u8 bus; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3a4b684f89bf78d1d2464bfe293612533c000be1..7192346e4a22dfd1f9f2a6c6067e92f4167b55ad 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5941,6 +5941,7 @@ void wiphy_delayed_work_cancel(struct wiphy *wiphy, * @event_lock: (private) lock for event list * @owner_nlportid: (private) owner socket port ID * @nl_owner_dead: (private) owner socket went away + * @cqm_rssi_work: (private) CQM RSSI reporting work * @cqm_config: (private) nl80211 RSSI monitor state * @pmsr_list: (private) peer measurement requests * @pmsr_lock: (private) peer measurements requests/results lock @@ -6013,7 +6014,8 @@ struct wireless_dev { } wext; #endif - struct cfg80211_cqm_config *cqm_config; + struct wiphy_work cqm_rssi_work; + struct cfg80211_cqm_config __rcu *cqm_config; struct list_head pmsr_list; spinlock_t pmsr_lock; @@ -7231,7 +7233,7 @@ struct cfg80211_rx_assoc_resp { int uapsd_queues; const u8 *ap_mld_addr; struct { - const u8 *addr; + u8 addr[ETH_ALEN] __aligned(2); struct cfg80211_bss *bss; u16 status; } links[IEEE80211_MLD_MAX_NUM_LINKS]; diff --git a/include/net/ip.h b/include/net/ip.h index 19adacd5ece031547e3ad70d524e89d8a6301a55..3489a1cca5e7bc315ba646f6bc125b2b6ded9416 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -57,6 +57,7 @@ struct inet_skb_parm { #define IPSKB_FRAG_PMTU BIT(6) #define IPSKB_L3SLAVE BIT(7) #define IPSKB_NOPOLICY BIT(8) +#define IPSKB_MULTIPATH BIT(9) u16 frag_max_size; }; @@ -94,7 +95,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm, ipcm_init(ipcm); ipcm->sockc.mark = READ_ONCE(inet->sk.sk_mark); - ipcm->sockc.tsflags = inet->sk.sk_tsflags; + ipcm->sockc.tsflags = READ_ONCE(inet->sk.sk_tsflags); ipcm->oif = READ_ONCE(inet->sk.sk_bound_dev_if); ipcm->addr = inet->inet_saddr; ipcm->protocol = inet->inet_num; diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index c9ff23cf313e4efd4afd889a38aa7872c08118ea..1ba9f4ddf2f6db6c6ebf0a0675ca74fea2273fd9 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -642,7 +642,10 @@ static inline bool fib6_rules_early_flow_dissect(struct net *net, if (!net->ipv6.fib6_rules_require_fldissect) return false; - skb_flow_dissect_flow_keys(skb, flkeys, flag); + memset(flkeys, 0, sizeof(*flkeys)); + __skb_flow_dissect(net, skb, &flow_keys_dissector, + flkeys, NULL, 0, 0, 0, flag); + fl6->fl6_sport = flkeys->ports.src; fl6->fl6_dport = flkeys->ports.dst; fl6->flowi6_proto = flkeys->basic.ip_proto; diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index a378eff827c7484937084c4e4cbbe3f96e2a67d5..15de07d36540526fd4ede91f77af17eaf9ea1a45 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -154,6 +154,7 @@ struct fib_info { int fib_nhs; bool fib_nh_is_v6; bool nh_updated; + bool pfsrc_removed; struct nexthop *nh; struct rcu_head rcu; struct fib_nh fib_nh[]; @@ -418,7 +419,10 @@ static inline bool fib4_rules_early_flow_dissect(struct net *net, if (!net->ipv4.fib_rules_require_fldissect) return false; - skb_flow_dissect_flow_keys(skb, flkeys, flag); + memset(flkeys, 0, sizeof(*flkeys)); + __skb_flow_dissect(net, skb, &flow_keys_dissector, + flkeys, NULL, 0, 0, 0, flag); + fl4->fl4_sport = flkeys->ports.src; fl4->fl4_dport = flkeys->ports.dst; fl4->flowi4_proto = flkeys->basic.ip_proto; diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index e8750b4ef7e17ad85c88ec1db73242795ede0281..f346b4efbc307bcca8893775d9bd4c12f5917293 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -483,15 +483,14 @@ static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len) u64_stats_inc(&tstats->tx_packets); u64_stats_update_end(&tstats->syncp); put_cpu_ptr(tstats); + return; + } + + if (pkt_len < 0) { + DEV_STATS_INC(dev, tx_errors); + DEV_STATS_INC(dev, tx_aborted_errors); } else { - struct net_device_stats *err_stats = &dev->stats; - - if (pkt_len < 0) { - err_stats->tx_errors++; - err_stats->tx_aborted_errors++; - } else { - err_stats->tx_dropped++; - } + DEV_STATS_INC(dev, tx_dropped); } } diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 0675be0f3fa0efc55575bb5b2569dc8a1dbb9f24..c6932d1a3fa806ab9d72948994dbf3b7ac742b1c 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -784,6 +784,11 @@ static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) cpu_to_be32(0x0000ffff))) == 0UL; } +static inline bool ipv6_addr_v4mapped_any(const struct in6_addr *a) +{ + return ipv6_addr_v4mapped(a) && ipv4_is_zeronet(a->s6_addr32[3]); +} + static inline bool ipv6_addr_v4mapped_loopback(const struct in6_addr *a) { return ipv6_addr_v4mapped(a) && ipv4_is_loopback(a->s6_addr32[3]); @@ -1360,7 +1365,7 @@ static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val) return 0; } -static inline int ip6_sock_set_addr_preferences(struct sock *sk, bool val) +static inline int ip6_sock_set_addr_preferences(struct sock *sk, int val) { int ret; diff --git a/include/net/mana/mana.h b/include/net/mana/mana.h index 9f70b43322383a1c3c980ec84b23e919903a7de1..4d43adf186064c4de216d6f83bdf4916a7b07877 100644 --- a/include/net/mana/mana.h +++ b/include/net/mana/mana.h @@ -103,9 +103,10 @@ struct mana_txq { /* skb data and frags dma mappings */ struct mana_skb_head { - dma_addr_t dma_handle[MAX_SKB_FRAGS + 1]; + /* GSO pkts may have 2 SGEs for the linear part*/ + dma_addr_t dma_handle[MAX_SKB_FRAGS + 2]; - u32 size[MAX_SKB_FRAGS + 1]; + u32 size[MAX_SKB_FRAGS + 2]; }; #define MANA_HEADROOM sizeof(struct mana_skb_head) diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 6da68886fabbcb33a63ef256242d51a618571c95..07022bb0d44d4b5eef5812cc86e042833cf3a337 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -539,7 +539,7 @@ static inline int neigh_output(struct neighbour *n, struct sk_buff *skb, READ_ONCE(hh->hh_len)) return neigh_hh_output(hh, skb); - return n->output(n, skb); + return READ_ONCE(n->output)(n, skb); } static inline struct neighbour * diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index dd40c75011d25fdf8ce2360c9b291ebc4ee6104b..7c816359d5a98884cf303b87af1bb2adbb1b48e2 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1682,7 +1682,7 @@ struct nft_trans_gc { struct net *net; struct nft_set *set; u32 seq; - u8 count; + u16 count; void *priv[NFT_TRANS_GC_BATCHCOUNT]; struct rcu_head rcu; }; @@ -1700,8 +1700,9 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans); void nft_trans_gc_elem_add(struct nft_trans_gc *gc, void *priv); -struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, - unsigned int gc_seq); +struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc, + unsigned int gc_seq); +struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc); void nft_setelem_data_deactivate(const struct net *net, const struct nft_set *set, diff --git a/include/net/page_pool/helpers.h b/include/net/page_pool/helpers.h index 94231533a3696849ec52210269086155dc68dc25..8e7751464ff59596e89f5c0d803c699ced9afa24 100644 --- a/include/net/page_pool/helpers.h +++ b/include/net/page_pool/helpers.h @@ -16,13 +16,13 @@ * page_pool_alloc_pages() call. Drivers should use * page_pool_dev_alloc_pages() replacing dev_alloc_pages(). * - * API keeps track of in-flight pages, in order to let API user know + * The API keeps track of in-flight pages, in order to let API users know * when it is safe to free a page_pool object. Thus, API users * must call page_pool_put_page() to free the page, or attach - * the page to a page_pool-aware objects like skbs marked with + * the page to a page_pool-aware object like skbs marked with * skb_mark_for_recycle(). * - * API user must call page_pool_put_page() once on a page, as it + * API users must call page_pool_put_page() once on a page, as it * will either recycle the page, or in case of refcnt > 1, it will * release the DMA mapping and in-flight state accounting. */ diff --git a/include/net/scm.h b/include/net/scm.h index c5bcdf65f55c964388ac11bb8d3236e9c045e3ee..e8c76b4be2fe71c62f841a8cbc27aa97acf670b3 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -9,6 +9,7 @@ #include #include #include +#include /* Well, we should have at least one descriptor open * to accept passed FDs 8) @@ -123,14 +124,17 @@ static inline bool scm_has_secdata(struct socket *sock) static __inline__ void scm_pidfd_recv(struct msghdr *msg, struct scm_cookie *scm) { struct file *pidfd_file = NULL; - int pidfd; + int len, pidfd; - /* - * put_cmsg() doesn't return an error if CMSG is truncated, + /* put_cmsg() doesn't return an error if CMSG is truncated, * that's why we need to opencode these checks here. */ - if ((msg->msg_controllen <= sizeof(struct cmsghdr)) || - (msg->msg_controllen - sizeof(struct cmsghdr)) < sizeof(int)) { + if (msg->msg_flags & MSG_CMSG_COMPAT) + len = sizeof(struct compat_cmsghdr) + sizeof(int); + else + len = sizeof(struct cmsghdr) + sizeof(int); + + if (msg->msg_controllen < len) { msg->msg_flags |= MSG_CTRUNC; return; } diff --git a/include/net/sock.h b/include/net/sock.h index 11d503417591ed758fe2b2acbf7d3a9d0f07b660..b770261fbdaf59d4d1c0b30adb2592c56442e9e3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1053,6 +1053,12 @@ static inline void sk_wmem_queued_add(struct sock *sk, int val) WRITE_ONCE(sk->sk_wmem_queued, sk->sk_wmem_queued + val); } +static inline void sk_forward_alloc_add(struct sock *sk, int val) +{ + /* Paired with lockless reads of sk->sk_forward_alloc */ + WRITE_ONCE(sk->sk_forward_alloc, sk->sk_forward_alloc + val); +} + void sk_stream_write_space(struct sock *sk); /* OOB backlog add */ @@ -1377,7 +1383,7 @@ static inline int sk_forward_alloc_get(const struct sock *sk) if (sk->sk_prot->forward_alloc_get) return sk->sk_prot->forward_alloc_get(sk); #endif - return sk->sk_forward_alloc; + return READ_ONCE(sk->sk_forward_alloc); } static inline bool __sk_stream_memory_free(const struct sock *sk, int wake) @@ -1673,14 +1679,14 @@ static inline void sk_mem_charge(struct sock *sk, int size) { if (!sk_has_account(sk)) return; - sk->sk_forward_alloc -= size; + sk_forward_alloc_add(sk, -size); } static inline void sk_mem_uncharge(struct sock *sk, int size) { if (!sk_has_account(sk)) return; - sk->sk_forward_alloc += size; + sk_forward_alloc_add(sk, size); sk_mem_reclaim(sk); } @@ -1900,7 +1906,9 @@ struct sockcm_cookie { static inline void sockcm_init(struct sockcm_cookie *sockc, const struct sock *sk) { - *sockc = (struct sockcm_cookie) { .tsflags = sk->sk_tsflags }; + *sockc = (struct sockcm_cookie) { + .tsflags = READ_ONCE(sk->sk_tsflags) + }; } int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg, @@ -2695,9 +2703,9 @@ void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, static inline void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { - ktime_t kt = skb->tstamp; struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); - + u32 tsflags = READ_ONCE(sk->sk_tsflags); + ktime_t kt = skb->tstamp; /* * generate control messages if * - receive time stamping in software requested @@ -2705,10 +2713,10 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) * - hardware time stamps available and wanted */ if (sock_flag(sk, SOCK_RCVTSTAMP) || - (sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) || - (kt && sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) || + (tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) || + (kt && tsflags & SOF_TIMESTAMPING_SOFTWARE) || (hwtstamps->hwtstamp && - (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE))) + (tsflags & SOF_TIMESTAMPING_RAW_HARDWARE))) __sock_recv_timestamp(msg, sk, skb); else sock_write_timestamp(sk, kt); @@ -2730,7 +2738,8 @@ static inline void sock_recv_cmsgs(struct msghdr *msg, struct sock *sk, #define TSFLAGS_ANY (SOF_TIMESTAMPING_SOFTWARE | \ SOF_TIMESTAMPING_RAW_HARDWARE) - if (sk->sk_flags & FLAGS_RECV_CMSGS || sk->sk_tsflags & TSFLAGS_ANY) + if (sk->sk_flags & FLAGS_RECV_CMSGS || + READ_ONCE(sk->sk_tsflags) & TSFLAGS_ANY) __sock_recv_cmsgs(msg, sk, skb); else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP))) sock_write_timestamp(sk, skb->tstamp); diff --git a/include/net/tcp.h b/include/net/tcp.h index 91688d0dadcd6f72144aac747178de8d85f15bf7..7b1a720691aec471edb48a99c8e7213050c77c61 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -348,12 +348,14 @@ ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos, struct sk_buff *tcp_stream_alloc_skb(struct sock *sk, gfp_t gfp, bool force_schedule); -static inline void tcp_dec_quickack_mode(struct sock *sk, - const unsigned int pkts) +static inline void tcp_dec_quickack_mode(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); if (icsk->icsk_ack.quick) { + /* How many ACKs S/ACKing new data have we sent? */ + const unsigned int pkts = inet_csk_ack_scheduled(sk) ? 1 : 0; + if (pkts >= icsk->icsk_ack.quick) { icsk->icsk_ack.quick = 0; /* Leaving quickack mode we deflate ATO. */ diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 1e7774ac808f08811afb8422d3dfb4de09714b1d..533ab92684d81e15439e53339ae2544cb8066373 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -4440,8 +4440,6 @@ struct net_device *ib_get_net_dev_by_params(struct ib_device *dev, u32 port, const struct sockaddr *addr); int ib_device_set_netdev(struct ib_device *ib_dev, struct net_device *ndev, unsigned int port); -struct net_device *ib_device_netdev(struct ib_device *dev, u32 port); - struct ib_wq *ib_create_wq(struct ib_pd *pd, struct ib_wq_init_attr *init_attr); int ib_destroy_wq_user(struct ib_wq *wq, struct ib_udata *udata); diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h index 03abd30e6c8cacce00506dfd7f09140410833b3b..2b22f153ef636a7baee956007446e4d3d49f3e91 100644 --- a/include/rdma/iw_cm.h +++ b/include/rdma/iw_cm.h @@ -114,27 +114,6 @@ struct iw_cm_id *iw_create_cm_id(struct ib_device *device, */ void iw_destroy_cm_id(struct iw_cm_id *cm_id); -/** - * iw_cm_bind_qp - Unbind the specified IW CM identifier and QP - * - * @cm_id: The IW CM idenfier to unbind from the QP. - * @qp: The QP - * - * This is called by the provider when destroying the QP to ensure - * that any references held by the IWCM are released. It may also - * be called by the IWCM when destroying a CM_ID to that any - * references held by the provider are released. - */ -void iw_cm_unbind_qp(struct iw_cm_id *cm_id, struct ib_qp *qp); - -/** - * iw_cm_get_qp - Return the ib_qp associated with a QPN - * - * @ib_device: The IB device - * @qpn: The queue pair number - */ -struct ib_qp *iw_cm_get_qp(struct ib_device *device, int qpn); - /** * iw_cm_listen - Listen for incoming connection requests on the * specified IW CM id. diff --git a/include/rv/da_monitor.h b/include/rv/da_monitor.h index 9eb75683e0125aeed2dfb1406b78698605a9af3d..9705b2a98e49e1ae014eb031dd5f9652343efb50 100644 --- a/include/rv/da_monitor.h +++ b/include/rv/da_monitor.h @@ -262,7 +262,7 @@ static inline void da_monitor_destroy_##name(void) \ /* \ * per-cpu monitor variables \ */ \ -DEFINE_PER_CPU(struct da_monitor, da_mon_##name); \ +static DEFINE_PER_CPU(struct da_monitor, da_mon_##name); \ \ /* \ * da_get_monitor_##name - return current CPU monitor address \ diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 159823e0afbf6797ee30a4a8d15e3d782a02d934..8a43534eea5cb1654b2141acd83800fd2fe5d89d 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -23,22 +23,12 @@ struct block_device; -enum sas_class { - SAS, - EXPANDER -}; - enum sas_phy_role { PHY_ROLE_NONE = 0, PHY_ROLE_TARGET = 0x40, PHY_ROLE_INITIATOR = 0x80, }; -enum sas_phy_type { - PHY_TYPE_PHYSICAL, - PHY_TYPE_VIRTUAL -}; - /* The events are mnemonically described in sas_dump.c * so when updating/adding events here, please also * update the other file too. @@ -258,7 +248,6 @@ struct asd_sas_port { /* public: */ int id; - enum sas_class class; u8 sas_addr[SAS_ADDR_SIZE]; u8 attached_sas_addr[SAS_ADDR_SIZE]; enum sas_protocol iproto; @@ -319,11 +308,9 @@ struct asd_sas_phy { int enabled; /* must be set */ int id; /* must be set */ - enum sas_class class; enum sas_protocol iproto; enum sas_protocol tproto; - enum sas_phy_type type; enum sas_phy_role role; enum sas_oob_mode oob_mode; enum sas_linkrate linkrate; @@ -346,11 +333,6 @@ struct asd_sas_phy { void *lldd_phy; /* not touched by the sas_class_code */ }; -struct scsi_core { - struct Scsi_Host *shost; - -}; - enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, @@ -371,12 +353,11 @@ struct sas_ha_struct { struct mutex disco_mutex; - struct scsi_core core; + struct Scsi_Host *shost; /* public: */ char *sas_ha_name; struct device *dev; /* should be set */ - struct module *lldd_module; /* should be set */ struct workqueue_struct *event_q; struct workqueue_struct *disco_q; @@ -544,12 +525,9 @@ struct sas_ata_task { struct host_to_dev_fis fis; u8 atapi_packet[16]; /* 0 if not ATAPI task */ - u8 retry_count; /* hardware retry, should be > 0 */ - u8 dma_xfer:1; /* PIO:0 or DMA:1 */ u8 use_ncq:1; - u8 set_affil_pol:1; - u8 stp_affil_pol:1; + u8 return_fis_on_success:1; u8 device_control_reg_update:1; @@ -582,12 +560,8 @@ enum task_attribute { }; struct sas_ssp_task { - u8 retry_count; /* hardware retry, should be > 0 */ - u8 LUN[8]; - u8 enable_first_burst:1; enum task_attribute task_attr; - u8 task_prio; struct scsi_cmnd *cmd; }; @@ -727,8 +701,6 @@ extern struct device_attribute dev_attr_phy_event_threshold; int sas_discover_root_expander(struct domain_device *); -void sas_init_ex_attr(void); - int sas_ex_revalidate_domain(struct domain_device *); void sas_unregister_domain_devices(struct asd_sas_port *port, int gone); diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index ec093594ba53df4e8328a821c5e92f6f88c5a9c0..4498f845b1122185017a83a5c75fd07ad4ef5566 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -157,6 +157,9 @@ enum scsi_disposition { #define SCSI_3 4 /* SPC */ #define SCSI_SPC_2 5 #define SCSI_SPC_3 6 +#define SCSI_SPC_4 7 +#define SCSI_SPC_5 8 +#define SCSI_SPC_6 14 /* * INQ PERIPHERAL QUALIFIERS diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b9230b6add041dbba870995974bffd4dc31f728b..fd41fdac0a8e647765f3dc522152718b214173b0 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -161,6 +161,10 @@ struct scsi_device { * pass settings from slave_alloc to scsi * core. */ unsigned int eh_timeout; /* Error handling timeout */ + + bool manage_system_start_stop; /* Let HLD (sd) manage system start/stop */ + bool manage_runtime_start_stop; /* Let HLD (sd) manage runtime start/stop */ + unsigned removable:1; unsigned changed:1; /* Data invalid due to media change */ unsigned busy:1; /* Used to prevent races */ @@ -193,7 +197,6 @@ struct scsi_device { unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */ unsigned no_start_on_add:1; /* do not issue start on add */ unsigned allow_restart:1; /* issue START_UNIT in error handler */ - unsigned manage_start_stop:1; /* Let HLD (sd) manage start/stop */ unsigned no_start_on_resume:1; /* Do not issue START_STOP_UNIT on resume */ unsigned start_stop_pwr_cond:1; /* Set power cond. in START_STOP_UNIT */ unsigned no_uld_attach:1; /* disable connecting to upper level drivers */ diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 70b7475dcf56b74fadb5965867a23dc9b35acbc3..4c2dc8150c6d71c4c83c1cb3894ffdaf69ff75e7 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -764,12 +764,12 @@ scsi_template_proc_dir(const struct scsi_host_template *sht); #define scsi_template_proc_dir(sht) NULL #endif extern void scsi_scan_host(struct Scsi_Host *); -extern void scsi_rescan_device(struct device *); +extern int scsi_rescan_device(struct scsi_device *sdev); extern void scsi_remove_host(struct Scsi_Host *); extern struct Scsi_Host *scsi_host_get(struct Scsi_Host *); extern int scsi_host_busy(struct Scsi_Host *shost); extern void scsi_host_put(struct Scsi_Host *t); -extern struct Scsi_Host *scsi_host_lookup(unsigned short); +extern struct Scsi_Host *scsi_host_lookup(unsigned int hostnum); extern const char *scsi_host_state_name(enum scsi_host_state); extern void scsi_host_complete_all_commands(struct Scsi_Host *shost, enum scsi_host_status status); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 34c03707fb6efe23d04943a443252cbfbf22686d..fb3399e4cd292d7e676e0d7b889e6aa7d53f53c2 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -472,7 +472,6 @@ extern struct iscsi_iface *iscsi_create_iface(struct Scsi_Host *shost, uint32_t iface_type, uint32_t iface_num, int dd_size); extern void iscsi_destroy_iface(struct iscsi_iface *iface); -extern struct iscsi_iface *iscsi_lookup_iface(int handle); extern char *iscsi_get_port_speed_name(struct Scsi_Host *shost); extern char *iscsi_get_port_state_name(struct Scsi_Host *shost); extern int iscsi_is_session_dev(const struct device *dev); diff --git a/include/soc/mediatek/smi.h b/include/soc/mediatek/smi.h index dfd8efca5e606accf947ff3878a28791d4bd58de..000eb1cf68b7a7bb5e151d023238281ceb400c8a 100644 --- a/include/soc/mediatek/smi.h +++ b/include/soc/mediatek/smi.h @@ -13,6 +13,7 @@ enum iommu_atf_cmd { IOMMU_ATF_CMD_CONFIG_SMI_LARB, /* For mm master to en/disable iommu */ + IOMMU_ATF_CMD_CONFIG_INFRA_IOMMU, /* For infra master to enable iommu */ IOMMU_ATF_CMD_MAX, }; diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h index c9a8bce9a78539f02ef2b83386187c556105857d..d70c55f17df7ca8f9e68f91beb86a93b5f4711d4 100644 --- a/include/sound/dmaengine_pcm.h +++ b/include/sound/dmaengine_pcm.h @@ -142,7 +142,7 @@ struct snd_dmaengine_pcm_config { struct snd_pcm_substream *substream); int (*process)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - struct iov_iter *buf, unsigned long bytes); + unsigned long bytes); dma_filter_fn compat_filter_fn; struct device *dma_dev; const char *chan_names[SNDRV_PCM_STREAM_LAST + 1]; diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 17bea31445510a41a351e63dcc286167abe9c2b3..ceca69b46a821416ad095cb5e36a1e16a77eb048 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -139,7 +139,7 @@ struct snd_soc_component_driver { struct snd_pcm_audio_tstamp_report *audio_tstamp_report); int (*copy)(struct snd_soc_component *component, struct snd_pcm_substream *substream, int channel, - unsigned long pos, struct iov_iter *buf, + unsigned long pos, struct iov_iter *iter, unsigned long bytes); struct page *(*page)(struct snd_soc_component *component, struct snd_pcm_substream *substream, @@ -511,7 +511,7 @@ int snd_soc_pcm_component_ioctl(struct snd_pcm_substream *substream, int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream); int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - struct iov_iter *buf, unsigned long bytes); + struct iov_iter *iter, unsigned long bytes); struct page *snd_soc_pcm_component_page(struct snd_pcm_substream *substream, unsigned long offset); int snd_soc_pcm_component_mmap(struct snd_pcm_substream *substream, diff --git a/include/sound/soc.h b/include/sound/soc.h index fa2337a3cf4c775b566e1d133844b5dcefb1516a..37f9d3fe302a60460852f19a5b5e303fb5108c47 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1126,6 +1126,8 @@ struct snd_soc_pcm_runtime { unsigned int pop_wait:1; unsigned int fe_compr:1; /* for Dynamic PCM */ + bool initialized; + int num_components; struct snd_soc_component *components[]; /* CPU/Codec/Platform */ }; diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index 4c15420e8965d25bdadf2aeb81716dba10fd1d28..60af7c63b34e6afaff10a0c25e8fc8fd843ee93b 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -50,9 +50,6 @@ struct sock; #define TA_LOGIN_TIMEOUT 15 #define TA_LOGIN_TIMEOUT_MAX 30 #define TA_LOGIN_TIMEOUT_MIN 5 -#define TA_NETIF_TIMEOUT 2 -#define TA_NETIF_TIMEOUT_MAX 15 -#define TA_NETIF_TIMEOUT_MIN 2 #define TA_GENERATE_NODE_ACLS 0 #define TA_DEFAULT_CMDSN_DEPTH 64 #define TA_DEFAULT_CMDSN_DEPTH_MAX 512 @@ -773,7 +770,6 @@ to_iscsi_nacl(struct se_node_acl *se_nacl) struct iscsi_tpg_attrib { u32 authentication; u32 login_timeout; - u32 netif_timeout; u32 generate_node_acls; u32 cache_dynamic_acls; u32 default_cmdsn_depth; diff --git a/include/trace/events/task.h b/include/trace/events/task.h index 64d160930b0dcb1f0fc9ae510b6610fd64c30d1e..47b527464d1a2f833fa45f0c6a6e1e70b07310dd 100644 --- a/include/trace/events/task.h +++ b/include/trace/events/task.h @@ -47,7 +47,7 @@ TRACE_EVENT(task_rename, TP_fast_assign( __entry->pid = task->pid; memcpy(entry->oldcomm, task->comm, TASK_COMM_LEN); - strlcpy(entry->newcomm, comm, TASK_COMM_LEN); + strscpy(entry->newcomm, comm, TASK_COMM_LEN); __entry->oom_score_adj = task->signal->oom_score_adj; ), diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h index 44a3f565264d931b1b9257ad4090e5182f322bb6..0577f0cdd23162d6abd7a88b005d9479a7c0f5b0 100644 --- a/include/trace/events/xen.h +++ b/include/trace/events/xen.h @@ -6,26 +6,26 @@ #define _TRACE_XEN_H #include -#include +#include #include struct multicall_entry; /* Multicalls */ DECLARE_EVENT_CLASS(xen_mc__batch, - TP_PROTO(enum paravirt_lazy_mode mode), + TP_PROTO(enum xen_lazy_mode mode), TP_ARGS(mode), TP_STRUCT__entry( - __field(enum paravirt_lazy_mode, mode) + __field(enum xen_lazy_mode, mode) ), TP_fast_assign(__entry->mode = mode), TP_printk("start batch LAZY_%s", - (__entry->mode == PARAVIRT_LAZY_MMU) ? "MMU" : - (__entry->mode == PARAVIRT_LAZY_CPU) ? "CPU" : "NONE") + (__entry->mode == XEN_LAZY_MMU) ? "MMU" : + (__entry->mode == XEN_LAZY_CPU) ? "CPU" : "NONE") ); #define DEFINE_XEN_MC_BATCH(name) \ DEFINE_EVENT(xen_mc__batch, name, \ - TP_PROTO(enum paravirt_lazy_mode mode), \ + TP_PROTO(enum xen_lazy_mode mode), \ TP_ARGS(mode)) DEFINE_XEN_MC_BATCH(xen_mc_batch); diff --git a/include/uapi/drm/nouveau_drm.h b/include/uapi/drm/nouveau_drm.h index 8d7402c13e564220d91f36ea3ac7dcd4d3f5b0c9..eaf9f248619f35640c59dd8bbf4e940a272b258c 100644 --- a/include/uapi/drm/nouveau_drm.h +++ b/include/uapi/drm/nouveau_drm.h @@ -44,6 +44,16 @@ extern "C" { #define NOUVEAU_GETPARAM_PTIMER_TIME 14 #define NOUVEAU_GETPARAM_HAS_BO_USAGE 15 #define NOUVEAU_GETPARAM_HAS_PAGEFLIP 16 + +/** + * @NOUVEAU_GETPARAM_EXEC_PUSH_MAX + * + * Query the maximum amount of IBs that can be pushed through a single + * &drm_nouveau_exec structure and hence a single &DRM_IOCTL_NOUVEAU_EXEC + * ioctl(). + */ +#define NOUVEAU_GETPARAM_EXEC_PUSH_MAX 17 + struct drm_nouveau_getparam { __u64 param; __u64 value; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 8790b3962e4b85f22593f8f9c9f8d94eaf6981f3..0448700890f77d9a4fffa6fed5c17aa02b9f718b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1962,7 +1962,9 @@ union bpf_attr { * performed again, if the helper is used in combination with * direct packet access. * Return - * 0 on success, or a negative error in case of failure. + * 0 on success, or a negative error in case of failure. Positive + * error indicates a potential drop or congestion in the target + * device. The particular positive error codes are not defined. * * u64 bpf_get_current_pid_tgid(void) * Description diff --git a/include/uapi/linux/cgroupstats.h b/include/uapi/linux/cgroupstats.h index aa306e4cd6c1f8ab2f7c82f24ba42c56be4ac957..80b2c85944806d07aa34543a37b4383127ad85d8 100644 --- a/include/uapi/linux/cgroupstats.h +++ b/include/uapi/linux/cgroupstats.h @@ -24,8 +24,6 @@ * basis. This data is shared using taskstats. * * Most of these states are derived by looking at the task->state value - * For the nr_io_wait state, a flag in the delay accounting structure - * indicates that the task is waiting on IO * * Each member is aligned to a 8 byte boundary. */ diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index ee0bcff14b6930d6306abc3ca63639818c57d01c..9b731976ce2fde51d7a3c54358d9c1380604212c 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -445,6 +445,8 @@ typedef struct elf64_shdr { #define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */ #define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode */ #define NT_MIPS_MSA 0x802 /* MIPS SIMD registers */ +#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ +#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */ #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and status registers */ #define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD Extension registers */ diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index b3fcab13fcd3d46bfc077cd9c9df95503fb1e787..db92a7202b342b3a1df3f7583c86b79f3ad4e3df 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -207,6 +207,10 @@ * - add FUSE_EXT_GROUPS * - add FUSE_CREATE_SUPP_GROUP * - add FUSE_HAS_EXPIRE_ONLY + * + * 7.39 + * - add FUSE_DIRECT_IO_RELAX + * - add FUSE_STATX and related structures */ #ifndef _LINUX_FUSE_H @@ -242,7 +246,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 38 +#define FUSE_KERNEL_MINOR_VERSION 39 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -269,6 +273,40 @@ struct fuse_attr { uint32_t flags; }; +/* + * The following structures are bit-for-bit compatible with the statx(2) ABI in + * Linux. + */ +struct fuse_sx_time { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t __reserved; +}; + +struct fuse_statx { + uint32_t mask; + uint32_t blksize; + uint64_t attributes; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint16_t mode; + uint16_t __spare0[1]; + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t attributes_mask; + struct fuse_sx_time atime; + struct fuse_sx_time btime; + struct fuse_sx_time ctime; + struct fuse_sx_time mtime; + uint32_t rdev_major; + uint32_t rdev_minor; + uint32_t dev_major; + uint32_t dev_minor; + uint64_t __spare2[14]; +}; + struct fuse_kstatfs { uint64_t blocks; uint64_t bfree; @@ -371,6 +409,8 @@ struct fuse_file_lock { * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, * symlink and mknod (single group that matches parent) * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation + * FUSE_DIRECT_IO_RELAX: relax restrictions in FOPEN_DIRECT_IO mode, for now + * allow shared mmap */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -409,6 +449,7 @@ struct fuse_file_lock { #define FUSE_HAS_INODE_DAX (1ULL << 33) #define FUSE_CREATE_SUPP_GROUP (1ULL << 34) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) +#define FUSE_DIRECT_IO_RELAX (1ULL << 36) /** * CUSE INIT request/reply flags @@ -575,6 +616,7 @@ enum fuse_opcode { FUSE_REMOVEMAPPING = 49, FUSE_SYNCFS = 50, FUSE_TMPFILE = 51, + FUSE_STATX = 52, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -639,6 +681,22 @@ struct fuse_attr_out { struct fuse_attr attr; }; +struct fuse_statx_in { + uint32_t getattr_flags; + uint32_t reserved; + uint64_t fh; + uint32_t sx_flags; + uint32_t sx_mask; +}; + +struct fuse_statx_out { + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t flags; + uint64_t spare[2]; + struct fuse_statx stat; +}; + #define FUSE_COMPAT_MKNOD_IN_SIZE 8 struct fuse_mknod_in { diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 8466c2a9938f3d429b37cbf64923039d13912964..ca30232b7bc8af49a6c3dd1c03e105628aafabf9 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -263,6 +263,7 @@ enum nft_chain_attributes { * @NFTA_RULE_USERDATA: user data (NLA_BINARY, NFT_USERDATA_MAXLEN) * @NFTA_RULE_ID: uniquely identifies a rule in a transaction (NLA_U32) * @NFTA_RULE_POSITION_ID: transaction unique identifier of the previous rule (NLA_U32) + * @NFTA_RULE_CHAIN_ID: add the rule to chain by ID, alternative to @NFTA_RULE_CHAIN (NLA_U32) */ enum nft_rule_attributes { NFTA_RULE_UNSPEC, diff --git a/include/uapi/linux/rpmsg.h b/include/uapi/linux/rpmsg.h index 1637e68177d9d9370126f16982449b81186acd7a..f0c8da2b185b43bc58ea4640b66c8fdfa08e970f 100644 --- a/include/uapi/linux/rpmsg.h +++ b/include/uapi/linux/rpmsg.h @@ -43,4 +43,14 @@ struct rpmsg_endpoint_info { */ #define RPMSG_RELEASE_DEV_IOCTL _IOW(0xb5, 0x4, struct rpmsg_endpoint_info) +/** + * Get the flow control state of the remote rpmsg char device. + */ +#define RPMSG_GET_OUTGOING_FLOWCONTROL _IOR(0xb5, 0x5, int) + +/** + * Set the flow control state of the local rpmsg char device. + */ +#define RPMSG_SET_INCOMING_FLOWCONTROL _IOR(0xb5, 0x6, int) + #endif diff --git a/include/uapi/linux/stddef.h b/include/uapi/linux/stddef.h index 7c3fc39808811022b849d9f715b7b6c62a10ef18..5c6c4269f7efe4d649366907191adfbcffef41e4 100644 --- a/include/uapi/linux/stddef.h +++ b/include/uapi/linux/stddef.h @@ -29,6 +29,11 @@ struct TAG { MEMBERS } ATTRS NAME; \ } +#ifdef __cplusplus +/* sizeof(struct{}) is 1 in C++, not 0, can't use C version of the macro. */ +#define __DECLARE_FLEX_ARRAY(T, member) \ + T member[0] +#else /** * __DECLARE_FLEX_ARRAY() - Declare a flexible array usable in a union * @@ -49,3 +54,5 @@ #ifndef __counted_by #define __counted_by(m) #endif + +#endif /* _UAPI_LINUX_STDDEF_H */ diff --git a/include/uapi/linux/vhost_types.h b/include/uapi/linux/vhost_types.h index d3aad12ad1fa7875a8597c06cc5a998b60f6e72e..2d827d22cd99d2e507acaebcb3d502ac15417770 100644 --- a/include/uapi/linux/vhost_types.h +++ b/include/uapi/linux/vhost_types.h @@ -181,5 +181,9 @@ struct vhost_vdpa_iova_range { #define VHOST_BACKEND_F_SUSPEND 0x4 /* Device can be resumed */ #define VHOST_BACKEND_F_RESUME 0x5 +/* Device supports the driver enabling virtqueues both before and after + * DRIVER_OK + */ +#define VHOST_BACKEND_F_ENABLE_AFTER_DRIVER_OK 0x6 #endif diff --git a/include/uapi/rdma/bnxt_re-abi.h b/include/uapi/rdma/bnxt_re-abi.h index 8a2a1d4f6b2975597bdf041061efa7228d0daa64..6e7c67a0cca3a8dc91b2d0d5da1a19f144aed79c 100644 --- a/include/uapi/rdma/bnxt_re-abi.h +++ b/include/uapi/rdma/bnxt_re-abi.h @@ -53,6 +53,7 @@ enum { BNXT_RE_UCNTX_CMASK_HAVE_CCTX = 0x1ULL, BNXT_RE_UCNTX_CMASK_HAVE_MODE = 0x02ULL, BNXT_RE_UCNTX_CMASK_WC_DPI_ENABLED = 0x04ULL, + BNXT_RE_UCNTX_CMASK_DBR_PACING_ENABLED = 0x08ULL, }; enum bnxt_re_wqe_mode { @@ -131,10 +132,13 @@ enum bnxt_re_shpg_offt { enum bnxt_re_objects { BNXT_RE_OBJECT_ALLOC_PAGE = (1U << UVERBS_ID_NS_SHIFT), + BNXT_RE_OBJECT_NOTIFY_DRV, }; enum bnxt_re_alloc_page_type { BNXT_RE_ALLOC_WC_PAGE = 0, + BNXT_RE_ALLOC_DBR_BAR_PAGE, + BNXT_RE_ALLOC_DBR_PAGE, }; enum bnxt_re_var_alloc_page_attrs { @@ -154,4 +158,7 @@ enum bnxt_re_alloc_page_methods { BNXT_RE_METHOD_DESTROY_PAGE, }; +enum bnxt_re_notify_drv_methods { + BNXT_RE_METHOD_NOTIFY_DRV = (1U << UVERBS_ID_NS_SHIFT), +}; #endif /* __BNXT_RE_UVERBS_ABI_H__*/ diff --git a/include/uapi/rdma/irdma-abi.h b/include/uapi/rdma/irdma-abi.h index a7085e092d34873f8745724058a82436d83ab6fc..bb18f15489e37ba8bc38bc4d252c33e7d2b1a2a3 100644 --- a/include/uapi/rdma/irdma-abi.h +++ b/include/uapi/rdma/irdma-abi.h @@ -22,10 +22,16 @@ enum irdma_memreg_type { IRDMA_MEMREG_TYPE_CQ = 2, }; +enum { + IRDMA_ALLOC_UCTX_USE_RAW_ATTR = 1 << 0, + IRDMA_ALLOC_UCTX_MIN_HW_WQ_SIZE = 1 << 1, +}; + struct irdma_alloc_ucontext_req { __u32 rsvd32; __u8 userspace_ver; __u8 rsvd8[3]; + __aligned_u64 comp_mask; }; struct irdma_alloc_ucontext_resp { @@ -46,6 +52,9 @@ struct irdma_alloc_ucontext_resp { __u16 max_hw_sq_chunk; __u8 hw_rev; __u8 rsvd2; + __aligned_u64 comp_mask; + __u16 min_hw_wq_size; + __u8 rsvd3[6]; }; struct irdma_alloc_pd_resp { diff --git a/include/uapi/scsi/scsi_bsg_ufs.h b/include/uapi/scsi/scsi_bsg_ufs.h index fd3f9e5ee2415373f1575f83a9e433f5ee41f9f2..03f2beadf2019b61ef8abd0e9cc42a7d4c421631 100644 --- a/include/uapi/scsi/scsi_bsg_ufs.h +++ b/include/uapi/scsi/scsi_bsg_ufs.h @@ -8,6 +8,7 @@ #ifndef SCSI_BSG_UFS_H #define SCSI_BSG_UFS_H +#include #include /* * This file intended to be included by both kernel and user space @@ -40,11 +41,56 @@ enum ufs_rpmb_op_type { * @dword_0: UPIU header DW-0 * @dword_1: UPIU header DW-1 * @dword_2: UPIU header DW-2 + * + * @transaction_code: Type of request or response. See also enum + * upiu_request_transaction and enum upiu_response_transaction. + * @flags: UPIU flags. The meaning of individual flags depends on the + * transaction code. + * @lun: Logical unit number. + * @task_tag: Task tag. + * @iid: Initiator ID. + * @command_set_type: 0 for SCSI command set; 1 for UFS specific. + * @tm_function: Task management function in case of a task management request + * UPIU. + * @query_function: Query function in case of a query request UPIU. + * @response: 0 for success; 1 for failure. + * @status: SCSI status if this is the header of a response to a SCSI command. + * @ehs_length: EHS length in units of 32 bytes. + * @device_information: + * @data_segment_length: data segment length. */ struct utp_upiu_header { - __be32 dword_0; - __be32 dword_1; - __be32 dword_2; + union { + struct { + __be32 dword_0; + __be32 dword_1; + __be32 dword_2; + }; + struct { + __u8 transaction_code; + __u8 flags; + __u8 lun; + __u8 task_tag; +#if defined(__BIG_ENDIAN) + __u8 iid: 4; + __u8 command_set_type: 4; +#elif defined(__LITTLE_ENDIAN) + __u8 command_set_type: 4; + __u8 iid: 4; +#else +#error +#endif + union { + __u8 tm_function; + __u8 query_function; + } __attribute__((packed)); + __u8 response; + __u8 status; + __u8 ehs_length; + __u8 device_information; + __be16 data_segment_length; + }; + }; }; /** diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h index 198cb391f9db2e9b4473c63f9139633c14942304..0cced88f4531e87384ab676d1c01149ccb4ef36b 100644 --- a/include/ufs/ufs.h +++ b/include/ufs/ufs.h @@ -11,10 +11,16 @@ #ifndef _UFS_H #define _UFS_H -#include +#include #include #include +/* + * Using static_assert() is not allowed in UAPI header files. Hence the check + * in this header file of the size of struct utp_upiu_header. + */ +static_assert(sizeof(struct utp_upiu_header) == 12); + #define GENERAL_UPIU_REQUEST_SIZE (sizeof(struct utp_upiu_req)) #define QUERY_DESC_MAX_SIZE 255 #define QUERY_DESC_MIN_SIZE 2 @@ -23,9 +29,6 @@ (sizeof(struct utp_upiu_header))) #define UFS_SENSE_SIZE 18 -#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\ - cpu_to_be32((byte3 << 24) | (byte2 << 16) |\ - (byte1 << 8) | (byte0)) /* * UFS device may have standard LUs and LUN id could be from 0x00 to * 0x7F. Standard LUs use "Peripheral Device Addressing Format". @@ -76,7 +79,7 @@ enum { }; /* UTP UPIU Transaction Codes Initiator to Target */ -enum { +enum upiu_request_transaction { UPIU_TRANSACTION_NOP_OUT = 0x00, UPIU_TRANSACTION_COMMAND = 0x01, UPIU_TRANSACTION_DATA_OUT = 0x02, @@ -85,7 +88,7 @@ enum { }; /* UTP UPIU Transaction Codes Target to Initiator */ -enum { +enum upiu_response_transaction { UPIU_TRANSACTION_NOP_IN = 0x20, UPIU_TRANSACTION_RESPONSE = 0x21, UPIU_TRANSACTION_DATA_IN = 0x22, @@ -102,6 +105,12 @@ enum { UPIU_CMD_FLAGS_READ = 0x40, }; +/* UPIU response flags */ +enum { + UPIU_RSP_FLAG_UNDERFLOW = 0x20, + UPIU_RSP_FLAG_OVERFLOW = 0x40, +}; + /* UPIU Task Attributes */ enum { UPIU_TASK_ATTR_SIMPLE = 0x00, @@ -466,21 +475,11 @@ enum { UPIU_COMMAND_SET_TYPE_QUERY = 0x2, }; -/* UTP Transfer Request Command Offset */ -#define UPIU_COMMAND_TYPE_OFFSET 28 - /* Offset of the response code in the UPIU header */ #define UPIU_RSP_CODE_OFFSET 8 enum { - MASK_SCSI_STATUS = 0xFF, - MASK_TASK_RESPONSE = 0xFF00, - MASK_RSP_UPIU_RESULT = 0xFFFF, - MASK_QUERY_DATA_SEG_LEN = 0xFFFF, - MASK_RSP_UPIU_DATA_SEG_LEN = 0xFFFF, - MASK_RSP_EXCEPTION_EVENT = 0x10000, MASK_TM_SERVICE_RESP = 0xFF, - MASK_TM_FUNC = 0xFF, }; /* Task management service response */ @@ -516,41 +515,6 @@ struct utp_cmd_rsp { u8 sense_data[UFS_SENSE_SIZE]; }; -struct ufshpb_active_field { - __be16 active_rgn; - __be16 active_srgn; -}; -#define HPB_ACT_FIELD_SIZE 4 - -/** - * struct utp_hpb_rsp - Response UPIU structure - * @residual_transfer_count: Residual transfer count DW-3 - * @reserved1: Reserved double words DW-4 to DW-7 - * @sense_data_len: Sense data length DW-8 U16 - * @desc_type: Descriptor type of sense data - * @additional_len: Additional length of sense data - * @hpb_op: HPB operation type - * @lun: LUN of response UPIU - * @active_rgn_cnt: Active region count - * @inactive_rgn_cnt: Inactive region count - * @hpb_active_field: Recommended to read HPB region and subregion - * @hpb_inactive_field: To be inactivated HPB region and subregion - */ -struct utp_hpb_rsp { - __be32 residual_transfer_count; - __be32 reserved1[4]; - __be16 sense_data_len; - u8 desc_type; - u8 additional_len; - u8 hpb_op; - u8 lun; - u8 active_rgn_cnt; - u8 inactive_rgn_cnt; - struct ufshpb_active_field hpb_active_field[2]; - __be16 hpb_inactive_field[2]; -}; -#define UTP_HPB_RSP_SIZE 40 - /** * struct utp_upiu_rsp - general upiu response structure * @header: UPIU header structure DW-0 to DW-2 @@ -561,31 +525,10 @@ struct utp_upiu_rsp { struct utp_upiu_header header; union { struct utp_cmd_rsp sr; - struct utp_hpb_rsp hr; struct utp_upiu_query qr; }; }; -/** - * struct ufs_query_req - parameters for building a query request - * @query_func: UPIU header query function - * @upiu_req: the query request data - */ -struct ufs_query_req { - u8 query_func; - struct utp_upiu_query upiu_req; -}; - -/** - * struct ufs_query_resp - UPIU QUERY - * @response: device response code - * @upiu_res: query response data - */ -struct ufs_query_res { - u8 response; - struct utp_upiu_query upiu_res; -}; - /* * VCCQ & VCCQ2 current requirement when UFS device is in sleep state * and link is in Hibern8 state. @@ -621,9 +564,6 @@ struct ufs_dev_info { /* Stores the depth of queue in UFS device */ u8 bqueuedepth; - /* UFS HPB related flag */ - bool hpb_enabled; - /* UFS WB related flags */ bool wb_enabled; bool wb_buf_flush_enabled; diff --git a/include/ufs/ufs_quirks.h b/include/ufs/ufs_quirks.h index bcb4f004bed5784b7824a5df777f3c9b99196ac1..41ff44dfa1db3f293d16d8c917effcf6ef5bd792 100644 --- a/include/ufs/ufs_quirks.h +++ b/include/ufs/ufs_quirks.h @@ -107,10 +107,4 @@ struct ufs_dev_quirk { */ #define UFS_DEVICE_QUIRK_DELAY_AFTER_LPM (1 << 11) -/* - * Some UFS devices require L2P entry should be swapped before being sent to the - * UFS device for HPB READ command. - */ -#define UFS_DEVICE_QUIRK_SWAP_L2P_ENTRY_FOR_HPB_READ (1 << 12) - #endif /* UFS_QUIRKS_H_ */ diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index 6dc11fa0ebb10ecc04cb7b686da0b63e12de4568..7d07b256e906b9ceab7f79c09a458a6f014521f6 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -201,6 +202,25 @@ struct ufshcd_lrb { bool req_abort_skip; }; +/** + * struct ufs_query_req - parameters for building a query request + * @query_func: UPIU header query function + * @upiu_req: the query request data + */ +struct ufs_query_req { + u8 query_func; + struct utp_upiu_query upiu_req; +}; + +/** + * struct ufs_query_resp - UPIU QUERY + * @response: device response code + * @upiu_res: query response data + */ +struct ufs_query_res { + struct utp_upiu_query upiu_res; +}; + /** * struct ufs_query - holds relevant data structures for query request * @request: request upiu and function @@ -709,31 +729,6 @@ struct ufs_hba_variant_params { u32 wb_flush_threshold; }; -#ifdef CONFIG_SCSI_UFS_HPB -/** - * struct ufshpb_dev_info - UFSHPB device related info - * @num_lu: the number of user logical unit to check whether all lu finished - * initialization - * @rgn_size: device reported HPB region size - * @srgn_size: device reported HPB sub-region size - * @slave_conf_cnt: counter to check all lu finished initialization - * @hpb_disabled: flag to check if HPB is disabled - * @max_hpb_single_cmd: device reported bMAX_DATA_SIZE_FOR_SINGLE_CMD value - * @is_legacy: flag to check HPB 1.0 - * @control_mode: either host or device - */ -struct ufshpb_dev_info { - int num_lu; - int rgn_size; - int srgn_size; - atomic_t slave_conf_cnt; - bool hpb_disabled; - u8 max_hpb_single_cmd; - bool is_legacy; - u8 control_mode; -}; -#endif - struct ufs_hba_monitor { unsigned long chunk_size; @@ -894,7 +889,6 @@ enum ufshcd_mcq_opr { * @rpm_dev_flush_recheck_work: used to suspend from RPM (runtime power * management) after the UFS device has finished a WriteBooster buffer * flush or auto BKOP. - * @ufshpb_dev: information related to HPB (Host Performance Booster). * @monitor: statistics about UFS commands * @crypto_capabilities: Content of crypto capabilities register (0x100) * @crypto_cap_array: Array of crypto capabilities @@ -1050,10 +1044,6 @@ struct ufs_hba { struct request_queue *bsg_queue; struct delayed_work rpm_dev_flush_recheck_work; -#ifdef CONFIG_SCSI_UFS_HPB - struct ufshpb_dev_info ufshpb_dev; -#endif - struct ufs_hba_monitor monitor; #ifdef CONFIG_SCSI_UFS_CRYPTO @@ -1254,9 +1244,12 @@ void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk); void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val); void ufshcd_hba_stop(struct ufs_hba *hba); void ufshcd_schedule_eh_work(struct ufs_hba *hba); +void ufshcd_mcq_config_mac(struct ufs_hba *hba, u32 max_active_cmds); +u32 ufshcd_mcq_read_cqis(struct ufs_hba *hba, int i); void ufshcd_mcq_write_cqis(struct ufs_hba *hba, u32 val, int i); unsigned long ufshcd_mcq_poll_cqe_lock(struct ufs_hba *hba, struct ufs_hw_queue *hwq); +void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba); void ufshcd_mcq_enable_esi(struct ufs_hba *hba); void ufshcd_mcq_config_esi(struct ufs_hba *hba, struct msi_msg *msg); @@ -1383,12 +1376,6 @@ int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg); int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd); -int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, - struct utp_upiu_req *req_upiu, - struct utp_upiu_req *rsp_upiu, - int msgcode, - u8 *desc_buff, int *buff_len, - enum query_opcode desc_op); int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *req_upiu, struct utp_upiu_req *rsp_upiu, struct ufs_ehs *ehs_req, struct ufs_ehs *ehs_rsp, int sg_cnt, @@ -1398,6 +1385,7 @@ int ufshcd_wb_toggle_buf_flush(struct ufs_hba *hba, bool enable); int ufshcd_suspend_prepare(struct device *dev); int __ufshcd_suspend_prepare(struct device *dev, bool rpm_ok_for_spm); void ufshcd_resume_complete(struct device *dev); +bool ufshcd_is_hba_active(struct ufs_hba *hba); /* Wrapper functions for safely calling variant operations */ static inline int ufshcd_vops_init(struct ufs_hba *hba) diff --git a/include/ufs/ufshci.h b/include/ufs/ufshci.h index 146fbea76d98db00f617cec316153b44784e3518..d5accacae6bca758d75aa2d13a79053c569fef73 100644 --- a/include/ufs/ufshci.h +++ b/include/ufs/ufshci.h @@ -11,7 +11,8 @@ #ifndef _UFSHCI_H #define _UFSHCI_H -#include +#include +#include enum { TASK_REQ_UPIU_SIZE_DWORDS = 8, @@ -126,7 +127,6 @@ enum { }; #define SQ_ICU_ERR_CODE_MASK GENMASK(7, 4) -#define UPIU_COMMAND_TYPE_MASK GENMASK(31, 28) #define UFS_MASK(mask, offset) ((mask) << (offset)) /* UFS Version 08h */ @@ -438,15 +438,13 @@ enum { UTP_SCSI_COMMAND = 0x00000000, UTP_NATIVE_UFS_COMMAND = 0x10000000, UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000, - UTP_REQ_DESC_INT_CMD = 0x01000000, - UTP_REQ_DESC_CRYPTO_ENABLE_CMD = 0x00800000, }; /* UTP Transfer Request Data Direction (DD) */ -enum { - UTP_NO_DATA_TRANSFER = 0x00000000, - UTP_HOST_TO_DEVICE = 0x02000000, - UTP_DEVICE_TO_HOST = 0x04000000, +enum utp_data_direction { + UTP_NO_DATA_TRANSFER = 0, + UTP_HOST_TO_DEVICE = 1, + UTP_DEVICE_TO_HOST = 2, }; /* Overall command status values */ @@ -505,17 +503,38 @@ struct utp_transfer_cmd_desc { /** * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD - * @dword0: Descriptor Header DW0 - * @dword1: Descriptor Header DW1 - * @dword2: Descriptor Header DW2 - * @dword3: Descriptor Header DW3 */ struct request_desc_header { - __le32 dword_0; - __le32 dword_1; - __le32 dword_2; - __le32 dword_3; -}; + u8 cci; + u8 ehs_length; +#if defined(__BIG_ENDIAN) + u8 enable_crypto:1; + u8 reserved2:7; + + u8 command_type:4; + u8 reserved1:1; + u8 data_direction:2; + u8 interrupt:1; +#elif defined(__LITTLE_ENDIAN) + u8 reserved2:7; + u8 enable_crypto:1; + + u8 interrupt:1; + u8 data_direction:2; + u8 reserved1:1; + u8 command_type:4; +#else +#error +#endif + + __le32 dunl; + u8 ocs; + u8 cds; + __le16 ldbc; + __le32 dunu; +}; + +static_assert(sizeof(struct request_desc_header) == 16); /** * struct utp_transfer_req_desc - UTP Transfer Request Descriptor (UTRD) diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h index dc9dd1d23f0f72a2618ec17ae8cc36a39a5a4809..256eb3a43f54078e2f71699c33ce78a67f4ea7f9 100644 --- a/include/ufs/unipro.h +++ b/include/ufs/unipro.h @@ -230,6 +230,12 @@ enum ufs_hs_gear_tag { UFS_HS_G5 /* HS Gear 5 */ }; +enum ufs_lanes { + UFS_LANE_DONT_CHANGE, /* Don't change Lane */ + UFS_LANE_1, /* Lane 1 (default for reset) */ + UFS_LANE_2, /* Lane 2 */ +}; + enum ufs_unipro_ver { UFS_UNIPRO_VER_RESERVED = 0, UFS_UNIPRO_VER_1_40 = 1, /* UniPro version 1.40 */ diff --git a/include/xen/arm/hypervisor.h b/include/xen/arm/hypervisor.h index 43ef24dd030e10237a97373e635632ae5a8e4a9c..9995695204f5d25730ddbc6c9663117eac557072 100644 --- a/include/xen/arm/hypervisor.h +++ b/include/xen/arm/hypervisor.h @@ -7,18 +7,6 @@ extern struct shared_info *HYPERVISOR_shared_info; extern struct start_info *xen_start_info; -/* Lazy mode for batching updates / context switch */ -enum paravirt_lazy_mode { - PARAVIRT_LAZY_NONE, - PARAVIRT_LAZY_MMU, - PARAVIRT_LAZY_CPU, -}; - -static inline enum paravirt_lazy_mode paravirt_get_lazy_mode(void) -{ - return PARAVIRT_LAZY_NONE; -} - #ifdef CONFIG_XEN void __init xen_early_init(void); #else diff --git a/include/xen/events.h b/include/xen/events.h index 95d5e28de324af3c46f1fd876e7e3e26c8120c70..23932b0673dc7459037c16e97642598a16f08877 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -105,8 +105,7 @@ int irq_from_virq(unsigned int cpu, unsigned int virq); evtchn_port_t evtchn_from_irq(unsigned irq); int xen_set_callback_via(uint64_t via); -void xen_evtchn_do_upcall(struct pt_regs *regs); -int xen_hvm_evtchn_do_upcall(void); +int xen_evtchn_do_upcall(void); /* Bind a pirq for a physical interrupt to an irq. */ int xen_bind_pirq_gsi_to_irq(unsigned gsi, diff --git a/init/main.c b/init/main.c index ad920fac325c367d813f7e9a552deb89b7e0a933..436d73261810bd8ccd7e65a121fdc1556eb67ae9 100644 --- a/init/main.c +++ b/init/main.c @@ -1540,6 +1540,7 @@ static noinline void __init kernel_init_freeable(void) smp_init(); sched_init_smp(); + workqueue_init_topology(); padata_init(); page_alloc_init_late(); diff --git a/io_uring/fdinfo.c b/io_uring/fdinfo.c index 300455b4bc122b14c7f5bffdde2380ad726af920..c536788754164293058b2b852493293369828124 100644 --- a/io_uring/fdinfo.c +++ b/io_uring/fdinfo.c @@ -93,6 +93,8 @@ __cold void io_uring_show_fdinfo(struct seq_file *m, struct file *f) struct io_uring_sqe *sqe; unsigned int sq_idx; + if (ctx->flags & IORING_SETUP_NO_SQARRAY) + break; sq_idx = READ_ONCE(ctx->sq_array[entry & sq_mask]); if (sq_idx > sq_mask) continue; diff --git a/io_uring/fs.c b/io_uring/fs.c index f6a69a549fd4503fc15a51bf417733fad44c1ebc..08e3b175469c685d64186595da74c778210df88a 100644 --- a/io_uring/fs.c +++ b/io_uring/fs.c @@ -243,7 +243,7 @@ int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); const char __user *oldf, *newf; - if (sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) + if (sqe->buf_index || sqe->splice_fd_in) return -EINVAL; if (unlikely(req->flags & REQ_F_FIXED_FILE)) return -EBADF; diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c index 62f345587df54774919c1eef0661448c5b36af9d..522196dfb0ff5a2c8054842569d5b1a8d1d43d2d 100644 --- a/io_uring/io-wq.c +++ b/io_uring/io-wq.c @@ -174,6 +174,16 @@ static void io_worker_ref_put(struct io_wq *wq) complete(&wq->worker_done); } +bool io_wq_worker_stopped(void) +{ + struct io_worker *worker = current->worker_private; + + if (WARN_ON_ONCE(!io_wq_current_is_worker())) + return true; + + return test_bit(IO_WQ_BIT_EXIT, &worker->wq->state); +} + static void io_worker_cancel_cb(struct io_worker *worker) { struct io_wq_acct *acct = io_wq_get_acct(worker); @@ -1141,9 +1151,6 @@ struct io_wq *io_wq_create(unsigned bounded, struct io_wq_data *data) wq = kzalloc(sizeof(struct io_wq), GFP_KERNEL); if (!wq) return ERR_PTR(-ENOMEM); - ret = cpuhp_state_add_instance_nocalls(io_wq_online, &wq->cpuhp_node); - if (ret) - goto err_wq; refcount_inc(&data->hash->refs); wq->hash = data->hash; @@ -1176,13 +1183,14 @@ struct io_wq *io_wq_create(unsigned bounded, struct io_wq_data *data) wq->task = get_task_struct(data->task); atomic_set(&wq->worker_refs, 1); init_completion(&wq->worker_done); + ret = cpuhp_state_add_instance_nocalls(io_wq_online, &wq->cpuhp_node); + if (ret) + goto err; + return wq; err: io_wq_put_hash(data->hash); - cpuhp_state_remove_instance_nocalls(io_wq_online, &wq->cpuhp_node); - free_cpumask_var(wq->cpu_mask); -err_wq: kfree(wq); return ERR_PTR(ret); } diff --git a/io_uring/io-wq.h b/io_uring/io-wq.h index 06d9ca90c5771d3ba379d0f96bf8d4cb7da1f01a..2b2a6406dd8ee81e73a8a94bed9a162991a92fb8 100644 --- a/io_uring/io-wq.h +++ b/io_uring/io-wq.h @@ -52,6 +52,7 @@ void io_wq_hash_work(struct io_wq_work *work, void *val); int io_wq_cpu_affinity(struct io_uring_task *tctx, cpumask_var_t mask); int io_wq_max_workers(struct io_wq *wq, int *new_count); +bool io_wq_worker_stopped(void); static inline bool io_wq_is_hashed(struct io_wq_work *work) { diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index e7675355048dbfa8966280b97d1def21d66c17fa..d839a80a6751c15e56ffbcd8277149001643e78c 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -150,6 +150,31 @@ static void io_queue_sqe(struct io_kiocb *req); struct kmem_cache *req_cachep; +static int __read_mostly sysctl_io_uring_disabled; +static int __read_mostly sysctl_io_uring_group = -1; + +#ifdef CONFIG_SYSCTL +static struct ctl_table kernel_io_uring_disabled_table[] = { + { + .procname = "io_uring_disabled", + .data = &sysctl_io_uring_disabled, + .maxlen = sizeof(sysctl_io_uring_disabled), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_TWO, + }, + { + .procname = "io_uring_group", + .data = &sysctl_io_uring_group, + .maxlen = sizeof(gid_t), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + {}, +}; +#endif + struct sock *io_uring_get_socket(struct file *file) { #if defined(CONFIG_UNIX) @@ -883,7 +908,7 @@ static void __io_flush_post_cqes(struct io_ring_ctx *ctx) struct io_uring_cqe *cqe = &ctx->completion_cqes[i]; if (!io_fill_cqe_aux(ctx, cqe->user_data, cqe->res, cqe->flags)) { - if (ctx->task_complete) { + if (ctx->lockless_cq) { spin_lock(&ctx->completion_lock); io_cqring_event_overflow(ctx, cqe->user_data, cqe->res, cqe->flags, 0, 0); @@ -1541,7 +1566,7 @@ void __io_submit_flush_completions(struct io_ring_ctx *ctx) if (!(req->flags & REQ_F_CQE_SKIP) && unlikely(!io_fill_cqe_req(ctx, req))) { - if (ctx->task_complete) { + if (ctx->lockless_cq) { spin_lock(&ctx->completion_lock); io_req_cqe_overflow(req); spin_unlock(&ctx->completion_lock); @@ -1950,6 +1975,8 @@ void io_wq_submit_work(struct io_wq_work *work) if (!needs_poll) { if (!(req->ctx->flags & IORING_SETUP_IOPOLL)) break; + if (io_wq_worker_stopped()) + break; cond_resched(); continue; } @@ -2659,7 +2686,7 @@ static void *__io_uaddr_map(struct page ***pages, unsigned short *npages, { struct page **page_array; unsigned int nr_pages; - int ret; + int ret, i; *npages = 0; @@ -2689,6 +2716,20 @@ static void *__io_uaddr_map(struct page ***pages, unsigned short *npages, */ if (page_array[0] != page_array[ret - 1]) goto err; + + /* + * Can't support mapping user allocated ring memory on 32-bit archs + * where it could potentially reside in highmem. Just fail those with + * -EINVAL, just like we did on kernels that didn't support this + * feature. + */ + for (i = 0; i < nr_pages; i++) { + if (PageHighMem(page_array[i])) { + ret = -EINVAL; + goto err; + } + } + *pages = page_array; *npages = nr_pages; return page_to_virt(page_array[0]); @@ -4038,9 +4079,30 @@ static long io_uring_setup(u32 entries, struct io_uring_params __user *params) return io_uring_create(entries, &p, params); } +static inline bool io_uring_allowed(void) +{ + int disabled = READ_ONCE(sysctl_io_uring_disabled); + kgid_t io_uring_group; + + if (disabled == 2) + return false; + + if (disabled == 0 || capable(CAP_SYS_ADMIN)) + return true; + + io_uring_group = make_kgid(&init_user_ns, sysctl_io_uring_group); + if (!gid_valid(io_uring_group)) + return false; + + return in_group_p(io_uring_group); +} + SYSCALL_DEFINE2(io_uring_setup, u32, entries, struct io_uring_params __user *, params) { + if (!io_uring_allowed()) + return -EPERM; + return io_uring_setup(entries, params); } @@ -4634,6 +4696,10 @@ static int __init io_uring_init(void) offsetof(struct io_kiocb, cmd.data), sizeof_field(struct io_kiocb, cmd.data), NULL); +#ifdef CONFIG_SYSCTL + register_sysctl_init("kernel", kernel_io_uring_disabled_table); +#endif + return 0; }; __initcall(io_uring_init); diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h index 547c30582fb8872b692b41dfc4986ffa257f808a..0bc145614a6e66ae2d2ec0933fa7165e11f74229 100644 --- a/io_uring/io_uring.h +++ b/io_uring/io_uring.h @@ -86,20 +86,33 @@ bool __io_alloc_req_refill(struct io_ring_ctx *ctx); bool io_match_task_safe(struct io_kiocb *head, struct task_struct *task, bool cancel_all); -#define io_lockdep_assert_cq_locked(ctx) \ - do { \ - lockdep_assert(in_task()); \ - \ - if (ctx->flags & IORING_SETUP_IOPOLL) { \ - lockdep_assert_held(&ctx->uring_lock); \ - } else if (!ctx->task_complete) { \ - lockdep_assert_held(&ctx->completion_lock); \ - } else if (ctx->submitter_task->flags & PF_EXITING) { \ - lockdep_assert(current_work()); \ - } else { \ - lockdep_assert(current == ctx->submitter_task); \ - } \ - } while (0) +#if defined(CONFIG_PROVE_LOCKING) +static inline void io_lockdep_assert_cq_locked(struct io_ring_ctx *ctx) +{ + lockdep_assert(in_task()); + + if (ctx->flags & IORING_SETUP_IOPOLL) { + lockdep_assert_held(&ctx->uring_lock); + } else if (!ctx->task_complete) { + lockdep_assert_held(&ctx->completion_lock); + } else if (ctx->submitter_task) { + /* + * ->submitter_task may be NULL and we can still post a CQE, + * if the ring has been setup with IORING_SETUP_R_DISABLED. + * Not from an SQE, as those cannot be submitted, but via + * updating tagged resources. + */ + if (ctx->submitter_task->flags & PF_EXITING) + lockdep_assert(current_work()); + else + lockdep_assert(current == ctx->submitter_task); + } +} +#else +static inline void io_lockdep_assert_cq_locked(struct io_ring_ctx *ctx) +{ +} +#endif static inline void io_req_task_work_add(struct io_kiocb *req) { diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index 556f4df25b0fa0119ea9a135311256b2fda2573a..9123138aa9f48b1acc216f08b70870e095523632 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -477,7 +477,7 @@ static int io_pin_pbuf_ring(struct io_uring_buf_reg *reg, { struct io_uring_buf_ring *br; struct page **pages; - int nr_pages; + int i, nr_pages; pages = io_pin_pages(reg->ring_addr, flex_array_size(br, bufs, reg->ring_entries), @@ -485,6 +485,17 @@ static int io_pin_pbuf_ring(struct io_uring_buf_reg *reg, if (IS_ERR(pages)) return PTR_ERR(pages); + /* + * Apparently some 32-bit boxes (ARM) will return highmem pages, + * which then need to be mapped. We could support that, but it'd + * complicate the code and slowdown the common cases quite a bit. + * So just error out, returning -EINVAL just like we did on kernels + * that didn't support mapped buffer rings. + */ + for (i = 0; i < nr_pages; i++) + if (PageHighMem(pages[i])) + goto error_unpin; + br = page_address(pages[0]); #ifdef SHM_COLOUR /* @@ -496,13 +507,8 @@ static int io_pin_pbuf_ring(struct io_uring_buf_reg *reg, * should use IOU_PBUF_RING_MMAP instead, and liburing will handle * this transparently. */ - if ((reg->ring_addr | (unsigned long) br) & (SHM_COLOUR - 1)) { - int i; - - for (i = 0; i < nr_pages; i++) - unpin_user_page(pages[i]); - return -EINVAL; - } + if ((reg->ring_addr | (unsigned long) br) & (SHM_COLOUR - 1)) + goto error_unpin; #endif bl->buf_pages = pages; bl->buf_nr_pages = nr_pages; @@ -510,6 +516,11 @@ static int io_pin_pbuf_ring(struct io_uring_buf_reg *reg, bl->is_mapped = 1; bl->is_mmap = 0; return 0; +error_unpin: + for (i = 0; i < nr_pages; i++) + unpin_user_page(pages[i]); + kvfree(pages); + return -EINVAL; } static int io_alloc_pbuf_ring(struct io_uring_buf_reg *reg, diff --git a/io_uring/net.c b/io_uring/net.c index 3d07bf79c1e025eb900eace985d14804ed392b2c..7a8e298af81b3b1db393efc547d04a8f2938f091 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -183,6 +183,10 @@ static int io_setup_async_msg(struct io_kiocb *req, memcpy(async_msg, kmsg, sizeof(*kmsg)); if (async_msg->msg.msg_name) async_msg->msg.msg_name = &async_msg->addr; + + if ((req->flags & REQ_F_BUFFER_SELECT) && !async_msg->msg.msg_iter.nr_segs) + return -EAGAIN; + /* if were using fast_iov, set it to the new one */ if (iter_is_iovec(&kmsg->msg.msg_iter) && !kmsg->free_iov) { size_t fast_idx = iter_iov(&kmsg->msg.msg_iter) - kmsg->fast_iov; @@ -542,6 +546,7 @@ static int io_recvmsg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg) { iomsg->msg.msg_name = &iomsg->addr; + iomsg->msg.msg_iter.nr_segs = 0; #ifdef CONFIG_COMPAT if (req->ctx->compat) diff --git a/io_uring/sqpoll.c b/io_uring/sqpoll.c index ee2d2c687fdaa954952eb0b1917d3d11b559da90..bd6c2c7959a5bf26c7a394bcd9cbf1b0e94fe595 100644 --- a/io_uring/sqpoll.c +++ b/io_uring/sqpoll.c @@ -430,7 +430,9 @@ __cold int io_sqpoll_wq_cpu_affinity(struct io_ring_ctx *ctx, if (sqd) { io_sq_thread_park(sqd); - ret = io_wq_cpu_affinity(sqd->thread->io_uring, mask); + /* Don't set affinity for a dying thread */ + if (sqd->thread) + ret = io_wq_cpu_affinity(sqd->thread->io_uring, mask); io_sq_thread_unpark(sqd); } diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b0cb7631e48ba12e8bd8408e2cbaa1dc4cb0e910..21d2fa815e782afddb5b2c56110fadf7e83376bc 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -143,6 +143,8 @@ static const struct audit_nfcfgop_tab audit_nfcfgs[] = { { AUDIT_NFT_OP_OBJ_RESET, "nft_reset_obj" }, { AUDIT_NFT_OP_FLOWTABLE_REGISTER, "nft_register_flowtable" }, { AUDIT_NFT_OP_FLOWTABLE_UNREGISTER, "nft_unregister_flowtable" }, + { AUDIT_NFT_OP_SETELEM_RESET, "nft_reset_setelem" }, + { AUDIT_NFT_OP_RULE_RESET, "nft_reset_rule" }, { AUDIT_NFT_OP_INVALID, "nft_invalid" }, }; diff --git a/kernel/bpf/bpf_local_storage.c b/kernel/bpf/bpf_local_storage.c index b5149cfce7d4defaac057a095cb2b055f888384b..146824cc96893c784f8a838f4ebca1760a05744d 100644 --- a/kernel/bpf/bpf_local_storage.c +++ b/kernel/bpf/bpf_local_storage.c @@ -553,7 +553,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, void *value, u64 map_flags, gfp_t gfp_flags) { struct bpf_local_storage_data *old_sdata = NULL; - struct bpf_local_storage_elem *selem = NULL; + struct bpf_local_storage_elem *alloc_selem, *selem = NULL; struct bpf_local_storage *local_storage; unsigned long flags; int err; @@ -607,11 +607,12 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, } } - if (gfp_flags == GFP_KERNEL) { - selem = bpf_selem_alloc(smap, owner, value, true, gfp_flags); - if (!selem) - return ERR_PTR(-ENOMEM); - } + /* A lookup has just been done before and concluded a new selem is + * needed. The chance of an unnecessary alloc is unlikely. + */ + alloc_selem = selem = bpf_selem_alloc(smap, owner, value, true, gfp_flags); + if (!alloc_selem) + return ERR_PTR(-ENOMEM); raw_spin_lock_irqsave(&local_storage->lock, flags); @@ -623,13 +624,13 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, * simple. */ err = -EAGAIN; - goto unlock_err; + goto unlock; } old_sdata = bpf_local_storage_lookup(local_storage, smap, false); err = check_flags(old_sdata, map_flags); if (err) - goto unlock_err; + goto unlock; if (old_sdata && (map_flags & BPF_F_LOCK)) { copy_map_value_locked(&smap->map, old_sdata->data, value, @@ -638,23 +639,7 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, goto unlock; } - if (gfp_flags != GFP_KERNEL) { - /* local_storage->lock is held. Hence, we are sure - * we can unlink and uncharge the old_sdata successfully - * later. Hence, instead of charging the new selem now - * and then uncharge the old selem later (which may cause - * a potential but unnecessary charge failure), avoid taking - * a charge at all here (the "!old_sdata" check) and the - * old_sdata will not be uncharged later during - * bpf_selem_unlink_storage_nolock(). - */ - selem = bpf_selem_alloc(smap, owner, value, !old_sdata, gfp_flags); - if (!selem) { - err = -ENOMEM; - goto unlock_err; - } - } - + alloc_selem = NULL; /* First, link the new selem to the map */ bpf_selem_link_map(smap, selem); @@ -665,20 +650,16 @@ bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap, if (old_sdata) { bpf_selem_unlink_map(SELEM(old_sdata)); bpf_selem_unlink_storage_nolock(local_storage, SELEM(old_sdata), - false, false); + true, false); } unlock: raw_spin_unlock_irqrestore(&local_storage->lock, flags); - return SDATA(selem); - -unlock_err: - raw_spin_unlock_irqrestore(&local_storage->lock, flags); - if (selem) { + if (alloc_selem) { mem_uncharge(smap, owner, smap->elem_size); - bpf_selem_free(selem, smap, true); + bpf_selem_free(alloc_selem, smap, true); } - return ERR_PTR(err); + return err ? ERR_PTR(err) : SDATA(selem); } static u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache) @@ -779,7 +760,7 @@ void bpf_local_storage_destroy(struct bpf_local_storage *local_storage) * of the loop will set the free_cgroup_storage to true. */ free_storage = bpf_selem_unlink_storage_nolock( - local_storage, selem, false, true); + local_storage, selem, true, true); } raw_spin_unlock_irqrestore(&local_storage->lock, flags); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 249657c466dd33af21c686e827d3608e31d832af..8090d7fb11ef686019b4734bbc7151057ee78efa 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -553,7 +553,7 @@ s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind) return -ENOENT; } -static s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p) +s32 bpf_find_btf_id(const char *name, u32 kind, struct btf **btf_p) { struct btf *btf; s32 ret; @@ -8501,7 +8501,7 @@ bool btf_nested_type_is_trusted(struct bpf_verifier_log *log, tname = btf_name_by_offset(btf, walk_type->name_off); ret = snprintf(safe_tname, sizeof(safe_tname), "%s%s", tname, suffix); - if (ret < 0) + if (ret >= sizeof(safe_tname)) return false; safe_id = btf_find_by_name_kind(btf, safe_tname, BTF_INFO_KIND(walk_type->info)); diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 5b2741aa0d9bb8e01d630ca548eb39ce10553e6a..03b3d4492980d04f33f63235c6fef18ee2667702 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -785,7 +785,8 @@ static void replace_effective_prog(struct cgroup *cgrp, * to descendants * @cgrp: The cgroup which descendants to traverse * @link: A link for which to replace BPF program - * @type: Type of attach operation + * @new_prog: &struct bpf_prog for the target BPF program with its refcnt + * incremented * * Must be called with cgroup_mutex held. */ @@ -1334,7 +1335,7 @@ int cgroup_bpf_prog_query(const union bpf_attr *attr, * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering * @sk: The socket sending or receiving traffic * @skb: The skb that is being sent or received - * @type: The type of program to be executed + * @atype: The type of program to be executed * * If no socket is passed, or the socket is not of type INET or INET6, * this function does nothing and returns 0. @@ -1424,7 +1425,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_skb); /** * __cgroup_bpf_run_filter_sk() - Run a program on a sock * @sk: sock structure to manipulate - * @type: The type of program to be executed + * @atype: The type of program to be executed * * socket is passed is expected to be of type INET or INET6. * @@ -1449,7 +1450,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); * provided by user sockaddr * @sk: sock struct that will use sockaddr * @uaddr: sockaddr struct provided by user - * @type: The type of program to be executed + * @atype: The type of program to be executed * @t_ctx: Pointer to attach type specific context * @flags: Pointer to u32 which contains higher bits of BPF program * return value (OR'ed together). @@ -1496,7 +1497,7 @@ EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_addr); * @sock_ops: bpf_sock_ops_kern struct to pass to program. Contains * sk with connection information (IP addresses, etc.) May not contain * cgroup info if it is a req sock. - * @type: The type of program to be executed + * @atype: The type of program to be executed * * socket passed is expected to be of type INET or INET6. * @@ -1670,7 +1671,7 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = { * @ppos: value-result argument: value is position at which read from or write * to sysctl is happening, result is new position if program overrode it, * initial value otherwise - * @type: type of program to be executed + * @atype: type of program to be executed * * Program is run when sysctl is being accessed, either read or written, and * can allow or deny such access. diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 0f8f036d8bd11be000d809a5fa2599e1acb56175..4e3ce0542e31f6c39a7567dc52a4ced08065092e 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -870,7 +870,7 @@ static struct bpf_prog_pack *alloc_new_pack(bpf_jit_fill_hole_t bpf_fill_ill_ins GFP_KERNEL); if (!pack) return NULL; - pack->ptr = module_alloc(BPF_PROG_PACK_SIZE); + pack->ptr = bpf_jit_alloc_exec(BPF_PROG_PACK_SIZE); if (!pack->ptr) { kfree(pack); return NULL; @@ -894,7 +894,7 @@ void *bpf_prog_pack_alloc(u32 size, bpf_jit_fill_hole_t bpf_fill_ill_insns) mutex_lock(&pack_mutex); if (size > BPF_PROG_PACK_SIZE) { size = round_up(size, PAGE_SIZE); - ptr = module_alloc(size); + ptr = bpf_jit_alloc_exec(size); if (ptr) { bpf_fill_ill_insns(ptr, size); set_vm_flush_reset_perms(ptr); @@ -932,7 +932,7 @@ void bpf_prog_pack_free(struct bpf_binary_header *hdr) mutex_lock(&pack_mutex); if (hdr->size > BPF_PROG_PACK_SIZE) { - module_memfree(hdr); + bpf_jit_free_exec(hdr); goto out; } @@ -956,7 +956,7 @@ void bpf_prog_pack_free(struct bpf_binary_header *hdr) if (bitmap_find_next_zero_area(pack->bitmap, BPF_PROG_CHUNK_COUNT, 0, BPF_PROG_CHUNK_COUNT, 0) == 0) { list_del(&pack->list); - module_memfree(pack->ptr); + bpf_jit_free_exec(pack->ptr); kfree(pack); } out: diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c index 9c49ae53deaf81f9e22fefc376657492abd2ebb9..d93ddac283d40144e2cb5ad990ca911a37041346 100644 --- a/kernel/bpf/memalloc.c +++ b/kernel/bpf/memalloc.c @@ -459,8 +459,7 @@ static void notrace irq_work_raise(struct bpf_mem_cache *c) * Typical case will be between 11K and 116K closer to 11K. * bpf progs can and should share bpf_mem_cache when possible. */ - -static void prefill_mem_cache(struct bpf_mem_cache *c, int cpu) +static void init_refill_work(struct bpf_mem_cache *c) { init_irq_work(&c->refill_work, bpf_mem_refill); if (c->unit_size <= 256) { @@ -476,7 +475,10 @@ static void prefill_mem_cache(struct bpf_mem_cache *c, int cpu) c->high_watermark = max(96 * 256 / c->unit_size, 3); } c->batch = max((c->high_watermark - c->low_watermark) / 4 * 3, 1); +} +static void prefill_mem_cache(struct bpf_mem_cache *c, int cpu) +{ /* To avoid consuming memory assume that 1st run of bpf * prog won't be doing more than 4 map_update_elem from * irq disabled region @@ -484,6 +486,31 @@ static void prefill_mem_cache(struct bpf_mem_cache *c, int cpu) alloc_bulk(c, c->unit_size <= 256 ? 4 : 1, cpu_to_node(cpu), false); } +static int check_obj_size(struct bpf_mem_cache *c, unsigned int idx) +{ + struct llist_node *first; + unsigned int obj_size; + + /* For per-cpu allocator, the size of free objects in free list doesn't + * match with unit_size and now there is no way to get the size of + * per-cpu pointer saved in free object, so just skip the checking. + */ + if (c->percpu_size) + return 0; + + first = c->free_llist.first; + if (!first) + return 0; + + obj_size = ksize(first); + if (obj_size != c->unit_size) { + WARN_ONCE(1, "bpf_mem_cache[%u]: unexpected object size %u, expect %u\n", + idx, obj_size, c->unit_size); + return -EINVAL; + } + return 0; +} + /* When size != 0 bpf_mem_cache for each cpu. * This is typical bpf hash map use case when all elements have equal size. * @@ -494,10 +521,10 @@ static void prefill_mem_cache(struct bpf_mem_cache *c, int cpu) int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu) { static u16 sizes[NUM_CACHES] = {96, 192, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096}; + int cpu, i, err, unit_size, percpu_size = 0; struct bpf_mem_caches *cc, __percpu *pcc; struct bpf_mem_cache *c, __percpu *pc; struct obj_cgroup *objcg = NULL; - int cpu, i, unit_size, percpu_size = 0; if (size) { pc = __alloc_percpu_gfp(sizeof(*pc), 8, GFP_KERNEL); @@ -521,6 +548,7 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu) c->objcg = objcg; c->percpu_size = percpu_size; c->tgt = c; + init_refill_work(c); prefill_mem_cache(c, cpu); } ma->cache = pc; @@ -534,6 +562,7 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu) pcc = __alloc_percpu_gfp(sizeof(*cc), 8, GFP_KERNEL); if (!pcc) return -ENOMEM; + err = 0; #ifdef CONFIG_MEMCG_KMEM objcg = get_obj_cgroup_from_current(); #endif @@ -544,11 +573,30 @@ int bpf_mem_alloc_init(struct bpf_mem_alloc *ma, int size, bool percpu) c->unit_size = sizes[i]; c->objcg = objcg; c->tgt = c; + + init_refill_work(c); + /* Another bpf_mem_cache will be used when allocating + * c->unit_size in bpf_mem_alloc(), so doesn't prefill + * for the bpf_mem_cache because these free objects will + * never be used. + */ + if (i != bpf_mem_cache_idx(c->unit_size)) + continue; prefill_mem_cache(c, cpu); + err = check_obj_size(c, i); + if (err) + goto out; } } + +out: ma->caches = pcc; - return 0; + /* refill_work is either zeroed or initialized, so it is safe to + * call irq_work_sync(). + */ + if (err) + bpf_mem_alloc_destroy(ma); + return err; } static void drain_mem_cache(struct bpf_mem_cache *c) @@ -916,3 +964,35 @@ void notrace *bpf_mem_cache_alloc_flags(struct bpf_mem_alloc *ma, gfp_t flags) return !ret ? NULL : ret + LLIST_NODE_SZ; } + +static __init int bpf_mem_cache_adjust_size(void) +{ + unsigned int size; + + /* Adjusting the indexes in size_index() according to the object_size + * of underlying slab cache, so bpf_mem_alloc() will select a + * bpf_mem_cache with unit_size equal to the object_size of + * the underlying slab cache. + * + * The maximal value of KMALLOC_MIN_SIZE and __kmalloc_minalign() is + * 256-bytes, so only do adjustment for [8-bytes, 192-bytes]. + */ + for (size = 192; size >= 8; size -= 8) { + unsigned int kmalloc_size, index; + + kmalloc_size = kmalloc_size_roundup(size); + if (kmalloc_size == size) + continue; + + if (kmalloc_size <= 192) + index = size_index[(kmalloc_size - 1) / 8]; + else + index = fls(kmalloc_size - 1) - 1; + /* Only overwrite if necessary */ + if (size_index[(size - 1) / 8] != index) + size_index[(size - 1) / 8] = index; + } + + return 0; +} +subsys_initcall(bpf_mem_cache_adjust_size); diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c index 32d2c4829eb8d16ce82d03e06773f67e7684b5d8..007d98c799e2d0a38717cf263fb1cf669d89f0fa 100644 --- a/kernel/bpf/mprog.c +++ b/kernel/bpf/mprog.c @@ -253,6 +253,9 @@ int bpf_mprog_attach(struct bpf_mprog_entry *entry, goto out; } idx = tidx; + } else if (bpf_mprog_total(entry) == bpf_mprog_max()) { + ret = -ERANGE; + goto out; } if (flags & BPF_F_BEFORE) { tidx = bpf_mprog_pos_before(entry, &rtuple); diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c index 3e4f2ec1af0687dc92d74a376072ab3a77d7b5e3..87d6693d8233f1aa3a1283e73600c9cd6fdf3cdf 100644 --- a/kernel/bpf/offload.c +++ b/kernel/bpf/offload.c @@ -199,12 +199,14 @@ static int __bpf_prog_dev_bound_init(struct bpf_prog *prog, struct net_device *n offload->netdev = netdev; ondev = bpf_offload_find_netdev(offload->netdev); + /* When program is offloaded require presence of "true" + * bpf_offload_netdev, avoid the one created for !ondev case below. + */ + if (bpf_prog_is_offloaded(prog->aux) && (!ondev || !ondev->offdev)) { + err = -EINVAL; + goto err_free; + } if (!ondev) { - if (bpf_prog_is_offloaded(prog->aux)) { - err = -EINVAL; - goto err_free; - } - /* When only binding to the device, explicitly * create an entry in the hashtable. */ diff --git a/kernel/bpf/queue_stack_maps.c b/kernel/bpf/queue_stack_maps.c index 8d2ddcb7566b7fe262e7571700f1b1ac48dcc782..d869f51ea93a0e918d365729ee5bcd8687a5ce4f 100644 --- a/kernel/bpf/queue_stack_maps.c +++ b/kernel/bpf/queue_stack_maps.c @@ -98,7 +98,12 @@ static long __queue_map_get(struct bpf_map *map, void *value, bool delete) int err = 0; void *ptr; - raw_spin_lock_irqsave(&qs->lock, flags); + if (in_nmi()) { + if (!raw_spin_trylock_irqsave(&qs->lock, flags)) + return -EBUSY; + } else { + raw_spin_lock_irqsave(&qs->lock, flags); + } if (queue_stack_map_is_empty(qs)) { memset(value, 0, qs->map.value_size); @@ -128,7 +133,12 @@ static long __stack_map_get(struct bpf_map *map, void *value, bool delete) void *ptr; u32 index; - raw_spin_lock_irqsave(&qs->lock, flags); + if (in_nmi()) { + if (!raw_spin_trylock_irqsave(&qs->lock, flags)) + return -EBUSY; + } else { + raw_spin_lock_irqsave(&qs->lock, flags); + } if (queue_stack_map_is_empty(qs)) { memset(value, 0, qs->map.value_size); @@ -193,7 +203,12 @@ static long queue_stack_map_push_elem(struct bpf_map *map, void *value, if (flags & BPF_NOEXIST || flags > BPF_EXIST) return -EINVAL; - raw_spin_lock_irqsave(&qs->lock, irq_flags); + if (in_nmi()) { + if (!raw_spin_trylock_irqsave(&qs->lock, irq_flags)) + return -EBUSY; + } else { + raw_spin_lock_irqsave(&qs->lock, irq_flags); + } if (queue_stack_map_is_full(qs)) { if (!replace) { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index ebeb0695305a7a5b2df7bed6f40b415531b2c37f..eb01c31ed591dde57dc8463a7d701dc46132c8f7 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -5502,9 +5502,9 @@ int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) } run_ctx.bpf_cookie = 0; - run_ctx.saved_run_ctx = NULL; if (!__bpf_prog_enter_sleepable_recur(prog, &run_ctx)) { /* recursion detected */ + __bpf_prog_exit_sleepable_recur(prog, 0, &run_ctx); bpf_prog_put(prog); return -EBUSY; } diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 78acf28d4873294e038c40d1a3ff873fce6319d1..53ff50cac61eaa5bf0aebe4425ced5b44d2086c7 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -926,13 +926,12 @@ u64 notrace __bpf_prog_enter_sleepable_recur(struct bpf_prog *prog, migrate_disable(); might_fault(); + run_ctx->saved_run_ctx = bpf_set_run_ctx(&run_ctx->run_ctx); + if (unlikely(this_cpu_inc_return(*(prog->active)) != 1)) { bpf_prog_inc_misses_counter(prog); return 0; } - - run_ctx->saved_run_ctx = bpf_set_run_ctx(&run_ctx->run_ctx); - return bpf_prog_start_time(); } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index bb78212fa5b27305e6672931ae815def8e931fe9..c0c7d137066ac8f1be839f9032f399bb96e6b3a6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4047,11 +4047,9 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int regno) bitmap_from_u64(mask, bt_reg_mask(bt)); for_each_set_bit(i, mask, 32) { reg = &st->frame[0]->regs[i]; - if (reg->type != SCALAR_VALUE) { - bt_clear_reg(bt, i); - continue; - } - reg->precise = true; + bt_clear_reg(bt, i); + if (reg->type == SCALAR_VALUE) + reg->precise = true; } return 0; } diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index 83044312bc413fc5367d816b76079f971eb33db3..c487ffef6652d75735bad19f5bb4eb4a38401b5c 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -431,7 +431,7 @@ static void *cgroup_pidlist_start(struct seq_file *s, loff_t *pos) if (l->list[mid] == pid) { index = mid; break; - } else if (l->list[mid] <= pid) + } else if (l->list[mid] < pid) index = mid + 1; else end = mid; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 5fa95f86cb4dd3d836e0292c6cb5dc6ad6777216..1fb7f562289d53263491738a6ea1e689933ab27f 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -492,28 +492,6 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, return &cgrp->self; } -/** - * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem - * @cgrp: the cgroup of interest - * @ss: the subsystem of interest - * - * Find and get @cgrp's css associated with @ss. If the css doesn't exist - * or is offline, %NULL is returned. - */ -static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp, - struct cgroup_subsys *ss) -{ - struct cgroup_subsys_state *css; - - rcu_read_lock(); - css = cgroup_css(cgrp, ss); - if (css && !css_tryget_online(css)) - css = NULL; - rcu_read_unlock(); - - return css; -} - /** * cgroup_e_css_by_mask - obtain a cgroup's effective css for the specified ss * @cgrp: the cgroup of interest @@ -679,7 +657,7 @@ EXPORT_SYMBOL_GPL(of_css); * @ssid: the index of the subsystem, CGROUP_SUBSYS_COUNT after reaching the end * @cgrp: the target cgroup to iterate css's of * - * Should be called under cgroup_[tree_]mutex. + * Should be called under cgroup_mutex. */ #define for_each_css(css, ssid, cgrp) \ for ((ssid) = 0; (ssid) < CGROUP_SUBSYS_COUNT; (ssid)++) \ @@ -929,7 +907,7 @@ static void css_set_move_task(struct task_struct *task, #define CSS_SET_HASH_BITS 7 static DEFINE_HASHTABLE(css_set_table, CSS_SET_HASH_BITS); -static unsigned long css_set_hash(struct cgroup_subsys_state *css[]) +static unsigned long css_set_hash(struct cgroup_subsys_state **css) { unsigned long key = 0UL; struct cgroup_subsys *ss; @@ -1070,7 +1048,7 @@ static bool compare_css_sets(struct css_set *cset, */ static struct css_set *find_existing_css_set(struct css_set *old_cset, struct cgroup *cgrp, - struct cgroup_subsys_state *template[]) + struct cgroup_subsys_state **template) { struct cgroup_root *root = cgrp->root; struct cgroup_subsys *ss; @@ -1736,7 +1714,7 @@ static int css_populate_dir(struct cgroup_subsys_state *css) struct cftype *cfts, *failed_cfts; int ret; - if ((css->flags & CSS_VISIBLE) || !cgrp->kn) + if (css->flags & CSS_VISIBLE) return 0; if (!css->ss) { @@ -2499,7 +2477,7 @@ struct task_struct *cgroup_taskset_next(struct cgroup_taskset *tset, /* * This function may be called both before and - * after cgroup_taskset_migrate(). The two cases + * after cgroup_migrate_execute(). The two cases * can be distinguished by looking at whether @cset * has its ->mg_dst_cset set. */ @@ -3654,9 +3632,32 @@ static int cgroup_stat_show(struct seq_file *seq, void *v) return 0; } -static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq, - struct cgroup *cgrp, int ssid) +#ifdef CONFIG_CGROUP_SCHED +/** + * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem + * @cgrp: the cgroup of interest + * @ss: the subsystem of interest + * + * Find and get @cgrp's css associated with @ss. If the css doesn't exist + * or is offline, %NULL is returned. + */ +static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp, + struct cgroup_subsys *ss) +{ + struct cgroup_subsys_state *css; + + rcu_read_lock(); + css = cgroup_css(cgrp, ss); + if (css && !css_tryget_online(css)) + css = NULL; + rcu_read_unlock(); + + return css; +} + +static int cgroup_extra_stat_show(struct seq_file *seq, int ssid) { + struct cgroup *cgrp = seq_css(seq)->cgroup; struct cgroup_subsys *ss = cgroup_subsys[ssid]; struct cgroup_subsys_state *css; int ret; @@ -3673,20 +3674,8 @@ static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq, return ret; } -static int cpu_stat_show(struct seq_file *seq, void *v) -{ - struct cgroup __maybe_unused *cgrp = seq_css(seq)->cgroup; - int ret = 0; - - cgroup_base_stat_cputime_show(seq); -#ifdef CONFIG_CGROUP_SCHED - ret = cgroup_extra_stat_show(seq, cgrp, cpu_cgrp_id); -#endif - return ret; -} - -static int __maybe_unused cgroup_local_stat_show(struct seq_file *seq, - struct cgroup *cgrp, int ssid) +static int cgroup_local_stat_show(struct seq_file *seq, + struct cgroup *cgrp, int ssid) { struct cgroup_subsys *ss = cgroup_subsys[ssid]; struct cgroup_subsys_state *css; @@ -3703,6 +3692,18 @@ static int __maybe_unused cgroup_local_stat_show(struct seq_file *seq, css_put(css); return ret; } +#endif + +static int cpu_stat_show(struct seq_file *seq, void *v) +{ + int ret = 0; + + cgroup_base_stat_cputime_show(seq); +#ifdef CONFIG_CGROUP_SCHED + ret = cgroup_extra_stat_show(seq, cpu_cgrp_id); +#endif + return ret; +} static int cpu_local_stat_show(struct seq_file *seq, void *v) { @@ -4350,14 +4351,13 @@ static int cgroup_init_cftypes(struct cgroup_subsys *ss, struct cftype *cfts) return ret; } -static int cgroup_rm_cftypes_locked(struct cftype *cfts) +static void cgroup_rm_cftypes_locked(struct cftype *cfts) { lockdep_assert_held(&cgroup_mutex); list_del(&cfts->node); cgroup_apply_cftypes(cfts, false); cgroup_exit_cftypes(cfts); - return 0; } /** @@ -4373,8 +4373,6 @@ static int cgroup_rm_cftypes_locked(struct cftype *cfts) */ int cgroup_rm_cftypes(struct cftype *cfts) { - int ret; - if (!cfts || cfts[0].name[0] == '\0') return 0; @@ -4382,9 +4380,9 @@ int cgroup_rm_cftypes(struct cftype *cfts) return -ENOENT; cgroup_lock(); - ret = cgroup_rm_cftypes_locked(cfts); + cgroup_rm_cftypes_locked(cfts); cgroup_unlock(); - return ret; + return 0; } /** @@ -5337,7 +5335,7 @@ static struct cftype cgroup_psi_files[] = { * RCU callback. * * 4. After the grace period, the css can be freed. Implemented in - * css_free_work_fn(). + * css_free_rwork_fn(). * * It is actually hairier because both step 2 and 4 require process context * and thus involve punting to css->destroy_work adding two additional @@ -5581,8 +5579,7 @@ static struct cgroup_subsys_state *css_create(struct cgroup *cgrp, /* * The returned cgroup is fully initialized including its control mask, but - * it isn't associated with its kernfs_node and doesn't have the control - * mask applied. + * it doesn't have the control mask applied. */ static struct cgroup *cgroup_create(struct cgroup *parent, const char *name, umode_t mode) @@ -5908,7 +5905,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) /* * Mark @cgrp and the associated csets dead. The former prevents * further task migration and child creation by disabling - * cgroup_lock_live_group(). The latter makes the csets ignored by + * cgroup_kn_lock_live(). The latter makes the csets ignored by * the migration path. */ cgrp->self.flags &= ~CSS_ONLINE; @@ -5930,7 +5927,7 @@ static int cgroup_destroy_locked(struct cgroup *cgrp) parent->nr_threaded_children--; spin_lock_irq(&css_set_lock); - for (tcgrp = cgroup_parent(cgrp); tcgrp; tcgrp = cgroup_parent(tcgrp)) { + for (tcgrp = parent; tcgrp; tcgrp = cgroup_parent(tcgrp)) { tcgrp->nr_descendants--; tcgrp->nr_dying_descendants++; /* @@ -6123,8 +6120,8 @@ int __init cgroup_init(void) continue; if (cgroup1_ssid_disabled(ssid)) - printk(KERN_INFO "Disabling %s control group subsystem in v1 mounts\n", - ss->name); + pr_info("Disabling %s control group subsystem in v1 mounts\n", + ss->name); cgrp_dfl_root.subsys_mask |= 1 << ss->id; diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 58e6f18f01c1b54f3572c16a32a355a9bab771fe..58ec88efa4f82cc9a29c33deb6634fc281a2036a 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -1230,7 +1230,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus) /* * Percpu kthreads in top_cpuset are ignored */ - if ((task->flags & PF_KTHREAD) && kthread_is_per_cpu(task)) + if (kthread_is_per_cpu(task)) continue; cpumask_andnot(new_cpus, possible_mask, cs->subparts_cpus); } else { @@ -1255,7 +1255,7 @@ static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus) static void compute_effective_cpumask(struct cpumask *new_cpus, struct cpuset *cs, struct cpuset *parent) { - if (parent->nr_subparts_cpus) { + if (parent->nr_subparts_cpus && is_partition_valid(cs)) { cpumask_or(new_cpus, parent->effective_cpus, parent->subparts_cpus); cpumask_and(new_cpus, new_cpus, cs->cpus_allowed); @@ -1277,6 +1277,52 @@ enum subparts_cmd { static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, int turning_on); +static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, + struct tmpmasks *tmp); + +/* + * Update partition exclusive flag + * + * Return: 0 if successful, an error code otherwise + */ +static int update_partition_exclusive(struct cpuset *cs, int new_prs) +{ + bool exclusive = (new_prs > 0); + + if (exclusive && !is_cpu_exclusive(cs)) { + if (update_flag(CS_CPU_EXCLUSIVE, cs, 1)) + return PERR_NOTEXCL; + } else if (!exclusive && is_cpu_exclusive(cs)) { + /* Turning off CS_CPU_EXCLUSIVE will not return error */ + update_flag(CS_CPU_EXCLUSIVE, cs, 0); + } + return 0; +} + +/* + * Update partition load balance flag and/or rebuild sched domain + * + * Changing load balance flag will automatically call + * rebuild_sched_domains_locked(). + */ +static void update_partition_sd_lb(struct cpuset *cs, int old_prs) +{ + int new_prs = cs->partition_root_state; + bool new_lb = (new_prs != PRS_ISOLATED); + bool rebuild_domains = (new_prs > 0) || (old_prs > 0); + + if (new_lb != !!is_sched_load_balance(cs)) { + rebuild_domains = true; + if (new_lb) + set_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); + else + clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); + } + + if (rebuild_domains) + rebuild_sched_domains_locked(); +} + /** * update_parent_subparts_cpumask - update subparts_cpus mask of parent cpuset * @cs: The cpuset that requests change in partition root state @@ -1336,8 +1382,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, return is_partition_invalid(parent) ? PERR_INVPARENT : PERR_NOTPART; } - if ((newmask && cpumask_empty(newmask)) || - (!newmask && cpumask_empty(cs->cpus_allowed))) + if (!newmask && cpumask_empty(cs->cpus_allowed)) return PERR_CPUSEMPTY; /* @@ -1403,11 +1448,16 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, cpumask_and(tmp->addmask, newmask, parent->cpus_allowed); adding = cpumask_andnot(tmp->addmask, tmp->addmask, parent->subparts_cpus); + /* + * Empty cpumask is not allowed + */ + if (cpumask_empty(newmask)) { + part_error = PERR_CPUSEMPTY; /* * Make partition invalid if parent's effective_cpus could * become empty and there are tasks in the parent. */ - if (adding && + } else if (adding && cpumask_subset(parent->effective_cpus, tmp->addmask) && !cpumask_intersects(tmp->delmask, cpu_active_mask) && partition_is_populated(parent, cs)) { @@ -1480,14 +1530,13 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, /* * Transitioning between invalid to valid or vice versa may require - * changing CS_CPU_EXCLUSIVE and CS_SCHED_LOAD_BALANCE. + * changing CS_CPU_EXCLUSIVE. */ if (old_prs != new_prs) { - if (is_prs_invalid(old_prs) && !is_cpu_exclusive(cs) && - (update_flag(CS_CPU_EXCLUSIVE, cs, 1) < 0)) - return PERR_NOTEXCL; - if (is_prs_invalid(new_prs) && is_cpu_exclusive(cs)) - update_flag(CS_CPU_EXCLUSIVE, cs, 0); + int err = update_partition_exclusive(cs, new_prs); + + if (err) + return err; } /* @@ -1520,23 +1569,33 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, spin_unlock_irq(&callback_lock); - if (adding || deleting) + if (adding || deleting) { update_tasks_cpumask(parent, tmp->addmask); + if (parent->child_ecpus_count) + update_sibling_cpumasks(parent, cs, tmp); + } /* - * Set or clear CS_SCHED_LOAD_BALANCE when partcmd_update, if necessary. - * rebuild_sched_domains_locked() may be called. + * For partcmd_update without newmask, it is being called from + * cpuset_hotplug_workfn() where cpus_read_lock() wasn't taken. + * Update the load balance flag and scheduling domain if + * cpus_read_trylock() is successful. */ - if (old_prs != new_prs) { - if (old_prs == PRS_ISOLATED) - update_flag(CS_SCHED_LOAD_BALANCE, cs, 1); - else if (new_prs == PRS_ISOLATED) - update_flag(CS_SCHED_LOAD_BALANCE, cs, 0); + if ((cmd == partcmd_update) && !newmask && cpus_read_trylock()) { + update_partition_sd_lb(cs, old_prs); + cpus_read_unlock(); } + notify_partition_change(cs, old_prs); return 0; } +/* + * update_cpumasks_hier() flags + */ +#define HIER_CHECKALL 0x01 /* Check all cpusets with no skipping */ +#define HIER_NO_SD_REBUILD 0x02 /* Don't rebuild sched domains */ + /* * update_cpumasks_hier - Update effective cpumasks and tasks in the subtree * @cs: the cpuset to consider @@ -1551,7 +1610,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd, * Called with cpuset_mutex held */ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, - bool force) + int flags) { struct cpuset *cp; struct cgroup_subsys_state *pos_css; @@ -1588,11 +1647,16 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, } /* - * Skip the whole subtree if the cpumask remains the same - * and has no partition root state and force flag not set. + * Skip the whole subtree if + * 1) the cpumask remains the same, + * 2) has no partition root state, + * 3) HIER_CHECKALL flag not set, and + * 4) for v2 load balance state same as its parent. */ - if (!cp->partition_root_state && !force && - cpumask_equal(tmp->new_cpus, cp->effective_cpus)) { + if (!cp->partition_root_state && !(flags & HIER_CHECKALL) && + cpumask_equal(tmp->new_cpus, cp->effective_cpus) && + (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) || + (is_sched_load_balance(parent) == is_sched_load_balance(cp)))) { pos_css = css_rightmost_descendant(pos_css); continue; } @@ -1675,6 +1739,20 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, update_tasks_cpumask(cp, tmp->new_cpus); + /* + * On default hierarchy, inherit the CS_SCHED_LOAD_BALANCE + * from parent if current cpuset isn't a valid partition root + * and their load balance states differ. + */ + if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) && + !is_partition_valid(cp) && + (is_sched_load_balance(parent) != is_sched_load_balance(cp))) { + if (is_sched_load_balance(parent)) + set_bit(CS_SCHED_LOAD_BALANCE, &cp->flags); + else + clear_bit(CS_SCHED_LOAD_BALANCE, &cp->flags); + } + /* * On legacy hierarchy, if the effective cpumask of any non- * empty cpuset is changed, we need to rebuild sched domains. @@ -1692,7 +1770,7 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp, } rcu_read_unlock(); - if (need_rebuild_sched_domains) + if (need_rebuild_sched_domains && !(flags & HIER_NO_SD_REBUILD)) rebuild_sched_domains_locked(); } @@ -1716,7 +1794,9 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, * to use the right effective_cpus value. * * The update_cpumasks_hier() function may sleep. So we have to - * release the RCU read lock before calling it. + * release the RCU read lock before calling it. HIER_NO_SD_REBUILD + * flag is used to suppress rebuild of sched domains as the callers + * will take care of that. */ rcu_read_lock(); cpuset_for_each_child(sibling, pos_css, parent) { @@ -1728,7 +1808,7 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, continue; rcu_read_unlock(); - update_cpumasks_hier(sibling, tmp, false); + update_cpumasks_hier(sibling, tmp, HIER_NO_SD_REBUILD); rcu_read_lock(); css_put(&sibling->css); } @@ -1747,6 +1827,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, int retval; struct tmpmasks tmp; bool invalidate = false; + int old_prs = cs->partition_root_state; /* top_cpuset.cpus_allowed tracks cpu_online_mask; it's read-only */ if (cs == &top_cpuset) @@ -1774,18 +1855,8 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, if (cpumask_equal(cs->cpus_allowed, trialcs->cpus_allowed)) return 0; -#ifdef CONFIG_CPUMASK_OFFSTACK - /* - * Use the cpumasks in trialcs for tmpmasks when they are pointers - * to allocated cpumasks. - * - * Note that update_parent_subparts_cpumask() uses only addmask & - * delmask, but not new_cpus. - */ - tmp.addmask = trialcs->subparts_cpus; - tmp.delmask = trialcs->effective_cpus; - tmp.new_cpus = NULL; -#endif + if (alloc_cpumasks(NULL, &tmp)) + return -ENOMEM; retval = validate_change(cs, trialcs); @@ -1814,7 +1885,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, retval = 0; } if (retval < 0) - return retval; + goto out_free; if (cs->partition_root_state) { if (invalidate) @@ -1849,13 +1920,8 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, } spin_unlock_irq(&callback_lock); -#ifdef CONFIG_CPUMASK_OFFSTACK - /* Now trialcs->cpus_allowed is available */ - tmp.new_cpus = trialcs->cpus_allowed; -#endif - /* effective_cpus will be updated here */ - update_cpumasks_hier(cs, &tmp, false); + update_cpumasks_hier(cs, &tmp, 0); if (cs->partition_root_state) { struct cpuset *parent = parent_cs(cs); @@ -1866,7 +1932,12 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs, */ if (parent->child_ecpus_count) update_sibling_cpumasks(parent, cs, &tmp); + + /* Update CS_SCHED_LOAD_BALANCE and/or sched_domains */ + update_partition_sd_lb(cs, old_prs); } +out_free: + free_cpumasks(NULL, &tmp); return 0; } @@ -2242,7 +2313,6 @@ static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs, static int update_prstate(struct cpuset *cs, int new_prs) { int err = PERR_NONE, old_prs = cs->partition_root_state; - bool sched_domain_rebuilt = false; struct cpuset *parent = parent_cs(cs); struct tmpmasks tmpmask; @@ -2261,45 +2331,26 @@ static int update_prstate(struct cpuset *cs, int new_prs) if (alloc_cpumasks(NULL, &tmpmask)) return -ENOMEM; + err = update_partition_exclusive(cs, new_prs); + if (err) + goto out; + if (!old_prs) { /* - * Turning on partition root requires setting the - * CS_CPU_EXCLUSIVE bit implicitly as well and cpus_allowed - * cannot be empty. + * cpus_allowed cannot be empty. */ if (cpumask_empty(cs->cpus_allowed)) { err = PERR_CPUSEMPTY; goto out; } - err = update_flag(CS_CPU_EXCLUSIVE, cs, 1); - if (err) { - err = PERR_NOTEXCL; - goto out; - } - err = update_parent_subparts_cpumask(cs, partcmd_enable, NULL, &tmpmask); - if (err) { - update_flag(CS_CPU_EXCLUSIVE, cs, 0); - goto out; - } - - if (new_prs == PRS_ISOLATED) { - /* - * Disable the load balance flag should not return an - * error unless the system is running out of memory. - */ - update_flag(CS_SCHED_LOAD_BALANCE, cs, 0); - sched_domain_rebuilt = true; - } } else if (old_prs && new_prs) { /* * A change in load balance state only, no change in cpumasks. */ - update_flag(CS_SCHED_LOAD_BALANCE, cs, (new_prs != PRS_ISOLATED)); - sched_domain_rebuilt = true; - goto out; /* Sched domain is rebuilt in update_flag() */ + ; } else { /* * Switching back to member is always allowed even if it @@ -2318,40 +2369,31 @@ static int update_prstate(struct cpuset *cs, int new_prs) compute_effective_cpumask(cs->effective_cpus, cs, parent); spin_unlock_irq(&callback_lock); } - - /* Turning off CS_CPU_EXCLUSIVE will not return error */ - update_flag(CS_CPU_EXCLUSIVE, cs, 0); - - if (!is_sched_load_balance(cs)) { - /* Make sure load balance is on */ - update_flag(CS_SCHED_LOAD_BALANCE, cs, 1); - sched_domain_rebuilt = true; - } } - - update_tasks_cpumask(parent, tmpmask.new_cpus); - - if (parent->child_ecpus_count) - update_sibling_cpumasks(parent, cs, &tmpmask); - - if (!sched_domain_rebuilt) - rebuild_sched_domains_locked(); out: /* - * Make partition invalid if an error happen + * Make partition invalid & disable CS_CPU_EXCLUSIVE if an error + * happens. */ - if (err) + if (err) { new_prs = -new_prs; + update_partition_exclusive(cs, new_prs); + } + spin_lock_irq(&callback_lock); cs->partition_root_state = new_prs; WRITE_ONCE(cs->prs_err, err); spin_unlock_irq(&callback_lock); + /* * Update child cpusets, if present. * Force update if switching back to member. */ if (!list_empty(&cs->css.children)) - update_cpumasks_hier(cs, &tmpmask, !new_prs); + update_cpumasks_hier(cs, &tmpmask, !new_prs ? HIER_CHECKALL : 0); + + /* Update sched domains and load balance flag */ + update_partition_sd_lb(cs, old_prs); notify_partition_change(cs, old_prs); free_cpumasks(NULL, &tmpmask); @@ -2487,6 +2529,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) struct cgroup_subsys_state *css; struct cpuset *cs, *oldcs; struct task_struct *task; + bool cpus_updated, mems_updated; int ret; /* used later by cpuset_attach() */ @@ -2501,13 +2544,25 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) if (ret) goto out_unlock; + cpus_updated = !cpumask_equal(cs->effective_cpus, oldcs->effective_cpus); + mems_updated = !nodes_equal(cs->effective_mems, oldcs->effective_mems); + cgroup_taskset_for_each(task, css, tset) { ret = task_can_attach(task); if (ret) goto out_unlock; - ret = security_task_setscheduler(task); - if (ret) - goto out_unlock; + + /* + * Skip rights over task check in v2 when nothing changes, + * migration permission derives from hierarchy ownership in + * cgroup_procs_write_permission()). + */ + if (!cgroup_subsys_on_dfl(cpuset_cgrp_subsys) || + (cpus_updated || mems_updated)) { + ret = security_task_setscheduler(task); + if (ret) + goto out_unlock; + } if (dl_task(task)) { cs->nr_migrate_dl_tasks++; @@ -3222,6 +3277,14 @@ static int cpuset_css_online(struct cgroup_subsys_state *css) cs->use_parent_ecpus = true; parent->child_ecpus_count++; } + + /* + * For v2, clear CS_SCHED_LOAD_BALANCE if parent is isolated + */ + if (cgroup_subsys_on_dfl(cpuset_cgrp_subsys) && + !is_sched_load_balance(parent)) + clear_bit(CS_SCHED_LOAD_BALANCE, &cs->flags); + spin_unlock_irq(&callback_lock); if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags)) @@ -3521,17 +3584,16 @@ hotplug_update_tasks_legacy(struct cpuset *cs, is_empty = cpumask_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed); - mutex_unlock(&cpuset_mutex); - /* * Move tasks to the nearest ancestor with execution resources, * This is full cgroup operation which will also call back into * cpuset. Should be done outside any lock. */ - if (is_empty) + if (is_empty) { + mutex_unlock(&cpuset_mutex); remove_tasks_in_empty_cpuset(cs); - - mutex_lock(&cpuset_mutex); + mutex_lock(&cpuset_mutex); + } } static void @@ -3691,6 +3753,7 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) /** * cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset + * @work: unused * * This function is called after either CPU or memory configuration has * changed and updates cpuset accordingly. The top_cpuset is always @@ -4073,6 +4136,7 @@ bool cpuset_node_allowed(int node, gfp_t gfp_mask) /** * cpuset_spread_node() - On which node to begin search for a page + * @rotor: round robin rotor * * If a task is marked PF_SPREAD_PAGE or PF_SPREAD_SLAB (as for * tasks in a cpuset with is_spread_page or is_spread_slab set), diff --git a/kernel/cgroup/misc.c b/kernel/cgroup/misc.c index ae2f4dd47508d421dfd36f141204198726570d47..79a3717a5803aa40c7674edc8e0c37443168b33f 100644 --- a/kernel/cgroup/misc.c +++ b/kernel/cgroup/misc.c @@ -14,7 +14,7 @@ #include #define MAX_STR "max" -#define MAX_NUM ULONG_MAX +#define MAX_NUM U64_MAX /* Miscellaneous res name, keep it in sync with enum misc_res_type */ static const char *const misc_res_name[] = { @@ -37,7 +37,7 @@ static struct misc_cg root_cg; * more than the actual capacity. We are using Limits resource distribution * model of cgroup for miscellaneous controller. */ -static unsigned long misc_res_capacity[MISC_CG_RES_TYPES]; +static u64 misc_res_capacity[MISC_CG_RES_TYPES]; /** * parent_misc() - Get the parent of the passed misc cgroup. @@ -74,10 +74,10 @@ static inline bool valid_type(enum misc_res_type type) * Context: Any context. * Return: Current total usage of the resource. */ -unsigned long misc_cg_res_total_usage(enum misc_res_type type) +u64 misc_cg_res_total_usage(enum misc_res_type type) { if (valid_type(type)) - return atomic_long_read(&root_cg.res[type].usage); + return atomic64_read(&root_cg.res[type].usage); return 0; } @@ -95,7 +95,7 @@ EXPORT_SYMBOL_GPL(misc_cg_res_total_usage); * * %0 - Successfully registered the capacity. * * %-EINVAL - If @type is invalid. */ -int misc_cg_set_capacity(enum misc_res_type type, unsigned long capacity) +int misc_cg_set_capacity(enum misc_res_type type, u64 capacity) { if (!valid_type(type)) return -EINVAL; @@ -114,9 +114,9 @@ EXPORT_SYMBOL_GPL(misc_cg_set_capacity); * Context: Any context. */ static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) + u64 amount) { - WARN_ONCE(atomic_long_add_negative(-amount, &cg->res[type].usage), + WARN_ONCE(atomic64_add_negative(-amount, &cg->res[type].usage), "misc cgroup resource %s became less than 0", misc_res_name[type]); } @@ -137,13 +137,12 @@ static void misc_cg_cancel_charge(enum misc_res_type type, struct misc_cg *cg, * * -EBUSY - If max limit will be crossed or total usage will be more than the * capacity. */ -int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) +int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, u64 amount) { struct misc_cg *i, *j; int ret; struct misc_res *res; - int new_usage; + u64 new_usage; if (!(valid_type(type) && cg && READ_ONCE(misc_res_capacity[type]))) return -EINVAL; @@ -154,7 +153,7 @@ int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, for (i = cg; i; i = parent_misc(i)) { res = &i->res[type]; - new_usage = atomic_long_add_return(amount, &res->usage); + new_usage = atomic64_add_return(amount, &res->usage); if (new_usage > READ_ONCE(res->max) || new_usage > READ_ONCE(misc_res_capacity[type])) { ret = -EBUSY; @@ -165,7 +164,7 @@ int misc_cg_try_charge(enum misc_res_type type, struct misc_cg *cg, err_charge: for (j = i; j; j = parent_misc(j)) { - atomic_long_inc(&j->res[type].events); + atomic64_inc(&j->res[type].events); cgroup_file_notify(&j->events_file); } @@ -184,8 +183,7 @@ EXPORT_SYMBOL_GPL(misc_cg_try_charge); * * Context: Any context. */ -void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, - unsigned long amount) +void misc_cg_uncharge(enum misc_res_type type, struct misc_cg *cg, u64 amount) { struct misc_cg *i; @@ -209,7 +207,7 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) { int i; struct misc_cg *cg = css_misc(seq_css(sf)); - unsigned long max; + u64 max; for (i = 0; i < MISC_CG_RES_TYPES; i++) { if (READ_ONCE(misc_res_capacity[i])) { @@ -217,7 +215,7 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) if (max == MAX_NUM) seq_printf(sf, "%s max\n", misc_res_name[i]); else - seq_printf(sf, "%s %lu\n", misc_res_name[i], + seq_printf(sf, "%s %llu\n", misc_res_name[i], max); } } @@ -241,13 +239,13 @@ static int misc_cg_max_show(struct seq_file *sf, void *v) * Return: * * >= 0 - Number of bytes processed in the input. * * -EINVAL - If buf is not valid. - * * -ERANGE - If number is bigger than the unsigned long capacity. + * * -ERANGE - If number is bigger than the u64 capacity. */ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct misc_cg *cg; - unsigned long max; + u64 max; int ret = 0, i; enum misc_res_type type = MISC_CG_RES_TYPES; char *token; @@ -271,7 +269,7 @@ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, if (!strcmp(MAX_STR, buf)) { max = MAX_NUM; } else { - ret = kstrtoul(buf, 0, &max); + ret = kstrtou64(buf, 0, &max); if (ret) return ret; } @@ -297,13 +295,13 @@ static ssize_t misc_cg_max_write(struct kernfs_open_file *of, char *buf, static int misc_cg_current_show(struct seq_file *sf, void *v) { int i; - unsigned long usage; + u64 usage; struct misc_cg *cg = css_misc(seq_css(sf)); for (i = 0; i < MISC_CG_RES_TYPES; i++) { - usage = atomic_long_read(&cg->res[i].usage); + usage = atomic64_read(&cg->res[i].usage); if (READ_ONCE(misc_res_capacity[i]) || usage) - seq_printf(sf, "%s %lu\n", misc_res_name[i], usage); + seq_printf(sf, "%s %llu\n", misc_res_name[i], usage); } return 0; @@ -322,12 +320,12 @@ static int misc_cg_current_show(struct seq_file *sf, void *v) static int misc_cg_capacity_show(struct seq_file *sf, void *v) { int i; - unsigned long cap; + u64 cap; for (i = 0; i < MISC_CG_RES_TYPES; i++) { cap = READ_ONCE(misc_res_capacity[i]); if (cap) - seq_printf(sf, "%s %lu\n", misc_res_name[i], cap); + seq_printf(sf, "%s %llu\n", misc_res_name[i], cap); } return 0; @@ -336,12 +334,13 @@ static int misc_cg_capacity_show(struct seq_file *sf, void *v) static int misc_events_show(struct seq_file *sf, void *v) { struct misc_cg *cg = css_misc(seq_css(sf)); - unsigned long events, i; + u64 events; + int i; for (i = 0; i < MISC_CG_RES_TYPES; i++) { - events = atomic_long_read(&cg->res[i].events); + events = atomic64_read(&cg->res[i].events); if (READ_ONCE(misc_res_capacity[i]) || events) - seq_printf(sf, "%s.max %lu\n", misc_res_name[i], events); + seq_printf(sf, "%s.max %llu\n", misc_res_name[i], events); } return 0; } @@ -397,7 +396,7 @@ misc_cg_alloc(struct cgroup_subsys_state *parent_css) for (i = 0; i < MISC_CG_RES_TYPES; i++) { WRITE_ONCE(cg->res[i].max, MAX_NUM); - atomic_long_set(&cg->res[i].usage, 0); + atomic64_set(&cg->res[i].usage, 0); } return &cg->css; diff --git a/kernel/cgroup/namespace.c b/kernel/cgroup/namespace.c index 0d5c29879a50b78fba44058be31930b9f99615b3..144a464e45c66447235b3b265efcd1092f32c2a2 100644 --- a/kernel/cgroup/namespace.c +++ b/kernel/cgroup/namespace.c @@ -149,9 +149,3 @@ const struct proc_ns_operations cgroupns_operations = { .install = cgroupns_install, .owner = cgroupns_owner, }; - -static __init int cgroup_namespaces_init(void) -{ - return 0; -} -subsys_initcall(cgroup_namespaces_init); diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c index 2542c21b6b6d782f7a6786d8c74caee5e58c8a26..d80d7a6081412994582e2a3686442226b582cd50 100644 --- a/kernel/cgroup/rstat.c +++ b/kernel/cgroup/rstat.c @@ -344,6 +344,7 @@ static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) { struct cgroup_rstat_cpu *rstatc = cgroup_rstat_cpu(cgrp, cpu); struct cgroup *parent = cgroup_parent(cgrp); + struct cgroup_rstat_cpu *prstatc; struct cgroup_base_stat delta; unsigned seq; @@ -357,17 +358,24 @@ static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu) delta = rstatc->bstat; } while (__u64_stats_fetch_retry(&rstatc->bsync, seq)); - /* propagate percpu delta to global */ + /* propagate per-cpu delta to cgroup and per-cpu global statistics */ cgroup_base_stat_sub(&delta, &rstatc->last_bstat); cgroup_base_stat_add(&cgrp->bstat, &delta); cgroup_base_stat_add(&rstatc->last_bstat, &delta); + cgroup_base_stat_add(&rstatc->subtree_bstat, &delta); - /* propagate global delta to parent (unless that's root) */ + /* propagate cgroup and per-cpu global delta to parent (unless that's root) */ if (cgroup_parent(parent)) { delta = cgrp->bstat; cgroup_base_stat_sub(&delta, &cgrp->last_bstat); cgroup_base_stat_add(&parent->bstat, &delta); cgroup_base_stat_add(&cgrp->last_bstat, &delta); + + delta = rstatc->subtree_bstat; + prstatc = cgroup_rstat_cpu(parent, cpu); + cgroup_base_stat_sub(&delta, &rstatc->last_subtree_bstat); + cgroup_base_stat_add(&prstatc->subtree_bstat, &delta); + cgroup_base_stat_add(&rstatc->last_subtree_bstat, &delta); } } diff --git a/kernel/configs/debug.config b/kernel/configs/debug.config index e8db8d938661774a8917a42951d519f7bf415c77..4722b998a32453243cf0f7d29e081a48bc8e406e 100644 --- a/kernel/configs/debug.config +++ b/kernel/configs/debug.config @@ -1,3 +1,5 @@ +# Help: Debugging for CI systems and finding regressions +# # The config is based on running daily CI for enterprise Linux distros to # seek regressions on linux-next builds on different bare-metal and virtual # platforms. It can be used for example, diff --git a/kernel/configs/kvm_guest.config b/kernel/configs/kvm_guest.config index 208481d9109030403357b36ada51074f14f37293..d0877063d925cd6db3136c9efd175669c1317131 100644 --- a/kernel/configs/kvm_guest.config +++ b/kernel/configs/kvm_guest.config @@ -1,3 +1,4 @@ +# Help: Bootable as a KVM guest CONFIG_NET=y CONFIG_NET_CORE=y CONFIG_NETDEVICES=y diff --git a/kernel/configs/nopm.config b/kernel/configs/nopm.config index 81ff078635769ef5ee6dd09ca9a1abe1696df639..ebfdc3d8aa9add0d5e90ed373b58e2cbc8cce4ec 100644 --- a/kernel/configs/nopm.config +++ b/kernel/configs/nopm.config @@ -1,3 +1,5 @@ +# Help: Disable Power Management + CONFIG_PM=n CONFIG_SUSPEND=n CONFIG_HIBERNATION=n diff --git a/kernel/configs/rust.config b/kernel/configs/rust.config index 38a7c5362c9cf1f8b5956e673de7c90359bf7503..2c6e001a728497e53174b3b3adc6282219f128d0 100644 --- a/kernel/configs/rust.config +++ b/kernel/configs/rust.config @@ -1 +1,2 @@ +# Help: Enable Rust CONFIG_RUST=y diff --git a/kernel/configs/x86_debug.config b/kernel/configs/x86_debug.config index 6fac5b405334e199148a41a3320809b8fa7423eb..35f48671b8d5c2cde361b3129ee1e56051663781 100644 --- a/kernel/configs/x86_debug.config +++ b/kernel/configs/x86_debug.config @@ -1,3 +1,4 @@ +# Help: Debugging options for tip tree testing CONFIG_X86_DEBUG_FPU=y CONFIG_LOCK_STAT=y CONFIG_DEBUG_VM=y diff --git a/kernel/configs/xen.config b/kernel/configs/xen.config index 436f806aa1ed0d31714c90f9bfe027adaf16ca7b..6878b9a49be8d442f01c5ffb28e27bf413d28fd0 100644 --- a/kernel/configs/xen.config +++ b/kernel/configs/xen.config @@ -1,3 +1,5 @@ +# Help: Bootable as a Xen guest +# # global stuff - these enable us to allow some # of the not so generic stuff below for xen CONFIG_PARAVIRT=y diff --git a/kernel/cpu.c b/kernel/cpu.c index f6811c857102329c4f543f496a31e647b886ebb3..6de7c6bb74eeea98c217374cb5bed57c7b38a452 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1487,8 +1487,22 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen, return ret; } +struct cpu_down_work { + unsigned int cpu; + enum cpuhp_state target; +}; + +static long __cpu_down_maps_locked(void *arg) +{ + struct cpu_down_work *work = arg; + + return _cpu_down(work->cpu, 0, work->target); +} + static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target) { + struct cpu_down_work work = { .cpu = cpu, .target = target, }; + /* * If the platform does not support hotplug, report it explicitly to * differentiate it from a transient offlining failure. @@ -1497,7 +1511,15 @@ static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target) return -EOPNOTSUPP; if (cpu_hotplug_disabled) return -EBUSY; - return _cpu_down(cpu, 0, target); + + /* + * Ensure that the control task does not run on the to be offlined + * CPU to prevent a deadlock against cfs_b->period_timer. + */ + cpu = cpumask_any_but(cpu_online_mask, cpu); + if (cpu >= nr_cpu_ids) + return -EBUSY; + return work_on_cpu(cpu, __cpu_down_maps_locked, &work); } static int cpu_down(unsigned int cpu, enum cpuhp_state target) diff --git a/kernel/crash_core.c b/kernel/crash_core.c index 03a7932cde0a518939f8325fb450c91391b08891..2f675ef045d40db9704ad940663847db42bdf196 100644 --- a/kernel/crash_core.c +++ b/kernel/crash_core.c @@ -739,6 +739,17 @@ subsys_initcall(crash_notes_memory_init); #undef pr_fmt #define pr_fmt(fmt) "crash hp: " fmt +/* + * Different than kexec/kdump loading/unloading/jumping/shrinking which + * usually rarely happen, there will be many crash hotplug events notified + * during one short period, e.g one memory board is hot added and memory + * regions are online. So mutex lock __crash_hotplug_lock is used to + * serialize the crash hotplug handling specifically. + */ +DEFINE_MUTEX(__crash_hotplug_lock); +#define crash_hotplug_lock() mutex_lock(&__crash_hotplug_lock) +#define crash_hotplug_unlock() mutex_unlock(&__crash_hotplug_lock) + /* * This routine utilized when the crash_hotplug sysfs node is read. * It reflects the kernel's ability/permission to update the crash @@ -748,9 +759,11 @@ int crash_check_update_elfcorehdr(void) { int rc = 0; + crash_hotplug_lock(); /* Obtain lock while reading crash information */ if (!kexec_trylock()) { pr_info("kexec_trylock() failed, elfcorehdr may be inaccurate\n"); + crash_hotplug_unlock(); return 0; } if (kexec_crash_image) { @@ -761,6 +774,7 @@ int crash_check_update_elfcorehdr(void) } /* Release lock now that update complete */ kexec_unlock(); + crash_hotplug_unlock(); return rc; } @@ -783,9 +797,11 @@ static void crash_handle_hotplug_event(unsigned int hp_action, unsigned int cpu) { struct kimage *image; + crash_hotplug_lock(); /* Obtain lock while changing crash information */ if (!kexec_trylock()) { pr_info("kexec_trylock() failed, elfcorehdr may be inaccurate\n"); + crash_hotplug_unlock(); return; } @@ -852,6 +868,7 @@ static void crash_handle_hotplug_event(unsigned int hp_action, unsigned int cpu) out: /* Release lock now that update complete */ kexec_unlock(); + crash_hotplug_unlock(); } static int crash_memhp_notifier(struct notifier_block *nb, unsigned long val, void *v) diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 813cb6cf72d6b59bcf73edf75c761334e0b7fd73..9443bc63c5a245548a1390ee85dd096091649607 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -590,6 +590,8 @@ static void kdb_msg_write(const char *msg, int msg_len) continue; if (c == dbg_io_ops->cons) continue; + if (!c->write) + continue; /* * Set oops_in_progress to encourage the console drivers to * disregard their internal spin locks: in the current calling diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig index 4c1e9a3c0ab6f0a779ed69dfa88c5369ec462f42..f488997b0717126b40b59e91a7d0cd1d8b4f94bf 100644 --- a/kernel/dma/Kconfig +++ b/kernel/dma/Kconfig @@ -160,7 +160,7 @@ if DMA_CMA config DMA_NUMA_CMA bool "Enable separate DMA Contiguous Memory Area for NUMA Node" - default NUMA + depends on NUMA help Enable this option to get numa CMA areas so that NUMA devices can get local memory by DMA coherent APIs. diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c index 88c595e49e3452b0a252c40af595c9b2ff39b293..f005c66f378c32e9bae2d84b0ca80d1c126f0c15 100644 --- a/kernel/dma/contiguous.c +++ b/kernel/dma/contiguous.c @@ -473,11 +473,6 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem) return -EBUSY; } - if (memblock_is_region_reserved(rmem->base, rmem->size)) { - pr_info("Reserved memory: overlap with other memblock reserved region\n"); - return -EBUSY; - } - if (!of_get_flat_dt_prop(node, "reusable", NULL) || of_get_flat_dt_prop(node, "no-map", NULL)) return -EINVAL; diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index f190651bcaddc9e9f0df4e5d88ab282aafd75aca..06366acd27b08224d7abeb04f0aa8e04bc771574 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -637,15 +637,19 @@ static struct dma_debug_entry *__dma_entry_alloc(void) return entry; } -static void __dma_entry_alloc_check_leak(void) +/* + * This should be called outside of free_entries_lock scope to avoid potential + * deadlocks with serial consoles that use DMA. + */ +static void __dma_entry_alloc_check_leak(u32 nr_entries) { - u32 tmp = nr_total_entries % nr_prealloc_entries; + u32 tmp = nr_entries % nr_prealloc_entries; /* Shout each time we tick over some multiple of the initial pool */ if (tmp < DMA_DEBUG_DYNAMIC_ENTRIES) { pr_info("dma_debug_entry pool grown to %u (%u00%%)\n", - nr_total_entries, - (nr_total_entries / nr_prealloc_entries)); + nr_entries, + (nr_entries / nr_prealloc_entries)); } } @@ -656,8 +660,10 @@ static void __dma_entry_alloc_check_leak(void) */ static struct dma_debug_entry *dma_entry_alloc(void) { + bool alloc_check_leak = false; struct dma_debug_entry *entry; unsigned long flags; + u32 nr_entries; spin_lock_irqsave(&free_entries_lock, flags); if (num_free_entries == 0) { @@ -667,13 +673,17 @@ static struct dma_debug_entry *dma_entry_alloc(void) pr_err("debugging out of memory - disabling\n"); return NULL; } - __dma_entry_alloc_check_leak(); + alloc_check_leak = true; + nr_entries = nr_total_entries; } entry = __dma_entry_alloc(); spin_unlock_irqrestore(&free_entries_lock, flags); + if (alloc_check_leak) + __dma_entry_alloc_check_leak(nr_entries); + #ifdef CONFIG_STACKTRACE entry->stack_len = stack_trace_save(entry->stack_entries, ARRAY_SIZE(entry->stack_entries), diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c index 1acec2e228273f3be269bed0aa84ae5156496265..b481c48a31a630284f9c32d94213a210ea792334 100644 --- a/kernel/dma/pool.c +++ b/kernel/dma/pool.c @@ -135,9 +135,9 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size, remove_mapping: #ifdef CONFIG_DMA_DIRECT_REMAP dma_common_free_remap(addr, pool_size); -#endif -free_page: __maybe_unused +free_page: __free_pages(page, order); +#endif out: return ret; } diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c index 394494a6b1f35cc7038a2c3ffa00d3984eedd334..01637677736f4a9007ba93daf38daf089280141c 100644 --- a/kernel/dma/swiotlb.c +++ b/kernel/dma/swiotlb.c @@ -399,14 +399,13 @@ void __init swiotlb_init_remap(bool addressing_limit, unsigned int flags, } mem->areas = memblock_alloc(array_size(sizeof(struct io_tlb_area), - default_nareas), SMP_CACHE_BYTES); + nareas), SMP_CACHE_BYTES); if (!mem->areas) { pr_warn("%s: Failed to allocate mem->areas.\n", __func__); return; } - swiotlb_init_io_tlb_pool(mem, __pa(tlb), nslabs, false, - default_nareas); + swiotlb_init_io_tlb_pool(mem, __pa(tlb), nslabs, false, nareas); add_mem_pool(&io_tlb_default_mem, mem); if (flags & SWIOTLB_VERBOSE) @@ -729,9 +728,6 @@ static void swiotlb_dyn_alloc(struct work_struct *work) } add_mem_pool(mem, pool); - - /* Pairs with smp_rmb() in is_swiotlb_buffer(). */ - smp_wmb(); } /** @@ -1152,9 +1148,26 @@ static int swiotlb_find_slots(struct device *dev, phys_addr_t orig_addr, spin_unlock_irqrestore(&dev->dma_io_tlb_lock, flags); found: - dev->dma_uses_io_tlb = true; - /* Pairs with smp_rmb() in is_swiotlb_buffer() */ - smp_wmb(); + WRITE_ONCE(dev->dma_uses_io_tlb, true); + + /* + * The general barrier orders reads and writes against a presumed store + * of the SWIOTLB buffer address by a device driver (to a driver private + * data structure). It serves two purposes. + * + * First, the store to dev->dma_uses_io_tlb must be ordered before the + * presumed store. This guarantees that the returned buffer address + * cannot be passed to another CPU before updating dev->dma_uses_io_tlb. + * + * Second, the load from mem->pools must be ordered before the same + * presumed store. This guarantees that the returned buffer address + * cannot be observed by another CPU before an update of the RCU list + * that was made by swiotlb_dyn_alloc() on a third CPU (cf. multicopy + * atomicity). + * + * See also the comment in is_swiotlb_buffer(). + */ + smp_mb(); *retpool = pool; return index; diff --git a/kernel/fork.c b/kernel/fork.c index a9c18d480dc5906b9536173d61b825757f820638..3b6d20dfb9a85ea3305a892d20316524a2c70bb0 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -909,8 +909,6 @@ static void cleanup_lazy_tlbs(struct mm_struct *mm) */ void __mmdrop(struct mm_struct *mm) { - int i; - BUG_ON(mm == &init_mm); WARN_ON_ONCE(mm == current->mm); @@ -925,9 +923,8 @@ void __mmdrop(struct mm_struct *mm) put_user_ns(mm->user_ns); mm_pasid_drop(mm); mm_destroy_cid(mm); + percpu_counter_destroy_many(mm->rss_stat, NR_MM_COUNTERS); - for (i = 0; i < NR_MM_COUNTERS; i++) - percpu_counter_destroy(&mm->rss_stat[i]); free_mm(mm); } EXPORT_SYMBOL_GPL(__mmdrop); @@ -1260,8 +1257,6 @@ static void mm_init_uprobes_state(struct mm_struct *mm) static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, struct user_namespace *user_ns) { - int i; - mt_init_flags(&mm->mm_mt, MM_MT_FLAGS); mt_set_external_lock(&mm->mm_mt, &mm->mmap_lock); atomic_set(&mm->mm_users, 1); @@ -1309,17 +1304,15 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p, if (mm_alloc_cid(mm)) goto fail_cid; - for (i = 0; i < NR_MM_COUNTERS; i++) - if (percpu_counter_init(&mm->rss_stat[i], 0, GFP_KERNEL_ACCOUNT)) - goto fail_pcpu; + if (percpu_counter_init_many(mm->rss_stat, 0, GFP_KERNEL_ACCOUNT, + NR_MM_COUNTERS)) + goto fail_pcpu; mm->user_ns = get_user_ns(user_ns); lru_gen_init_mm(mm); return mm; fail_pcpu: - while (i > 0) - percpu_counter_destroy(&mm->rss_stat[--i]); mm_destroy_cid(mm); fail_cid: destroy_context(mm); diff --git a/kernel/kprobes.c b/kernel/kprobes.c index ca385b61d546722161f3c720fdf3198f4e9445d1..0c6185aefaef576c55fb324e6d30a8e2c0050d1f 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -2232,8 +2232,7 @@ int register_kretprobe(struct kretprobe *rp) return -ENOMEM; for (i = 0; i < rp->maxactive; i++) { - inst = kzalloc(sizeof(struct kretprobe_instance) + - rp->data_size, GFP_KERNEL); + inst = kzalloc(struct_size(inst, data, rp->data_size), GFP_KERNEL); if (inst == NULL) { rethook_free(rp->rh); rp->rh = NULL; @@ -2256,8 +2255,7 @@ int register_kretprobe(struct kretprobe *rp) rp->rph->rp = rp; for (i = 0; i < rp->maxactive; i++) { - inst = kzalloc(sizeof(struct kretprobe_instance) + - rp->data_size, GFP_KERNEL); + inst = kzalloc(struct_size(inst, data, rp->data_size), GFP_KERNEL); if (inst == NULL) { refcount_set(&rp->rph->ref, i); free_rp_inst(rp); diff --git a/kernel/panic.c b/kernel/panic.c index 07239d4ad81e87e1de4477f8196e8ffcd105161b..ffa037fa777d5f7f1cb8596893fe90c3fb0a2f46 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -697,6 +697,7 @@ void warn_slowpath_fmt(const char *file, int line, unsigned taint, if (!fmt) { __warn(file, line, __builtin_return_address(0), taint, NULL, NULL); + warn_rcu_exit(rcu); return; } diff --git a/kernel/pid.c b/kernel/pid.c index fee14a4486a310ee650e1249b3a85d0719f9a7a7..6500ef956f2f885793833e160891b4f441acd104 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -609,7 +609,7 @@ int pidfd_create(struct pid *pid, unsigned int flags) } /** - * pidfd_open() - Open new pid file descriptor. + * sys_pidfd_open() - Open new pid file descriptor. * * @pid: pid for which to retrieve a pidfd * @flags: flags to pass diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 2b4a946a6ff5cfcae69fd9e03ee1d5cc0728736e..8d35b9f9aaa3f203e3f2bb103c2cce72fbc44157 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -786,9 +786,9 @@ int hibernate(void) unlock_device_hotplug(); if (snapshot_test) { pm_pr_dbg("Checking hibernation image\n"); - error = swsusp_check(snapshot_test); + error = swsusp_check(false); if (!error) - error = load_image_and_restore(snapshot_test); + error = load_image_and_restore(false); } thaw_processes(); @@ -945,14 +945,14 @@ static int software_resume(void) pm_pr_dbg("Looking for hibernation image.\n"); mutex_lock(&system_transition_mutex); - error = swsusp_check(false); + error = swsusp_check(true); if (error) goto Unlock; /* The snapshot device should not be opened while we're running */ if (!hibernate_acquire()) { error = -EBUSY; - swsusp_close(false); + swsusp_close(true); goto Unlock; } @@ -973,7 +973,7 @@ static int software_resume(void) goto Close_Finish; } - error = load_image_and_restore(false); + error = load_image_and_restore(true); thaw_processes(); Finish: pm_notifier_call_chain(PM_POST_RESTORE); @@ -987,7 +987,7 @@ static int software_resume(void) pm_pr_dbg("Hibernation image not present or could not be loaded.\n"); return error; Close_Finish: - swsusp_close(false); + swsusp_close(true); goto Finish; } diff --git a/kernel/power/power.h b/kernel/power/power.h index 46eb14dc50c31ddef2fb29b4fe9a925002e01c18..a98f95e309a338c5a895ebbdff15f21f2ba730d9 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -168,11 +168,11 @@ extern int swsusp_swap_in_use(void); #define SF_HW_SIG 8 /* kernel/power/hibernate.c */ -int swsusp_check(bool snapshot_test); +int swsusp_check(bool exclusive); extern void swsusp_free(void); extern int swsusp_read(unsigned int *flags_p); extern int swsusp_write(unsigned int flags); -void swsusp_close(bool snapshot_test); +void swsusp_close(bool exclusive); #ifdef CONFIG_SUSPEND extern int swsusp_unmark(void); #endif diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 87e9f7e2bdc05787ab346d2b7a91124692ab5ca2..0f12e0a97e432e312f971bb9dae07639ffe1428b 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -2647,7 +2647,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm, memory_bm_free(bm, PG_UNSAFE_KEEP); /* Make a copy of zero_bm so it can be created in safe pages */ - error = memory_bm_create(&tmp, GFP_ATOMIC, PG_ANY); + error = memory_bm_create(&tmp, GFP_ATOMIC, PG_SAFE); if (error) goto Free; @@ -2660,7 +2660,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm, goto Free; duplicate_memory_bitmap(zero_bm, &tmp); - memory_bm_free(&tmp, PG_UNSAFE_KEEP); + memory_bm_free(&tmp, PG_UNSAFE_CLEAR); /* At this point zero_bm is in safe pages and it can be used for restoring. */ if (nr_highmem > 0) { diff --git a/kernel/power/swap.c b/kernel/power/swap.c index f6ebcd00c4100ba1c722501d06092a653e513a8c..74edbce2320bae543c522b97301ec9719725cfa1 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -1513,12 +1513,13 @@ int swsusp_read(unsigned int *flags_p) static void *swsusp_holder; /** - * swsusp_check - Check for swsusp signature in the resume device + * swsusp_check - Check for swsusp signature in the resume device + * @exclusive: Open the resume device exclusively. */ -int swsusp_check(bool snapshot_test) +int swsusp_check(bool exclusive) { - void *holder = snapshot_test ? &swsusp_holder : NULL; + void *holder = exclusive ? &swsusp_holder : NULL; int error; hib_resume_bdev = blkdev_get_by_dev(swsusp_resume_device, BLK_OPEN_READ, @@ -1563,17 +1564,18 @@ int swsusp_check(bool snapshot_test) } /** - * swsusp_close - close swap device. + * swsusp_close - close swap device. + * @exclusive: Close the resume device which is exclusively opened. */ -void swsusp_close(bool snapshot_test) +void swsusp_close(bool exclusive) { if (IS_ERR(hib_resume_bdev)) { pr_debug("Image device not initialised\n"); return; } - blkdev_put(hib_resume_bdev, snapshot_test ? &swsusp_holder : NULL); + blkdev_put(hib_resume_bdev, exclusive ? &swsusp_holder : NULL); } /** diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h index 2a17704136f1dbbb3394a73813a4de40873d684b..7d4979d5c3ce6f1ded693de5f44df1db12e7d389 100644 --- a/kernel/printk/internal.h +++ b/kernel/printk/internal.h @@ -103,3 +103,5 @@ struct printk_message { u64 seq; unsigned long dropped; }; + +bool other_cpu_in_panic(void); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 357a4d18f6387cc5a626346dea2d3518fc2e4c86..0b3af1529778c0ffe58675af216faeba434aba5e 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -88,7 +88,7 @@ EXPORT_SYMBOL(oops_in_progress); static DEFINE_MUTEX(console_mutex); /* - * console_sem protects updates to console->seq and console_suspended, + * console_sem protects updates to console->seq * and also provides serialization for console printing. */ static DEFINE_SEMAPHORE(console_sem, 1); @@ -361,7 +361,7 @@ static bool panic_in_progress(void) * paths in the console code where we end up in places I want * locked without the console semaphore held). */ -static int console_locked, console_suspended; +static int console_locked; /* * Array of consoles built from command line options (console=) @@ -2308,7 +2308,11 @@ asmlinkage int vprintk_emit(int facility, int level, preempt_enable(); } - wake_up_klogd(); + if (in_sched) + defer_console_output(); + else + wake_up_klogd(); + return printed_len; } EXPORT_SYMBOL(vprintk_emit); @@ -2547,22 +2551,46 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig */ void suspend_console(void) { + struct console *con; + if (!console_suspend_enabled) return; pr_info("Suspending console(s) (use no_console_suspend to debug)\n"); pr_flush(1000, true); - console_lock(); - console_suspended = 1; - up_console_sem(); + + console_list_lock(); + for_each_console(con) + console_srcu_write_flags(con, con->flags | CON_SUSPENDED); + console_list_unlock(); + + /* + * Ensure that all SRCU list walks have completed. All printing + * contexts must be able to see that they are suspended so that it + * is guaranteed that all printing has stopped when this function + * completes. + */ + synchronize_srcu(&console_srcu); } void resume_console(void) { + struct console *con; + if (!console_suspend_enabled) return; - down_console_sem(); - console_suspended = 0; - console_unlock(); + + console_list_lock(); + for_each_console(con) + console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED); + console_list_unlock(); + + /* + * Ensure that all SRCU list walks have completed. All printing + * contexts must be able to see they are no longer suspended so + * that they are guaranteed to wake up and resume printing. + */ + synchronize_srcu(&console_srcu); + pr_flush(1000, true); } @@ -2585,6 +2613,26 @@ static int console_cpu_notify(unsigned int cpu) return 0; } +/* + * Return true if a panic is in progress on a remote CPU. + * + * On true, the local CPU should immediately release any printing resources + * that may be needed by the panic CPU. + */ +bool other_cpu_in_panic(void) +{ + if (!panic_in_progress()) + return false; + + /* + * We can use raw_smp_processor_id() here because it is impossible for + * the task to be migrated to the panic_cpu, or away from it. If + * panic_cpu has already been set, and we're not currently executing on + * that CPU, then we never will be. + */ + return atomic_read(&panic_cpu) != raw_smp_processor_id(); +} + /** * console_lock - block the console subsystem from printing * @@ -2597,9 +2645,11 @@ void console_lock(void) { might_sleep(); + /* On panic, the console_lock must be left to the panic cpu. */ + while (other_cpu_in_panic()) + msleep(1000); + down_console_sem(); - if (console_suspended) - return; console_locked = 1; console_may_schedule = 1; } @@ -2615,12 +2665,11 @@ EXPORT_SYMBOL(console_lock); */ int console_trylock(void) { - if (down_trylock_console_sem()) + /* On panic, the console_lock must be left to the panic cpu. */ + if (other_cpu_in_panic()) return 0; - if (console_suspended) { - up_console_sem(); + if (down_trylock_console_sem()) return 0; - } console_locked = 1; console_may_schedule = 0; return 1; @@ -2633,25 +2682,6 @@ int is_console_locked(void) } EXPORT_SYMBOL(is_console_locked); -/* - * Return true when this CPU should unlock console_sem without pushing all - * messages to the console. This reduces the chance that the console is - * locked when the panic CPU tries to use it. - */ -static bool abandon_console_lock_in_panic(void) -{ - if (!panic_in_progress()) - return false; - - /* - * We can use raw_smp_processor_id() here because it is impossible for - * the task to be migrated to the panic_cpu, or away from it. If - * panic_cpu has already been set, and we're not currently executing on - * that CPU, then we never will be. - */ - return atomic_read(&panic_cpu) != raw_smp_processor_id(); -} - /* * Check if the given console is currently capable and allowed to print * records. @@ -2665,6 +2695,9 @@ static inline bool console_is_usable(struct console *con) if (!(flags & CON_ENABLED)) return false; + if ((flags & CON_SUSPENDED)) + return false; + if (!con->write) return false; @@ -2948,7 +2981,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove any_progress = true; /* Allow panic_cpu to take over the consoles safely. */ - if (abandon_console_lock_in_panic()) + if (other_cpu_in_panic()) goto abandon; if (do_cond_resched) @@ -2983,11 +3016,6 @@ void console_unlock(void) bool flushed; u64 next_seq; - if (console_suspended) { - up_console_sem(); - return; - } - /* * Console drivers are called with interrupts disabled, so * @console_may_schedule should be cleared before; however, we may @@ -3045,9 +3073,27 @@ EXPORT_SYMBOL(console_conditional_schedule); void console_unblank(void) { + bool found_unblank = false; struct console *c; int cookie; + /* + * First check if there are any consoles implementing the unblank() + * callback. If not, there is no reason to continue and take the + * console lock, which in particular can be dangerous if + * @oops_in_progress is set. + */ + cookie = console_srcu_read_lock(); + for_each_console_srcu(c) { + if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank) { + found_unblank = true; + break; + } + } + console_srcu_read_unlock(cookie); + if (!found_unblank) + return; + /* * Stop console printing because the unblank() callback may * assume the console is not within its write() callback. @@ -3056,6 +3102,16 @@ void console_unblank(void) * In that case, attempt a trylock as best-effort. */ if (oops_in_progress) { + /* Semaphores are not NMI-safe. */ + if (in_nmi()) + return; + + /* + * Attempting to trylock the console lock can deadlock + * if another CPU was stopped while modifying the + * semaphore. "Hope and pray" that this is not the + * current situation. + */ if (down_trylock_console_sem() != 0) return; } else @@ -3085,14 +3141,24 @@ void console_unblank(void) */ void console_flush_on_panic(enum con_flush_mode mode) { + bool handover; + u64 next_seq; + + /* + * Ignore the console lock and flush out the messages. Attempting a + * trylock would not be useful because: + * + * - if it is contended, it must be ignored anyway + * - console_lock() and console_trylock() block and fail + * respectively in panic for non-panic CPUs + * - semaphores are not NMI-safe + */ + /* - * If someone else is holding the console lock, trylock will fail - * and may_schedule may be set. Ignore and proceed to unlock so - * that messages are flushed out. As this can be called from any - * context and we don't want to get preempted while flushing, - * ensure may_schedule is cleared. + * If another context is holding the console lock, + * @console_may_schedule might be set. Clear it so that + * this context does not call cond_resched() while flushing. */ - console_trylock(); console_may_schedule = 0; if (mode == CONSOLE_REPLAY_ALL) { @@ -3105,15 +3171,15 @@ void console_flush_on_panic(enum con_flush_mode mode) cookie = console_srcu_read_lock(); for_each_console_srcu(c) { /* - * If the above console_trylock() failed, this is an - * unsynchronized assignment. But in that case, the + * This is an unsynchronized assignment, but the * kernel is in "hope and pray" mode anyway. */ c->seq = seq; } console_srcu_read_unlock(cookie); } - console_unlock(); + + console_flush_all(false, &next_seq, &handover); } /* @@ -3674,13 +3740,18 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre seq = prb_next_seq(prb); + /* Flush the consoles so that records up to @seq are printed. */ + console_lock(); + console_unlock(); + for (;;) { diff = 0; /* * Hold the console_lock to guarantee safe access to - * console->seq and to prevent changes to @console_suspended - * until all consoles have been processed. + * console->seq. Releasing console_lock flushes more + * records in case @seq is still not printed on all + * usable consoles. */ console_lock(); @@ -3688,6 +3759,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre for_each_console_srcu(c) { if (con && con != c) continue; + /* + * If consoles are not usable, it cannot be expected + * that they make forward progress, so only increment + * @diff for usable consoles. + */ if (!console_is_usable(c)) continue; printk_seq = c->seq; @@ -3696,18 +3772,12 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre } console_srcu_read_unlock(cookie); - /* - * If consoles are suspended, it cannot be expected that they - * make forward progress, so timeout immediately. @diff is - * still used to return a valid flush status. - */ - if (console_suspended) - remaining = 0; - else if (diff != last_diff && reset_on_progress) + if (diff != last_diff && reset_on_progress) remaining = timeout_ms; console_unlock(); + /* Note: @diff is 0 if there are no usable consoles. */ if (diff == 0 || remaining == 0) break; @@ -3741,7 +3811,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre * printer has been seen to make some forward progress. * * Context: Process context. May sleep while acquiring console lock. - * Return: true if all enabled printers are caught up. + * Return: true if all usable printers are caught up. */ static bool pr_flush(int timeout_ms, bool reset_on_progress) { @@ -3798,11 +3868,33 @@ static void __wake_up_klogd(int val) preempt_enable(); } +/** + * wake_up_klogd - Wake kernel logging daemon + * + * Use this function when new records have been added to the ringbuffer + * and the console printing of those records has already occurred or is + * known to be handled by some other context. This function will only + * wake the logging daemon. + * + * Context: Any context. + */ void wake_up_klogd(void) { __wake_up_klogd(PRINTK_PENDING_WAKEUP); } +/** + * defer_console_output - Wake kernel logging daemon and trigger + * console printing in a deferred context + * + * Use this function when new records have been added to the ringbuffer, + * this context is responsible for console printing those records, but + * the current context is not allowed to perform the console printing. + * Trigger an irq_work context to perform the console printing. This + * function also wakes the logging daemon. + * + * Context: Any context. + */ void defer_console_output(void) { /* @@ -3819,12 +3911,7 @@ void printk_trigger_flush(void) int vprintk_deferred(const char *fmt, va_list args) { - int r; - - r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args); - defer_console_output(); - - return r; + return vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args); } int _printk_deferred(const char *fmt, ...) diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c index 2dc4d5a1f1ff824031a25c8d0cb5dd68925d6b14..fde338606ce83c1a013a270eac64dda039d4e677 100644 --- a/kernel/printk/printk_ringbuffer.c +++ b/kernel/printk/printk_ringbuffer.c @@ -1735,7 +1735,7 @@ static bool copy_data(struct prb_data_ring *data_ring, if (!buf || !buf_size) return true; - data_size = min_t(u16, buf_size, len); + data_size = min_t(unsigned int, buf_size, len); memcpy(&buf[0], data, data_size); /* LMM(copy_data:A) */ return true; diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c index ef0f9a2044da10739e4ec865ab35e97db9c02d5f..6d10927a07d835043d1fe9f383053b9254691742 100644 --- a/kernel/printk/printk_safe.c +++ b/kernel/printk/printk_safe.c @@ -38,13 +38,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) * Use the main logbuf even in NMI. But avoid calling console * drivers that might have their own locks. */ - if (this_cpu_read(printk_context) || in_nmi()) { - int len; - - len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args); - defer_console_output(); - return len; - } + if (this_cpu_read(printk_context) || in_nmi()) + return vprintk_deferred(fmt, args); /* No obstacles. */ return vprintk_default(fmt, args); diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 2299a5cfbfb9ed250695edb75d977edbe61eaf43..802551e0009bf1ef66191441a802633bb57543bc 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -9269,7 +9269,7 @@ void __init init_idle(struct task_struct *idle, int cpu) * PF_KTHREAD should already be set at this point; regardless, make it * look like a proper per-CPU kthread. */ - idle->flags |= PF_IDLE | PF_KTHREAD | PF_NO_SETAFFINITY; + idle->flags |= PF_KTHREAD | PF_NO_SETAFFINITY; kthread_set_per_cpu(idle, cpu); #ifdef CONFIG_SMP diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 4492608b7d7f1c715f46ae462fd760e074066ced..458d359f5991ca7977fb655da4cbb1f71b53bebc 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -350,7 +350,8 @@ static void sugov_update_single_freq(struct update_util_data *hook, u64 time, * Except when the rq is capped by uclamp_max. */ if (!uclamp_rq_is_capped(cpu_rq(sg_cpu->cpu)) && - sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq) { + sugov_cpu_is_busy(sg_cpu) && next_f < sg_policy->next_freq && + !sg_policy->need_freq_update) { next_f = sg_policy->next_freq; /* Restore cached freq as next_freq has changed */ diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c index a286e726eb4b857d12f2a03b2cadfa07d0df9e52..42c40cfdf83630be349949e5498bd16522405461 100644 --- a/kernel/sched/cpupri.c +++ b/kernel/sched/cpupri.c @@ -101,6 +101,7 @@ static inline int __cpupri_find(struct cpupri *cp, struct task_struct *p, if (lowest_mask) { cpumask_and(lowest_mask, &p->cpus_mask, vec->mask); + cpumask_and(lowest_mask, lowest_mask, cpu_active_mask); /* * We have to ensure that we have at least one bit diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 911d0063763cbdbfef36eb9dd6bafc552343efa1..ef7490c4b8b40ded1c02a709bdc684a24ae7a69b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -664,6 +664,10 @@ void avg_vruntime_update(struct cfs_rq *cfs_rq, s64 delta) cfs_rq->avg_vruntime -= cfs_rq->avg_load * delta; } +/* + * Specifically: avg_runtime() + 0 must result in entity_eligible() := true + * For this to be so, the result of this function must have a left bias. + */ u64 avg_vruntime(struct cfs_rq *cfs_rq) { struct sched_entity *curr = cfs_rq->curr; @@ -677,8 +681,12 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq) load += weight; } - if (load) + if (load) { + /* sign flips effective floor / ceil */ + if (avg < 0) + avg -= (load - 1); avg = div_s64(avg, load); + } return cfs_rq->min_vruntime + avg; } @@ -699,7 +707,7 @@ u64 avg_vruntime(struct cfs_rq *cfs_rq) * * XXX could add max_slice to the augmented data to track this. */ -void update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) +static void update_entity_lag(struct cfs_rq *cfs_rq, struct sched_entity *se) { s64 lag, limit; @@ -4919,10 +4927,12 @@ static inline void update_misfit_status(struct task_struct *p, struct rq *rq) {} static void place_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) { - u64 vslice = calc_delta_fair(se->slice, se); - u64 vruntime = avg_vruntime(cfs_rq); + u64 vslice, vruntime = avg_vruntime(cfs_rq); s64 lag = 0; + se->slice = sysctl_sched_base_slice; + vslice = calc_delta_fair(se->slice, se); + /* * Due to how V is constructed as the weighted average of entities, * adding tasks with positive lag, or removing tasks with negative lag @@ -6619,6 +6629,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) /* Working cpumask for: load_balance, load_balance_newidle. */ static DEFINE_PER_CPU(cpumask_var_t, load_balance_mask); static DEFINE_PER_CPU(cpumask_var_t, select_rq_mask); +static DEFINE_PER_CPU(cpumask_var_t, should_we_balance_tmpmask); #ifdef CONFIG_NO_HZ_COMMON @@ -9579,7 +9590,7 @@ static inline long sibling_imbalance(struct lb_env *env, imbalance /= ncores_local + ncores_busiest; /* Take advantage of resource in an empty sched group */ - if (imbalance == 0 && local->sum_nr_running == 0 && + if (imbalance <= 1 && local->sum_nr_running == 0 && busiest->sum_nr_running > 1) imbalance = 2; @@ -9767,6 +9778,15 @@ static bool update_sd_pick_busiest(struct lb_env *env, break; case group_smt_balance: + /* + * Check if we have spare CPUs on either SMT group to + * choose has spare or fully busy handling. + */ + if (sgs->idle_cpus != 0 || busiest->idle_cpus != 0) + goto has_spare; + + fallthrough; + case group_fully_busy: /* * Select the fully busy group with highest avg_load. In @@ -9806,6 +9826,7 @@ static bool update_sd_pick_busiest(struct lb_env *env, else return true; } +has_spare: /* * Select not overloaded group with lowest number of idle cpus @@ -10917,6 +10938,7 @@ static int active_load_balance_cpu_stop(void *data); static int should_we_balance(struct lb_env *env) { + struct cpumask *swb_cpus = this_cpu_cpumask_var_ptr(should_we_balance_tmpmask); struct sched_group *sg = env->sd->groups; int cpu, idle_smt = -1; @@ -10940,8 +10962,9 @@ static int should_we_balance(struct lb_env *env) return 1; } + cpumask_copy(swb_cpus, group_balance_mask(sg)); /* Try to find first idle CPU */ - for_each_cpu_and(cpu, group_balance_mask(sg), env->cpus) { + for_each_cpu_and(cpu, swb_cpus, env->cpus) { if (!idle_cpu(cpu)) continue; @@ -10953,6 +10976,14 @@ static int should_we_balance(struct lb_env *env) if (!(env->sd->flags & SD_SHARE_CPUCAPACITY) && !is_core_idle(cpu)) { if (idle_smt == -1) idle_smt = cpu; + /* + * If the core is not idle, and first SMT sibling which is + * idle has been found, then its not needed to check other + * SMT siblings for idleness: + */ +#ifdef CONFIG_SCHED_SMT + cpumask_andnot(swb_cpus, swb_cpus, cpu_smt_mask(cpu)); +#endif continue; } @@ -12918,6 +12949,8 @@ __init void init_sched_fair_class(void) for_each_possible_cpu(i) { zalloc_cpumask_var_node(&per_cpu(load_balance_mask, i), GFP_KERNEL, cpu_to_node(i)); zalloc_cpumask_var_node(&per_cpu(select_rq_mask, i), GFP_KERNEL, cpu_to_node(i)); + zalloc_cpumask_var_node(&per_cpu(should_we_balance_tmpmask, i), + GFP_KERNEL, cpu_to_node(i)); #ifdef CONFIG_CFS_BANDWIDTH INIT_CSD(&cpu_rq(i)->cfsb_csd, __cfsb_csd_unthrottle, cpu_rq(i)); diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 342f58a329f528fcc6060208cfba9b7e908d3a98..5007b25c5bc653a9ca0696af1f7136150cd6131e 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -373,6 +373,7 @@ EXPORT_SYMBOL_GPL(play_idle_precise); void cpu_startup_entry(enum cpuhp_state state) { + current->flags |= PF_IDLE; arch_cpu_idle_prepare(); cpuhp_online_idle(state); while (1) diff --git a/kernel/task_work.c b/kernel/task_work.c index 065e1ef8fc8d72e55c66000b7b25f9ba0189db45..95a7e1b7f1dab2d3e5eef4b16e2cefe7f25f2f22 100644 --- a/kernel/task_work.c +++ b/kernel/task_work.c @@ -78,6 +78,7 @@ int task_work_add(struct task_struct *task, struct callback_head *work, * task_work_cancel_match - cancel a pending work added by task_work_add() * @task: the task which should execute the work * @match: match function to call + * @data: data to be passed in to match function * * RETURNS: * The found work or NULL if not found. diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 4df14db4da490d96f340ad4f49c2ea6b2029ddae..87015e9deacc99942486b533729ab470138cf375 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c @@ -1045,7 +1045,7 @@ static bool report_idle_softirq(void) return false; /* On RT, softirqs handling may be waiting on some lock */ - if (!local_bh_blocked()) + if (local_bh_blocked()) return false; pr_warn("NOHZ tick-stop error: local softirq work is pending, handler #%02x!!!\n", diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 64b61f67a403e249c04769c7931194b2132da078..057cd975d0146a38933364bfe834b9b536115e5d 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_KGDB_KDB) += trace_kdb.o endif obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o +obj-$(CONFIG_PROBE_EVENTS_BTF_ARGS) += trace_btf.o obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index a7264b2c17ad771d3bfc2a752da2339c157f4993..868008f56fec24fe7030d2fed9b063d1e1dd7926 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2853,6 +2853,17 @@ static int get_modules_for_addrs(struct module ***mods, unsigned long *addrs, u3 return arr.mods_cnt; } +static int addrs_check_error_injection_list(unsigned long *addrs, u32 cnt) +{ + u32 i; + + for (i = 0; i < cnt; i++) { + if (!within_error_injection_list(addrs[i])) + return -EINVAL; + } + return 0; +} + int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) { struct bpf_kprobe_multi_link *link = NULL; @@ -2930,6 +2941,11 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr goto error; } + if (prog->kprobe_override && addrs_check_error_injection_list(addrs, cnt)) { + err = -EINVAL; + goto error; + } + link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) { err = -ENOMEM; @@ -3207,8 +3223,10 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr rcu_read_lock(); task = get_pid_task(find_vpid(pid), PIDTYPE_PID); rcu_read_unlock(); - if (!task) + if (!task) { + err = -ESRCH; goto error_path_put; + } } err = -ENOMEM; diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 05c0024815bf98c8d35ce8411d4a6950f539a166..8de8bec5f366404a06941885cfeedfd41facfeba 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -6779,8 +6779,7 @@ void ftrace_release_mod(struct module *mod) last_pg = &ftrace_pages_start; for (pg = ftrace_pages_start; pg; pg = *last_pg) { rec = &pg->records[0]; - if (within_module_core(rec->ip, mod) || - within_module_init(rec->ip, mod)) { + if (within_module(rec->ip, mod)) { /* * As core pages are first, the first * page should never be a module page. @@ -6852,8 +6851,7 @@ void ftrace_module_enable(struct module *mod) * not part of this module, then skip this pg, * which the "break" will do. */ - if (!within_module_core(rec->ip, mod) && - !within_module_init(rec->ip, mod)) + if (!within_module(rec->ip, mod)) break; /* Weak functions should still be ignored */ @@ -7142,9 +7140,7 @@ void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) struct dyn_ftrace key; struct ftrace_mod_map *mod_map = NULL; struct ftrace_init_func *func, *func_next; - struct list_head clear_hash; - - INIT_LIST_HEAD(&clear_hash); + LIST_HEAD(clear_hash); key.ip = start; key.flags = end; /* overload flags, as it is unsigned long */ diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 52dea5dd5362ec5e83d3708b4c5d0e83561684cd..515cafdb18d98a0f6846f078847bead4c8ecd87d 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -354,6 +354,11 @@ static void rb_init_page(struct buffer_data_page *bpage) local_set(&bpage->commit, 0); } +static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage) +{ + return local_read(&bpage->page->commit); +} + static void free_buffer_page(struct buffer_page *bpage) { free_page((unsigned long)bpage->page); @@ -692,10 +697,7 @@ static void rb_time_set(rb_time_t *t, u64 val) static inline bool rb_time_read_cmpxchg(local_t *l, unsigned long expect, unsigned long set) { - unsigned long ret; - - ret = local_cmpxchg(l, expect, set); - return ret == expect; + return local_try_cmpxchg(l, &expect, set); } static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) @@ -752,9 +754,7 @@ static void rb_time_set(rb_time_t *t, u64 val) static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set) { - u64 val; - val = local64_cmpxchg(&t->time, expect, set); - return val == expect; + return local64_try_cmpxchg(&t->time, &expect, set); } #endif @@ -1137,6 +1137,9 @@ __poll_t ring_buffer_poll_wait(struct trace_buffer *buffer, int cpu, if (full) { poll_wait(filp, &work->full_waiters, poll_table); work->full_waiters_pending = true; + if (!cpu_buffer->shortest_full || + cpu_buffer->shortest_full > full) + cpu_buffer->shortest_full = full; } else { poll_wait(filp, &work->waiters, poll_table); work->waiters_pending = true; @@ -1494,14 +1497,11 @@ static bool rb_head_page_replace(struct buffer_page *old, { unsigned long *ptr = (unsigned long *)&old->list.prev->next; unsigned long val; - unsigned long ret; val = *ptr & ~RB_FLAG_MASK; val |= RB_PAGE_HEAD; - ret = cmpxchg(ptr, val, (unsigned long)&new->list); - - return ret == val; + return try_cmpxchg(ptr, &val, (unsigned long)&new->list); } /* @@ -2011,7 +2011,7 @@ rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned long nr_pages) * Increment overrun to account for the lost events. */ local_add(page_entries, &cpu_buffer->overrun); - local_sub(BUF_PAGE_SIZE, &cpu_buffer->entries_bytes); + local_sub(rb_page_commit(to_remove_page), &cpu_buffer->entries_bytes); local_inc(&cpu_buffer->pages_lost); } @@ -2206,6 +2206,8 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size, err = -ENOMEM; goto out_err; } + + cond_resched(); } cpus_read_lock(); @@ -2373,11 +2375,6 @@ rb_reader_event(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->reader_page->read); } -static __always_inline unsigned rb_page_commit(struct buffer_page *bpage) -{ - return local_read(&bpage->page->commit); -} - static struct ring_buffer_event * rb_iter_head_event(struct ring_buffer_iter *iter) { @@ -2396,6 +2393,11 @@ rb_iter_head_event(struct ring_buffer_iter *iter) */ commit = rb_page_commit(iter_head_page); smp_rmb(); + + /* An event needs to be at least 8 bytes in size */ + if (iter->head > commit - 8) + goto reset; + event = __rb_page_index(iter_head_page, iter->head); length = rb_event_length(event); @@ -2518,7 +2520,7 @@ rb_handle_head_page(struct ring_buffer_per_cpu *cpu_buffer, * the counters. */ local_add(entries, &cpu_buffer->overrun); - local_sub(BUF_PAGE_SIZE, &cpu_buffer->entries_bytes); + local_sub(rb_page_commit(next_page), &cpu_buffer->entries_bytes); local_inc(&cpu_buffer->pages_lost); /* @@ -2661,9 +2663,6 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer, event = __rb_page_index(tail_page, tail); - /* account for padding bytes */ - local_add(BUF_PAGE_SIZE - tail, &cpu_buffer->entries_bytes); - /* * Save the original length to the meta data. * This will be used by the reader to add lost event @@ -2677,7 +2676,8 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer, * write counter enough to allow another writer to slip * in on this page. * We put in a discarded commit instead, to make sure - * that this space is not used again. + * that this space is not used again, and this space will + * not be accounted into 'entries_bytes'. * * If we are less than the minimum size, we don't need to * worry about it. @@ -2702,6 +2702,9 @@ rb_reset_tail(struct ring_buffer_per_cpu *cpu_buffer, /* time delta must be non zero */ event->time_delta = 1; + /* account for padding bytes */ + local_add(BUF_PAGE_SIZE - tail, &cpu_buffer->entries_bytes); + /* Make sure the padding is visible before the tail_page->write update */ smp_wmb(); @@ -3003,7 +3006,6 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, { unsigned long new_index, old_index; struct buffer_page *bpage; - unsigned long index; unsigned long addr; u64 write_stamp; u64 delta; @@ -3060,8 +3062,9 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer, */ old_index += write_mask; new_index += write_mask; - index = local_cmpxchg(&bpage->write, old_index, new_index); - if (index == old_index) { + + /* caution: old_index gets updated on cmpxchg failure */ + if (local_try_cmpxchg(&bpage->write, &old_index, new_index)) { /* update counters */ local_sub(event_length, &cpu_buffer->entries_bytes); return true; @@ -4216,7 +4219,7 @@ u64 ring_buffer_oldest_event_ts(struct trace_buffer *buffer, int cpu) EXPORT_SYMBOL_GPL(ring_buffer_oldest_event_ts); /** - * ring_buffer_bytes_cpu - get the number of bytes consumed in a cpu buffer + * ring_buffer_bytes_cpu - get the number of bytes unconsumed in a cpu buffer * @buffer: The ring buffer * @cpu: The per CPU buffer to read from. */ @@ -4724,6 +4727,7 @@ static void rb_advance_reader(struct ring_buffer_per_cpu *cpu_buffer) length = rb_event_length(event); cpu_buffer->reader_page->read += length; + cpu_buffer->read_bytes += length; } static void rb_advance_iter(struct ring_buffer_iter *iter) @@ -5817,7 +5821,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer, } else { /* update the entry counter */ cpu_buffer->read += rb_page_entries(reader); - cpu_buffer->read_bytes += BUF_PAGE_SIZE; + cpu_buffer->read_bytes += rb_page_commit(reader); /* swap the pages */ rb_init_page(bpage); diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8e64aaad53619f60a851d9ef72063e8c9a4ef874..abaaf516fcae9ba9d510808ad13ad97e70cbcc07 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1772,7 +1772,7 @@ static void trace_create_maxlat_file(struct trace_array *tr, init_irq_work(&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq); tr->d_max_latency = trace_create_file("tracing_max_latency", TRACE_MODE_WRITE, - d_tracer, &tr->max_latency, + d_tracer, tr, &tracing_max_lat_fops); } @@ -1805,7 +1805,7 @@ void latency_fsnotify(struct trace_array *tr) #define trace_create_maxlat_file(tr, d_tracer) \ trace_create_file("tracing_max_latency", TRACE_MODE_WRITE, \ - d_tracer, &tr->max_latency, &tracing_max_lat_fops) + d_tracer, tr, &tracing_max_lat_fops) #endif @@ -3119,7 +3119,6 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer, struct ftrace_stack *fstack; struct stack_entry *entry; int stackidx; - void *ptr; /* * Add one, for this function and the call to save_stack_trace() @@ -3157,32 +3156,16 @@ static void __ftrace_trace_stack(struct trace_buffer *buffer, nr_entries = stack_trace_save(fstack->calls, size, skip); } - size = nr_entries * sizeof(unsigned long); event = __trace_buffer_lock_reserve(buffer, TRACE_STACK, - (sizeof(*entry) - sizeof(entry->caller)) + size, + struct_size(entry, caller, nr_entries), trace_ctx); if (!event) goto out; - ptr = ring_buffer_event_data(event); - entry = ptr; - - /* - * For backward compatibility reasons, the entry->caller is an - * array of 8 slots to store the stack. This is also exported - * to user space. The amount allocated on the ring buffer actually - * holds enough for the stack specified by nr_entries. This will - * go into the location of entry->caller. Due to string fortifiers - * checking the size of the destination of memcpy() it triggers - * when it detects that size is greater than 8. To hide this from - * the fortifiers, we use "ptr" and pointer arithmetic to assign caller. - * - * The below is really just: - * memcpy(&entry->caller, fstack->calls, size); - */ - ptr += offsetof(typeof(*entry), caller); - memcpy(ptr, fstack->calls, size); + entry = ring_buffer_event_data(event); entry->size = nr_entries; + memcpy(&entry->caller, fstack->calls, + flex_array_size(entry, caller, nr_entries)); if (!call_filter_check_discard(call, entry, buffer, event)) __buffer_unlock_commit(buffer, event); @@ -4206,18 +4189,12 @@ static void *s_start(struct seq_file *m, loff_t *pos) loff_t l = 0; int cpu; - /* - * copy the tracer to avoid using a global lock all around. - * iter->trace is a copy of current_trace, the pointer to the - * name may be used instead of a strcmp(), as iter->trace->name - * will point to the same string as current_trace->name. - */ mutex_lock(&trace_types_lock); - if (unlikely(tr->current_trace && iter->trace->name != tr->current_trace->name)) { + if (unlikely(tr->current_trace != iter->trace)) { /* Close iter->trace before switching to the new current tracer */ if (iter->trace->close) iter->trace->close(iter); - *iter->trace = *tr->current_trace; + iter->trace = tr->current_trace; /* Reopen the new current tracer */ if (iter->trace->open) iter->trace->open(iter); @@ -4829,6 +4806,25 @@ static const struct seq_operations tracer_seq_ops = { .show = s_show, }; +/* + * Note, as iter itself can be allocated and freed in different + * ways, this function is only used to free its content, and not + * the iterator itself. The only requirement to all the allocations + * is that it must zero all fields (kzalloc), as freeing works with + * ethier allocated content or NULL. + */ +static void free_trace_iter_content(struct trace_iterator *iter) +{ + /* The fmt is either NULL, allocated or points to static_fmt_buf */ + if (iter->fmt != static_fmt_buf) + kfree(iter->fmt); + + kfree(iter->temp); + kfree(iter->buffer_iter); + mutex_destroy(&iter->mutex); + free_cpumask_var(iter->started); +} + static struct trace_iterator * __tracing_open(struct inode *inode, struct file *file, bool snapshot) { @@ -4870,16 +4866,8 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) iter->fmt = NULL; iter->fmt_size = 0; - /* - * We make a copy of the current tracer to avoid concurrent - * changes on it while we are reading. - */ mutex_lock(&trace_types_lock); - iter->trace = kzalloc(sizeof(*iter->trace), GFP_KERNEL); - if (!iter->trace) - goto fail; - - *iter->trace = *tr->current_trace; + iter->trace = tr->current_trace; if (!zalloc_cpumask_var(&iter->started, GFP_KERNEL)) goto fail; @@ -4944,9 +4932,7 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot) fail: mutex_unlock(&trace_types_lock); - kfree(iter->trace); - kfree(iter->temp); - kfree(iter->buffer_iter); + free_trace_iter_content(iter); release: seq_release_private(inode, file); return ERR_PTR(-ENOMEM); @@ -4987,6 +4973,33 @@ int tracing_open_generic_tr(struct inode *inode, struct file *filp) return 0; } +/* + * The private pointer of the inode is the trace_event_file. + * Update the tr ref count associated to it. + */ +int tracing_open_file_tr(struct inode *inode, struct file *filp) +{ + struct trace_event_file *file = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(file->tr); + if (ret) + return ret; + + filp->private_data = inode->i_private; + + return 0; +} + +int tracing_release_file_tr(struct inode *inode, struct file *filp) +{ + struct trace_event_file *file = inode->i_private; + + trace_array_put(file->tr); + + return 0; +} + static int tracing_mark_open(struct inode *inode, struct file *filp) { stream_open(inode, filp); @@ -5025,12 +5038,7 @@ static int tracing_release(struct inode *inode, struct file *file) mutex_unlock(&trace_types_lock); - mutex_destroy(&iter->mutex); - free_cpumask_var(iter->started); - kfree(iter->fmt); - kfree(iter->temp); - kfree(iter->trace); - kfree(iter->buffer_iter); + free_trace_iter_content(iter); seq_release_private(inode, file); return 0; @@ -5730,7 +5738,8 @@ static const char readme_msg[] = "\t fetcharg: (%|$), @
, @[+|-],\n" #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS - "\t $stack, $stack, $retval, $comm, $arg, \n" + "\t $stack, $stack, $retval, $comm, $arg,\n" + "\t [->field[->field|.field...]],\n" #else "\t $stack, $stack, $retval, $comm, $arg,\n" #endif @@ -6318,6 +6327,15 @@ static void set_buffer_entries(struct array_buffer *buf, unsigned long val) per_cpu_ptr(buf->data, cpu)->entries = val; } +static void update_buffer_entries(struct array_buffer *buf, int cpu) +{ + if (cpu == RING_BUFFER_ALL_CPUS) { + set_buffer_entries(buf, ring_buffer_size(buf->buffer, 0)); + } else { + per_cpu_ptr(buf->data, cpu)->entries = ring_buffer_size(buf->buffer, cpu); + } +} + #ifdef CONFIG_TRACER_MAX_TRACE /* resize @tr's buffer to the size of @size_tr's entries */ static int resize_buffer_duplicate_size(struct array_buffer *trace_buf, @@ -6396,18 +6414,12 @@ static int __tracing_resize_ring_buffer(struct trace_array *tr, return ret; } - if (cpu == RING_BUFFER_ALL_CPUS) - set_buffer_entries(&tr->max_buffer, size); - else - per_cpu_ptr(tr->max_buffer.data, cpu)->entries = size; + update_buffer_entries(&tr->max_buffer, cpu); out: #endif /* CONFIG_TRACER_MAX_TRACE */ - if (cpu == RING_BUFFER_ALL_CPUS) - set_buffer_entries(&tr->array_buffer, size); - else - per_cpu_ptr(tr->array_buffer.data, cpu)->entries = size; + update_buffer_entries(&tr->array_buffer, cpu); return ret; } @@ -6706,14 +6718,18 @@ static ssize_t tracing_max_lat_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - return tracing_nsecs_read(filp->private_data, ubuf, cnt, ppos); + struct trace_array *tr = filp->private_data; + + return tracing_nsecs_read(&tr->max_latency, ubuf, cnt, ppos); } static ssize_t tracing_max_lat_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - return tracing_nsecs_write(filp->private_data, ubuf, cnt, ppos); + struct trace_array *tr = filp->private_data; + + return tracing_nsecs_write(&tr->max_latency, ubuf, cnt, ppos); } #endif @@ -6825,10 +6841,7 @@ static int tracing_release_pipe(struct inode *inode, struct file *file) close_pipe_on_cpu(tr, iter->cpu_file); mutex_unlock(&trace_types_lock); - free_cpumask_var(iter->started); - kfree(iter->fmt); - kfree(iter->temp); - mutex_destroy(&iter->mutex); + free_trace_iter_content(iter); kfree(iter); trace_array_put(tr); @@ -7618,6 +7631,11 @@ static int tracing_snapshot_open(struct inode *inode, struct file *file) return ret; } +static void tracing_swap_cpu_buffer(void *tr) +{ + update_max_tr_single((struct trace_array *)tr, current, smp_processor_id()); +} + static ssize_t tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) @@ -7676,13 +7694,15 @@ tracing_snapshot_write(struct file *filp, const char __user *ubuf, size_t cnt, ret = tracing_alloc_snapshot_instance(tr); if (ret < 0) break; - local_irq_disable(); /* Now, we're going to swap */ - if (iter->cpu_file == RING_BUFFER_ALL_CPUS) + if (iter->cpu_file == RING_BUFFER_ALL_CPUS) { + local_irq_disable(); update_max_tr(tr, current, smp_processor_id(), NULL); - else - update_max_tr_single(tr, current, iter->cpu_file); - local_irq_enable(); + local_irq_enable(); + } else { + smp_call_function_single(iter->cpu_file, tracing_swap_cpu_buffer, + (void *)tr, 1); + } break; default: if (tr->allocated_snapshot) { @@ -7763,18 +7783,20 @@ static const struct file_operations tracing_thresh_fops = { #ifdef CONFIG_TRACER_MAX_TRACE static const struct file_operations tracing_max_lat_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .read = tracing_max_lat_read, .write = tracing_max_lat_write, .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, }; #endif static const struct file_operations set_tracer_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .read = tracing_set_trace_read, .write = tracing_set_trace_write, .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, }; static const struct file_operations tracing_pipe_fops = { @@ -8967,12 +8989,33 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt, return cnt; } +static int tracing_open_options(struct inode *inode, struct file *filp) +{ + struct trace_option_dentry *topt = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(topt->tr); + if (ret) + return ret; + + filp->private_data = inode->i_private; + return 0; +} + +static int tracing_release_options(struct inode *inode, struct file *file) +{ + struct trace_option_dentry *topt = file->private_data; + + trace_array_put(topt->tr); + return 0; +} static const struct file_operations trace_options_fops = { - .open = tracing_open_generic, + .open = tracing_open_options, .read = trace_options_read, .write = trace_options_write, .llseek = generic_file_llseek, + .release = tracing_release_options, }; /* @@ -9486,7 +9529,7 @@ static struct trace_array *trace_array_create(const char *name) if (!alloc_cpumask_var(&tr->tracing_cpumask, GFP_KERNEL)) goto out_free_tr; - if (!alloc_cpumask_var(&tr->pipe_cpumask, GFP_KERNEL)) + if (!zalloc_cpumask_var(&tr->pipe_cpumask, GFP_KERNEL)) goto out_free_tr; tr->trace_flags = global_trace.trace_flags & ~ZEROED_TRACE_FLAGS; @@ -9750,8 +9793,8 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer) tr, &tracing_mark_fops); file = __find_event_file(tr, "ftrace", "print"); - if (file && file->dir) - trace_create_file("trigger", TRACE_MODE_WRITE, file->dir, + if (file && file->ef) + eventfs_add_file("trigger", TRACE_MODE_WRITE, file->ef, file, &event_trigger_fops); tr->trace_marker_file = file; @@ -10431,7 +10474,7 @@ __init static int tracer_alloc_buffers(void) if (trace_create_savedcmd() < 0) goto out_free_temp_buffer; - if (!alloc_cpumask_var(&global_trace.pipe_cpumask, GFP_KERNEL)) + if (!zalloc_cpumask_var(&global_trace.pipe_cpumask, GFP_KERNEL)) goto out_free_savedcmd; /* TODO: make the number of buffers hot pluggable with CPUS */ diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 73eaec158473e3edcf8a94cccc247ddc68453928..77debe53f07cf5663f8421214c613505a584a314 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -77,6 +77,16 @@ enum trace_type { #undef __array #define __array(type, item, size) type item[size]; +/* + * For backward compatibility, older user space expects to see the + * kernel_stack event with a fixed size caller field. But today the fix + * size is ignored by the kernel, and the real structure is dynamic. + * Expose to user space: "unsigned long caller[8];" but the real structure + * will be "unsigned long caller[] __counted_by(size)" + */ +#undef __stack_array +#define __stack_array(type, item, size, field) type item[] __counted_by(field); + #undef __array_desc #define __array_desc(type, container, item, size) @@ -596,11 +606,12 @@ trace_buffer_iter(struct trace_iterator *iter, int cpu) int tracer_init(struct tracer *t, struct trace_array *tr); int tracing_is_enabled(void); void tracing_reset_online_cpus(struct array_buffer *buf); -void tracing_reset_current(int cpu); void tracing_reset_all_online_cpus(void); void tracing_reset_all_online_cpus_unlocked(void); int tracing_open_generic(struct inode *inode, struct file *filp); int tracing_open_generic_tr(struct inode *inode, struct file *filp); +int tracing_open_file_tr(struct inode *inode, struct file *filp); +int tracing_release_file_tr(struct inode *inode, struct file *filp); bool tracing_is_disabled(void); bool tracer_tracing_is_on(struct trace_array *tr); void tracer_tracing_on(struct trace_array *tr); @@ -697,7 +708,6 @@ void trace_filter_add_remove_task(struct trace_pid_list *pid_list, void *trace_pid_next(struct trace_pid_list *pid_list, void *v, loff_t *pos); void *trace_pid_start(struct trace_pid_list *pid_list, loff_t *pos); int trace_pid_show(struct seq_file *m, void *v); -void trace_free_pid_list(struct trace_pid_list *pid_list); int trace_pid_write(struct trace_pid_list *filtered_pids, struct trace_pid_list **new_pid_list, const char __user *ubuf, size_t cnt); @@ -1334,7 +1344,7 @@ struct trace_subsystem_dir { struct list_head list; struct event_subsystem *subsystem; struct trace_array *tr; - struct dentry *entry; + struct eventfs_file *ef; int ref_count; int nr_events; }; diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c new file mode 100644 index 0000000000000000000000000000000000000000..ca224d53bfdcd0df9f6609d3f30a4a6054868136 --- /dev/null +++ b/kernel/trace/trace_btf.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include + +#include "trace_btf.h" + +/* + * Find a function proto type by name, and return the btf_type with its btf + * in *@btf_p. Return NULL if not found. + * Note that caller has to call btf_put(*@btf_p) after using the btf_type. + */ +const struct btf_type *btf_find_func_proto(const char *func_name, struct btf **btf_p) +{ + const struct btf_type *t; + s32 id; + + id = bpf_find_btf_id(func_name, BTF_KIND_FUNC, btf_p); + if (id < 0) + return NULL; + + /* Get BTF_KIND_FUNC type */ + t = btf_type_by_id(*btf_p, id); + if (!t || !btf_type_is_func(t)) + goto err; + + /* The type of BTF_KIND_FUNC is BTF_KIND_FUNC_PROTO */ + t = btf_type_by_id(*btf_p, t->type); + if (!t || !btf_type_is_func_proto(t)) + goto err; + + return t; +err: + btf_put(*btf_p); + return NULL; +} + +/* + * Get function parameter with the number of parameters. + * This can return NULL if the function has no parameters. + * It can return -EINVAL if the @func_proto is not a function proto type. + */ +const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, s32 *nr) +{ + if (!btf_type_is_func_proto(func_proto)) + return ERR_PTR(-EINVAL); + + *nr = btf_type_vlen(func_proto); + if (*nr > 0) + return (const struct btf_param *)(func_proto + 1); + else + return NULL; +} + +#define BTF_ANON_STACK_MAX 16 + +struct btf_anon_stack { + u32 tid; + u32 offset; +}; + +/* + * Find a member of data structure/union by name and return it. + * Return NULL if not found, or -EINVAL if parameter is invalid. + * If the member is an member of anonymous union/structure, the offset + * of that anonymous union/structure is stored into @anon_offset. Caller + * can calculate the correct offset from the root data structure by + * adding anon_offset to the member's offset. + */ +const struct btf_member *btf_find_struct_member(struct btf *btf, + const struct btf_type *type, + const char *member_name, + u32 *anon_offset) +{ + struct btf_anon_stack *anon_stack; + const struct btf_member *member; + u32 tid, cur_offset = 0; + const char *name; + int i, top = 0; + + anon_stack = kcalloc(BTF_ANON_STACK_MAX, sizeof(*anon_stack), GFP_KERNEL); + if (!anon_stack) + return ERR_PTR(-ENOMEM); + +retry: + if (!btf_type_is_struct(type)) { + member = ERR_PTR(-EINVAL); + goto out; + } + + for_each_member(i, type, member) { + if (!member->name_off) { + /* Anonymous union/struct: push it for later use */ + type = btf_type_skip_modifiers(btf, member->type, &tid); + if (type && top < BTF_ANON_STACK_MAX) { + anon_stack[top].tid = tid; + anon_stack[top++].offset = + cur_offset + member->offset; + } + } else { + name = btf_name_by_offset(btf, member->name_off); + if (name && !strcmp(member_name, name)) { + if (anon_offset) + *anon_offset = cur_offset; + goto out; + } + } + } + if (top > 0) { + /* Pop from the anonymous stack and retry */ + tid = anon_stack[--top].tid; + cur_offset = anon_stack[top].offset; + type = btf_type_by_id(btf, tid); + goto retry; + } + member = NULL; + +out: + kfree(anon_stack); + return member; +} + diff --git a/kernel/trace/trace_btf.h b/kernel/trace/trace_btf.h new file mode 100644 index 0000000000000000000000000000000000000000..4bc44bc261e6f9cb496ad31eb5534171905c8b5a --- /dev/null +++ b/kernel/trace/trace_btf.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include + +const struct btf_type *btf_find_func_proto(const char *func_name, + struct btf **btf_p); +const struct btf_param *btf_get_func_param(const struct btf_type *func_proto, + s32 *nr); +const struct btf_member *btf_find_struct_member(struct btf *btf, + const struct btf_type *type, + const char *member_name, + u32 *anon_offset); diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 340b2fa98218aa3e42e123ae81744cbe866556b2..c47422b209085958f185c17d11323c50ffac8a83 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -190,7 +190,7 @@ FTRACE_ENTRY(kernel_stack, stack_entry, F_STRUCT( __field( int, size ) - __array( unsigned long, caller, FTRACE_STACK_ENTRIES ) + __stack_array( unsigned long, caller, FTRACE_STACK_ENTRIES, size) ), F_printk("\t=> %ps\n\t=> %ps\n\t=> %ps\n" diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index a0a704ba27db3471b4d029a387d50b2b12c78fed..72714cbf475c78377925ea4c13a42ce76529dc69 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -41,6 +41,10 @@ struct eprobe_data { struct trace_eprobe *ep; }; + +#define for_each_trace_eprobe_tp(ep, _tp) \ + list_for_each_entry(ep, trace_probe_probe_list(_tp), tp.list) + static int __trace_eprobe_create(int argc, const char *argv[]); static void trace_event_probe_cleanup(struct trace_eprobe *ep) @@ -640,7 +644,7 @@ static int disable_eprobe(struct trace_eprobe *ep, static int enable_trace_eprobe(struct trace_event_call *call, struct trace_event_file *file) { - struct trace_probe *pos, *tp; + struct trace_probe *tp; struct trace_eprobe *ep; bool enabled; int ret = 0; @@ -662,8 +666,7 @@ static int enable_trace_eprobe(struct trace_event_call *call, if (enabled) return 0; - list_for_each_entry(pos, trace_probe_probe_list(tp), list) { - ep = container_of(pos, struct trace_eprobe, tp); + for_each_trace_eprobe_tp(ep, tp) { ret = enable_eprobe(ep, file); if (ret) break; @@ -680,8 +683,7 @@ static int enable_trace_eprobe(struct trace_event_call *call, */ WARN_ON_ONCE(ret != -ENOMEM); - list_for_each_entry(pos, trace_probe_probe_list(tp), list) { - ep = container_of(pos, struct trace_eprobe, tp); + for_each_trace_eprobe_tp(ep, tp) { disable_eprobe(ep, file->tr); if (!--cnt) break; @@ -699,7 +701,7 @@ static int enable_trace_eprobe(struct trace_event_call *call, static int disable_trace_eprobe(struct trace_event_call *call, struct trace_event_file *file) { - struct trace_probe *pos, *tp; + struct trace_probe *tp; struct trace_eprobe *ep; tp = trace_probe_primary_from_call(call); @@ -716,10 +718,8 @@ static int disable_trace_eprobe(struct trace_event_call *call, trace_probe_clear_flag(tp, TP_FLAG_PROFILE); if (!trace_probe_is_enabled(tp)) { - list_for_each_entry(pos, trace_probe_probe_list(tp), list) { - ep = container_of(pos, struct trace_eprobe, tp); + for_each_trace_eprobe_tp(ep, tp) disable_eprobe(ep, file->tr); - } } out: @@ -807,13 +807,11 @@ static int trace_eprobe_tp_update_arg(struct trace_eprobe *ep, const char *argv[ int ret; ret = traceprobe_parse_probe_arg(&ep->tp, i, argv[i], &ctx); - if (ret) - return ret; - /* Handle symbols "@" */ if (!ret) ret = traceprobe_update_arg(&ep->tp.args[i]); + traceprobe_finish_parse(&ctx); return ret; } diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 578f1f7d49a61a2a3f9a3fc4a67cb292c7b764da..f49d6ddb634255335ebdd4e0d938ac841e69bad1 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -984,7 +984,7 @@ static void remove_subsystem(struct trace_subsystem_dir *dir) return; if (!--dir->nr_events) { - tracefs_remove(dir->entry); + eventfs_remove(dir->ef); list_del(&dir->list); __put_system_dir(dir); } @@ -992,20 +992,7 @@ static void remove_subsystem(struct trace_subsystem_dir *dir) static void remove_event_file_dir(struct trace_event_file *file) { - struct dentry *dir = file->dir; - struct dentry *child; - - if (dir) { - spin_lock(&dir->d_lock); /* probably unneeded */ - list_for_each_entry(child, &dir->d_subdirs, d_child) { - if (d_really_is_positive(child)) /* probably unneeded */ - d_inode(child)->i_private = NULL; - } - spin_unlock(&dir->d_lock); - - tracefs_remove(dir); - } - + eventfs_remove(file->ef); list_del(&file->list); remove_subsystem(file->system); free_event_filter(file->filter); @@ -2103,9 +2090,10 @@ static const struct file_operations ftrace_set_event_notrace_pid_fops = { }; static const struct file_operations ftrace_enable_fops = { - .open = tracing_open_generic, + .open = tracing_open_file_tr, .read = event_enable_read, .write = event_enable_write, + .release = tracing_release_file_tr, .llseek = default_llseek, }; @@ -2122,9 +2110,10 @@ static const struct file_operations ftrace_event_id_fops = { }; static const struct file_operations ftrace_event_filter_fops = { - .open = tracing_open_generic, + .open = tracing_open_file_tr, .read = event_filter_read, .write = event_filter_write, + .release = tracing_release_file_tr, .llseek = default_llseek, }; @@ -2291,13 +2280,14 @@ create_new_subsystem(const char *name) return NULL; } -static struct dentry * +static struct eventfs_file * event_subsystem_dir(struct trace_array *tr, const char *name, struct trace_event_file *file, struct dentry *parent) { struct event_subsystem *system, *iter; struct trace_subsystem_dir *dir; - struct dentry *entry; + struct eventfs_file *ef; + int res; /* First see if we did not already create this dir */ list_for_each_entry(dir, &tr->systems, list) { @@ -2305,7 +2295,7 @@ event_subsystem_dir(struct trace_array *tr, const char *name, if (strcmp(system->name, name) == 0) { dir->nr_events++; file->system = dir; - return dir->entry; + return dir->ef; } } @@ -2329,13 +2319,14 @@ event_subsystem_dir(struct trace_array *tr, const char *name, } else __get_system(system); - dir->entry = tracefs_create_dir(name, parent); - if (!dir->entry) { + ef = eventfs_add_subsystem_dir(name, parent); + if (IS_ERR(ef)) { pr_warn("Failed to create system directory %s\n", name); __put_system(system); goto out_free; } + dir->ef = ef; dir->tr = tr; dir->ref_count = 1; dir->nr_events = 1; @@ -2345,22 +2336,22 @@ event_subsystem_dir(struct trace_array *tr, const char *name, /* the ftrace system is special, do not create enable or filter files */ if (strcmp(name, "ftrace") != 0) { - entry = tracefs_create_file("filter", TRACE_MODE_WRITE, - dir->entry, dir, + res = eventfs_add_file("filter", TRACE_MODE_WRITE, + dir->ef, dir, &ftrace_subsystem_filter_fops); - if (!entry) { + if (res) { kfree(system->filter); system->filter = NULL; pr_warn("Could not create tracefs '%s/filter' entry\n", name); } - trace_create_file("enable", TRACE_MODE_WRITE, dir->entry, dir, + eventfs_add_file("enable", TRACE_MODE_WRITE, dir->ef, dir, &ftrace_system_enable_fops); } list_add(&dir->list, &tr->systems); - return dir->entry; + return dir->ef; out_free: kfree(dir); @@ -2413,36 +2404,40 @@ static int event_create_dir(struct dentry *parent, struct trace_event_file *file) { struct trace_event_call *call = file->event_call; + struct eventfs_file *ef_subsystem = NULL; struct trace_array *tr = file->tr; - struct dentry *d_events; + struct eventfs_file *ef; const char *name; int ret; /* * If the trace point header did not define TRACE_SYSTEM - * then the system would be called "TRACE_SYSTEM". + * then the system would be called "TRACE_SYSTEM". This should + * never happen. */ - if (strcmp(call->class->system, TRACE_SYSTEM) != 0) { - d_events = event_subsystem_dir(tr, call->class->system, file, parent); - if (!d_events) - return -ENOMEM; - } else - d_events = parent; + if (WARN_ON_ONCE(strcmp(call->class->system, TRACE_SYSTEM) == 0)) + return -ENODEV; + + ef_subsystem = event_subsystem_dir(tr, call->class->system, file, parent); + if (!ef_subsystem) + return -ENOMEM; name = trace_event_name(call); - file->dir = tracefs_create_dir(name, d_events); - if (!file->dir) { + ef = eventfs_add_dir(name, ef_subsystem); + if (IS_ERR(ef)) { pr_warn("Could not create tracefs '%s' directory\n", name); return -1; } + file->ef = ef; + if (call->class->reg && !(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) - trace_create_file("enable", TRACE_MODE_WRITE, file->dir, file, + eventfs_add_file("enable", TRACE_MODE_WRITE, file->ef, file, &ftrace_enable_fops); #ifdef CONFIG_PERF_EVENTS if (call->event.type && call->class->reg) - trace_create_file("id", TRACE_MODE_READ, file->dir, + eventfs_add_file("id", TRACE_MODE_READ, file->ef, (void *)(long)call->event.type, &ftrace_event_id_fops); #endif @@ -2458,27 +2453,27 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) * triggers or filters. */ if (!(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) { - trace_create_file("filter", TRACE_MODE_WRITE, file->dir, + eventfs_add_file("filter", TRACE_MODE_WRITE, file->ef, file, &ftrace_event_filter_fops); - trace_create_file("trigger", TRACE_MODE_WRITE, file->dir, + eventfs_add_file("trigger", TRACE_MODE_WRITE, file->ef, file, &event_trigger_fops); } #ifdef CONFIG_HIST_TRIGGERS - trace_create_file("hist", TRACE_MODE_READ, file->dir, file, + eventfs_add_file("hist", TRACE_MODE_READ, file->ef, file, &event_hist_fops); #endif #ifdef CONFIG_HIST_TRIGGERS_DEBUG - trace_create_file("hist_debug", TRACE_MODE_READ, file->dir, file, + eventfs_add_file("hist_debug", TRACE_MODE_READ, file->ef, file, &event_hist_debug_fops); #endif - trace_create_file("format", TRACE_MODE_READ, file->dir, call, + eventfs_add_file("format", TRACE_MODE_READ, file->ef, call, &ftrace_event_format_fops); #ifdef CONFIG_TRACE_EVENT_INJECT if (call->event.type && call->class->reg) - trace_create_file("inject", 0200, file->dir, file, + eventfs_add_file("inject", 0200, file->ef, file, &event_inject_fops); #endif @@ -2775,6 +2770,7 @@ void trace_event_eval_update(struct trace_eval_map **map, int len) update_event_fields(call, map[i]); } } + cond_resched(); } up_write(&trace_event_sem); } @@ -3631,21 +3627,22 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr) { struct dentry *d_events; struct dentry *entry; + int error = 0; entry = trace_create_file("set_event", TRACE_MODE_WRITE, parent, tr, &ftrace_set_event_fops); if (!entry) return -ENOMEM; - d_events = tracefs_create_dir("events", parent); - if (!d_events) { + d_events = eventfs_create_events_dir("events", parent); + if (IS_ERR(d_events)) { pr_warn("Could not create tracefs 'events' directory\n"); return -ENOMEM; } - entry = trace_create_file("enable", TRACE_MODE_WRITE, d_events, + error = eventfs_add_events_file("enable", TRACE_MODE_WRITE, d_events, tr, &ftrace_tr_enable_fops); - if (!entry) + if (error) return -ENOMEM; /* There are not as crucial, just warn if they are not created */ @@ -3658,11 +3655,11 @@ create_event_toplevel_files(struct dentry *parent, struct trace_array *tr) &ftrace_set_event_notrace_pid_fops); /* ring buffer internal formats */ - trace_create_file("header_page", TRACE_MODE_READ, d_events, + eventfs_add_events_file("header_page", TRACE_MODE_READ, d_events, ring_buffer_print_page_header, &ftrace_show_header_fops); - trace_create_file("header_event", TRACE_MODE_READ, d_events, + eventfs_add_events_file("header_event", TRACE_MODE_READ, d_events, ring_buffer_print_entry_header, &ftrace_show_header_fops); @@ -3750,7 +3747,7 @@ int event_trace_del_tracer(struct trace_array *tr) down_write(&trace_event_sem); __trace_remove_event_dirs(tr); - tracefs_remove(tr->event_dir); + eventfs_remove_events_dir(tr->event_dir); up_write(&trace_event_sem); tr->event_dir = NULL; diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 1dad64267878c1f1b3f25ff2206165df49089c1b..33264e510d161f5c78fdd464e45b6f966dd765d9 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -46,15 +46,19 @@ static const char * ops[] = { OPS }; enum filter_pred_fn { FILTER_PRED_FN_NOP, FILTER_PRED_FN_64, + FILTER_PRED_FN_64_CPUMASK, FILTER_PRED_FN_S64, FILTER_PRED_FN_U64, FILTER_PRED_FN_32, + FILTER_PRED_FN_32_CPUMASK, FILTER_PRED_FN_S32, FILTER_PRED_FN_U32, FILTER_PRED_FN_16, + FILTER_PRED_FN_16_CPUMASK, FILTER_PRED_FN_S16, FILTER_PRED_FN_U16, FILTER_PRED_FN_8, + FILTER_PRED_FN_8_CPUMASK, FILTER_PRED_FN_S8, FILTER_PRED_FN_U8, FILTER_PRED_FN_COMM, @@ -64,21 +68,25 @@ enum filter_pred_fn { FILTER_PRED_FN_PCHAR_USER, FILTER_PRED_FN_PCHAR, FILTER_PRED_FN_CPU, + FILTER_PRED_FN_CPU_CPUMASK, + FILTER_PRED_FN_CPUMASK, + FILTER_PRED_FN_CPUMASK_CPU, FILTER_PRED_FN_FUNCTION, FILTER_PRED_FN_, FILTER_PRED_TEST_VISITED, }; struct filter_pred { - enum filter_pred_fn fn_num; - u64 val; - u64 val2; - struct regex regex; + struct regex *regex; + struct cpumask *mask; unsigned short *ops; struct ftrace_event_field *field; - int offset; + u64 val; + u64 val2; + enum filter_pred_fn fn_num; + int offset; int not; - int op; + int op; }; /* @@ -94,6 +102,8 @@ struct filter_pred { C(TOO_MANY_OPEN, "Too many '('"), \ C(TOO_MANY_CLOSE, "Too few '('"), \ C(MISSING_QUOTE, "Missing matching quote"), \ + C(MISSING_BRACE_OPEN, "Missing '{'"), \ + C(MISSING_BRACE_CLOSE, "Missing '}'"), \ C(OPERAND_TOO_LONG, "Operand too long"), \ C(EXPECT_STRING, "Expecting string field"), \ C(EXPECT_DIGIT, "Expecting numeric field"), \ @@ -103,6 +113,7 @@ struct filter_pred { C(BAD_SUBSYS_FILTER, "Couldn't find or set field in one of a subsystem's events"), \ C(TOO_MANY_PREDS, "Too many terms in predicate expression"), \ C(INVALID_FILTER, "Meaningless filter expression"), \ + C(INVALID_CPULIST, "Invalid cpulist"), \ C(IP_FIELD_ONLY, "Only 'ip' field is supported for function trace"), \ C(INVALID_VALUE, "Invalid value (did you forget quotes)?"), \ C(NO_FUNCTION, "Function not found"), \ @@ -186,6 +197,15 @@ enum { PROCESS_OR = 4, }; +static void free_predicate(struct filter_pred *pred) +{ + if (pred) { + kfree(pred->regex); + kfree(pred->mask); + kfree(pred); + } +} + /* * Without going into a formal proof, this explains the method that is used in * parsing the logical expressions. @@ -623,12 +643,64 @@ predicate_parse(const char *str, int nr_parens, int nr_preds, kfree(inverts); if (prog_stack) { for (i = 0; prog_stack[i].pred; i++) - kfree(prog_stack[i].pred); + free_predicate(prog_stack[i].pred); kfree(prog_stack); } return ERR_PTR(ret); } +static inline int +do_filter_cpumask(int op, const struct cpumask *mask, const struct cpumask *cmp) +{ + switch (op) { + case OP_EQ: + return cpumask_equal(mask, cmp); + case OP_NE: + return !cpumask_equal(mask, cmp); + case OP_BAND: + return cpumask_intersects(mask, cmp); + default: + return 0; + } +} + +/* Optimisation of do_filter_cpumask() for scalar fields */ +static inline int +do_filter_scalar_cpumask(int op, unsigned int cpu, const struct cpumask *mask) +{ + /* + * Per the weight-of-one cpumask optimisations, the mask passed in this + * function has a weight >= 2, so it is never equal to a single scalar. + */ + switch (op) { + case OP_EQ: + return false; + case OP_NE: + return true; + case OP_BAND: + return cpumask_test_cpu(cpu, mask); + default: + return 0; + } +} + +static inline int +do_filter_cpumask_scalar(int op, const struct cpumask *mask, unsigned int cpu) +{ + switch (op) { + case OP_EQ: + return cpumask_test_cpu(cpu, mask) && + cpumask_nth(1, mask) >= nr_cpu_ids; + case OP_NE: + return !cpumask_test_cpu(cpu, mask) || + cpumask_nth(1, mask) < nr_cpu_ids; + case OP_BAND: + return cpumask_test_cpu(cpu, mask); + default: + return 0; + } +} + enum pred_cmp_types { PRED_CMP_TYPE_NOP, PRED_CMP_TYPE_LT, @@ -672,6 +744,18 @@ static int filter_pred_##type(struct filter_pred *pred, void *event) \ } \ } +#define DEFINE_CPUMASK_COMPARISON_PRED(size) \ +static int filter_pred_##size##_cpumask(struct filter_pred *pred, void *event) \ +{ \ + u##size *addr = (u##size *)(event + pred->offset); \ + unsigned int cpu = *addr; \ + \ + if (cpu >= nr_cpu_ids) \ + return 0; \ + \ + return do_filter_scalar_cpumask(pred->op, cpu, pred->mask); \ +} + #define DEFINE_EQUALITY_PRED(size) \ static int filter_pred_##size(struct filter_pred *pred, void *event) \ { \ @@ -693,6 +777,11 @@ DEFINE_COMPARISON_PRED(u16); DEFINE_COMPARISON_PRED(s8); DEFINE_COMPARISON_PRED(u8); +DEFINE_CPUMASK_COMPARISON_PRED(64); +DEFINE_CPUMASK_COMPARISON_PRED(32); +DEFINE_CPUMASK_COMPARISON_PRED(16); +DEFINE_CPUMASK_COMPARISON_PRED(8); + DEFINE_EQUALITY_PRED(64); DEFINE_EQUALITY_PRED(32); DEFINE_EQUALITY_PRED(16); @@ -750,7 +839,7 @@ static int filter_pred_string(struct filter_pred *pred, void *event) char *addr = (char *)(event + pred->offset); int cmp, match; - cmp = pred->regex.match(addr, &pred->regex, pred->regex.field_len); + cmp = pred->regex->match(addr, pred->regex, pred->regex->field_len); match = cmp ^ pred->not; @@ -763,7 +852,7 @@ static __always_inline int filter_pchar(struct filter_pred *pred, char *str) int len; len = strlen(str) + 1; /* including tailing '\0' */ - cmp = pred->regex.match(str, &pred->regex, len); + cmp = pred->regex->match(str, pred->regex, len); match = cmp ^ pred->not; @@ -813,7 +902,7 @@ static int filter_pred_strloc(struct filter_pred *pred, void *event) char *addr = (char *)(event + str_loc); int cmp, match; - cmp = pred->regex.match(addr, &pred->regex, str_len); + cmp = pred->regex->match(addr, pred->regex, str_len); match = cmp ^ pred->not; @@ -836,7 +925,7 @@ static int filter_pred_strrelloc(struct filter_pred *pred, void *event) char *addr = (char *)(&item[1]) + str_loc; int cmp, match; - cmp = pred->regex.match(addr, &pred->regex, str_len); + cmp = pred->regex->match(addr, pred->regex, str_len); match = cmp ^ pred->not; @@ -869,12 +958,42 @@ static int filter_pred_cpu(struct filter_pred *pred, void *event) } } +/* Filter predicate for current CPU vs user-provided cpumask */ +static int filter_pred_cpu_cpumask(struct filter_pred *pred, void *event) +{ + int cpu = raw_smp_processor_id(); + + return do_filter_scalar_cpumask(pred->op, cpu, pred->mask); +} + +/* Filter predicate for cpumask field vs user-provided cpumask */ +static int filter_pred_cpumask(struct filter_pred *pred, void *event) +{ + u32 item = *(u32 *)(event + pred->offset); + int loc = item & 0xffff; + const struct cpumask *mask = (event + loc); + const struct cpumask *cmp = pred->mask; + + return do_filter_cpumask(pred->op, mask, cmp); +} + +/* Filter predicate for cpumask field vs user-provided scalar */ +static int filter_pred_cpumask_cpu(struct filter_pred *pred, void *event) +{ + u32 item = *(u32 *)(event + pred->offset); + int loc = item & 0xffff; + const struct cpumask *mask = (event + loc); + unsigned int cpu = pred->val; + + return do_filter_cpumask_scalar(pred->op, mask, cpu); +} + /* Filter predicate for COMM. */ static int filter_pred_comm(struct filter_pred *pred, void *event) { int cmp; - cmp = pred->regex.match(current->comm, &pred->regex, + cmp = pred->regex->match(current->comm, pred->regex, TASK_COMM_LEN); return cmp ^ pred->not; } @@ -1004,7 +1123,7 @@ enum regex_type filter_parse_regex(char *buff, int len, char **search, int *not) static void filter_build_regex(struct filter_pred *pred) { - struct regex *r = &pred->regex; + struct regex *r = pred->regex; char *search; enum regex_type type = MATCH_FULL; @@ -1169,7 +1288,7 @@ static void free_prog(struct event_filter *filter) return; for (i = 0; prog[i].pred; i++) - kfree(prog[i].pred); + free_predicate(prog[i].pred); kfree(prog); } @@ -1236,8 +1355,12 @@ static void filter_free_subsystem_filters(struct trace_subsystem_dir *dir, int filter_assign_type(const char *type) { - if (strstr(type, "__data_loc") && strstr(type, "char")) - return FILTER_DYN_STRING; + if (strstr(type, "__data_loc")) { + if (strstr(type, "char")) + return FILTER_DYN_STRING; + if (strstr(type, "cpumask_t")) + return FILTER_CPUMASK; + } if (strstr(type, "__rel_loc") && strstr(type, "char")) return FILTER_RDYN_STRING; @@ -1313,24 +1436,32 @@ static int filter_pred_fn_call(struct filter_pred *pred, void *event) switch (pred->fn_num) { case FILTER_PRED_FN_64: return filter_pred_64(pred, event); + case FILTER_PRED_FN_64_CPUMASK: + return filter_pred_64_cpumask(pred, event); case FILTER_PRED_FN_S64: return filter_pred_s64(pred, event); case FILTER_PRED_FN_U64: return filter_pred_u64(pred, event); case FILTER_PRED_FN_32: return filter_pred_32(pred, event); + case FILTER_PRED_FN_32_CPUMASK: + return filter_pred_32_cpumask(pred, event); case FILTER_PRED_FN_S32: return filter_pred_s32(pred, event); case FILTER_PRED_FN_U32: return filter_pred_u32(pred, event); case FILTER_PRED_FN_16: return filter_pred_16(pred, event); + case FILTER_PRED_FN_16_CPUMASK: + return filter_pred_16_cpumask(pred, event); case FILTER_PRED_FN_S16: return filter_pred_s16(pred, event); case FILTER_PRED_FN_U16: return filter_pred_u16(pred, event); case FILTER_PRED_FN_8: return filter_pred_8(pred, event); + case FILTER_PRED_FN_8_CPUMASK: + return filter_pred_8_cpumask(pred, event); case FILTER_PRED_FN_S8: return filter_pred_s8(pred, event); case FILTER_PRED_FN_U8: @@ -1349,6 +1480,12 @@ static int filter_pred_fn_call(struct filter_pred *pred, void *event) return filter_pred_pchar(pred, event); case FILTER_PRED_FN_CPU: return filter_pred_cpu(pred, event); + case FILTER_PRED_FN_CPU_CPUMASK: + return filter_pred_cpu_cpumask(pred, event); + case FILTER_PRED_FN_CPUMASK: + return filter_pred_cpumask(pred, event); + case FILTER_PRED_FN_CPUMASK_CPU: + return filter_pred_cpumask_cpu(pred, event); case FILTER_PRED_FN_FUNCTION: return filter_pred_function(pred, event); case FILTER_PRED_TEST_VISITED: @@ -1553,9 +1690,130 @@ static int parse_pred(const char *str, void *data, goto err_free; } - pred->regex.len = len; - strncpy(pred->regex.pattern, str + s, len); - pred->regex.pattern[len] = 0; + pred->regex = kzalloc(sizeof(*pred->regex), GFP_KERNEL); + if (!pred->regex) + goto err_mem; + pred->regex->len = len; + strncpy(pred->regex->pattern, str + s, len); + pred->regex->pattern[len] = 0; + + } else if (!strncmp(str + i, "CPUS", 4)) { + unsigned int maskstart; + bool single; + char *tmp; + + switch (field->filter_type) { + case FILTER_CPUMASK: + case FILTER_CPU: + case FILTER_OTHER: + break; + default: + parse_error(pe, FILT_ERR_ILLEGAL_FIELD_OP, pos + i); + goto err_free; + } + + switch (op) { + case OP_EQ: + case OP_NE: + case OP_BAND: + break; + default: + parse_error(pe, FILT_ERR_ILLEGAL_FIELD_OP, pos + i); + goto err_free; + } + + /* Skip CPUS */ + i += 4; + if (str[i++] != '{') { + parse_error(pe, FILT_ERR_MISSING_BRACE_OPEN, pos + i); + goto err_free; + } + maskstart = i; + + /* Walk the cpulist until closing } */ + for (; str[i] && str[i] != '}'; i++) + ; + + if (str[i] != '}') { + parse_error(pe, FILT_ERR_MISSING_BRACE_CLOSE, pos + i); + goto err_free; + } + + if (maskstart == i) { + parse_error(pe, FILT_ERR_INVALID_CPULIST, pos + i); + goto err_free; + } + + /* Copy the cpulist between { and } */ + tmp = kmalloc((i - maskstart) + 1, GFP_KERNEL); + if (!tmp) + goto err_mem; + + strscpy(tmp, str + maskstart, (i - maskstart) + 1); + pred->mask = kzalloc(cpumask_size(), GFP_KERNEL); + if (!pred->mask) { + kfree(tmp); + goto err_mem; + } + + /* Now parse it */ + if (cpulist_parse(tmp, pred->mask)) { + kfree(tmp); + parse_error(pe, FILT_ERR_INVALID_CPULIST, pos + i); + goto err_free; + } + kfree(tmp); + + /* Move along */ + i++; + + /* + * Optimisation: if the user-provided mask has a weight of one + * then we can treat it as a scalar input. + */ + single = cpumask_weight(pred->mask) == 1; + if (single) { + pred->val = cpumask_first(pred->mask); + kfree(pred->mask); + pred->mask = NULL; + } + + if (field->filter_type == FILTER_CPUMASK) { + pred->fn_num = single ? + FILTER_PRED_FN_CPUMASK_CPU : + FILTER_PRED_FN_CPUMASK; + } else if (field->filter_type == FILTER_CPU) { + if (single) { + if (pred->op == OP_BAND) + pred->op = OP_EQ; + + pred->fn_num = FILTER_PRED_FN_CPU; + } else { + pred->fn_num = FILTER_PRED_FN_CPU_CPUMASK; + } + } else if (single) { + if (pred->op == OP_BAND) + pred->op = OP_EQ; + + pred->fn_num = select_comparison_fn(pred->op, field->size, false); + if (pred->op == OP_NE) + pred->not = 1; + } else { + switch (field->size) { + case 8: + pred->fn_num = FILTER_PRED_FN_64_CPUMASK; + break; + case 4: + pred->fn_num = FILTER_PRED_FN_32_CPUMASK; + break; + case 2: + pred->fn_num = FILTER_PRED_FN_16_CPUMASK; + break; + case 1: + pred->fn_num = FILTER_PRED_FN_8_CPUMASK; + break; + } + } /* This is either a string, or an integer */ } else if (str[i] == '\'' || str[i] == '"') { @@ -1597,9 +1855,12 @@ static int parse_pred(const char *str, void *data, goto err_free; } - pred->regex.len = len; - strncpy(pred->regex.pattern, str + s, len); - pred->regex.pattern[len] = 0; + pred->regex = kzalloc(sizeof(*pred->regex), GFP_KERNEL); + if (!pred->regex) + goto err_mem; + pred->regex->len = len; + strncpy(pred->regex->pattern, str + s, len); + pred->regex->pattern[len] = 0; filter_build_regex(pred); @@ -1608,7 +1869,7 @@ static int parse_pred(const char *str, void *data, } else if (field->filter_type == FILTER_STATIC_STRING) { pred->fn_num = FILTER_PRED_FN_STRING; - pred->regex.field_len = field->size; + pred->regex->field_len = field->size; } else if (field->filter_type == FILTER_DYN_STRING) { pred->fn_num = FILTER_PRED_FN_STRLOC; @@ -1691,10 +1952,10 @@ static int parse_pred(const char *str, void *data, return i; err_free: - kfree(pred); + free_predicate(pred); return -EINVAL; err_mem: - kfree(pred); + free_predicate(pred); return -ENOMEM; } @@ -2287,8 +2548,8 @@ static int ftrace_function_set_filter_pred(struct filter_pred *pred, return ret; return __ftrace_function_set_filter(pred->op == OP_EQ, - pred->regex.pattern, - pred->regex.len, + pred->regex->pattern, + pred->regex->len, data); } diff --git a/kernel/trace/trace_events_inject.c b/kernel/trace/trace_events_inject.c index abe805d471eb82e22d66f09573bd5f28ec8b2def..8650562bdaa9886e59a519aec516fc4b9209f0b5 100644 --- a/kernel/trace/trace_events_inject.c +++ b/kernel/trace/trace_events_inject.c @@ -328,7 +328,8 @@ event_inject_read(struct file *file, char __user *buf, size_t size, } const struct file_operations event_inject_fops = { - .open = tracing_open_generic, + .open = tracing_open_file_tr, .read = event_inject_read, .write = event_inject_write, + .release = tracing_release_file_tr, }; diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index 9897d0bfcab7101a54c4dd01a698d505dcb74d90..14cb275a0bab01ba4e816af6e4df810aa830bee5 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -337,7 +337,7 @@ static void print_synth_event_num_val(struct trace_seq *s, break; default: - trace_seq_printf(s, print_fmt, name, val, space); + trace_seq_printf(s, print_fmt, name, val->as_u64, space); break; } } diff --git a/kernel/trace/trace_events_user.c b/kernel/trace/trace_events_user.c index 33cb6af31f39572c6b7a491a204c85fd68b52ddc..b87f41187c6a9252be444f00f42049cf3941455e 100644 --- a/kernel/trace/trace_events_user.c +++ b/kernel/trace/trace_events_user.c @@ -127,8 +127,13 @@ struct user_event_enabler { /* Bit 7 is for freeing status of enablement */ #define ENABLE_VAL_FREEING_BIT 7 -/* Only duplicate the bit value */ -#define ENABLE_VAL_DUP_MASK ENABLE_VAL_BIT_MASK +/* Bit 8 is for marking 32-bit on 64-bit */ +#define ENABLE_VAL_32_ON_64_BIT 8 + +#define ENABLE_VAL_COMPAT_MASK (1 << ENABLE_VAL_32_ON_64_BIT) + +/* Only duplicate the bit and compat values */ +#define ENABLE_VAL_DUP_MASK (ENABLE_VAL_BIT_MASK | ENABLE_VAL_COMPAT_MASK) #define ENABLE_BITOPS(e) (&(e)->values) @@ -174,6 +179,30 @@ struct user_event_validator { int flags; }; +static inline void align_addr_bit(unsigned long *addr, int *bit, + unsigned long *flags) +{ + if (IS_ALIGNED(*addr, sizeof(long))) { +#ifdef __BIG_ENDIAN + /* 32 bit on BE 64 bit requires a 32 bit offset when aligned. */ + if (test_bit(ENABLE_VAL_32_ON_64_BIT, flags)) + *bit += 32; +#endif + return; + } + + *addr = ALIGN_DOWN(*addr, sizeof(long)); + + /* + * We only support 32 and 64 bit values. The only time we need + * to align is a 32 bit value on a 64 bit kernel, which on LE + * is always 32 bits, and on BE requires no change when unaligned. + */ +#ifdef __LITTLE_ENDIAN + *bit += 32; +#endif +} + typedef void (*user_event_func_t) (struct user_event *user, struct iov_iter *i, void *tpdata, bool *faulted); @@ -482,6 +511,7 @@ static int user_event_enabler_write(struct user_event_mm *mm, unsigned long *ptr; struct page *page; void *kaddr; + int bit = ENABLE_BIT(enabler); int ret; lockdep_assert_held(&event_mutex); @@ -497,6 +527,8 @@ static int user_event_enabler_write(struct user_event_mm *mm, test_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler)))) return -EBUSY; + align_addr_bit(&uaddr, &bit, ENABLE_BITOPS(enabler)); + ret = pin_user_pages_remote(mm->mm, uaddr, 1, FOLL_WRITE | FOLL_NOFAULT, &page, NULL); @@ -515,9 +547,9 @@ static int user_event_enabler_write(struct user_event_mm *mm, /* Update bit atomically, user tracers must be atomic as well */ if (enabler->event && enabler->event->status) - set_bit(ENABLE_BIT(enabler), ptr); + set_bit(bit, ptr); else - clear_bit(ENABLE_BIT(enabler), ptr); + clear_bit(bit, ptr); kunmap_local(kaddr); unpin_user_pages_dirty_lock(&page, 1, true); @@ -849,6 +881,12 @@ static struct user_event_enabler enabler->event = user; enabler->addr = uaddr; enabler->values = reg->enable_bit; + +#if BITS_PER_LONG >= 64 + if (reg->enable_size == 4) + set_bit(ENABLE_VAL_32_ON_64_BIT, ENABLE_BITOPS(enabler)); +#endif + retry: /* Prevents state changes from racing with new enablers */ mutex_lock(&event_mutex); @@ -1328,14 +1366,14 @@ static int user_field_set_string(struct ftrace_event_field *field, static int user_event_set_print_fmt(struct user_event *user, char *buf, int len) { - struct ftrace_event_field *field, *next; + struct ftrace_event_field *field; struct list_head *head = &user->fields; int pos = 0, depth = 0; const char *str_func; pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); - list_for_each_entry_safe_reverse(field, next, head, link) { + list_for_each_entry_reverse(field, head, link) { if (depth != 0) pos += snprintf(buf + pos, LEN_OR_ZERO, " "); @@ -1347,7 +1385,7 @@ static int user_event_set_print_fmt(struct user_event *user, char *buf, int len) pos += snprintf(buf + pos, LEN_OR_ZERO, "\""); - list_for_each_entry_safe_reverse(field, next, head, link) { + list_for_each_entry_reverse(field, head, link) { if (user_field_is_dyn_string(field->type, &str_func)) pos += snprintf(buf + pos, LEN_OR_ZERO, ", %s(%s)", str_func, field->name); @@ -1732,7 +1770,7 @@ static int user_event_create(const char *raw_command) static int user_event_show(struct seq_file *m, struct dyn_event *ev) { struct user_event *user = container_of(ev, struct user_event, devent); - struct ftrace_event_field *field, *next; + struct ftrace_event_field *field; struct list_head *head; int depth = 0; @@ -1740,7 +1778,7 @@ static int user_event_show(struct seq_file *m, struct dyn_event *ev) head = trace_get_fields(&user->call); - list_for_each_entry_safe_reverse(field, next, head, link) { + list_for_each_entry_reverse(field, head, link) { if (depth == 0) seq_puts(m, " "); else @@ -1816,13 +1854,14 @@ static bool user_field_match(struct ftrace_event_field *field, int argc, static bool user_fields_match(struct user_event *user, int argc, const char **argv) { - struct ftrace_event_field *field, *next; + struct ftrace_event_field *field; struct list_head *head = &user->fields; int i = 0; - list_for_each_entry_safe_reverse(field, next, head, link) + list_for_each_entry_reverse(field, head, link) { if (!user_field_match(field, argc, argv, &i)) return false; + } if (i != argc) return false; @@ -2376,7 +2415,8 @@ static long user_unreg_get(struct user_unreg __user *ureg, } static int user_event_mm_clear_bit(struct user_event_mm *user_mm, - unsigned long uaddr, unsigned char bit) + unsigned long uaddr, unsigned char bit, + unsigned long flags) { struct user_event_enabler enabler; int result; @@ -2384,7 +2424,7 @@ static int user_event_mm_clear_bit(struct user_event_mm *user_mm, memset(&enabler, 0, sizeof(enabler)); enabler.addr = uaddr; - enabler.values = bit; + enabler.values = bit | flags; retry: /* Prevents state changes from racing with new enablers */ mutex_lock(&event_mutex); @@ -2414,6 +2454,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) struct user_event_mm *mm = current->user_event_mm; struct user_event_enabler *enabler, *next; struct user_unreg reg; + unsigned long flags; long ret; ret = user_unreg_get(ureg, ®); @@ -2424,6 +2465,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) if (!mm) return -ENOENT; + flags = 0; ret = -ENOENT; /* @@ -2440,6 +2482,9 @@ static long user_events_ioctl_unreg(unsigned long uarg) ENABLE_BIT(enabler) == reg.disable_bit) { set_bit(ENABLE_VAL_FREEING_BIT, ENABLE_BITOPS(enabler)); + /* We must keep compat flags for the clear */ + flags |= enabler->values & ENABLE_VAL_COMPAT_MASK; + if (!test_bit(ENABLE_VAL_FAULTING_BIT, ENABLE_BITOPS(enabler))) user_event_enabler_destroy(enabler, true); @@ -2453,7 +2498,7 @@ static long user_events_ioctl_unreg(unsigned long uarg) /* Ensure bit is now cleared for user, regardless of event status */ if (!ret) ret = user_event_mm_clear_bit(mm, reg.disable_addr, - reg.disable_bit); + reg.disable_bit, flags); return ret; } diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index 58f3946081e21ae5998205f1171407ca5420e09e..1698fc22afa0a5d22e244ed4279001821a7ad138 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c @@ -51,6 +51,9 @@ static int ftrace_event_register(struct trace_event_call *call, #undef __array #define __array(type, item, size) type item[size]; +#undef __stack_array +#define __stack_array(type, item, size, field) __array(type, item, size) + #undef __array_desc #define __array_desc(type, container, item, size) type item[size]; @@ -114,6 +117,9 @@ static void __always_unused ____ftrace_check_##name(void) \ is_signed_type(_type), .filter_type = FILTER_OTHER, \ .len = _len }, +#undef __stack_array +#define __stack_array(_type, _item, _len, _field) __array(_type, _item, _len) + #undef __array_desc #define __array_desc(_type, _container, _item, _len) __array(_type, _item, _len) @@ -149,6 +155,9 @@ static struct trace_event_fields ftrace_event_fields_##name[] = { \ #undef __array #define __array(type, item, len) +#undef __stack_array +#define __stack_array(type, item, len, field) + #undef __array_desc #define __array_desc(type, container, item, len) diff --git a/kernel/trace/trace_fprobe.c b/kernel/trace/trace_fprobe.c index dfe2e546acdcd6dbea9996d4dd48f6fd6d9d44ae..8bfe23af9c739a5042e93b2bba956a6e366de2b9 100644 --- a/kernel/trace/trace_fprobe.c +++ b/kernel/trace/trace_fprobe.c @@ -898,6 +898,46 @@ static struct tracepoint *find_tracepoint(const char *tp_name) return data.tpoint; } +static int parse_symbol_and_return(int argc, const char *argv[], + char **symbol, bool *is_return, + bool is_tracepoint) +{ + char *tmp = strchr(argv[1], '%'); + int i; + + if (tmp) { + int len = tmp - argv[1]; + + if (!is_tracepoint && !strcmp(tmp, "%return")) { + *is_return = true; + } else { + trace_probe_log_err(len, BAD_ADDR_SUFFIX); + return -EINVAL; + } + *symbol = kmemdup_nul(argv[1], len, GFP_KERNEL); + } else + *symbol = kstrdup(argv[1], GFP_KERNEL); + if (!*symbol) + return -ENOMEM; + + if (*is_return) + return 0; + + /* If there is $retval, this should be a return fprobe. */ + for (i = 2; i < argc; i++) { + tmp = strstr(argv[i], "$retval"); + if (tmp && !isalnum(tmp[7]) && tmp[7] != '_') { + *is_return = true; + /* + * NOTE: Don't check is_tracepoint here, because it will + * be checked when the argument is parsed. + */ + break; + } + } + return 0; +} + static int __trace_fprobe_create(int argc, const char *argv[]) { /* @@ -927,7 +967,7 @@ static int __trace_fprobe_create(int argc, const char *argv[]) struct trace_fprobe *tf = NULL; int i, len, new_argc = 0, ret = 0; bool is_return = false; - char *symbol = NULL, *tmp = NULL; + char *symbol = NULL; const char *event = NULL, *group = FPROBE_EVENT_SYSTEM; const char **new_argv = NULL; int maxactive = 0; @@ -983,20 +1023,10 @@ static int __trace_fprobe_create(int argc, const char *argv[]) trace_probe_log_set_index(1); /* a symbol(or tracepoint) must be specified */ - symbol = kstrdup(argv[1], GFP_KERNEL); - if (!symbol) - return -ENOMEM; + ret = parse_symbol_and_return(argc, argv, &symbol, &is_return, is_tracepoint); + if (ret < 0) + goto parse_error; - tmp = strchr(symbol, '%'); - if (tmp) { - if (!is_tracepoint && !strcmp(tmp, "%return")) { - *tmp = '\0'; - is_return = true; - } else { - trace_probe_log_err(tmp - symbol, BAD_ADDR_SUFFIX); - goto parse_error; - } - } if (!is_return && maxactive) { trace_probe_log_set_index(0); trace_probe_log_err(1, BAD_MAXACT_TYPE); @@ -1096,6 +1126,7 @@ static int __trace_fprobe_create(int argc, const char *argv[]) } out: + traceprobe_finish_parse(&ctx); trace_probe_log_clear(); kfree(new_argv); kfree(symbol); diff --git a/kernel/trace/trace_hwlat.c b/kernel/trace/trace_hwlat.c index 2f37a6e68aa9f851f19b968eeafdb1034d3efae2..b791524a6536ac9178d5ae23e3036143d6f1d63d 100644 --- a/kernel/trace/trace_hwlat.c +++ b/kernel/trace/trace_hwlat.c @@ -635,7 +635,7 @@ static int s_mode_show(struct seq_file *s, void *v) else seq_printf(s, "%s", thread_mode_str[mode]); - if (mode != MODE_MAX) + if (mode < MODE_MAX - 1) /* if mode is any but last */ seq_puts(s, " "); return 0; diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 17c21c0b2dd1e0869f53a82e8c55a036ececcb79..3d7a180a84272e891458c5274daa2897a91f867c 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -907,6 +907,7 @@ static int __trace_kprobe_create(int argc, const char *argv[]) } out: + traceprobe_finish_parse(&ctx); trace_probe_log_clear(); kfree(new_argv); kfree(symbol); diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index c68a727078525ce7d867ae4e6eb2097a74eaf869..4dc74d73fc1df5af9ee297611865705d44259663 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "trace_probe: " fmt #include +#include "trace_btf.h" #include "trace_probe.h" @@ -304,31 +305,90 @@ static int parse_trace_event_arg(char *arg, struct fetch_insn *code, #ifdef CONFIG_PROBE_EVENTS_BTF_ARGS -static struct btf *traceprobe_get_btf(void) +static u32 btf_type_int(const struct btf_type *t) { - struct btf *btf = bpf_get_btf_vmlinux(); + return *(u32 *)(t + 1); +} - if (IS_ERR_OR_NULL(btf)) - return NULL; +static bool btf_type_is_char_ptr(struct btf *btf, const struct btf_type *type) +{ + const struct btf_type *real_type; + u32 intdata; + s32 tid; + + real_type = btf_type_skip_modifiers(btf, type->type, &tid); + if (!real_type) + return false; + + if (BTF_INFO_KIND(real_type->info) != BTF_KIND_INT) + return false; - return btf; + intdata = btf_type_int(real_type); + return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) + && BTF_INT_BITS(intdata) == 8; } -static u32 btf_type_int(const struct btf_type *t) +static bool btf_type_is_char_array(struct btf *btf, const struct btf_type *type) { - return *(u32 *)(t + 1); + const struct btf_type *real_type; + const struct btf_array *array; + u32 intdata; + s32 tid; + + if (BTF_INFO_KIND(type->info) != BTF_KIND_ARRAY) + return false; + + array = (const struct btf_array *)(type + 1); + + real_type = btf_type_skip_modifiers(btf, array->type, &tid); + + intdata = btf_type_int(real_type); + return !(BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) + && BTF_INT_BITS(intdata) == 8; } -static const char *type_from_btf_id(struct btf *btf, s32 id) +static int check_prepare_btf_string_fetch(char *typename, + struct fetch_insn **pcode, + struct traceprobe_parse_context *ctx) +{ + struct btf *btf = ctx->btf; + + if (!btf || !ctx->last_type) + return 0; + + /* char [] does not need any change. */ + if (btf_type_is_char_array(btf, ctx->last_type)) + return 0; + + /* char * requires dereference the pointer. */ + if (btf_type_is_char_ptr(btf, ctx->last_type)) { + struct fetch_insn *code = *pcode + 1; + + if (code->op == FETCH_OP_END) { + trace_probe_log_err(ctx->offset, TOO_MANY_OPS); + return -E2BIG; + } + if (typename[0] == 'u') + code->op = FETCH_OP_UDEREF; + else + code->op = FETCH_OP_DEREF; + code->offset = 0; + *pcode = code; + return 0; + } + /* Other types are not available for string */ + trace_probe_log_err(ctx->offset, BAD_TYPE4STR); + return -EINVAL; +} + +static const char *fetch_type_from_btf_type(struct btf *btf, + const struct btf_type *type, + struct traceprobe_parse_context *ctx) { - const struct btf_type *t; u32 intdata; - s32 tid; /* TODO: const char * could be converted as a string */ - t = btf_type_skip_modifiers(btf, id, &tid); - - switch (BTF_INFO_KIND(t->info)) { + switch (BTF_INFO_KIND(type->info)) { case BTF_KIND_ENUM: /* enum is "int", so convert to "s32" */ return "s32"; @@ -341,7 +401,7 @@ static const char *type_from_btf_id(struct btf *btf, s32 id) else return "x32"; case BTF_KIND_INT: - intdata = btf_type_int(t); + intdata = btf_type_int(type); if (BTF_INT_ENCODING(intdata) & BTF_INT_SIGNED) { switch (BTF_INT_BITS(intdata)) { case 8: @@ -364,6 +424,10 @@ static const char *type_from_btf_id(struct btf *btf, s32 id) case 64: return "u64"; } + /* bitfield, size is encoded in the type */ + ctx->last_bitsize = BTF_INT_BITS(intdata); + ctx->last_bitoffs += BTF_INT_OFFSET(intdata); + return "u64"; } } /* TODO: support other types */ @@ -371,88 +435,223 @@ static const char *type_from_btf_id(struct btf *btf, s32 id) return NULL; } -static const struct btf_type *find_btf_func_proto(const char *funcname) +static int query_btf_context(struct traceprobe_parse_context *ctx) { - struct btf *btf = traceprobe_get_btf(); - const struct btf_type *t; - s32 id; + const struct btf_param *param; + const struct btf_type *type; + struct btf *btf; + s32 nr; - if (!btf || !funcname) - return ERR_PTR(-EINVAL); + if (ctx->btf) + return 0; + + if (!ctx->funcname) + return -EINVAL; + + type = btf_find_func_proto(ctx->funcname, &btf); + if (!type) + return -ENOENT; - id = btf_find_by_name_kind(btf, funcname, BTF_KIND_FUNC); - if (id <= 0) - return ERR_PTR(-ENOENT); + ctx->btf = btf; + ctx->proto = type; + + /* ctx->params is optional, since func(void) will not have params. */ + nr = 0; + param = btf_get_func_param(type, &nr); + if (!IS_ERR_OR_NULL(param)) { + /* Hide the first 'data' argument of tracepoint */ + if (ctx->flags & TPARG_FL_TPOINT) { + nr--; + param++; + } + } - /* Get BTF_KIND_FUNC type */ - t = btf_type_by_id(btf, id); - if (!t || !btf_type_is_func(t)) - return ERR_PTR(-ENOENT); + if (nr > 0) { + ctx->nr_params = nr; + ctx->params = param; + } else { + ctx->nr_params = 0; + ctx->params = NULL; + } - /* The type of BTF_KIND_FUNC is BTF_KIND_FUNC_PROTO */ - t = btf_type_by_id(btf, t->type); - if (!t || !btf_type_is_func_proto(t)) - return ERR_PTR(-ENOENT); + return 0; +} - return t; +static void clear_btf_context(struct traceprobe_parse_context *ctx) +{ + if (ctx->btf) { + btf_put(ctx->btf); + ctx->btf = NULL; + ctx->proto = NULL; + ctx->params = NULL; + ctx->nr_params = 0; + } } -static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr, - bool tracepoint) +/* Return 1 if the field separater is arrow operator ('->') */ +static int split_next_field(char *varname, char **next_field, + struct traceprobe_parse_context *ctx) { - const struct btf_param *param; - const struct btf_type *t; + char *field; + int ret = 0; + + field = strpbrk(varname, ".-"); + if (field) { + if (field[0] == '-' && field[1] == '>') { + field[0] = '\0'; + field += 2; + ret = 1; + } else if (field[0] == '.') { + field[0] = '\0'; + field += 1; + } else { + trace_probe_log_err(ctx->offset + field - varname, BAD_HYPHEN); + return -EINVAL; + } + *next_field = field; + } - if (!funcname || !nr) - return ERR_PTR(-EINVAL); + return ret; +} - t = find_btf_func_proto(funcname); - if (IS_ERR(t)) - return (const struct btf_param *)t; +/* + * Parse the field of data structure. The @type must be a pointer type + * pointing the target data structure type. + */ +static int parse_btf_field(char *fieldname, const struct btf_type *type, + struct fetch_insn **pcode, struct fetch_insn *end, + struct traceprobe_parse_context *ctx) +{ + struct fetch_insn *code = *pcode; + const struct btf_member *field; + u32 bitoffs, anon_offs; + char *next; + int is_ptr; + s32 tid; - *nr = btf_type_vlen(t); - param = (const struct btf_param *)(t + 1); + do { + /* Outer loop for solving arrow operator ('->') */ + if (BTF_INFO_KIND(type->info) != BTF_KIND_PTR) { + trace_probe_log_err(ctx->offset, NO_PTR_STRCT); + return -EINVAL; + } + /* Convert a struct pointer type to a struct type */ + type = btf_type_skip_modifiers(ctx->btf, type->type, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } - /* Hide the first 'data' argument of tracepoint */ - if (tracepoint) { - (*nr)--; - param++; - } + bitoffs = 0; + do { + /* Inner loop for solving dot operator ('.') */ + next = NULL; + is_ptr = split_next_field(fieldname, &next, ctx); + if (is_ptr < 0) + return is_ptr; + + anon_offs = 0; + field = btf_find_struct_member(ctx->btf, type, fieldname, + &anon_offs); + if (!field) { + trace_probe_log_err(ctx->offset, NO_BTF_FIELD); + return -ENOENT; + } + /* Add anonymous structure/union offset */ + bitoffs += anon_offs; + + /* Accumulate the bit-offsets of the dot-connected fields */ + if (btf_type_kflag(type)) { + bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset); + ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset); + } else { + bitoffs += field->offset; + ctx->last_bitsize = 0; + } - if (*nr > 0) - return param; - else - return NULL; + type = btf_type_skip_modifiers(ctx->btf, field->type, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; + } + + ctx->offset += next - fieldname; + fieldname = next; + } while (!is_ptr && fieldname); + + if (++code == end) { + trace_probe_log_err(ctx->offset, TOO_MANY_OPS); + return -EINVAL; + } + code->op = FETCH_OP_DEREF; /* TODO: user deref support */ + code->offset = bitoffs / 8; + *pcode = code; + + ctx->last_bitoffs = bitoffs % 8; + ctx->last_type = type; + } while (fieldname); + + return 0; } -static int parse_btf_arg(const char *varname, struct fetch_insn *code, +static int parse_btf_arg(char *varname, + struct fetch_insn **pcode, struct fetch_insn *end, struct traceprobe_parse_context *ctx) { - struct btf *btf = traceprobe_get_btf(); + struct fetch_insn *code = *pcode; const struct btf_param *params; - int i; + const struct btf_type *type; + char *field = NULL; + int i, is_ptr, ret; + u32 tid; + + if (WARN_ON_ONCE(!ctx->funcname)) + return -EINVAL; - if (!btf) { - trace_probe_log_err(ctx->offset, NOSUP_BTFARG); + is_ptr = split_next_field(varname, &field, ctx); + if (is_ptr < 0) + return is_ptr; + if (!is_ptr && field) { + /* dot-connected field on an argument is not supported. */ + trace_probe_log_err(ctx->offset + field - varname, + NOSUP_DAT_ARG); return -EOPNOTSUPP; } - if (WARN_ON_ONCE(!ctx->funcname)) - return -EINVAL; + if (ctx->flags & TPARG_FL_RETURN) { + if (strcmp(varname, "$retval") != 0) { + trace_probe_log_err(ctx->offset, NO_BTFARG); + return -ENOENT; + } + code->op = FETCH_OP_RETVAL; + /* Check whether the function return type is not void */ + if (query_btf_context(ctx) == 0) { + if (ctx->proto->type == 0) { + trace_probe_log_err(ctx->offset, NO_RETVAL); + return -ENOENT; + } + tid = ctx->proto->type; + goto found; + } + if (field) { + trace_probe_log_err(ctx->offset + field - varname, + NO_BTF_ENTRY); + return -ENOENT; + } + return 0; + } - if (!ctx->params) { - params = find_btf_func_param(ctx->funcname, &ctx->nr_params, - ctx->flags & TPARG_FL_TPOINT); - if (IS_ERR_OR_NULL(params)) { + if (!ctx->btf) { + ret = query_btf_context(ctx); + if (ret < 0 || ctx->nr_params == 0) { trace_probe_log_err(ctx->offset, NO_BTF_ENTRY); return PTR_ERR(params); } - ctx->params = params; - } else - params = ctx->params; + } + params = ctx->params; for (i = 0; i < ctx->nr_params; i++) { - const char *name = btf_name_by_offset(btf, params[i].name_off); + const char *name = btf_name_by_offset(ctx->btf, params[i].name_off); if (name && !strcmp(name, varname)) { code->op = FETCH_OP_ARG; @@ -460,91 +659,114 @@ static int parse_btf_arg(const char *varname, struct fetch_insn *code, code->param = i + 1; else code->param = i; - return 0; + tid = params[i].type; + goto found; } } trace_probe_log_err(ctx->offset, NO_BTFARG); return -ENOENT; -} - -static const struct fetch_type *parse_btf_arg_type(int arg_idx, - struct traceprobe_parse_context *ctx) -{ - struct btf *btf = traceprobe_get_btf(); - const char *typestr = NULL; - if (btf && ctx->params) { - if (ctx->flags & TPARG_FL_TPOINT) - arg_idx--; - typestr = type_from_btf_id(btf, ctx->params[arg_idx].type); +found: + type = btf_type_skip_modifiers(ctx->btf, tid, &tid); + if (!type) { + trace_probe_log_err(ctx->offset, BAD_BTF_TID); + return -EINVAL; } - - return find_fetch_type(typestr, ctx->flags); + /* Initialize the last type information */ + ctx->last_type = type; + ctx->last_bitoffs = 0; + ctx->last_bitsize = 0; + if (field) { + ctx->offset += field - varname; + return parse_btf_field(field, type, pcode, end, ctx); + } + return 0; } -static const struct fetch_type *parse_btf_retval_type( +static const struct fetch_type *find_fetch_type_from_btf_type( struct traceprobe_parse_context *ctx) { - struct btf *btf = traceprobe_get_btf(); + struct btf *btf = ctx->btf; const char *typestr = NULL; - const struct btf_type *t; - if (btf && ctx->funcname) { - t = find_btf_func_proto(ctx->funcname); - if (!IS_ERR(t)) - typestr = type_from_btf_id(btf, t->type); - } + if (btf && ctx->last_type) + typestr = fetch_type_from_btf_type(btf, ctx->last_type, ctx); return find_fetch_type(typestr, ctx->flags); } -static bool is_btf_retval_void(const char *funcname) +static int parse_btf_bitfield(struct fetch_insn **pcode, + struct traceprobe_parse_context *ctx) { - const struct btf_type *t; + struct fetch_insn *code = *pcode; - t = find_btf_func_proto(funcname); - if (IS_ERR(t)) - return false; + if ((ctx->last_bitsize % 8 == 0) && ctx->last_bitoffs == 0) + return 0; + + code++; + if (code->op != FETCH_OP_NOP) { + trace_probe_log_err(ctx->offset, TOO_MANY_OPS); + return -EINVAL; + } + *pcode = code; - return t->type == 0; + code->op = FETCH_OP_MOD_BF; + code->lshift = 64 - (ctx->last_bitsize + ctx->last_bitoffs); + code->rshift = 64 - ctx->last_bitsize; + code->basesize = 64 / 8; + return 0; } + #else -static struct btf *traceprobe_get_btf(void) +static void clear_btf_context(struct traceprobe_parse_context *ctx) { - return NULL; + ctx->btf = NULL; } -static const struct btf_param *find_btf_func_param(const char *funcname, s32 *nr, - bool tracepoint) +static int query_btf_context(struct traceprobe_parse_context *ctx) { - return ERR_PTR(-EOPNOTSUPP); + return -EOPNOTSUPP; } -static int parse_btf_arg(const char *varname, struct fetch_insn *code, +static int parse_btf_arg(char *varname, + struct fetch_insn **pcode, struct fetch_insn *end, struct traceprobe_parse_context *ctx) { trace_probe_log_err(ctx->offset, NOSUP_BTFARG); return -EOPNOTSUPP; } -#define parse_btf_arg_type(idx, ctx) \ - find_fetch_type(NULL, ctx->flags) +static int parse_btf_bitfield(struct fetch_insn **pcode, + struct traceprobe_parse_context *ctx) +{ + trace_probe_log_err(ctx->offset, NOSUP_BTFARG); + return -EOPNOTSUPP; +} -#define parse_btf_retval_type(ctx) \ +#define find_fetch_type_from_btf_type(ctx) \ find_fetch_type(NULL, ctx->flags) -#define is_btf_retval_void(funcname) (false) +static int check_prepare_btf_string_fetch(char *typename, + struct fetch_insn **pcode, + struct traceprobe_parse_context *ctx) +{ + return 0; +} #endif #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) -static int parse_probe_vars(char *arg, const struct fetch_type *t, - struct fetch_insn *code, +/* Parse $vars. @orig_arg points '$', which syncs to @ctx->offset */ +static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, + struct fetch_insn **pcode, + struct fetch_insn *end, struct traceprobe_parse_context *ctx) { - unsigned long param; + struct fetch_insn *code = *pcode; int err = TP_ERR_BAD_VAR; + char *arg = orig_arg + 1; + unsigned long param; int ret = 0; int len; @@ -563,18 +785,17 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t, goto inval; } - if (strcmp(arg, "retval") == 0) { - if (ctx->flags & TPARG_FL_RETURN) { - if ((ctx->flags & TPARG_FL_KERNEL) && - is_btf_retval_void(ctx->funcname)) { - err = TP_ERR_NO_RETVAL; - goto inval; - } + if (str_has_prefix(arg, "retval")) { + if (!(ctx->flags & TPARG_FL_RETURN)) { + err = TP_ERR_RETVAL_ON_PROBE; + goto inval; + } + if (!(ctx->flags & TPARG_FL_KERNEL) || + !IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS)) { code->op = FETCH_OP_RETVAL; return 0; } - err = TP_ERR_RETVAL_ON_PROBE; - goto inval; + return parse_btf_arg(orig_arg, pcode, end, ctx); } len = str_has_prefix(arg, "stack"); @@ -676,7 +897,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, switch (arg[0]) { case '$': - ret = parse_probe_vars(arg + 1, type, code, ctx); + ret = parse_probe_vars(arg, type, pcode, end, ctx); break; case '%': /* named register */ @@ -795,6 +1016,8 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = deref; code->offset = offset; + /* Reset the last type if used */ + ctx->last_type = NULL; } break; case '\\': /* Immediate value */ @@ -818,7 +1041,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, trace_probe_log_err(ctx->offset, NOSUP_BTFARG); return -EINVAL; } - ret = parse_btf_arg(arg, code, ctx); + ret = parse_btf_arg(arg, pcode, end, ctx); break; } } @@ -964,17 +1187,22 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, goto out; code[FETCH_INSN_MAX - 1].op = FETCH_OP_END; + ctx->last_type = NULL; ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1], ctx); if (ret) goto fail; /* Update storing type if BTF is available */ - if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && !t) { - if (code->op == FETCH_OP_ARG) - parg->type = parse_btf_arg_type(code->param, ctx); - else if (code->op == FETCH_OP_RETVAL) - parg->type = parse_btf_retval_type(ctx); + if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && + ctx->last_type) { + if (!t) { + parg->type = find_fetch_type_from_btf_type(ctx); + } else if (strstr(t, "string")) { + ret = check_prepare_btf_string_fetch(t, &code, ctx); + if (ret) + goto fail; + } } ret = -EINVAL; @@ -1048,6 +1276,11 @@ static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size, trace_probe_log_err(ctx->offset + t - arg, BAD_BITFIELD); goto fail; } + } else if (IS_ENABLED(CONFIG_PROBE_EVENTS_BTF_ARGS) && + ctx->last_type) { + ret = parse_btf_bitfield(&code, ctx); + if (ret) + goto fail; } ret = -EINVAL; /* Loop(Array) operation */ @@ -1231,7 +1464,6 @@ static int sprint_nth_btf_arg(int idx, const char *type, char *buf, int bufsize, struct traceprobe_parse_context *ctx) { - struct btf *btf = traceprobe_get_btf(); const char *name; int ret; @@ -1239,7 +1471,7 @@ static int sprint_nth_btf_arg(int idx, const char *type, trace_probe_log_err(0, NO_BTFARG); return -ENOENT; } - name = btf_name_by_offset(btf, ctx->params[idx].name_off); + name = btf_name_by_offset(ctx->btf, ctx->params[idx].name_off); if (!name) { trace_probe_log_err(0, NO_BTF_ENTRY); return -ENOENT; @@ -1260,7 +1492,6 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], const struct btf_param *params = NULL; int i, j, n, used, ret, args_idx = -1; const char **new_argv = NULL; - int nr_params; ret = argv_has_var_arg(argc, argv, &args_idx, ctx); if (ret < 0) @@ -1271,9 +1502,8 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], return NULL; } - params = find_btf_func_param(ctx->funcname, &nr_params, - ctx->flags & TPARG_FL_TPOINT); - if (IS_ERR_OR_NULL(params)) { + ret = query_btf_context(ctx); + if (ret < 0 || ctx->nr_params == 0) { if (args_idx != -1) { /* $arg* requires BTF info */ trace_probe_log_err(0, NOSUP_BTFARG); @@ -1282,8 +1512,6 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], *new_argc = argc; return NULL; } - ctx->params = params; - ctx->nr_params = nr_params; if (args_idx >= 0) *new_argc = argc + ctx->nr_params - 1; @@ -1298,7 +1526,7 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], for (i = 0, j = 0; i < argc; i++) { trace_probe_log_set_index(i + 2); if (i == args_idx) { - for (n = 0; n < nr_params; n++) { + for (n = 0; n < ctx->nr_params; n++) { ret = sprint_nth_btf_arg(n, "", buf + used, bufsize - used, ctx); if (ret < 0) @@ -1337,6 +1565,11 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], return ERR_PTR(ret); } +void traceprobe_finish_parse(struct traceprobe_parse_context *ctx) +{ + clear_btf_context(ctx); +} + int traceprobe_update_arg(struct probe_arg *arg) { struct fetch_insn *code = arg->code; diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 7dde806be91ef5f60480cde8b51f3e8d4b6dd1f1..02b432ae7513137922d90a73621dad3071ec0fb5 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -383,9 +383,15 @@ static inline bool tparg_is_function_entry(unsigned int flags) struct traceprobe_parse_context { struct trace_event_call *event; - const struct btf_param *params; - s32 nr_params; - const char *funcname; + /* BTF related parameters */ + const char *funcname; /* Function name in BTF */ + const struct btf_type *proto; /* Prototype of the function */ + const struct btf_param *params; /* Parameter of the function */ + s32 nr_params; /* The number of the parameters */ + struct btf *btf; /* The BTF to be used */ + const struct btf_type *last_type; /* Saved type */ + u32 last_bitoffs; /* Saved bitoffs */ + u32 last_bitsize; /* Saved bitsize */ unsigned int flags; int offset; }; @@ -400,6 +406,12 @@ const char **traceprobe_expand_meta_args(int argc, const char *argv[], extern int traceprobe_update_arg(struct probe_arg *arg); extern void traceprobe_free_probe_arg(struct probe_arg *arg); +/* + * If either traceprobe_parse_probe_arg() or traceprobe_expand_meta_args() is called, + * this MUST be called for clean up the context and return a resource. + */ +void traceprobe_finish_parse(struct traceprobe_parse_context *ctx); + extern int traceprobe_split_symbol_offset(char *symbol, long *offset); int traceprobe_parse_event_name(const char **pevent, const char **pgroup, char *buf, int offset); @@ -495,7 +507,14 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(BAD_VAR_ARGS, "$arg* must be an independent parameter without name etc."),\ C(NOFENTRY_ARGS, "$arg* can be used only on function entry"), \ C(DOUBLE_ARGS, "$arg* can be used only once in the parameters"), \ - C(ARGS_2LONG, "$arg* failed because the argument list is too long"), + C(ARGS_2LONG, "$arg* failed because the argument list is too long"), \ + C(ARGIDX_2BIG, "$argN index is too big"), \ + C(NO_PTR_STRCT, "This is not a pointer to union/structure."), \ + C(NOSUP_DAT_ARG, "Non pointer structure/union argument is not supported."),\ + C(BAD_HYPHEN, "Failed to parse single hyphen. Forgot '>'?"), \ + C(NO_BTF_FIELD, "This field is not found."), \ + C(BAD_BTF_TID, "Failed to get BTF type info."),\ + C(BAD_TYPE4STR, "This type does not fit for string."), #undef C #define C(a, b) TP_ERR_##a diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 576b3bcb8ebd332ac1e1edf9ab21b972c616e99c..99c051de412afa4d6a433d1250a7abbd0d2deb3f 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -688,6 +688,7 @@ static int __trace_uprobe_create(int argc, const char **argv) trace_probe_log_set_index(i + 2); ret = traceprobe_parse_probe_arg(&tu->tp, i, argv[i], &ctx); + traceprobe_finish_parse(&ctx); if (ret) goto error; } diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 800b4208dba9a0ebf0a748f40b8366edc86f6a29..b9f053a5a5f07157a06ce08d60c9516faccb7d21 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -122,11 +122,6 @@ enum { * * L: pool->lock protected. Access with pool->lock held. * - * X: During normal operation, modification requires pool->lock and should - * be done only from local cpu. Either disabling preemption on local - * cpu or grabbing pool->lock is enough for read access. If - * POOL_DISASSOCIATED is set, it's identical to L. - * * K: Only modified by worker while holding pool->lock. Can be safely read by * self, while holding pool->lock or from IRQ context if %current is the * kworker. @@ -160,7 +155,7 @@ struct worker_pool { int cpu; /* I: the associated cpu */ int node; /* I: the associated node ID */ int id; /* I: pool ID */ - unsigned int flags; /* X: flags */ + unsigned int flags; /* L: flags */ unsigned long watchdog_ts; /* L: watchdog timestamp */ bool cpu_stall; /* WD: stalled cpu bound pool */ @@ -216,6 +211,7 @@ enum pool_workqueue_stats { PWQ_STAT_CPU_TIME, /* total CPU time consumed */ PWQ_STAT_CPU_INTENSIVE, /* wq_cpu_intensive_thresh_us violations */ PWQ_STAT_CM_WAKEUP, /* concurrency-management worker wakeups */ + PWQ_STAT_REPATRIATED, /* unbound workers brought back into scope */ PWQ_STAT_MAYDAY, /* maydays to rescuer */ PWQ_STAT_RESCUED, /* linked work items executed by rescuer */ @@ -262,12 +258,12 @@ struct pool_workqueue { u64 stats[PWQ_NR_STATS]; /* - * Release of unbound pwq is punted to system_wq. See put_pwq() - * and pwq_unbound_release_workfn() for details. pool_workqueue - * itself is also RCU protected so that the first pwq can be - * determined without grabbing wq->mutex. + * Release of unbound pwq is punted to a kthread_worker. See put_pwq() + * and pwq_release_workfn() for details. pool_workqueue itself is also + * RCU protected so that the first pwq can be determined without + * grabbing wq->mutex. */ - struct work_struct unbound_release_work; + struct kthread_work release_work; struct rcu_head rcu; } __aligned(1 << WORK_STRUCT_FLAG_BITS); @@ -326,14 +322,33 @@ struct workqueue_struct { /* hot fields used during command issue, aligned to cacheline */ unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */ - struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */ - struct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */ + struct pool_workqueue __percpu __rcu **cpu_pwq; /* I: per-cpu pwqs */ }; static struct kmem_cache *pwq_cache; -static cpumask_var_t *wq_numa_possible_cpumask; - /* possible CPUs of each node */ +/* + * Each pod type describes how CPUs should be grouped for unbound workqueues. + * See the comment above workqueue_attrs->affn_scope. + */ +struct wq_pod_type { + int nr_pods; /* number of pods */ + cpumask_var_t *pod_cpus; /* pod -> cpus */ + int *pod_node; /* pod -> node */ + int *cpu_pod; /* cpu -> pod */ +}; + +static struct wq_pod_type wq_pod_types[WQ_AFFN_NR_TYPES]; +static enum wq_affn_scope wq_affn_dfl = WQ_AFFN_CACHE; + +static const char *wq_affn_names[WQ_AFFN_NR_TYPES] = { + [WQ_AFFN_DFL] = "default", + [WQ_AFFN_CPU] = "cpu", + [WQ_AFFN_SMT] = "smt", + [WQ_AFFN_CACHE] = "cache", + [WQ_AFFN_NUMA] = "numa", + [WQ_AFFN_SYSTEM] = "system", +}; /* * Per-cpu work items which run for longer than the following threshold are @@ -345,19 +360,14 @@ static cpumask_var_t *wq_numa_possible_cpumask; static unsigned long wq_cpu_intensive_thresh_us = ULONG_MAX; module_param_named(cpu_intensive_thresh_us, wq_cpu_intensive_thresh_us, ulong, 0644); -static bool wq_disable_numa; -module_param_named(disable_numa, wq_disable_numa, bool, 0444); - /* see the comment above the definition of WQ_POWER_EFFICIENT */ static bool wq_power_efficient = IS_ENABLED(CONFIG_WQ_POWER_EFFICIENT_DEFAULT); module_param_named(power_efficient, wq_power_efficient, bool, 0444); static bool wq_online; /* can kworkers be created yet? */ -static bool wq_numa_enabled; /* unbound NUMA affinity enabled */ - -/* buf for wq_update_unbound_numa_attrs(), protected by CPU hotplug exclusion */ -static struct workqueue_attrs *wq_update_unbound_numa_attrs_buf; +/* buf for wq_update_unbound_pod_attrs(), protected by CPU hotplug exclusion */ +static struct workqueue_attrs *wq_update_pod_attrs_buf; static DEFINE_MUTEX(wq_pool_mutex); /* protects pools and workqueues list */ static DEFINE_MUTEX(wq_pool_attach_mutex); /* protects worker attach/detach */ @@ -371,6 +381,9 @@ static bool workqueue_freezing; /* PL: have wqs started freezing? */ /* PL&A: allowable cpus for unbound wqs and work items */ static cpumask_var_t wq_unbound_cpumask; +/* for further constrain wq_unbound_cpumask by cmdline parameter*/ +static struct cpumask wq_cmdline_cpumask __initdata; + /* CPU where unbound work was last round robin scheduled from this CPU */ static DEFINE_PER_CPU(int, wq_rr_cpu_last); @@ -400,6 +413,13 @@ static struct workqueue_attrs *unbound_std_wq_attrs[NR_STD_WORKER_POOLS]; /* I: attributes used when instantiating ordered pools on demand */ static struct workqueue_attrs *ordered_wq_attrs[NR_STD_WORKER_POOLS]; +/* + * I: kthread_worker to release pwq's. pwq release needs to be bounced to a + * process context while holding a pool lock. Bounce to a dedicated kthread + * worker to avoid A-A deadlocks. + */ +static struct kthread_worker *pwq_release_worker; + struct workqueue_struct *system_wq __read_mostly; EXPORT_SYMBOL(system_wq); struct workqueue_struct *system_highpri_wq __read_mostly; @@ -606,35 +626,6 @@ static int worker_pool_assign_id(struct worker_pool *pool) return ret; } -/** - * unbound_pwq_by_node - return the unbound pool_workqueue for the given node - * @wq: the target workqueue - * @node: the node ID - * - * This must be called with any of wq_pool_mutex, wq->mutex or RCU - * read locked. - * If the pwq needs to be used beyond the locking in effect, the caller is - * responsible for guaranteeing that the pwq stays online. - * - * Return: The unbound pool_workqueue for @node. - */ -static struct pool_workqueue *unbound_pwq_by_node(struct workqueue_struct *wq, - int node) -{ - assert_rcu_or_wq_mutex_or_pool_mutex(wq); - - /* - * XXX: @node can be NUMA_NO_NODE if CPU goes offline while a - * delayed item is pending. The plan is to keep CPU -> NODE - * mapping valid and stable across CPU on/offlines. Once that - * happens, this workaround can be removed. - */ - if (unlikely(node == NUMA_NO_NODE)) - return wq->dfl_pwq; - - return rcu_dereference_raw(wq->numa_pwq_tbl[node]); -} - static unsigned int work_color_to_flags(int color) { return color << WORK_STRUCT_COLOR_SHIFT; @@ -825,11 +816,6 @@ static bool work_is_canceling(struct work_struct *work) * they're being called with pool->lock held. */ -static bool __need_more_worker(struct worker_pool *pool) -{ - return !pool->nr_running; -} - /* * Need to wake up a worker? Called from anything but currently * running workers. @@ -840,7 +826,7 @@ static bool __need_more_worker(struct worker_pool *pool) */ static bool need_more_worker(struct worker_pool *pool) { - return !list_empty(&pool->worklist) && __need_more_worker(pool); + return !list_empty(&pool->worklist) && !pool->nr_running; } /* Can I start working? Called from busy but !running workers. */ @@ -871,51 +857,18 @@ static bool too_many_workers(struct worker_pool *pool) return nr_idle > 2 && (nr_idle - 2) * MAX_IDLE_WORKERS_RATIO >= nr_busy; } -/* - * Wake up functions. - */ - -/* Return the first idle worker. Called with pool->lock held. */ -static struct worker *first_idle_worker(struct worker_pool *pool) -{ - if (unlikely(list_empty(&pool->idle_list))) - return NULL; - - return list_first_entry(&pool->idle_list, struct worker, entry); -} - -/** - * wake_up_worker - wake up an idle worker - * @pool: worker pool to wake worker from - * - * Wake up the first idle worker of @pool. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock). - */ -static void wake_up_worker(struct worker_pool *pool) -{ - struct worker *worker = first_idle_worker(pool); - - if (likely(worker)) - wake_up_process(worker->task); -} - /** * worker_set_flags - set worker flags and adjust nr_running accordingly * @worker: self * @flags: flags to set * * Set @flags in @worker->flags and adjust nr_running accordingly. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock) */ static inline void worker_set_flags(struct worker *worker, unsigned int flags) { struct worker_pool *pool = worker->pool; - WARN_ON_ONCE(worker->task != current); + lockdep_assert_held(&pool->lock); /* If transitioning into NOT_RUNNING, adjust nr_running. */ if ((flags & WORKER_NOT_RUNNING) && @@ -932,16 +885,13 @@ static inline void worker_set_flags(struct worker *worker, unsigned int flags) * @flags: flags to clear * * Clear @flags in @worker->flags and adjust nr_running accordingly. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock) */ static inline void worker_clr_flags(struct worker *worker, unsigned int flags) { struct worker_pool *pool = worker->pool; unsigned int oflags = worker->flags; - WARN_ON_ONCE(worker->task != current); + lockdep_assert_held(&pool->lock); worker->flags &= ~flags; @@ -955,6 +905,244 @@ static inline void worker_clr_flags(struct worker *worker, unsigned int flags) pool->nr_running++; } +/* Return the first idle worker. Called with pool->lock held. */ +static struct worker *first_idle_worker(struct worker_pool *pool) +{ + if (unlikely(list_empty(&pool->idle_list))) + return NULL; + + return list_first_entry(&pool->idle_list, struct worker, entry); +} + +/** + * worker_enter_idle - enter idle state + * @worker: worker which is entering idle state + * + * @worker is entering idle state. Update stats and idle timer if + * necessary. + * + * LOCKING: + * raw_spin_lock_irq(pool->lock). + */ +static void worker_enter_idle(struct worker *worker) +{ + struct worker_pool *pool = worker->pool; + + if (WARN_ON_ONCE(worker->flags & WORKER_IDLE) || + WARN_ON_ONCE(!list_empty(&worker->entry) && + (worker->hentry.next || worker->hentry.pprev))) + return; + + /* can't use worker_set_flags(), also called from create_worker() */ + worker->flags |= WORKER_IDLE; + pool->nr_idle++; + worker->last_active = jiffies; + + /* idle_list is LIFO */ + list_add(&worker->entry, &pool->idle_list); + + if (too_many_workers(pool) && !timer_pending(&pool->idle_timer)) + mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT); + + /* Sanity check nr_running. */ + WARN_ON_ONCE(pool->nr_workers == pool->nr_idle && pool->nr_running); +} + +/** + * worker_leave_idle - leave idle state + * @worker: worker which is leaving idle state + * + * @worker is leaving idle state. Update stats. + * + * LOCKING: + * raw_spin_lock_irq(pool->lock). + */ +static void worker_leave_idle(struct worker *worker) +{ + struct worker_pool *pool = worker->pool; + + if (WARN_ON_ONCE(!(worker->flags & WORKER_IDLE))) + return; + worker_clr_flags(worker, WORKER_IDLE); + pool->nr_idle--; + list_del_init(&worker->entry); +} + +/** + * find_worker_executing_work - find worker which is executing a work + * @pool: pool of interest + * @work: work to find worker for + * + * Find a worker which is executing @work on @pool by searching + * @pool->busy_hash which is keyed by the address of @work. For a worker + * to match, its current execution should match the address of @work and + * its work function. This is to avoid unwanted dependency between + * unrelated work executions through a work item being recycled while still + * being executed. + * + * This is a bit tricky. A work item may be freed once its execution + * starts and nothing prevents the freed area from being recycled for + * another work item. If the same work item address ends up being reused + * before the original execution finishes, workqueue will identify the + * recycled work item as currently executing and make it wait until the + * current execution finishes, introducing an unwanted dependency. + * + * This function checks the work item address and work function to avoid + * false positives. Note that this isn't complete as one may construct a + * work function which can introduce dependency onto itself through a + * recycled work item. Well, if somebody wants to shoot oneself in the + * foot that badly, there's only so much we can do, and if such deadlock + * actually occurs, it should be easy to locate the culprit work function. + * + * CONTEXT: + * raw_spin_lock_irq(pool->lock). + * + * Return: + * Pointer to worker which is executing @work if found, %NULL + * otherwise. + */ +static struct worker *find_worker_executing_work(struct worker_pool *pool, + struct work_struct *work) +{ + struct worker *worker; + + hash_for_each_possible(pool->busy_hash, worker, hentry, + (unsigned long)work) + if (worker->current_work == work && + worker->current_func == work->func) + return worker; + + return NULL; +} + +/** + * move_linked_works - move linked works to a list + * @work: start of series of works to be scheduled + * @head: target list to append @work to + * @nextp: out parameter for nested worklist walking + * + * Schedule linked works starting from @work to @head. Work series to be + * scheduled starts at @work and includes any consecutive work with + * WORK_STRUCT_LINKED set in its predecessor. See assign_work() for details on + * @nextp. + * + * CONTEXT: + * raw_spin_lock_irq(pool->lock). + */ +static void move_linked_works(struct work_struct *work, struct list_head *head, + struct work_struct **nextp) +{ + struct work_struct *n; + + /* + * Linked worklist will always end before the end of the list, + * use NULL for list head. + */ + list_for_each_entry_safe_from(work, n, NULL, entry) { + list_move_tail(&work->entry, head); + if (!(*work_data_bits(work) & WORK_STRUCT_LINKED)) + break; + } + + /* + * If we're already inside safe list traversal and have moved + * multiple works to the scheduled queue, the next position + * needs to be updated. + */ + if (nextp) + *nextp = n; +} + +/** + * assign_work - assign a work item and its linked work items to a worker + * @work: work to assign + * @worker: worker to assign to + * @nextp: out parameter for nested worklist walking + * + * Assign @work and its linked work items to @worker. If @work is already being + * executed by another worker in the same pool, it'll be punted there. + * + * If @nextp is not NULL, it's updated to point to the next work of the last + * scheduled work. This allows assign_work() to be nested inside + * list_for_each_entry_safe(). + * + * Returns %true if @work was successfully assigned to @worker. %false if @work + * was punted to another worker already executing it. + */ +static bool assign_work(struct work_struct *work, struct worker *worker, + struct work_struct **nextp) +{ + struct worker_pool *pool = worker->pool; + struct worker *collision; + + lockdep_assert_held(&pool->lock); + + /* + * A single work shouldn't be executed concurrently by multiple workers. + * __queue_work() ensures that @work doesn't jump to a different pool + * while still running in the previous pool. Here, we should ensure that + * @work is not executed concurrently by multiple workers from the same + * pool. Check whether anyone is already processing the work. If so, + * defer the work to the currently executing one. + */ + collision = find_worker_executing_work(pool, work); + if (unlikely(collision)) { + move_linked_works(work, &collision->scheduled, nextp); + return false; + } + + move_linked_works(work, &worker->scheduled, nextp); + return true; +} + +/** + * kick_pool - wake up an idle worker if necessary + * @pool: pool to kick + * + * @pool may have pending work items. Wake up worker if necessary. Returns + * whether a worker was woken up. + */ +static bool kick_pool(struct worker_pool *pool) +{ + struct worker *worker = first_idle_worker(pool); + struct task_struct *p; + + lockdep_assert_held(&pool->lock); + + if (!need_more_worker(pool) || !worker) + return false; + + p = worker->task; + +#ifdef CONFIG_SMP + /* + * Idle @worker is about to execute @work and waking up provides an + * opportunity to migrate @worker at a lower cost by setting the task's + * wake_cpu field. Let's see if we want to move @worker to improve + * execution locality. + * + * We're waking the worker that went idle the latest and there's some + * chance that @worker is marked idle but hasn't gone off CPU yet. If + * so, setting the wake_cpu won't do anything. As this is a best-effort + * optimization and the race window is narrow, let's leave as-is for + * now. If this becomes pronounced, we can skip over workers which are + * still on cpu when picking an idle worker. + * + * If @pool has non-strict affinity, @worker might have ended up outside + * its affinity scope. Repatriate. + */ + if (!pool->attrs->affn_strict && + !cpumask_test_cpu(p->wake_cpu, pool->attrs->__pod_cpumask)) { + struct work_struct *work = list_first_entry(&pool->worklist, + struct work_struct, entry); + p->wake_cpu = cpumask_any_distribute(pool->attrs->__pod_cpumask); + get_work_pwq(work)->stats[PWQ_STAT_REPATRIATED]++; + } +#endif + wake_up_process(p); + return true; +} + #ifdef CONFIG_WQ_CPU_INTENSIVE_REPORT /* @@ -1120,10 +1308,9 @@ void wq_worker_sleeping(struct task_struct *task) } pool->nr_running--; - if (need_more_worker(pool)) { + if (kick_pool(pool)) worker->current_pwq->stats[PWQ_STAT_CM_WAKEUP]++; - wake_up_worker(pool); - } + raw_spin_unlock_irq(&pool->lock); } @@ -1171,10 +1358,8 @@ void wq_worker_tick(struct task_struct *task) wq_cpu_intensive_report(worker->current_func); pwq->stats[PWQ_STAT_CPU_INTENSIVE]++; - if (need_more_worker(pool)) { + if (kick_pool(pool)) pwq->stats[PWQ_STAT_CM_WAKEUP]++; - wake_up_worker(pool); - } raw_spin_unlock(&pool->lock); } @@ -1210,94 +1395,6 @@ work_func_t wq_worker_last_func(struct task_struct *task) return worker->last_func; } -/** - * find_worker_executing_work - find worker which is executing a work - * @pool: pool of interest - * @work: work to find worker for - * - * Find a worker which is executing @work on @pool by searching - * @pool->busy_hash which is keyed by the address of @work. For a worker - * to match, its current execution should match the address of @work and - * its work function. This is to avoid unwanted dependency between - * unrelated work executions through a work item being recycled while still - * being executed. - * - * This is a bit tricky. A work item may be freed once its execution - * starts and nothing prevents the freed area from being recycled for - * another work item. If the same work item address ends up being reused - * before the original execution finishes, workqueue will identify the - * recycled work item as currently executing and make it wait until the - * current execution finishes, introducing an unwanted dependency. - * - * This function checks the work item address and work function to avoid - * false positives. Note that this isn't complete as one may construct a - * work function which can introduce dependency onto itself through a - * recycled work item. Well, if somebody wants to shoot oneself in the - * foot that badly, there's only so much we can do, and if such deadlock - * actually occurs, it should be easy to locate the culprit work function. - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock). - * - * Return: - * Pointer to worker which is executing @work if found, %NULL - * otherwise. - */ -static struct worker *find_worker_executing_work(struct worker_pool *pool, - struct work_struct *work) -{ - struct worker *worker; - - hash_for_each_possible(pool->busy_hash, worker, hentry, - (unsigned long)work) - if (worker->current_work == work && - worker->current_func == work->func) - return worker; - - return NULL; -} - -/** - * move_linked_works - move linked works to a list - * @work: start of series of works to be scheduled - * @head: target list to append @work to - * @nextp: out parameter for nested worklist walking - * - * Schedule linked works starting from @work to @head. Work series to - * be scheduled starts at @work and includes any consecutive work with - * WORK_STRUCT_LINKED set in its predecessor. - * - * If @nextp is not NULL, it's updated to point to the next work of - * the last scheduled work. This allows move_linked_works() to be - * nested inside outer list_for_each_entry_safe(). - * - * CONTEXT: - * raw_spin_lock_irq(pool->lock). - */ -static void move_linked_works(struct work_struct *work, struct list_head *head, - struct work_struct **nextp) -{ - struct work_struct *n; - - /* - * Linked worklist will always end before the end of the list, - * use NULL for list head. - */ - list_for_each_entry_safe_from(work, n, NULL, entry) { - list_move_tail(&work->entry, head); - if (!(*work_data_bits(work) & WORK_STRUCT_LINKED)) - break; - } - - /* - * If we're already inside safe list traversal and have moved - * multiple works to the scheduled queue, the next position - * needs to be updated. - */ - if (nextp) - *nextp = n; -} - /** * get_pwq - get an extra reference on the specified pool_workqueue * @pwq: pool_workqueue to get @@ -1324,17 +1421,11 @@ static void put_pwq(struct pool_workqueue *pwq) lockdep_assert_held(&pwq->pool->lock); if (likely(--pwq->refcnt)) return; - if (WARN_ON_ONCE(!(pwq->wq->flags & WQ_UNBOUND))) - return; /* - * @pwq can't be released under pool->lock, bounce to - * pwq_unbound_release_workfn(). This never recurses on the same - * pool->lock as this path is taken only for unbound workqueues and - * the release work item is scheduled on a per-cpu workqueue. To - * avoid lockdep warning, unbound pool->locks are given lockdep - * subclass of 1 in get_unbound_pool(). + * @pwq can't be released under pool->lock, bounce to a dedicated + * kthread_worker to avoid A-A deadlocks. */ - schedule_work(&pwq->unbound_release_work); + kthread_queue_work(pwq_release_worker, &pwq->release_work); } /** @@ -1550,7 +1641,7 @@ static int try_to_grab_pending(struct work_struct *work, bool is_dwork, static void insert_work(struct pool_workqueue *pwq, struct work_struct *work, struct list_head *head, unsigned int extra_flags) { - struct worker_pool *pool = pwq->pool; + debug_work_activate(work); /* record the work call stack in order to print it in KASAN reports */ kasan_record_aux_stack_noalloc(work); @@ -1559,9 +1650,6 @@ static void insert_work(struct pool_workqueue *pwq, struct work_struct *work, set_work_pwq(work, pwq, extra_flags); list_add_tail(&work->entry, head); get_pwq(pwq); - - if (__need_more_worker(pool)) - wake_up_worker(pool); } /* @@ -1615,8 +1703,7 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, struct work_struct *work) { struct pool_workqueue *pwq; - struct worker_pool *last_pool; - struct list_head *worklist; + struct worker_pool *last_pool, *pool; unsigned int work_flags; unsigned int req_cpu = cpu; @@ -1640,23 +1727,23 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, rcu_read_lock(); retry: /* pwq which will be used unless @work is executing elsewhere */ - if (wq->flags & WQ_UNBOUND) { - if (req_cpu == WORK_CPU_UNBOUND) + if (req_cpu == WORK_CPU_UNBOUND) { + if (wq->flags & WQ_UNBOUND) cpu = wq_select_unbound_cpu(raw_smp_processor_id()); - pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu)); - } else { - if (req_cpu == WORK_CPU_UNBOUND) + else cpu = raw_smp_processor_id(); - pwq = per_cpu_ptr(wq->cpu_pwqs, cpu); } + pwq = rcu_dereference(*per_cpu_ptr(wq->cpu_pwq, cpu)); + pool = pwq->pool; + /* * If @work was previously on a different pool, it might still be * running there, in which case the work needs to be queued on that * pool to guarantee non-reentrancy. */ last_pool = get_work_pool(work); - if (last_pool && last_pool != pwq->pool) { + if (last_pool && last_pool != pool) { struct worker *worker; raw_spin_lock(&last_pool->lock); @@ -1665,26 +1752,27 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, if (worker && worker->current_pwq->wq == wq) { pwq = worker->current_pwq; + pool = pwq->pool; + WARN_ON_ONCE(pool != last_pool); } else { /* meh... not running there, queue here */ raw_spin_unlock(&last_pool->lock); - raw_spin_lock(&pwq->pool->lock); + raw_spin_lock(&pool->lock); } } else { - raw_spin_lock(&pwq->pool->lock); + raw_spin_lock(&pool->lock); } /* - * pwq is determined and locked. For unbound pools, we could have - * raced with pwq release and it could already be dead. If its - * refcnt is zero, repeat pwq selection. Note that pwqs never die - * without another pwq replacing it in the numa_pwq_tbl or while - * work items are executing on it, so the retrying is guaranteed to - * make forward-progress. + * pwq is determined and locked. For unbound pools, we could have raced + * with pwq release and it could already be dead. If its refcnt is zero, + * repeat pwq selection. Note that unbound pwqs never die without + * another pwq replacing it in cpu_pwq or while work items are executing + * on it, so the retrying is guaranteed to make forward-progress. */ if (unlikely(!pwq->refcnt)) { if (wq->flags & WQ_UNBOUND) { - raw_spin_unlock(&pwq->pool->lock); + raw_spin_unlock(&pool->lock); cpu_relax(); goto retry; } @@ -1703,21 +1791,20 @@ static void __queue_work(int cpu, struct workqueue_struct *wq, work_flags = work_color_to_flags(pwq->work_color); if (likely(pwq->nr_active < pwq->max_active)) { + if (list_empty(&pool->worklist)) + pool->watchdog_ts = jiffies; + trace_workqueue_activate_work(work); pwq->nr_active++; - worklist = &pwq->pool->worklist; - if (list_empty(worklist)) - pwq->pool->watchdog_ts = jiffies; + insert_work(pwq, work, &pool->worklist, work_flags); + kick_pool(pool); } else { work_flags |= WORK_STRUCT_INACTIVE; - worklist = &pwq->inactive_works; + insert_work(pwq, work, &pwq->inactive_works, work_flags); } - debug_work_activate(work); - insert_work(pwq, work, worklist, work_flags); - out: - raw_spin_unlock(&pwq->pool->lock); + raw_spin_unlock(&pool->lock); rcu_read_unlock(); } @@ -1754,7 +1841,7 @@ bool queue_work_on(int cpu, struct workqueue_struct *wq, EXPORT_SYMBOL(queue_work_on); /** - * workqueue_select_cpu_near - Select a CPU based on NUMA node + * select_numa_node_cpu - Select a CPU based on NUMA node * @node: NUMA node ID that we want to select a CPU from * * This function will attempt to find a "random" cpu available on a given @@ -1762,14 +1849,10 @@ EXPORT_SYMBOL(queue_work_on); * WORK_CPU_UNBOUND indicating that we should just schedule to any * available CPU if we need to schedule this work. */ -static int workqueue_select_cpu_near(int node) +static int select_numa_node_cpu(int node) { int cpu; - /* No point in doing this if NUMA isn't enabled for workqueues */ - if (!wq_numa_enabled) - return WORK_CPU_UNBOUND; - /* Delay binding to CPU if node is not valid or online */ if (node < 0 || node >= MAX_NUMNODES || !node_online(node)) return WORK_CPU_UNBOUND; @@ -1826,7 +1909,7 @@ bool queue_work_node(int node, struct workqueue_struct *wq, local_irq_save(flags); if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) { - int cpu = workqueue_select_cpu_near(node); + int cpu = select_numa_node_cpu(node); __queue_work(cpu, wq, work); ret = true; @@ -1981,60 +2064,6 @@ bool queue_rcu_work(struct workqueue_struct *wq, struct rcu_work *rwork) } EXPORT_SYMBOL(queue_rcu_work); -/** - * worker_enter_idle - enter idle state - * @worker: worker which is entering idle state - * - * @worker is entering idle state. Update stats and idle timer if - * necessary. - * - * LOCKING: - * raw_spin_lock_irq(pool->lock). - */ -static void worker_enter_idle(struct worker *worker) -{ - struct worker_pool *pool = worker->pool; - - if (WARN_ON_ONCE(worker->flags & WORKER_IDLE) || - WARN_ON_ONCE(!list_empty(&worker->entry) && - (worker->hentry.next || worker->hentry.pprev))) - return; - - /* can't use worker_set_flags(), also called from create_worker() */ - worker->flags |= WORKER_IDLE; - pool->nr_idle++; - worker->last_active = jiffies; - - /* idle_list is LIFO */ - list_add(&worker->entry, &pool->idle_list); - - if (too_many_workers(pool) && !timer_pending(&pool->idle_timer)) - mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT); - - /* Sanity check nr_running. */ - WARN_ON_ONCE(pool->nr_workers == pool->nr_idle && pool->nr_running); -} - -/** - * worker_leave_idle - leave idle state - * @worker: worker which is leaving idle state - * - * @worker is leaving idle state. Update stats. - * - * LOCKING: - * raw_spin_lock_irq(pool->lock). - */ -static void worker_leave_idle(struct worker *worker) -{ - struct worker_pool *pool = worker->pool; - - if (WARN_ON_ONCE(!(worker->flags & WORKER_IDLE))) - return; - worker_clr_flags(worker, WORKER_IDLE); - pool->nr_idle--; - list_del_init(&worker->entry); -} - static struct worker *alloc_worker(int node) { struct worker *worker; @@ -2050,6 +2079,14 @@ static struct worker *alloc_worker(int node) return worker; } +static cpumask_t *pool_allowed_cpus(struct worker_pool *pool) +{ + if (pool->cpu < 0 && pool->attrs->affn_strict) + return pool->attrs->__pod_cpumask; + else + return pool->attrs->cpumask; +} + /** * worker_attach_to_pool() - attach a worker to a pool * @worker: worker to be attached @@ -2075,7 +2112,7 @@ static void worker_attach_to_pool(struct worker *worker, kthread_set_per_cpu(worker->task, pool->cpu); if (worker->rescue_wq) - set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask); + set_cpus_allowed_ptr(worker->task, pool_allowed_cpus(pool)); list_add_tail(&worker->node, &pool->workers); worker->pool = pool; @@ -2167,16 +2204,25 @@ static struct worker *create_worker(struct worker_pool *pool) } set_user_nice(worker->task, pool->attrs->nice); - kthread_bind_mask(worker->task, pool->attrs->cpumask); + kthread_bind_mask(worker->task, pool_allowed_cpus(pool)); /* successful, attach the worker to the pool */ worker_attach_to_pool(worker, pool); /* start the newly created worker */ raw_spin_lock_irq(&pool->lock); + worker->pool->nr_workers++; worker_enter_idle(worker); + kick_pool(pool); + + /* + * @worker is waiting on a completion in kthread() and will trigger hung + * check if not woken up soon. As kick_pool() might not have waken it + * up, wake it up explicitly once more. + */ wake_up_process(worker->task); + raw_spin_unlock_irq(&pool->lock); return worker; @@ -2304,9 +2350,8 @@ static void idle_worker_timeout(struct timer_list *t) static void idle_cull_fn(struct work_struct *work) { struct worker_pool *pool = container_of(work, struct worker_pool, idle_cull_work); - struct list_head cull_list; + LIST_HEAD(cull_list); - INIT_LIST_HEAD(&cull_list); /* * Grabbing wq_pool_attach_mutex here ensures an already-running worker * cannot proceed beyong worker_detach_from_pool() in its self-destruct @@ -2495,7 +2540,6 @@ __acquires(&pool->lock) struct pool_workqueue *pwq = get_work_pwq(work); struct worker_pool *pool = worker->pool; unsigned long work_data; - struct worker *collision; #ifdef CONFIG_LOCKDEP /* * It is permissible to free the struct work_struct from @@ -2512,18 +2556,6 @@ __acquires(&pool->lock) WARN_ON_ONCE(!(pool->flags & POOL_DISASSOCIATED) && raw_smp_processor_id() != pool->cpu); - /* - * A single work shouldn't be executed concurrently by - * multiple workers on a single cpu. Check whether anyone is - * already processing the work. If so, defer the work to the - * currently executing one. - */ - collision = find_worker_executing_work(pool, work); - if (unlikely(collision)) { - move_linked_works(work, &collision->scheduled, NULL); - return; - } - /* claim and dequeue */ debug_work_deactivate(work); hash_add(pool->busy_hash, &worker->hentry, (unsigned long)work); @@ -2552,14 +2584,12 @@ __acquires(&pool->lock) worker_set_flags(worker, WORKER_CPU_INTENSIVE); /* - * Wake up another worker if necessary. The condition is always - * false for normal per-cpu workers since nr_running would always - * be >= 1 at this point. This is used to chain execution of the - * pending work items for WORKER_NOT_RUNNING workers such as the - * UNBOUND and CPU_INTENSIVE ones. + * Kick @pool if necessary. It's always noop for per-cpu worker pools + * since nr_running would always be >= 1 at this point. This is used to + * chain execution of the pending work items for WORKER_NOT_RUNNING + * workers such as the UNBOUND and CPU_INTENSIVE ones. */ - if (need_more_worker(pool)) - wake_up_worker(pool); + kick_pool(pool); /* * Record the last pool and clear PENDING which should be the last @@ -2569,6 +2599,7 @@ __acquires(&pool->lock) */ set_work_pool_and_clear_pending(work, pool->id); + pwq->stats[PWQ_STAT_STARTED]++; raw_spin_unlock_irq(&pool->lock); lock_map_acquire(&pwq->wq->lockdep_map); @@ -2595,7 +2626,6 @@ __acquires(&pool->lock) * workqueues), so hiding them isn't a problem. */ lockdep_invariant_state(true); - pwq->stats[PWQ_STAT_STARTED]++; trace_workqueue_execute_start(work); worker->current_func(work); /* @@ -2661,9 +2691,15 @@ __acquires(&pool->lock) */ static void process_scheduled_works(struct worker *worker) { - while (!list_empty(&worker->scheduled)) { - struct work_struct *work = list_first_entry(&worker->scheduled, - struct work_struct, entry); + struct work_struct *work; + bool first = true; + + while ((work = list_first_entry_or_null(&worker->scheduled, + struct work_struct, entry))) { + if (first) { + worker->pool->watchdog_ts = jiffies; + first = false; + } process_one_work(worker, work); } } @@ -2744,17 +2780,8 @@ static int worker_thread(void *__worker) list_first_entry(&pool->worklist, struct work_struct, entry); - pool->watchdog_ts = jiffies; - - if (likely(!(*work_data_bits(work) & WORK_STRUCT_LINKED))) { - /* optimization path, not strictly necessary */ - process_one_work(worker, work); - if (unlikely(!list_empty(&worker->scheduled))) - process_scheduled_works(worker); - } else { - move_linked_works(work, &worker->scheduled, NULL); + if (assign_work(work, worker, NULL)) process_scheduled_works(worker); - } } while (keep_working(pool)); worker_set_flags(worker, WORKER_PREP); @@ -2798,7 +2825,6 @@ static int rescuer_thread(void *__rescuer) { struct worker *rescuer = __rescuer; struct workqueue_struct *wq = rescuer->rescue_wq; - struct list_head *scheduled = &rescuer->scheduled; bool should_stop; set_user_nice(current, RESCUER_NICE_LEVEL); @@ -2829,7 +2855,6 @@ static int rescuer_thread(void *__rescuer) struct pool_workqueue, mayday_node); struct worker_pool *pool = pwq->pool; struct work_struct *work, *n; - bool first = true; __set_current_state(TASK_RUNNING); list_del_init(&pwq->mayday_node); @@ -2844,18 +2869,14 @@ static int rescuer_thread(void *__rescuer) * Slurp in all works issued via this workqueue and * process'em. */ - WARN_ON_ONCE(!list_empty(scheduled)); + WARN_ON_ONCE(!list_empty(&rescuer->scheduled)); list_for_each_entry_safe(work, n, &pool->worklist, entry) { - if (get_work_pwq(work) == pwq) { - if (first) - pool->watchdog_ts = jiffies; - move_linked_works(work, scheduled, &n); + if (get_work_pwq(work) == pwq && + assign_work(work, rescuer, &n)) pwq->stats[PWQ_STAT_RESCUED]++; - } - first = false; } - if (!list_empty(scheduled)) { + if (!list_empty(&rescuer->scheduled)) { process_scheduled_works(rescuer); /* @@ -2888,12 +2909,10 @@ static int rescuer_thread(void *__rescuer) put_pwq(pwq); /* - * Leave this pool. If need_more_worker() is %true, notify a - * regular worker; otherwise, we end up with 0 concurrency - * and stalling the execution. + * Leave this pool. Notify regular workers; otherwise, we end up + * with 0 concurrency and stalling the execution. */ - if (need_more_worker(pool)) - wake_up_worker(pool); + kick_pool(pool); raw_spin_unlock_irq(&pool->lock); @@ -3028,7 +3047,6 @@ static void insert_wq_barrier(struct pool_workqueue *pwq, pwq->nr_in_flight[work_color]++; work_flags |= work_color_to_flags(work_color); - debug_work_activate(&barr->work); insert_work(pwq, &barr->work, head, work_flags); } @@ -3691,6 +3709,7 @@ void free_workqueue_attrs(struct workqueue_attrs *attrs) { if (attrs) { free_cpumask_var(attrs->cpumask); + free_cpumask_var(attrs->__pod_cpumask); kfree(attrs); } } @@ -3712,8 +3731,11 @@ struct workqueue_attrs *alloc_workqueue_attrs(void) goto fail; if (!alloc_cpumask_var(&attrs->cpumask, GFP_KERNEL)) goto fail; + if (!alloc_cpumask_var(&attrs->__pod_cpumask, GFP_KERNEL)) + goto fail; cpumask_copy(attrs->cpumask, cpu_possible_mask); + attrs->affn_scope = WQ_AFFN_DFL; return attrs; fail: free_workqueue_attrs(attrs); @@ -3725,12 +3747,26 @@ static void copy_workqueue_attrs(struct workqueue_attrs *to, { to->nice = from->nice; cpumask_copy(to->cpumask, from->cpumask); + cpumask_copy(to->__pod_cpumask, from->__pod_cpumask); + to->affn_strict = from->affn_strict; + /* - * Unlike hash and equality test, this function doesn't ignore - * ->no_numa as it is used for both pool and wq attrs. Instead, - * get_unbound_pool() explicitly clears ->no_numa after copying. + * Unlike hash and equality test, copying shouldn't ignore wq-only + * fields as copying is used for both pool and wq attrs. Instead, + * get_unbound_pool() explicitly clears the fields. */ - to->no_numa = from->no_numa; + to->affn_scope = from->affn_scope; + to->ordered = from->ordered; +} + +/* + * Some attrs fields are workqueue-only. Clear them for worker_pool's. See the + * comments in 'struct workqueue_attrs' definition. + */ +static void wqattrs_clear_for_pool(struct workqueue_attrs *attrs) +{ + attrs->affn_scope = WQ_AFFN_NR_TYPES; + attrs->ordered = false; } /* hash value of the content of @attr */ @@ -3741,6 +3777,9 @@ static u32 wqattrs_hash(const struct workqueue_attrs *attrs) hash = jhash_1word(attrs->nice, hash); hash = jhash(cpumask_bits(attrs->cpumask), BITS_TO_LONGS(nr_cpumask_bits) * sizeof(long), hash); + hash = jhash(cpumask_bits(attrs->__pod_cpumask), + BITS_TO_LONGS(nr_cpumask_bits) * sizeof(long), hash); + hash = jhash_1word(attrs->affn_strict, hash); return hash; } @@ -3752,9 +3791,57 @@ static bool wqattrs_equal(const struct workqueue_attrs *a, return false; if (!cpumask_equal(a->cpumask, b->cpumask)) return false; + if (!cpumask_equal(a->__pod_cpumask, b->__pod_cpumask)) + return false; + if (a->affn_strict != b->affn_strict) + return false; return true; } +/* Update @attrs with actually available CPUs */ +static void wqattrs_actualize_cpumask(struct workqueue_attrs *attrs, + const cpumask_t *unbound_cpumask) +{ + /* + * Calculate the effective CPU mask of @attrs given @unbound_cpumask. If + * @attrs->cpumask doesn't overlap with @unbound_cpumask, we fallback to + * @unbound_cpumask. + */ + cpumask_and(attrs->cpumask, attrs->cpumask, unbound_cpumask); + if (unlikely(cpumask_empty(attrs->cpumask))) + cpumask_copy(attrs->cpumask, unbound_cpumask); +} + +/* find wq_pod_type to use for @attrs */ +static const struct wq_pod_type * +wqattrs_pod_type(const struct workqueue_attrs *attrs) +{ + enum wq_affn_scope scope; + struct wq_pod_type *pt; + + /* to synchronize access to wq_affn_dfl */ + lockdep_assert_held(&wq_pool_mutex); + + if (attrs->affn_scope == WQ_AFFN_DFL) + scope = wq_affn_dfl; + else + scope = attrs->affn_scope; + + pt = &wq_pod_types[scope]; + + if (!WARN_ON_ONCE(attrs->affn_scope == WQ_AFFN_NR_TYPES) && + likely(pt->nr_pods)) + return pt; + + /* + * Before workqueue_init_topology(), only SYSTEM is available which is + * initialized in workqueue_init_early(). + */ + pt = &wq_pod_types[WQ_AFFN_SYSTEM]; + BUG_ON(!pt->nr_pods); + return pt; +} + /** * init_worker_pool - initialize a newly zalloc'd worker_pool * @pool: worker_pool to initialize @@ -3793,6 +3880,9 @@ static int init_worker_pool(struct worker_pool *pool) pool->attrs = alloc_workqueue_attrs(); if (!pool->attrs) return -ENOMEM; + + wqattrs_clear_for_pool(pool->attrs); + return 0; } @@ -3840,12 +3930,8 @@ static void rcu_free_wq(struct rcu_head *rcu) container_of(rcu, struct workqueue_struct, rcu); wq_free_lockdep(wq); - - if (!(wq->flags & WQ_UNBOUND)) - free_percpu(wq->cpu_pwqs); - else - free_workqueue_attrs(wq->unbound_attrs); - + free_percpu(wq->cpu_pwq); + free_workqueue_attrs(wq->unbound_attrs); kfree(wq); } @@ -3872,10 +3958,8 @@ static void rcu_free_pool(struct rcu_head *rcu) static void put_unbound_pool(struct worker_pool *pool) { DECLARE_COMPLETION_ONSTACK(detach_completion); - struct list_head cull_list; struct worker *worker; - - INIT_LIST_HEAD(&cull_list); + LIST_HEAD(cull_list); lockdep_assert_held(&wq_pool_mutex); @@ -3959,10 +4043,10 @@ static void put_unbound_pool(struct worker_pool *pool) */ static struct worker_pool *get_unbound_pool(const struct workqueue_attrs *attrs) { + struct wq_pod_type *pt = &wq_pod_types[WQ_AFFN_NUMA]; u32 hash = wqattrs_hash(attrs); struct worker_pool *pool; - int node; - int target_node = NUMA_NO_NODE; + int pod, node = NUMA_NO_NODE; lockdep_assert_held(&wq_pool_mutex); @@ -3974,32 +4058,23 @@ static struct worker_pool *get_unbound_pool(const struct workqueue_attrs *attrs) } } - /* if cpumask is contained inside a NUMA node, we belong to that node */ - if (wq_numa_enabled) { - for_each_node(node) { - if (cpumask_subset(attrs->cpumask, - wq_numa_possible_cpumask[node])) { - target_node = node; - break; - } + /* If __pod_cpumask is contained inside a NUMA pod, that's our node */ + for (pod = 0; pod < pt->nr_pods; pod++) { + if (cpumask_subset(attrs->__pod_cpumask, pt->pod_cpus[pod])) { + node = pt->pod_node[pod]; + break; } } /* nope, create a new one */ - pool = kzalloc_node(sizeof(*pool), GFP_KERNEL, target_node); + pool = kzalloc_node(sizeof(*pool), GFP_KERNEL, node); if (!pool || init_worker_pool(pool) < 0) goto fail; - lockdep_set_subclass(&pool->lock, 1); /* see put_pwq() */ - copy_workqueue_attrs(pool->attrs, attrs); - pool->node = target_node; - - /* - * no_numa isn't a worker_pool attribute, always clear it. See - * 'struct workqueue_attrs' comments for detail. - */ - pool->attrs->no_numa = false; - + pool->node = node; + copy_workqueue_attrs(pool->attrs, attrs); + wqattrs_clear_for_pool(pool->attrs); + if (worker_pool_assign_id(pool) < 0) goto fail; @@ -4024,34 +4099,33 @@ static void rcu_free_pwq(struct rcu_head *rcu) } /* - * Scheduled on system_wq by put_pwq() when an unbound pwq hits zero refcnt - * and needs to be destroyed. + * Scheduled on pwq_release_worker by put_pwq() when an unbound pwq hits zero + * refcnt and needs to be destroyed. */ -static void pwq_unbound_release_workfn(struct work_struct *work) +static void pwq_release_workfn(struct kthread_work *work) { struct pool_workqueue *pwq = container_of(work, struct pool_workqueue, - unbound_release_work); + release_work); struct workqueue_struct *wq = pwq->wq; struct worker_pool *pool = pwq->pool; bool is_last = false; /* - * when @pwq is not linked, it doesn't hold any reference to the + * When @pwq is not linked, it doesn't hold any reference to the * @wq, and @wq is invalid to access. */ if (!list_empty(&pwq->pwqs_node)) { - if (WARN_ON_ONCE(!(wq->flags & WQ_UNBOUND))) - return; - mutex_lock(&wq->mutex); list_del_rcu(&pwq->pwqs_node); is_last = list_empty(&wq->pwqs); mutex_unlock(&wq->mutex); } - mutex_lock(&wq_pool_mutex); - put_unbound_pool(pool); - mutex_unlock(&wq_pool_mutex); + if (wq->flags & WQ_UNBOUND) { + mutex_lock(&wq_pool_mutex); + put_unbound_pool(pool); + mutex_unlock(&wq_pool_mutex); + } call_rcu(&pwq->rcu, rcu_free_pwq); @@ -4095,24 +4169,13 @@ static void pwq_adjust_max_active(struct pool_workqueue *pwq) * is updated and visible. */ if (!freezable || !workqueue_freezing) { - bool kick = false; - pwq->max_active = wq->saved_max_active; while (!list_empty(&pwq->inactive_works) && - pwq->nr_active < pwq->max_active) { + pwq->nr_active < pwq->max_active) pwq_activate_first_inactive(pwq); - kick = true; - } - /* - * Need to kick a worker after thawed or an unbound wq's - * max_active is bumped. In realtime scenarios, always kicking a - * worker will cause interference on the isolated cpu cores, so - * let's kick iff work items were activated. - */ - if (kick) - wake_up_worker(pwq->pool); + kick_pool(pwq->pool); } else { pwq->max_active = 0; } @@ -4135,7 +4198,7 @@ static void init_pwq(struct pool_workqueue *pwq, struct workqueue_struct *wq, INIT_LIST_HEAD(&pwq->inactive_works); INIT_LIST_HEAD(&pwq->pwqs_node); INIT_LIST_HEAD(&pwq->mayday_node); - INIT_WORK(&pwq->unbound_release_work, pwq_unbound_release_workfn); + kthread_init_work(&pwq->release_work, pwq_release_workfn); } /* sync @pwq with the current state of its associated wq and link it */ @@ -4183,61 +4246,49 @@ static struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq, } /** - * wq_calc_node_cpumask - calculate a wq_attrs' cpumask for the specified node + * wq_calc_pod_cpumask - calculate a wq_attrs' cpumask for a pod * @attrs: the wq_attrs of the default pwq of the target workqueue - * @node: the target NUMA node + * @cpu: the target CPU * @cpu_going_down: if >= 0, the CPU to consider as offline - * @cpumask: outarg, the resulting cpumask - * - * Calculate the cpumask a workqueue with @attrs should use on @node. If - * @cpu_going_down is >= 0, that cpu is considered offline during - * calculation. The result is stored in @cpumask. * - * If NUMA affinity is not enabled, @attrs->cpumask is always used. If - * enabled and @node has online CPUs requested by @attrs, the returned - * cpumask is the intersection of the possible CPUs of @node and - * @attrs->cpumask. + * Calculate the cpumask a workqueue with @attrs should use on @pod. If + * @cpu_going_down is >= 0, that cpu is considered offline during calculation. + * The result is stored in @attrs->__pod_cpumask. * - * The caller is responsible for ensuring that the cpumask of @node stays - * stable. + * If pod affinity is not enabled, @attrs->cpumask is always used. If enabled + * and @pod has online CPUs requested by @attrs, the returned cpumask is the + * intersection of the possible CPUs of @pod and @attrs->cpumask. * - * Return: %true if the resulting @cpumask is different from @attrs->cpumask, - * %false if equal. + * The caller is responsible for ensuring that the cpumask of @pod stays stable. */ -static bool wq_calc_node_cpumask(const struct workqueue_attrs *attrs, int node, - int cpu_going_down, cpumask_t *cpumask) +static void wq_calc_pod_cpumask(struct workqueue_attrs *attrs, int cpu, + int cpu_going_down) { - if (!wq_numa_enabled || attrs->no_numa) - goto use_dfl; + const struct wq_pod_type *pt = wqattrs_pod_type(attrs); + int pod = pt->cpu_pod[cpu]; - /* does @node have any online CPUs @attrs wants? */ - cpumask_and(cpumask, cpumask_of_node(node), attrs->cpumask); + /* does @pod have any online CPUs @attrs wants? */ + cpumask_and(attrs->__pod_cpumask, pt->pod_cpus[pod], attrs->cpumask); + cpumask_and(attrs->__pod_cpumask, attrs->__pod_cpumask, cpu_online_mask); if (cpu_going_down >= 0) - cpumask_clear_cpu(cpu_going_down, cpumask); + cpumask_clear_cpu(cpu_going_down, attrs->__pod_cpumask); - if (cpumask_empty(cpumask)) - goto use_dfl; + if (cpumask_empty(attrs->__pod_cpumask)) { + cpumask_copy(attrs->__pod_cpumask, attrs->cpumask); + return; + } - /* yeap, return possible CPUs in @node that @attrs wants */ - cpumask_and(cpumask, attrs->cpumask, wq_numa_possible_cpumask[node]); + /* yeap, return possible CPUs in @pod that @attrs wants */ + cpumask_and(attrs->__pod_cpumask, attrs->cpumask, pt->pod_cpus[pod]); - if (cpumask_empty(cpumask)) { + if (cpumask_empty(attrs->__pod_cpumask)) pr_warn_once("WARNING: workqueue cpumask: online intersect > " "possible intersect\n"); - return false; - } - - return !cpumask_equal(cpumask, attrs->cpumask); - -use_dfl: - cpumask_copy(cpumask, attrs->cpumask); - return false; } -/* install @pwq into @wq's numa_pwq_tbl[] for @node and return the old pwq */ -static struct pool_workqueue *numa_pwq_tbl_install(struct workqueue_struct *wq, - int node, - struct pool_workqueue *pwq) +/* install @pwq into @wq's cpu_pwq and return the old pwq */ +static struct pool_workqueue *install_unbound_pwq(struct workqueue_struct *wq, + int cpu, struct pool_workqueue *pwq) { struct pool_workqueue *old_pwq; @@ -4247,8 +4298,8 @@ static struct pool_workqueue *numa_pwq_tbl_install(struct workqueue_struct *wq, /* link_pwq() can handle duplicate calls */ link_pwq(pwq); - old_pwq = rcu_access_pointer(wq->numa_pwq_tbl[node]); - rcu_assign_pointer(wq->numa_pwq_tbl[node], pwq); + old_pwq = rcu_access_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu)); + rcu_assign_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu), pwq); return old_pwq; } @@ -4265,10 +4316,10 @@ struct apply_wqattrs_ctx { static void apply_wqattrs_cleanup(struct apply_wqattrs_ctx *ctx) { if (ctx) { - int node; + int cpu; - for_each_node(node) - put_pwq_unlocked(ctx->pwq_tbl[node]); + for_each_possible_cpu(cpu) + put_pwq_unlocked(ctx->pwq_tbl[cpu]); put_pwq_unlocked(ctx->dfl_pwq); free_workqueue_attrs(ctx->attrs); @@ -4284,76 +4335,64 @@ apply_wqattrs_prepare(struct workqueue_struct *wq, const cpumask_var_t unbound_cpumask) { struct apply_wqattrs_ctx *ctx; - struct workqueue_attrs *new_attrs, *tmp_attrs; - int node; + struct workqueue_attrs *new_attrs; + int cpu; lockdep_assert_held(&wq_pool_mutex); - ctx = kzalloc(struct_size(ctx, pwq_tbl, nr_node_ids), GFP_KERNEL); + if (WARN_ON(attrs->affn_scope < 0 || + attrs->affn_scope >= WQ_AFFN_NR_TYPES)) + return ERR_PTR(-EINVAL); + + ctx = kzalloc(struct_size(ctx, pwq_tbl, nr_cpu_ids), GFP_KERNEL); new_attrs = alloc_workqueue_attrs(); - tmp_attrs = alloc_workqueue_attrs(); - if (!ctx || !new_attrs || !tmp_attrs) + if (!ctx || !new_attrs) goto out_free; - /* - * Calculate the attrs of the default pwq with unbound_cpumask - * which is wq_unbound_cpumask or to set to wq_unbound_cpumask. - * If the user configured cpumask doesn't overlap with the - * wq_unbound_cpumask, we fallback to the wq_unbound_cpumask. - */ - copy_workqueue_attrs(new_attrs, attrs); - cpumask_and(new_attrs->cpumask, new_attrs->cpumask, unbound_cpumask); - if (unlikely(cpumask_empty(new_attrs->cpumask))) - cpumask_copy(new_attrs->cpumask, unbound_cpumask); - - /* - * We may create multiple pwqs with differing cpumasks. Make a - * copy of @new_attrs which will be modified and used to obtain - * pools. - */ - copy_workqueue_attrs(tmp_attrs, new_attrs); - /* * If something goes wrong during CPU up/down, we'll fall back to * the default pwq covering whole @attrs->cpumask. Always create * it even if we don't use it immediately. */ + copy_workqueue_attrs(new_attrs, attrs); + wqattrs_actualize_cpumask(new_attrs, unbound_cpumask); + cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask); ctx->dfl_pwq = alloc_unbound_pwq(wq, new_attrs); if (!ctx->dfl_pwq) goto out_free; - for_each_node(node) { - if (wq_calc_node_cpumask(new_attrs, node, -1, tmp_attrs->cpumask)) { - ctx->pwq_tbl[node] = alloc_unbound_pwq(wq, tmp_attrs); - if (!ctx->pwq_tbl[node]) - goto out_free; - } else { + for_each_possible_cpu(cpu) { + if (new_attrs->ordered) { ctx->dfl_pwq->refcnt++; - ctx->pwq_tbl[node] = ctx->dfl_pwq; + ctx->pwq_tbl[cpu] = ctx->dfl_pwq; + } else { + wq_calc_pod_cpumask(new_attrs, cpu, -1); + ctx->pwq_tbl[cpu] = alloc_unbound_pwq(wq, new_attrs); + if (!ctx->pwq_tbl[cpu]) + goto out_free; } } /* save the user configured attrs and sanitize it. */ copy_workqueue_attrs(new_attrs, attrs); cpumask_and(new_attrs->cpumask, new_attrs->cpumask, cpu_possible_mask); + cpumask_copy(new_attrs->__pod_cpumask, new_attrs->cpumask); ctx->attrs = new_attrs; ctx->wq = wq; - free_workqueue_attrs(tmp_attrs); return ctx; out_free: - free_workqueue_attrs(tmp_attrs); free_workqueue_attrs(new_attrs); apply_wqattrs_cleanup(ctx); - return NULL; + return ERR_PTR(-ENOMEM); } /* set attrs and install prepared pwqs, @ctx points to old pwqs on return */ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx) { - int node; + int cpu; /* all pwqs have been created successfully, let's install'em */ mutex_lock(&ctx->wq->mutex); @@ -4361,9 +4400,9 @@ static void apply_wqattrs_commit(struct apply_wqattrs_ctx *ctx) copy_workqueue_attrs(ctx->wq->unbound_attrs, ctx->attrs); /* save the previous pwq and install the new one */ - for_each_node(node) - ctx->pwq_tbl[node] = numa_pwq_tbl_install(ctx->wq, node, - ctx->pwq_tbl[node]); + for_each_possible_cpu(cpu) + ctx->pwq_tbl[cpu] = install_unbound_pwq(ctx->wq, cpu, + ctx->pwq_tbl[cpu]); /* @dfl_pwq might not have been used, ensure it's linked */ link_pwq(ctx->dfl_pwq); @@ -4403,8 +4442,8 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq, } ctx = apply_wqattrs_prepare(wq, attrs, wq_unbound_cpumask); - if (!ctx) - return -ENOMEM; + if (IS_ERR(ctx)) + return PTR_ERR(ctx); /* the ctx has been prepared successfully, let's commit it */ apply_wqattrs_commit(ctx); @@ -4418,12 +4457,11 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq, * @wq: the target workqueue * @attrs: the workqueue_attrs to apply, allocated with alloc_workqueue_attrs() * - * Apply @attrs to an unbound workqueue @wq. Unless disabled, on NUMA - * machines, this function maps a separate pwq to each NUMA node with - * possibles CPUs in @attrs->cpumask so that work items are affine to the - * NUMA node it was issued on. Older pwqs are released as in-flight work - * items finish. Note that a work item which repeatedly requeues itself - * back-to-back will stay on its current pwq. + * Apply @attrs to an unbound workqueue @wq. Unless disabled, this function maps + * a separate pwq to each CPU pod with possibles CPUs in @attrs->cpumask so that + * work items are affine to the pod it was issued on. Older pwqs are released as + * in-flight work items finish. Note that a work item which repeatedly requeues + * itself back-to-back will stay on its current pwq. * * Performs GFP_KERNEL allocations. * @@ -4446,40 +4484,37 @@ int apply_workqueue_attrs(struct workqueue_struct *wq, } /** - * wq_update_unbound_numa - update NUMA affinity of a wq for CPU hot[un]plug + * wq_update_pod - update pod affinity of a wq for CPU hot[un]plug * @wq: the target workqueue - * @cpu: the CPU coming up or going down + * @cpu: the CPU to update pool association for + * @hotplug_cpu: the CPU coming up or going down * @online: whether @cpu is coming up or going down * * This function is to be called from %CPU_DOWN_PREPARE, %CPU_ONLINE and - * %CPU_DOWN_FAILED. @cpu is being hot[un]plugged, update NUMA affinity of + * %CPU_DOWN_FAILED. @cpu is being hot[un]plugged, update pod affinity of * @wq accordingly. * - * If NUMA affinity can't be adjusted due to memory allocation failure, it - * falls back to @wq->dfl_pwq which may not be optimal but is always - * correct. - * - * Note that when the last allowed CPU of a NUMA node goes offline for a - * workqueue with a cpumask spanning multiple nodes, the workers which were - * already executing the work items for the workqueue will lose their CPU - * affinity and may execute on any CPU. This is similar to how per-cpu - * workqueues behave on CPU_DOWN. If a workqueue user wants strict - * affinity, it's the user's responsibility to flush the work item from - * CPU_DOWN_PREPARE. + * + * If pod affinity can't be adjusted due to memory allocation failure, it falls + * back to @wq->dfl_pwq which may not be optimal but is always correct. + * + * Note that when the last allowed CPU of a pod goes offline for a workqueue + * with a cpumask spanning multiple pods, the workers which were already + * executing the work items for the workqueue will lose their CPU affinity and + * may execute on any CPU. This is similar to how per-cpu workqueues behave on + * CPU_DOWN. If a workqueue user wants strict affinity, it's the user's + * responsibility to flush the work item from CPU_DOWN_PREPARE. */ -static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, - bool online) +static void wq_update_pod(struct workqueue_struct *wq, int cpu, + int hotplug_cpu, bool online) { - int node = cpu_to_node(cpu); - int cpu_off = online ? -1 : cpu; + int off_cpu = online ? -1 : hotplug_cpu; struct pool_workqueue *old_pwq = NULL, *pwq; struct workqueue_attrs *target_attrs; - cpumask_t *cpumask; lockdep_assert_held(&wq_pool_mutex); - if (!wq_numa_enabled || !(wq->flags & WQ_UNBOUND) || - wq->unbound_attrs->no_numa) + if (!(wq->flags & WQ_UNBOUND) || wq->unbound_attrs->ordered) return; /* @@ -4487,36 +4522,29 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, * Let's use a preallocated one. The following buf is protected by * CPU hotplug exclusion. */ - target_attrs = wq_update_unbound_numa_attrs_buf; - cpumask = target_attrs->cpumask; + target_attrs = wq_update_pod_attrs_buf; copy_workqueue_attrs(target_attrs, wq->unbound_attrs); - pwq = unbound_pwq_by_node(wq, node); + wqattrs_actualize_cpumask(target_attrs, wq_unbound_cpumask); - /* - * Let's determine what needs to be done. If the target cpumask is - * different from the default pwq's, we need to compare it to @pwq's - * and create a new one if they don't match. If the target cpumask - * equals the default pwq's, the default pwq should be used. - */ - if (wq_calc_node_cpumask(wq->dfl_pwq->pool->attrs, node, cpu_off, cpumask)) { - if (cpumask_equal(cpumask, pwq->pool->attrs->cpumask)) - return; - } else { - goto use_dfl_pwq; - } + /* nothing to do if the target cpumask matches the current pwq */ + wq_calc_pod_cpumask(target_attrs, cpu, off_cpu); + pwq = rcu_dereference_protected(*per_cpu_ptr(wq->cpu_pwq, cpu), + lockdep_is_held(&wq_pool_mutex)); + if (wqattrs_equal(target_attrs, pwq->pool->attrs)) + return; /* create a new pwq */ pwq = alloc_unbound_pwq(wq, target_attrs); if (!pwq) { - pr_warn("workqueue: allocation failed while updating NUMA affinity of \"%s\"\n", + pr_warn("workqueue: allocation failed while updating CPU pod affinity of \"%s\"\n", wq->name); goto use_dfl_pwq; } /* Install the new pwq. */ mutex_lock(&wq->mutex); - old_pwq = numa_pwq_tbl_install(wq, node, pwq); + old_pwq = install_unbound_pwq(wq, cpu, pwq); goto out_unlock; use_dfl_pwq: @@ -4524,7 +4552,7 @@ static void wq_update_unbound_numa(struct workqueue_struct *wq, int cpu, raw_spin_lock_irq(&wq->dfl_pwq->pool->lock); get_pwq(wq->dfl_pwq); raw_spin_unlock_irq(&wq->dfl_pwq->pool->lock); - old_pwq = numa_pwq_tbl_install(wq, node, wq->dfl_pwq); + old_pwq = install_unbound_pwq(wq, cpu, wq->dfl_pwq); out_unlock: mutex_unlock(&wq->mutex); put_pwq_unlocked(old_pwq); @@ -4535,21 +4563,26 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) bool highpri = wq->flags & WQ_HIGHPRI; int cpu, ret; - if (!(wq->flags & WQ_UNBOUND)) { - wq->cpu_pwqs = alloc_percpu(struct pool_workqueue); - if (!wq->cpu_pwqs) - return -ENOMEM; + wq->cpu_pwq = alloc_percpu(struct pool_workqueue *); + if (!wq->cpu_pwq) + goto enomem; + if (!(wq->flags & WQ_UNBOUND)) { for_each_possible_cpu(cpu) { - struct pool_workqueue *pwq = - per_cpu_ptr(wq->cpu_pwqs, cpu); - struct worker_pool *cpu_pools = - per_cpu(cpu_worker_pools, cpu); + struct pool_workqueue **pwq_p = + per_cpu_ptr(wq->cpu_pwq, cpu); + struct worker_pool *pool = + &(per_cpu_ptr(cpu_worker_pools, cpu)[highpri]); - init_pwq(pwq, wq, &cpu_pools[highpri]); + *pwq_p = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, + pool->node); + if (!*pwq_p) + goto enomem; + + init_pwq(*pwq_p, wq, pool); mutex_lock(&wq->mutex); - link_pwq(pwq); + link_pwq(*pwq_p); mutex_unlock(&wq->mutex); } return 0; @@ -4568,18 +4601,25 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq) cpus_read_unlock(); return ret; + +enomem: + if (wq->cpu_pwq) { + for_each_possible_cpu(cpu) + kfree(*per_cpu_ptr(wq->cpu_pwq, cpu)); + free_percpu(wq->cpu_pwq); + wq->cpu_pwq = NULL; + } + return -ENOMEM; } static int wq_clamp_max_active(int max_active, unsigned int flags, const char *name) { - int lim = flags & WQ_UNBOUND ? WQ_UNBOUND_MAX_ACTIVE : WQ_MAX_ACTIVE; - - if (max_active < 1 || max_active > lim) + if (max_active < 1 || max_active > WQ_MAX_ACTIVE) pr_warn("workqueue: max_active %d requested for %s is out of range, clamping between %d and %d\n", - max_active, name, 1, lim); + max_active, name, 1, WQ_MAX_ACTIVE); - return clamp_val(max_active, 1, lim); + return clamp_val(max_active, 1, WQ_MAX_ACTIVE); } /* @@ -4602,7 +4642,7 @@ static int init_rescuer(struct workqueue_struct *wq) } rescuer->rescue_wq = wq; - rescuer->task = kthread_create(rescuer_thread, rescuer, "%s", wq->name); + rescuer->task = kthread_create(rescuer_thread, rescuer, "kworker/R-%s", wq->name); if (IS_ERR(rescuer->task)) { ret = PTR_ERR(rescuer->task); pr_err("workqueue: Failed to create a rescuer kthread for wq \"%s\": %pe", @@ -4623,17 +4663,15 @@ struct workqueue_struct *alloc_workqueue(const char *fmt, unsigned int flags, int max_active, ...) { - size_t tbl_size = 0; va_list args; struct workqueue_struct *wq; struct pool_workqueue *pwq; /* - * Unbound && max_active == 1 used to imply ordered, which is no - * longer the case on NUMA machines due to per-node pools. While + * Unbound && max_active == 1 used to imply ordered, which is no longer + * the case on many machines due to per-pod pools. While * alloc_ordered_workqueue() is the right way to create an ordered - * workqueue, keep the previous behavior to avoid subtle breakages - * on NUMA. + * workqueue, keep the previous behavior to avoid subtle breakages. */ if ((flags & WQ_UNBOUND) && max_active == 1) flags |= __WQ_ORDERED; @@ -4643,10 +4681,7 @@ struct workqueue_struct *alloc_workqueue(const char *fmt, flags |= WQ_UNBOUND; /* allocate wq and format name */ - if (flags & WQ_UNBOUND) - tbl_size = nr_node_ids * sizeof(wq->numa_pwq_tbl[0]); - - wq = kzalloc(sizeof(*wq) + tbl_size, GFP_KERNEL); + wq = kzalloc(sizeof(*wq), GFP_KERNEL); if (!wq) return NULL; @@ -4741,7 +4776,7 @@ static bool pwq_busy(struct pool_workqueue *pwq) void destroy_workqueue(struct workqueue_struct *wq) { struct pool_workqueue *pwq; - int node; + int cpu; /* * Remove it from sysfs first so that sanity check failure doesn't @@ -4800,33 +4835,23 @@ void destroy_workqueue(struct workqueue_struct *wq) list_del_rcu(&wq->list); mutex_unlock(&wq_pool_mutex); - if (!(wq->flags & WQ_UNBOUND)) { - wq_unregister_lockdep(wq); - /* - * The base ref is never dropped on per-cpu pwqs. Directly - * schedule RCU free. - */ - call_rcu(&wq->rcu, rcu_free_wq); - } else { - /* - * We're the sole accessor of @wq at this point. Directly - * access numa_pwq_tbl[] and dfl_pwq to put the base refs. - * @wq will be freed when the last pwq is released. - */ - for_each_node(node) { - pwq = rcu_access_pointer(wq->numa_pwq_tbl[node]); - RCU_INIT_POINTER(wq->numa_pwq_tbl[node], NULL); - put_pwq_unlocked(pwq); - } + /* + * We're the sole accessor of @wq. Directly access cpu_pwq and dfl_pwq + * to put the base refs. @wq will be auto-destroyed from the last + * pwq_put. RCU read lock prevents @wq from going away from under us. + */ + rcu_read_lock(); - /* - * Put dfl_pwq. @wq may be freed any time after dfl_pwq is - * put. Don't access it afterwards. - */ - pwq = wq->dfl_pwq; - wq->dfl_pwq = NULL; + for_each_possible_cpu(cpu) { + pwq = rcu_access_pointer(*per_cpu_ptr(wq->cpu_pwq, cpu)); + RCU_INIT_POINTER(*per_cpu_ptr(wq->cpu_pwq, cpu), NULL); put_pwq_unlocked(pwq); } + + put_pwq_unlocked(wq->dfl_pwq); + wq->dfl_pwq = NULL; + + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(destroy_workqueue); @@ -4903,10 +4928,11 @@ bool current_is_workqueue_rescuer(void) * unreliable and only useful as advisory hints or for debugging. * * If @cpu is WORK_CPU_UNBOUND, the test is performed on the local CPU. - * Note that both per-cpu and unbound workqueues may be associated with - * multiple pool_workqueues which have separate congested states. A - * workqueue being congested on one CPU doesn't mean the workqueue is also - * contested on other CPUs / NUMA nodes. + * + * With the exception of ordered workqueues, all workqueues have per-cpu + * pool_workqueues, each with its own congested state. A workqueue being + * congested on one CPU doesn't mean that the workqueue is contested on any + * other CPUs. * * Return: * %true if congested, %false otherwise. @@ -4922,12 +4948,9 @@ bool workqueue_congested(int cpu, struct workqueue_struct *wq) if (cpu == WORK_CPU_UNBOUND) cpu = smp_processor_id(); - if (!(wq->flags & WQ_UNBOUND)) - pwq = per_cpu_ptr(wq->cpu_pwqs, cpu); - else - pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu)); - + pwq = *per_cpu_ptr(wq->cpu_pwq, cpu); ret = !list_empty(&pwq->inactive_works); + preempt_enable(); rcu_read_unlock(); @@ -5402,7 +5425,7 @@ static void unbind_workers(int cpu) * worker blocking could lead to lengthy stalls. Kick off * unbound chain execution of currently pending work items. */ - wake_up_worker(pool); + kick_pool(pool); raw_spin_unlock_irq(&pool->lock); @@ -5435,7 +5458,7 @@ static void rebind_workers(struct worker_pool *pool) for_each_pool_worker(worker, pool) { kthread_set_per_cpu(worker->task, pool->cpu); WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, - pool->attrs->cpumask) < 0); + pool_allowed_cpus(pool)) < 0); } raw_spin_lock_irq(&pool->lock); @@ -5529,9 +5552,18 @@ int workqueue_online_cpu(unsigned int cpu) mutex_unlock(&wq_pool_attach_mutex); } - /* update NUMA affinity of unbound workqueues */ - list_for_each_entry(wq, &workqueues, list) - wq_update_unbound_numa(wq, cpu, true); + /* update pod affinity of unbound workqueues */ + list_for_each_entry(wq, &workqueues, list) { + struct workqueue_attrs *attrs = wq->unbound_attrs; + + if (attrs) { + const struct wq_pod_type *pt = wqattrs_pod_type(attrs); + int tcpu; + + for_each_cpu(tcpu, pt->pod_cpus[pt->cpu_pod[cpu]]) + wq_update_pod(wq, tcpu, cpu, true); + } + } mutex_unlock(&wq_pool_mutex); return 0; @@ -5547,10 +5579,19 @@ int workqueue_offline_cpu(unsigned int cpu) unbind_workers(cpu); - /* update NUMA affinity of unbound workqueues */ + /* update pod affinity of unbound workqueues */ mutex_lock(&wq_pool_mutex); - list_for_each_entry(wq, &workqueues, list) - wq_update_unbound_numa(wq, cpu, false); + list_for_each_entry(wq, &workqueues, list) { + struct workqueue_attrs *attrs = wq->unbound_attrs; + + if (attrs) { + const struct wq_pod_type *pt = wqattrs_pod_type(attrs); + int tcpu; + + for_each_cpu(tcpu, pt->pod_cpus[pt->cpu_pod[cpu]]) + wq_update_pod(wq, tcpu, cpu, false); + } + } mutex_unlock(&wq_pool_mutex); return 0; @@ -5746,8 +5787,8 @@ static int workqueue_apply_unbound_cpumask(const cpumask_var_t unbound_cpumask) continue; ctx = apply_wqattrs_prepare(wq, wq->unbound_attrs, unbound_cpumask); - if (!ctx) { - ret = -ENOMEM; + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); break; } @@ -5805,21 +5846,72 @@ int workqueue_set_unbound_cpumask(cpumask_var_t cpumask) return ret; } +static int parse_affn_scope(const char *val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wq_affn_names); i++) { + if (!strncasecmp(val, wq_affn_names[i], strlen(wq_affn_names[i]))) + return i; + } + return -EINVAL; +} + +static int wq_affn_dfl_set(const char *val, const struct kernel_param *kp) +{ + struct workqueue_struct *wq; + int affn, cpu; + + affn = parse_affn_scope(val); + if (affn < 0) + return affn; + if (affn == WQ_AFFN_DFL) + return -EINVAL; + + cpus_read_lock(); + mutex_lock(&wq_pool_mutex); + + wq_affn_dfl = affn; + + list_for_each_entry(wq, &workqueues, list) { + for_each_online_cpu(cpu) { + wq_update_pod(wq, cpu, cpu, true); + } + } + + mutex_unlock(&wq_pool_mutex); + cpus_read_unlock(); + + return 0; +} + +static int wq_affn_dfl_get(char *buffer, const struct kernel_param *kp) +{ + return scnprintf(buffer, PAGE_SIZE, "%s\n", wq_affn_names[wq_affn_dfl]); +} + +static const struct kernel_param_ops wq_affn_dfl_ops = { + .set = wq_affn_dfl_set, + .get = wq_affn_dfl_get, +}; + +module_param_cb(default_affinity_scope, &wq_affn_dfl_ops, NULL, 0644); + #ifdef CONFIG_SYSFS /* * Workqueues with WQ_SYSFS flag set is visible to userland via * /sys/bus/workqueue/devices/WQ_NAME. All visible workqueues have the * following attributes. * - * per_cpu RO bool : whether the workqueue is per-cpu or unbound - * max_active RW int : maximum number of in-flight work items + * per_cpu RO bool : whether the workqueue is per-cpu or unbound + * max_active RW int : maximum number of in-flight work items * * Unbound workqueues have the following extra attributes. * - * pool_ids RO int : the associated pool IDs for each node - * nice RW int : nice value of the workers - * cpumask RW mask : bitmask of allowed CPUs for the workers - * numa RW bool : whether enable NUMA affinity + * nice RW int : nice value of the workers + * cpumask RW mask : bitmask of allowed CPUs for the workers + * affinity_scope RW str : worker CPU affinity scope (cache, numa, none) + * affinity_strict RW bool : worker CPU affinity is strict */ struct wq_device { struct workqueue_struct *wq; @@ -5872,28 +5964,6 @@ static struct attribute *wq_sysfs_attrs[] = { }; ATTRIBUTE_GROUPS(wq_sysfs); -static ssize_t wq_pool_ids_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct workqueue_struct *wq = dev_to_wq(dev); - const char *delim = ""; - int node, written = 0; - - cpus_read_lock(); - rcu_read_lock(); - for_each_node(node) { - written += scnprintf(buf + written, PAGE_SIZE - written, - "%s%d:%d", delim, node, - unbound_pwq_by_node(wq, node)->pool->id); - delim = " "; - } - written += scnprintf(buf + written, PAGE_SIZE - written, "\n"); - rcu_read_unlock(); - cpus_read_unlock(); - - return written; -} - static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -5984,50 +6054,84 @@ static ssize_t wq_cpumask_store(struct device *dev, return ret ?: count; } -static ssize_t wq_numa_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t wq_affn_scope_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct workqueue_struct *wq = dev_to_wq(dev); int written; mutex_lock(&wq->mutex); - written = scnprintf(buf, PAGE_SIZE, "%d\n", - !wq->unbound_attrs->no_numa); + if (wq->unbound_attrs->affn_scope == WQ_AFFN_DFL) + written = scnprintf(buf, PAGE_SIZE, "%s (%s)\n", + wq_affn_names[WQ_AFFN_DFL], + wq_affn_names[wq_affn_dfl]); + else + written = scnprintf(buf, PAGE_SIZE, "%s\n", + wq_affn_names[wq->unbound_attrs->affn_scope]); mutex_unlock(&wq->mutex); return written; } -static ssize_t wq_numa_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t wq_affn_scope_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct workqueue_struct *wq = dev_to_wq(dev); struct workqueue_attrs *attrs; - int v, ret = -ENOMEM; + int affn, ret = -ENOMEM; - apply_wqattrs_lock(); + affn = parse_affn_scope(buf); + if (affn < 0) + return affn; + apply_wqattrs_lock(); attrs = wq_sysfs_prep_attrs(wq); - if (!attrs) - goto out_unlock; - - ret = -EINVAL; - if (sscanf(buf, "%d", &v) == 1) { - attrs->no_numa = !v; + if (attrs) { + attrs->affn_scope = affn; ret = apply_workqueue_attrs_locked(wq, attrs); } + apply_wqattrs_unlock(); + free_workqueue_attrs(attrs); + return ret ?: count; +} -out_unlock: +static ssize_t wq_affinity_strict_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + wq->unbound_attrs->affn_strict); +} + +static ssize_t wq_affinity_strict_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct workqueue_struct *wq = dev_to_wq(dev); + struct workqueue_attrs *attrs; + int v, ret = -ENOMEM; + + if (sscanf(buf, "%d", &v) != 1) + return -EINVAL; + + apply_wqattrs_lock(); + attrs = wq_sysfs_prep_attrs(wq); + if (attrs) { + attrs->affn_strict = (bool)v; + ret = apply_workqueue_attrs_locked(wq, attrs); + } apply_wqattrs_unlock(); free_workqueue_attrs(attrs); return ret ?: count; } static struct device_attribute wq_sysfs_unbound_attrs[] = { - __ATTR(pool_ids, 0444, wq_pool_ids_show, NULL), __ATTR(nice, 0644, wq_nice_show, wq_nice_store), __ATTR(cpumask, 0644, wq_cpumask_show, wq_cpumask_store), - __ATTR(numa, 0644, wq_numa_show, wq_numa_store), + __ATTR(affinity_scope, 0644, wq_affn_scope_show, wq_affn_scope_store), + __ATTR(affinity_strict, 0644, wq_affinity_strict_show, wq_affinity_strict_store), __ATTR_NULL, }; @@ -6393,62 +6497,19 @@ static inline void wq_watchdog_init(void) { } #endif /* CONFIG_WQ_WATCHDOG */ -static void __init wq_numa_init(void) -{ - cpumask_var_t *tbl; - int node, cpu; - - if (num_possible_nodes() <= 1) - return; - - if (wq_disable_numa) { - pr_info("workqueue: NUMA affinity support disabled\n"); - return; - } - - for_each_possible_cpu(cpu) { - if (WARN_ON(cpu_to_node(cpu) == NUMA_NO_NODE)) { - pr_warn("workqueue: NUMA node mapping not available for cpu%d, disabling NUMA support\n", cpu); - return; - } - } - - wq_update_unbound_numa_attrs_buf = alloc_workqueue_attrs(); - BUG_ON(!wq_update_unbound_numa_attrs_buf); - - /* - * We want masks of possible CPUs of each node which isn't readily - * available. Build one from cpu_to_node() which should have been - * fully initialized by now. - */ - tbl = kcalloc(nr_node_ids, sizeof(tbl[0]), GFP_KERNEL); - BUG_ON(!tbl); - - for_each_node(node) - BUG_ON(!zalloc_cpumask_var_node(&tbl[node], GFP_KERNEL, - node_online(node) ? node : NUMA_NO_NODE)); - - for_each_possible_cpu(cpu) { - node = cpu_to_node(cpu); - cpumask_set_cpu(cpu, tbl[node]); - } - - wq_numa_possible_cpumask = tbl; - wq_numa_enabled = true; -} - /** * workqueue_init_early - early init for workqueue subsystem * - * This is the first half of two-staged workqueue subsystem initialization - * and invoked as soon as the bare basics - memory allocation, cpumasks and - * idr are up. It sets up all the data structures and system workqueues - * and allows early boot code to create workqueues and queue/cancel work - * items. Actual work item execution starts only after kthreads can be - * created and scheduled right before early initcalls. + * This is the first step of three-staged workqueue subsystem initialization and + * invoked as soon as the bare basics - memory allocation, cpumasks and idr are + * up. It sets up all the data structures and system workqueues and allows early + * boot code to create workqueues and queue/cancel work items. Actual work item + * execution starts only after kthreads can be created and scheduled right + * before early initcalls. */ void __init workqueue_init_early(void) { + struct wq_pod_type *pt = &wq_pod_types[WQ_AFFN_SYSTEM]; int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL }; int i, cpu; @@ -6458,8 +6519,27 @@ void __init workqueue_init_early(void) cpumask_copy(wq_unbound_cpumask, housekeeping_cpumask(HK_TYPE_WQ)); cpumask_and(wq_unbound_cpumask, wq_unbound_cpumask, housekeeping_cpumask(HK_TYPE_DOMAIN)); + if (!cpumask_empty(&wq_cmdline_cpumask)) + cpumask_and(wq_unbound_cpumask, wq_unbound_cpumask, &wq_cmdline_cpumask); + pwq_cache = KMEM_CACHE(pool_workqueue, SLAB_PANIC); + wq_update_pod_attrs_buf = alloc_workqueue_attrs(); + BUG_ON(!wq_update_pod_attrs_buf); + + /* initialize WQ_AFFN_SYSTEM pods */ + pt->pod_cpus = kcalloc(1, sizeof(pt->pod_cpus[0]), GFP_KERNEL); + pt->pod_node = kcalloc(1, sizeof(pt->pod_node[0]), GFP_KERNEL); + pt->cpu_pod = kcalloc(nr_cpu_ids, sizeof(pt->cpu_pod[0]), GFP_KERNEL); + BUG_ON(!pt->pod_cpus || !pt->pod_node || !pt->cpu_pod); + + BUG_ON(!zalloc_cpumask_var_node(&pt->pod_cpus[0], GFP_KERNEL, NUMA_NO_NODE)); + + pt->nr_pods = 1; + cpumask_copy(pt->pod_cpus[0], cpu_possible_mask); + pt->pod_node[0] = NUMA_NO_NODE; + pt->cpu_pod[0] = 0; + /* initialize CPU pools */ for_each_possible_cpu(cpu) { struct worker_pool *pool; @@ -6469,7 +6549,9 @@ void __init workqueue_init_early(void) BUG_ON(init_worker_pool(pool)); pool->cpu = cpu; cpumask_copy(pool->attrs->cpumask, cpumask_of(cpu)); + cpumask_copy(pool->attrs->__pod_cpumask, cpumask_of(cpu)); pool->attrs->nice = std_nice[i++]; + pool->attrs->affn_strict = true; pool->node = cpu_to_node(cpu); /* alloc pool ID */ @@ -6490,11 +6572,10 @@ void __init workqueue_init_early(void) /* * An ordered wq should have only one pwq as ordering is * guaranteed by max_active which is enforced by pwqs. - * Turn off NUMA so that dfl_pwq is used for all nodes. */ BUG_ON(!(attrs = alloc_workqueue_attrs())); attrs->nice = std_nice[i]; - attrs->no_numa = true; + attrs->ordered = true; ordered_wq_attrs[i] = attrs; } @@ -6502,7 +6583,7 @@ void __init workqueue_init_early(void) system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0); system_long_wq = alloc_workqueue("events_long", 0, 0); system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND, - WQ_UNBOUND_MAX_ACTIVE); + WQ_MAX_ACTIVE); system_freezable_wq = alloc_workqueue("events_freezable", WQ_FREEZABLE, 0); system_power_efficient_wq = alloc_workqueue("events_power_efficient", @@ -6521,6 +6602,9 @@ static void __init wq_cpu_intensive_thresh_init(void) unsigned long thresh; unsigned long bogo; + pwq_release_worker = kthread_create_worker(0, "pool_workqueue_release"); + BUG_ON(IS_ERR(pwq_release_worker)); + /* if the user set it to a specific value, keep it */ if (wq_cpu_intensive_thresh_us != ULONG_MAX) return; @@ -6555,11 +6639,11 @@ static void __init wq_cpu_intensive_thresh_init(void) /** * workqueue_init - bring workqueue subsystem fully online * - * This is the latter half of two-staged workqueue subsystem initialization - * and invoked as soon as kthreads can be created and scheduled. - * Workqueues have been created and work items queued on them, but there - * are no kworkers executing the work items yet. Populate the worker pools - * with the initial workers and enable future kworker creations. + * This is the second step of three-staged workqueue subsystem initialization + * and invoked as soon as kthreads can be created and scheduled. Workqueues have + * been created and work items queued on them, but there are no kworkers + * executing the work items yet. Populate the worker pools with the initial + * workers and enable future kworker creations. */ void __init workqueue_init(void) { @@ -6569,19 +6653,12 @@ void __init workqueue_init(void) wq_cpu_intensive_thresh_init(); - /* - * It'd be simpler to initialize NUMA in workqueue_init_early() but - * CPU to node mapping may not be available that early on some - * archs such as power and arm64. As per-cpu pools created - * previously could be missing node hint and unbound pools NUMA - * affinity, fix them up. - * - * Also, while iterating workqueues, create rescuers if requested. - */ - wq_numa_init(); - mutex_lock(&wq_pool_mutex); + /* + * Per-cpu pools created earlier could be missing node hint. Fix them + * up. Also, create a rescuer for workqueues that requested it. + */ for_each_possible_cpu(cpu) { for_each_cpu_worker_pool(pool, cpu) { pool->node = cpu_to_node(cpu); @@ -6589,7 +6666,6 @@ void __init workqueue_init(void) } list_for_each_entry(wq, &workqueues, list) { - wq_update_unbound_numa(wq, smp_processor_id(), true); WARN(init_rescuer(wq), "workqueue: failed to create early rescuer for %s", wq->name); @@ -6613,9 +6689,114 @@ void __init workqueue_init(void) } /* - * Despite the naming, this is a no-op function which is here only for avoiding - * link error. Since compile-time warning may fail to catch, we will need to - * emit run-time warning from __flush_workqueue(). + * Initialize @pt by first initializing @pt->cpu_pod[] with pod IDs according to + * @cpu_shares_pod(). Each subset of CPUs that share a pod is assigned a unique + * and consecutive pod ID. The rest of @pt is initialized accordingly. + */ +static void __init init_pod_type(struct wq_pod_type *pt, + bool (*cpus_share_pod)(int, int)) +{ + int cur, pre, cpu, pod; + + pt->nr_pods = 0; + + /* init @pt->cpu_pod[] according to @cpus_share_pod() */ + pt->cpu_pod = kcalloc(nr_cpu_ids, sizeof(pt->cpu_pod[0]), GFP_KERNEL); + BUG_ON(!pt->cpu_pod); + + for_each_possible_cpu(cur) { + for_each_possible_cpu(pre) { + if (pre >= cur) { + pt->cpu_pod[cur] = pt->nr_pods++; + break; + } + if (cpus_share_pod(cur, pre)) { + pt->cpu_pod[cur] = pt->cpu_pod[pre]; + break; + } + } + } + + /* init the rest to match @pt->cpu_pod[] */ + pt->pod_cpus = kcalloc(pt->nr_pods, sizeof(pt->pod_cpus[0]), GFP_KERNEL); + pt->pod_node = kcalloc(pt->nr_pods, sizeof(pt->pod_node[0]), GFP_KERNEL); + BUG_ON(!pt->pod_cpus || !pt->pod_node); + + for (pod = 0; pod < pt->nr_pods; pod++) + BUG_ON(!zalloc_cpumask_var(&pt->pod_cpus[pod], GFP_KERNEL)); + + for_each_possible_cpu(cpu) { + cpumask_set_cpu(cpu, pt->pod_cpus[pt->cpu_pod[cpu]]); + pt->pod_node[pt->cpu_pod[cpu]] = cpu_to_node(cpu); + } +} + +static bool __init cpus_dont_share(int cpu0, int cpu1) +{ + return false; +} + +static bool __init cpus_share_smt(int cpu0, int cpu1) +{ +#ifdef CONFIG_SCHED_SMT + return cpumask_test_cpu(cpu0, cpu_smt_mask(cpu1)); +#else + return false; +#endif +} + +static bool __init cpus_share_numa(int cpu0, int cpu1) +{ + return cpu_to_node(cpu0) == cpu_to_node(cpu1); +} + +/** + * workqueue_init_topology - initialize CPU pods for unbound workqueues + * + * This is the third step of there-staged workqueue subsystem initialization and + * invoked after SMP and topology information are fully initialized. It + * initializes the unbound CPU pods accordingly. */ -void __warn_flushing_systemwide_wq(void) { } +void __init workqueue_init_topology(void) +{ + struct workqueue_struct *wq; + int cpu; + + init_pod_type(&wq_pod_types[WQ_AFFN_CPU], cpus_dont_share); + init_pod_type(&wq_pod_types[WQ_AFFN_SMT], cpus_share_smt); + init_pod_type(&wq_pod_types[WQ_AFFN_CACHE], cpus_share_cache); + init_pod_type(&wq_pod_types[WQ_AFFN_NUMA], cpus_share_numa); + + mutex_lock(&wq_pool_mutex); + + /* + * Workqueues allocated earlier would have all CPUs sharing the default + * worker pool. Explicitly call wq_update_pod() on all workqueue and CPU + * combinations to apply per-pod sharing. + */ + list_for_each_entry(wq, &workqueues, list) { + for_each_online_cpu(cpu) { + wq_update_pod(wq, cpu, cpu, true); + } + } + + mutex_unlock(&wq_pool_mutex); +} + +void __warn_flushing_systemwide_wq(void) +{ + pr_warn("WARNING: Flushing system-wide workqueues will be prohibited in near future.\n"); + dump_stack(); +} EXPORT_SYMBOL(__warn_flushing_systemwide_wq); + +static int __init workqueue_unbound_cpus_setup(char *str) +{ + if (cpulist_parse(str, &wq_cmdline_cpumask) < 0) { + cpumask_clear(&wq_cmdline_cpumask); + pr_warn("workqueue.unbound_cpus: incorrect CPU range, using default\n"); + } + + return 1; +} +__setup("workqueue.unbound_cpus=", workqueue_unbound_cpus_setup); diff --git a/kernel/workqueue_internal.h b/kernel/workqueue_internal.h index 6b1d66e2826962bfb864b4abc1d68212cf7cca6f..f6275944ada776814787ac5a1cfa01f891bfe934 100644 --- a/kernel/workqueue_internal.h +++ b/kernel/workqueue_internal.h @@ -48,7 +48,7 @@ struct worker { /* A: runs through worker->node */ unsigned long last_active; /* K: last active timestamp */ - unsigned int flags; /* X: flags */ + unsigned int flags; /* L: flags */ int id; /* I: worker id */ /* diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 319cfbeb0738acf4867640b06c3062a4e0bb52e7..fa307f93fa2e20d043b4a1c9094023aedd3d1d92 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2237,6 +2237,17 @@ config TEST_DIV64 If unsure, say N. +config TEST_IOV_ITER + tristate "Test iov_iter operation" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + Enable this to turn on testing of the operation of the I/O iterator + (iov_iter). This test is executed only once during system boot (so + affects only boot time), or at module load time. + + If unsure, say N. + config KPROBES_SANITY_TEST tristate "Kprobes sanity tests" if !KUNIT_ALL_TESTS depends on DEBUG_KERNEL diff --git a/lib/Makefile b/lib/Makefile index 2e08397f6210159b9b48f76af853e248008e6a1d..740109b6e2c89f0748d4ed5013139db0803c9bb2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_TEST_BITOPS) += test_bitops.o CFLAGS_test_bitops.o += -Werror obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o +obj-$(CONFIG_TEST_IOV_ITER) += kunit_iov_iter.o obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o obj-$(CONFIG_TEST_IDA) += test_ida.o obj-$(CONFIG_TEST_UBSAN) += test_ubsan.o diff --git a/lib/argv_split.c b/lib/argv_split.c index 1a19a0a93dc1cb8931c2f9f5d512650a5a1a0fe6..e28db8e3b58c8154ef571b5359cab90b32f6acba 100644 --- a/lib/argv_split.c +++ b/lib/argv_split.c @@ -28,7 +28,7 @@ static int count_argc(const char *str) /** * argv_free - free an argv - * @argv - the argument vector to be freed + * @argv: the argument vector to be freed * * Frees an argv and the strings it points to. */ @@ -46,7 +46,7 @@ EXPORT_SYMBOL(argv_free); * @str: the string to be split * @argcp: returned argument count * - * Returns an array of pointers to strings which are split out from + * Returns: an array of pointers to strings which are split out from * @str. This is performed by strictly splitting on white-space; no * quote processing is performed. Multiple whitespace characters are * considered to be a single argument separator. The returned array diff --git a/lib/idr.c b/lib/idr.c index 7ecdfdb5309e749b626a7b3db925c20b7846365f..13f2758c2377358602a3486aa5f08763c128be7d 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -100,7 +100,7 @@ EXPORT_SYMBOL_GPL(idr_alloc); * @end: The maximum ID (exclusive). * @gfp: Memory allocation flags. * - * Allocates an unused ID in the range specified by @nextid and @end. If + * Allocates an unused ID in the range specified by @start and @end. If * @end is <= 0, it is treated as one larger than %INT_MAX. This allows * callers to use @start + N as @end as long as N is within integer range. * The search for an unused ID will start at the last ID allocated and will diff --git a/lib/iov_iter.c b/lib/iov_iter.c index b31597b0ca20ed3a3671f7fadd0ebb0382742065..27234a820eeb390f99e26f55002e51b2f95864e7 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1654,14 +1654,14 @@ static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i, size_t *offset0) { struct page **p, *page; - size_t skip = i->iov_offset, offset; + size_t skip = i->iov_offset, offset, size; int k; for (;;) { if (i->nr_segs == 0) return 0; - maxsize = min(maxsize, i->bvec->bv_len - skip); - if (maxsize) + size = min(maxsize, i->bvec->bv_len - skip); + if (size) break; i->iov_offset = 0; i->nr_segs--; @@ -1674,16 +1674,16 @@ static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i, offset = skip % PAGE_SIZE; *offset0 = offset; - maxpages = want_pages_array(pages, maxsize, offset, maxpages); + maxpages = want_pages_array(pages, size, offset, maxpages); if (!maxpages) return -ENOMEM; p = *pages; for (k = 0; k < maxpages; k++) p[k] = page + k; - maxsize = min_t(size_t, maxsize, maxpages * PAGE_SIZE - offset); - iov_iter_advance(i, maxsize); - return maxsize; + size = min_t(size_t, size, maxpages * PAGE_SIZE - offset); + iov_iter_advance(i, size); + return size; } /* @@ -1698,14 +1698,14 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov_iter *i, { struct page **p, *page; const void *kaddr; - size_t skip = i->iov_offset, offset, len; + size_t skip = i->iov_offset, offset, len, size; int k; for (;;) { if (i->nr_segs == 0) return 0; - maxsize = min(maxsize, i->kvec->iov_len - skip); - if (maxsize) + size = min(maxsize, i->kvec->iov_len - skip); + if (size) break; i->iov_offset = 0; i->nr_segs--; @@ -1717,13 +1717,13 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov_iter *i, offset = (unsigned long)kaddr & ~PAGE_MASK; *offset0 = offset; - maxpages = want_pages_array(pages, maxsize, offset, maxpages); + maxpages = want_pages_array(pages, size, offset, maxpages); if (!maxpages) return -ENOMEM; p = *pages; kaddr -= offset; - len = offset + maxsize; + len = offset + size; for (k = 0; k < maxpages; k++) { size_t seg = min_t(size_t, len, PAGE_SIZE); @@ -1737,9 +1737,9 @@ static ssize_t iov_iter_extract_kvec_pages(struct iov_iter *i, kaddr += PAGE_SIZE; } - maxsize = min_t(size_t, maxsize, maxpages * PAGE_SIZE - offset); - iov_iter_advance(i, maxsize); - return maxsize; + size = min_t(size_t, size, maxpages * PAGE_SIZE - offset); + iov_iter_advance(i, size); + return size; } /* diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index 5181aa2e760b641f48575e5603db75679b6f04d3..a6348489d45fe8f9f6937069c56ce7978d971486 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -65,7 +65,7 @@ struct kunit_glob_filter { }; /* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */ -static void kunit_parse_glob_filter(struct kunit_glob_filter *parsed, +static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed, const char *filter_glob) { const int len = strlen(filter_glob); @@ -73,16 +73,28 @@ static void kunit_parse_glob_filter(struct kunit_glob_filter *parsed, if (!period) { parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL); + if (!parsed->suite_glob) + return -ENOMEM; + parsed->test_glob = NULL; strcpy(parsed->suite_glob, filter_glob); - return; + return 0; } parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL); + if (!parsed->suite_glob) + return -ENOMEM; + parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL); + if (!parsed->test_glob) { + kfree(parsed->suite_glob); + return -ENOMEM; + } strncpy(parsed->suite_glob, filter_glob, period - filter_glob); strncpy(parsed->test_glob, period + 1, len - (period - filter_glob)); + + return 0; } /* Create a copy of suite with only tests that match test_glob. */ @@ -152,21 +164,24 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, } copy_start = copy; - if (filter_glob) - kunit_parse_glob_filter(&parsed_glob, filter_glob); + if (filter_glob) { + *err = kunit_parse_glob_filter(&parsed_glob, filter_glob); + if (*err) + goto free_copy; + } /* Parse attribute filters */ if (filters) { filter_count = kunit_get_filter_count(filters); parsed_filters = kcalloc(filter_count, sizeof(*parsed_filters), GFP_KERNEL); if (!parsed_filters) { - kfree(copy); - return filtered; + *err = -ENOMEM; + goto free_parsed_glob; } for (j = 0; j < filter_count; j++) parsed_filters[j] = kunit_next_attr_filter(&filters, err); if (*err) - goto err; + goto free_parsed_filters; } for (i = 0; &suite_set->start[i] != suite_set->end; i++) { @@ -178,7 +193,7 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, parsed_glob.test_glob); if (IS_ERR(filtered_suite)) { *err = PTR_ERR(filtered_suite); - goto err; + goto free_parsed_filters; } } if (filter_count > 0 && parsed_filters != NULL) { @@ -195,10 +210,11 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, filtered_suite = new_filtered_suite; if (*err) - goto err; + goto free_parsed_filters; + if (IS_ERR(filtered_suite)) { *err = PTR_ERR(filtered_suite); - goto err; + goto free_parsed_filters; } if (!filtered_suite) break; @@ -213,17 +229,19 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, filtered.start = copy_start; filtered.end = copy; -err: - if (*err) - kfree(copy); +free_parsed_filters: + if (filter_count) + kfree(parsed_filters); +free_parsed_glob: if (filter_glob) { kfree(parsed_glob.suite_glob); kfree(parsed_glob.test_glob); } - if (filter_count) - kfree(parsed_filters); +free_copy: + if (*err) + kfree(copy); return filtered; } diff --git a/lib/kunit/executor_test.c b/lib/kunit/executor_test.c index 4084071d0eb5b77ff7066e63df1263f980a29afb..b4f6f96b28445fb62a2ff856def2d9a7877fa236 100644 --- a/lib/kunit/executor_test.c +++ b/lib/kunit/executor_test.c @@ -119,7 +119,7 @@ static void parse_filter_attr_test(struct kunit *test) { int j, filter_count; struct kunit_attr_filter *parsed_filters; - char *filters = "speed>slow, module!=example"; + char filters[] = "speed>slow, module!=example", *filter = filters; int err = 0; filter_count = kunit_get_filter_count(filters); @@ -128,7 +128,7 @@ static void parse_filter_attr_test(struct kunit *test) parsed_filters = kunit_kcalloc(test, filter_count, sizeof(*parsed_filters), GFP_KERNEL); for (j = 0; j < filter_count; j++) { - parsed_filters[j] = kunit_next_attr_filter(&filters, &err); + parsed_filters[j] = kunit_next_attr_filter(&filter, &err); KUNIT_ASSERT_EQ_MSG(test, err, 0, "failed to parse filter '%s'", filters[j]); } @@ -154,6 +154,7 @@ static void filter_attr_test(struct kunit *test) .start = subsuite, .end = &subsuite[2], }; struct kunit_suite_set got; + char filter[] = "speed>slow"; int err = 0; subsuite[0] = alloc_fake_suite(test, "normal_suite", dummy_attr_test_cases); @@ -168,7 +169,7 @@ static void filter_attr_test(struct kunit *test) * attribute is unset and thus, the filtering is based on the parent attribute * of slow. */ - got = kunit_filter_suites(&suite_set, NULL, "speed>slow", NULL, &err); + got = kunit_filter_suites(&suite_set, NULL, filter, NULL, &err); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start); KUNIT_ASSERT_EQ(test, err, 0); kfree_at_end(test, got.start); @@ -191,12 +192,13 @@ static void filter_attr_empty_test(struct kunit *test) .start = subsuite, .end = &subsuite[2], }; struct kunit_suite_set got; + char filter[] = "module!=dummy"; int err = 0; subsuite[0] = alloc_fake_suite(test, "suite1", dummy_attr_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_attr_test_cases); - got = kunit_filter_suites(&suite_set, NULL, "module!=dummy", NULL, &err); + got = kunit_filter_suites(&suite_set, NULL, filter, NULL, &err); KUNIT_ASSERT_EQ(test, err, 0); kfree_at_end(test, got.start); /* just in case */ @@ -211,12 +213,13 @@ static void filter_attr_skip_test(struct kunit *test) .start = subsuite, .end = &subsuite[1], }; struct kunit_suite_set got; + char filter[] = "speed>slow"; int err = 0; subsuite[0] = alloc_fake_suite(test, "suite", dummy_attr_test_cases); /* Want: suite(slow, normal), NULL -> suite(slow with SKIP, normal), NULL */ - got = kunit_filter_suites(&suite_set, NULL, "speed>slow", "skip", &err); + got = kunit_filter_suites(&suite_set, NULL, filter, "skip", &err); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start); KUNIT_ASSERT_EQ(test, err, 0); kfree_at_end(test, got.start); diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 49698a168437ad1507580106aae91cde6c12a0e1..421f13981412302d07552f95fa286f117e81c9c1 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -784,12 +784,13 @@ static int kunit_module_notify(struct notifier_block *nb, unsigned long val, switch (val) { case MODULE_STATE_LIVE: - kunit_module_init(mod); break; case MODULE_STATE_GOING: kunit_module_exit(mod); break; case MODULE_STATE_COMING: + kunit_module_init(mod); + break; case MODULE_STATE_UNFORMED: break; } diff --git a/lib/kunit_iov_iter.c b/lib/kunit_iov_iter.c new file mode 100644 index 0000000000000000000000000000000000000000..859b67c4d69787c4272a3686095f8f935812e115 --- /dev/null +++ b/lib/kunit_iov_iter.c @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* I/O iterator tests. This can only test kernel-backed iterator types. + * + * Copyright (C) 2023 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("iov_iter testing"); +MODULE_AUTHOR("David Howells "); +MODULE_LICENSE("GPL"); + +struct kvec_test_range { + int from, to; +}; + +static const struct kvec_test_range kvec_test_ranges[] = { + { 0x00002, 0x00002 }, + { 0x00027, 0x03000 }, + { 0x05193, 0x18794 }, + { 0x20000, 0x20000 }, + { 0x20000, 0x24000 }, + { 0x24000, 0x27001 }, + { 0x29000, 0xffffb }, + { 0xffffd, 0xffffe }, + { -1 } +}; + +static inline u8 pattern(unsigned long x) +{ + return x & 0xff; +} + +static void iov_kunit_unmap(void *data) +{ + vunmap(data); +} + +static void *__init iov_kunit_create_buffer(struct kunit *test, + struct page ***ppages, + size_t npages) +{ + struct page **pages; + unsigned long got; + void *buffer; + + pages = kunit_kcalloc(test, npages, sizeof(struct page *), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages); + *ppages = pages; + + got = alloc_pages_bulk_array(GFP_KERNEL, npages, pages); + if (got != npages) { + release_pages(pages, got); + KUNIT_ASSERT_EQ(test, got, npages); + } + + buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer); + + kunit_add_action_or_reset(test, iov_kunit_unmap, buffer); + return buffer; +} + +static void __init iov_kunit_load_kvec(struct kunit *test, + struct iov_iter *iter, int dir, + struct kvec *kvec, unsigned int kvmax, + void *buffer, size_t bufsize, + const struct kvec_test_range *pr) +{ + size_t size = 0; + int i; + + for (i = 0; i < kvmax; i++, pr++) { + if (pr->from < 0) + break; + KUNIT_ASSERT_GE(test, pr->to, pr->from); + KUNIT_ASSERT_LE(test, pr->to, bufsize); + kvec[i].iov_base = buffer + pr->from; + kvec[i].iov_len = pr->to - pr->from; + size += pr->to - pr->from; + } + KUNIT_ASSERT_LE(test, size, bufsize); + + iov_iter_kvec(iter, dir, kvec, i, size); +} + +/* + * Test copying to a ITER_KVEC-type iterator. + */ +static void __init iov_kunit_copy_to_kvec(struct kunit *test) +{ + const struct kvec_test_range *pr; + struct iov_iter iter; + struct page **spages, **bpages; + struct kvec kvec[8]; + u8 *scratch, *buffer; + size_t bufsize, npages, size, copied; + int i, patt; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + scratch = iov_kunit_create_buffer(test, &spages, npages); + for (i = 0; i < bufsize; i++) + scratch[i] = pattern(i); + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + memset(buffer, 0, bufsize); + + iov_kunit_load_kvec(test, &iter, READ, kvec, ARRAY_SIZE(kvec), + buffer, bufsize, kvec_test_ranges); + size = iter.count; + + copied = copy_to_iter(scratch, size, &iter); + + KUNIT_EXPECT_EQ(test, copied, size); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); + + /* Build the expected image in the scratch buffer. */ + patt = 0; + memset(scratch, 0, bufsize); + for (pr = kvec_test_ranges; pr->from >= 0; pr++) + for (i = pr->from; i < pr->to; i++) + scratch[i] = pattern(patt++); + + /* Compare the images */ + for (i = 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i); + if (buffer[i] != scratch[i]) + return; + } + + KUNIT_SUCCEED(); +} + +/* + * Test copying from a ITER_KVEC-type iterator. + */ +static void __init iov_kunit_copy_from_kvec(struct kunit *test) +{ + const struct kvec_test_range *pr; + struct iov_iter iter; + struct page **spages, **bpages; + struct kvec kvec[8]; + u8 *scratch, *buffer; + size_t bufsize, npages, size, copied; + int i, j; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + for (i = 0; i < bufsize; i++) + buffer[i] = pattern(i); + + scratch = iov_kunit_create_buffer(test, &spages, npages); + memset(scratch, 0, bufsize); + + iov_kunit_load_kvec(test, &iter, WRITE, kvec, ARRAY_SIZE(kvec), + buffer, bufsize, kvec_test_ranges); + size = min(iter.count, bufsize); + + copied = copy_from_iter(scratch, size, &iter); + + KUNIT_EXPECT_EQ(test, copied, size); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); + + /* Build the expected image in the main buffer. */ + i = 0; + memset(buffer, 0, bufsize); + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { + for (j = pr->from; j < pr->to; j++) { + buffer[i++] = pattern(j); + if (i >= bufsize) + goto stop; + } + } +stop: + + /* Compare the images */ + for (i = 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i); + if (scratch[i] != buffer[i]) + return; + } + + KUNIT_SUCCEED(); +} + +struct bvec_test_range { + int page, from, to; +}; + +static const struct bvec_test_range bvec_test_ranges[] = { + { 0, 0x0002, 0x0002 }, + { 1, 0x0027, 0x0893 }, + { 2, 0x0193, 0x0794 }, + { 3, 0x0000, 0x1000 }, + { 4, 0x0000, 0x1000 }, + { 5, 0x0000, 0x1000 }, + { 6, 0x0000, 0x0ffb }, + { 6, 0x0ffd, 0x0ffe }, + { -1, -1, -1 } +}; + +static void __init iov_kunit_load_bvec(struct kunit *test, + struct iov_iter *iter, int dir, + struct bio_vec *bvec, unsigned int bvmax, + struct page **pages, size_t npages, + size_t bufsize, + const struct bvec_test_range *pr) +{ + struct page *can_merge = NULL, *page; + size_t size = 0; + int i; + + for (i = 0; i < bvmax; i++, pr++) { + if (pr->from < 0) + break; + KUNIT_ASSERT_LT(test, pr->page, npages); + KUNIT_ASSERT_LT(test, pr->page * PAGE_SIZE, bufsize); + KUNIT_ASSERT_GE(test, pr->from, 0); + KUNIT_ASSERT_GE(test, pr->to, pr->from); + KUNIT_ASSERT_LE(test, pr->to, PAGE_SIZE); + + page = pages[pr->page]; + if (pr->from == 0 && pr->from != pr->to && page == can_merge) { + i--; + bvec[i].bv_len += pr->to; + } else { + bvec_set_page(&bvec[i], page, pr->to - pr->from, pr->from); + } + + size += pr->to - pr->from; + if ((pr->to & ~PAGE_MASK) == 0) + can_merge = page + pr->to / PAGE_SIZE; + else + can_merge = NULL; + } + + iov_iter_bvec(iter, dir, bvec, i, size); +} + +/* + * Test copying to a ITER_BVEC-type iterator. + */ +static void __init iov_kunit_copy_to_bvec(struct kunit *test) +{ + const struct bvec_test_range *pr; + struct iov_iter iter; + struct bio_vec bvec[8]; + struct page **spages, **bpages; + u8 *scratch, *buffer; + size_t bufsize, npages, size, copied; + int i, b, patt; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + scratch = iov_kunit_create_buffer(test, &spages, npages); + for (i = 0; i < bufsize; i++) + scratch[i] = pattern(i); + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + memset(buffer, 0, bufsize); + + iov_kunit_load_bvec(test, &iter, READ, bvec, ARRAY_SIZE(bvec), + bpages, npages, bufsize, bvec_test_ranges); + size = iter.count; + + copied = copy_to_iter(scratch, size, &iter); + + KUNIT_EXPECT_EQ(test, copied, size); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); + + /* Build the expected image in the scratch buffer. */ + b = 0; + patt = 0; + memset(scratch, 0, bufsize); + for (pr = bvec_test_ranges; pr->from >= 0; pr++, b++) { + u8 *p = scratch + pr->page * PAGE_SIZE; + + for (i = pr->from; i < pr->to; i++) + p[i] = pattern(patt++); + } + + /* Compare the images */ + for (i = 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i); + if (buffer[i] != scratch[i]) + return; + } + + KUNIT_SUCCEED(); +} + +/* + * Test copying from a ITER_BVEC-type iterator. + */ +static void __init iov_kunit_copy_from_bvec(struct kunit *test) +{ + const struct bvec_test_range *pr; + struct iov_iter iter; + struct bio_vec bvec[8]; + struct page **spages, **bpages; + u8 *scratch, *buffer; + size_t bufsize, npages, size, copied; + int i, j; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + for (i = 0; i < bufsize; i++) + buffer[i] = pattern(i); + + scratch = iov_kunit_create_buffer(test, &spages, npages); + memset(scratch, 0, bufsize); + + iov_kunit_load_bvec(test, &iter, WRITE, bvec, ARRAY_SIZE(bvec), + bpages, npages, bufsize, bvec_test_ranges); + size = iter.count; + + copied = copy_from_iter(scratch, size, &iter); + + KUNIT_EXPECT_EQ(test, copied, size); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.nr_segs, 0); + + /* Build the expected image in the main buffer. */ + i = 0; + memset(buffer, 0, bufsize); + for (pr = bvec_test_ranges; pr->from >= 0; pr++) { + size_t patt = pr->page * PAGE_SIZE; + + for (j = pr->from; j < pr->to; j++) { + buffer[i++] = pattern(patt + j); + if (i >= bufsize) + goto stop; + } + } +stop: + + /* Compare the images */ + for (i = 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i); + if (scratch[i] != buffer[i]) + return; + } + + KUNIT_SUCCEED(); +} + +static void iov_kunit_destroy_xarray(void *data) +{ + struct xarray *xarray = data; + + xa_destroy(xarray); + kfree(xarray); +} + +static void __init iov_kunit_load_xarray(struct kunit *test, + struct iov_iter *iter, int dir, + struct xarray *xarray, + struct page **pages, size_t npages) +{ + size_t size = 0; + int i; + + for (i = 0; i < npages; i++) { + void *x = xa_store(xarray, i, pages[i], GFP_KERNEL); + + KUNIT_ASSERT_FALSE(test, xa_is_err(x)); + size += PAGE_SIZE; + } + iov_iter_xarray(iter, dir, xarray, 0, size); +} + +static struct xarray *iov_kunit_create_xarray(struct kunit *test) +{ + struct xarray *xarray; + + xarray = kzalloc(sizeof(struct xarray), GFP_KERNEL); + xa_init(xarray); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xarray); + kunit_add_action_or_reset(test, iov_kunit_destroy_xarray, xarray); + return xarray; +} + +/* + * Test copying to a ITER_XARRAY-type iterator. + */ +static void __init iov_kunit_copy_to_xarray(struct kunit *test) +{ + const struct kvec_test_range *pr; + struct iov_iter iter; + struct xarray *xarray; + struct page **spages, **bpages; + u8 *scratch, *buffer; + size_t bufsize, npages, size, copied; + int i, patt; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + xarray = iov_kunit_create_xarray(test); + + scratch = iov_kunit_create_buffer(test, &spages, npages); + for (i = 0; i < bufsize; i++) + scratch[i] = pattern(i); + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + memset(buffer, 0, bufsize); + + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); + + i = 0; + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { + size = pr->to - pr->from; + KUNIT_ASSERT_LE(test, pr->to, bufsize); + + iov_iter_xarray(&iter, READ, xarray, pr->from, size); + copied = copy_to_iter(scratch + i, size, &iter); + + KUNIT_EXPECT_EQ(test, copied, size); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.iov_offset, size); + i += size; + } + + /* Build the expected image in the scratch buffer. */ + patt = 0; + memset(scratch, 0, bufsize); + for (pr = kvec_test_ranges; pr->from >= 0; pr++) + for (i = pr->from; i < pr->to; i++) + scratch[i] = pattern(patt++); + + /* Compare the images */ + for (i = 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, buffer[i], scratch[i], "at i=%x", i); + if (buffer[i] != scratch[i]) + return; + } + + KUNIT_SUCCEED(); +} + +/* + * Test copying from a ITER_XARRAY-type iterator. + */ +static void __init iov_kunit_copy_from_xarray(struct kunit *test) +{ + const struct kvec_test_range *pr; + struct iov_iter iter; + struct xarray *xarray; + struct page **spages, **bpages; + u8 *scratch, *buffer; + size_t bufsize, npages, size, copied; + int i, j; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + xarray = iov_kunit_create_xarray(test); + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + for (i = 0; i < bufsize; i++) + buffer[i] = pattern(i); + + scratch = iov_kunit_create_buffer(test, &spages, npages); + memset(scratch, 0, bufsize); + + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); + + i = 0; + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { + size = pr->to - pr->from; + KUNIT_ASSERT_LE(test, pr->to, bufsize); + + iov_iter_xarray(&iter, WRITE, xarray, pr->from, size); + copied = copy_from_iter(scratch + i, size, &iter); + + KUNIT_EXPECT_EQ(test, copied, size); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.iov_offset, size); + i += size; + } + + /* Build the expected image in the main buffer. */ + i = 0; + memset(buffer, 0, bufsize); + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { + for (j = pr->from; j < pr->to; j++) { + buffer[i++] = pattern(j); + if (i >= bufsize) + goto stop; + } + } +stop: + + /* Compare the images */ + for (i = 0; i < bufsize; i++) { + KUNIT_EXPECT_EQ_MSG(test, scratch[i], buffer[i], "at i=%x", i); + if (scratch[i] != buffer[i]) + return; + } + + KUNIT_SUCCEED(); +} + +/* + * Test the extraction of ITER_KVEC-type iterators. + */ +static void __init iov_kunit_extract_pages_kvec(struct kunit *test) +{ + const struct kvec_test_range *pr; + struct iov_iter iter; + struct page **bpages, *pagelist[8], **pages = pagelist; + struct kvec kvec[8]; + u8 *buffer; + ssize_t len; + size_t bufsize, size = 0, npages; + int i, from; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + buffer = iov_kunit_create_buffer(test, &bpages, npages); + + iov_kunit_load_kvec(test, &iter, READ, kvec, ARRAY_SIZE(kvec), + buffer, bufsize, kvec_test_ranges); + size = iter.count; + + pr = kvec_test_ranges; + from = pr->from; + do { + size_t offset0 = LONG_MAX; + + for (i = 0; i < ARRAY_SIZE(pagelist); i++) + pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL; + + len = iov_iter_extract_pages(&iter, &pages, 100 * 1024, + ARRAY_SIZE(pagelist), 0, &offset0); + KUNIT_EXPECT_GE(test, len, 0); + if (len < 0) + break; + KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0); + KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE); + KUNIT_EXPECT_LE(test, len, size); + KUNIT_EXPECT_EQ(test, iter.count, size - len); + size -= len; + + if (len == 0) + break; + + for (i = 0; i < ARRAY_SIZE(pagelist); i++) { + struct page *p; + ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0); + int ix; + + KUNIT_ASSERT_GE(test, part, 0); + while (from == pr->to) { + pr++; + from = pr->from; + if (from < 0) + goto stop; + } + ix = from / PAGE_SIZE; + KUNIT_ASSERT_LT(test, ix, npages); + p = bpages[ix]; + KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p); + KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE); + from += part; + len -= part; + KUNIT_ASSERT_GE(test, len, 0); + if (len == 0) + break; + offset0 = 0; + } + + if (test->status == KUNIT_FAILURE) + break; + } while (iov_iter_count(&iter) > 0); + +stop: + KUNIT_EXPECT_EQ(test, size, 0); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_SUCCEED(); +} + +/* + * Test the extraction of ITER_BVEC-type iterators. + */ +static void __init iov_kunit_extract_pages_bvec(struct kunit *test) +{ + const struct bvec_test_range *pr; + struct iov_iter iter; + struct page **bpages, *pagelist[8], **pages = pagelist; + struct bio_vec bvec[8]; + ssize_t len; + size_t bufsize, size = 0, npages; + int i, from; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + iov_kunit_create_buffer(test, &bpages, npages); + iov_kunit_load_bvec(test, &iter, READ, bvec, ARRAY_SIZE(bvec), + bpages, npages, bufsize, bvec_test_ranges); + size = iter.count; + + pr = bvec_test_ranges; + from = pr->from; + do { + size_t offset0 = LONG_MAX; + + for (i = 0; i < ARRAY_SIZE(pagelist); i++) + pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL; + + len = iov_iter_extract_pages(&iter, &pages, 100 * 1024, + ARRAY_SIZE(pagelist), 0, &offset0); + KUNIT_EXPECT_GE(test, len, 0); + if (len < 0) + break; + KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0); + KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE); + KUNIT_EXPECT_LE(test, len, size); + KUNIT_EXPECT_EQ(test, iter.count, size - len); + size -= len; + + if (len == 0) + break; + + for (i = 0; i < ARRAY_SIZE(pagelist); i++) { + struct page *p; + ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0); + int ix; + + KUNIT_ASSERT_GE(test, part, 0); + while (from == pr->to) { + pr++; + from = pr->from; + if (from < 0) + goto stop; + } + ix = pr->page + from / PAGE_SIZE; + KUNIT_ASSERT_LT(test, ix, npages); + p = bpages[ix]; + KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p); + KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE); + from += part; + len -= part; + KUNIT_ASSERT_GE(test, len, 0); + if (len == 0) + break; + offset0 = 0; + } + + if (test->status == KUNIT_FAILURE) + break; + } while (iov_iter_count(&iter) > 0); + +stop: + KUNIT_EXPECT_EQ(test, size, 0); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_SUCCEED(); +} + +/* + * Test the extraction of ITER_XARRAY-type iterators. + */ +static void __init iov_kunit_extract_pages_xarray(struct kunit *test) +{ + const struct kvec_test_range *pr; + struct iov_iter iter; + struct xarray *xarray; + struct page **bpages, *pagelist[8], **pages = pagelist; + ssize_t len; + size_t bufsize, size = 0, npages; + int i, from; + + bufsize = 0x100000; + npages = bufsize / PAGE_SIZE; + + xarray = iov_kunit_create_xarray(test); + + iov_kunit_create_buffer(test, &bpages, npages); + iov_kunit_load_xarray(test, &iter, READ, xarray, bpages, npages); + + for (pr = kvec_test_ranges; pr->from >= 0; pr++) { + from = pr->from; + size = pr->to - from; + KUNIT_ASSERT_LE(test, pr->to, bufsize); + + iov_iter_xarray(&iter, WRITE, xarray, from, size); + + do { + size_t offset0 = LONG_MAX; + + for (i = 0; i < ARRAY_SIZE(pagelist); i++) + pagelist[i] = (void *)(unsigned long)0xaa55aa55aa55aa55ULL; + + len = iov_iter_extract_pages(&iter, &pages, 100 * 1024, + ARRAY_SIZE(pagelist), 0, &offset0); + KUNIT_EXPECT_GE(test, len, 0); + if (len < 0) + break; + KUNIT_EXPECT_LE(test, len, size); + KUNIT_EXPECT_EQ(test, iter.count, size - len); + if (len == 0) + break; + size -= len; + KUNIT_EXPECT_GE(test, (ssize_t)offset0, 0); + KUNIT_EXPECT_LT(test, offset0, PAGE_SIZE); + + for (i = 0; i < ARRAY_SIZE(pagelist); i++) { + struct page *p; + ssize_t part = min_t(ssize_t, len, PAGE_SIZE - offset0); + int ix; + + KUNIT_ASSERT_GE(test, part, 0); + ix = from / PAGE_SIZE; + KUNIT_ASSERT_LT(test, ix, npages); + p = bpages[ix]; + KUNIT_EXPECT_PTR_EQ(test, pagelist[i], p); + KUNIT_EXPECT_EQ(test, offset0, from % PAGE_SIZE); + from += part; + len -= part; + KUNIT_ASSERT_GE(test, len, 0); + if (len == 0) + break; + offset0 = 0; + } + + if (test->status == KUNIT_FAILURE) + goto stop; + } while (iov_iter_count(&iter) > 0); + + KUNIT_EXPECT_EQ(test, size, 0); + KUNIT_EXPECT_EQ(test, iter.count, 0); + KUNIT_EXPECT_EQ(test, iter.iov_offset, pr->to - pr->from); + } + +stop: + KUNIT_SUCCEED(); +} + +static struct kunit_case __refdata iov_kunit_cases[] = { + KUNIT_CASE(iov_kunit_copy_to_kvec), + KUNIT_CASE(iov_kunit_copy_from_kvec), + KUNIT_CASE(iov_kunit_copy_to_bvec), + KUNIT_CASE(iov_kunit_copy_from_bvec), + KUNIT_CASE(iov_kunit_copy_to_xarray), + KUNIT_CASE(iov_kunit_copy_from_xarray), + KUNIT_CASE(iov_kunit_extract_pages_kvec), + KUNIT_CASE(iov_kunit_extract_pages_bvec), + KUNIT_CASE(iov_kunit_extract_pages_xarray), + {} +}; + +static struct kunit_suite iov_kunit_suite = { + .name = "iov_iter", + .test_cases = iov_kunit_cases, +}; + +kunit_test_suites(&iov_kunit_suite); diff --git a/lib/maple_tree.c b/lib/maple_tree.c index ee1ff0c59fd7534325759c2c07525ba16f0beb8a..0e00a84e8e8faa95e572b23778ef5b6bf9179630 100644 --- a/lib/maple_tree.c +++ b/lib/maple_tree.c @@ -256,6 +256,22 @@ bool mas_is_err(struct ma_state *mas) return xa_is_err(mas->node); } +static __always_inline bool mas_is_overflow(struct ma_state *mas) +{ + if (unlikely(mas->node == MAS_OVERFLOW)) + return true; + + return false; +} + +static __always_inline bool mas_is_underflow(struct ma_state *mas) +{ + if (unlikely(mas->node == MAS_UNDERFLOW)) + return true; + + return false; +} + static inline bool mas_searchable(struct ma_state *mas) { if (mas_is_none(mas)) @@ -4415,10 +4431,13 @@ static inline int mas_prev_node(struct ma_state *mas, unsigned long min) * * @mas: The maple state * @max: The minimum starting range + * @empty: Can be empty + * @set_underflow: Set the @mas->node to underflow state on limit. * * Return: The entry in the previous slot which is possibly NULL */ -static void *mas_prev_slot(struct ma_state *mas, unsigned long min, bool empty) +static void *mas_prev_slot(struct ma_state *mas, unsigned long min, bool empty, + bool set_underflow) { void *entry; void __rcu **slots; @@ -4435,7 +4454,6 @@ static void *mas_prev_slot(struct ma_state *mas, unsigned long min, bool empty) if (unlikely(mas_rewalk_if_dead(mas, node, save_point))) goto retry; -again: if (mas->min <= min) { pivot = mas_safe_min(mas, pivots, mas->offset); @@ -4443,9 +4461,10 @@ static void *mas_prev_slot(struct ma_state *mas, unsigned long min, bool empty) goto retry; if (pivot <= min) - return NULL; + goto underflow; } +again: if (likely(mas->offset)) { mas->offset--; mas->last = mas->index - 1; @@ -4457,7 +4476,7 @@ static void *mas_prev_slot(struct ma_state *mas, unsigned long min, bool empty) } if (mas_is_none(mas)) - return NULL; + goto underflow; mas->last = mas->max; node = mas_mn(mas); @@ -4474,10 +4493,19 @@ static void *mas_prev_slot(struct ma_state *mas, unsigned long min, bool empty) if (likely(entry)) return entry; - if (!empty) + if (!empty) { + if (mas->index <= min) + goto underflow; + goto again; + } return entry; + +underflow: + if (set_underflow) + mas->node = MAS_UNDERFLOW; + return NULL; } /* @@ -4567,10 +4595,13 @@ static inline int mas_next_node(struct ma_state *mas, struct maple_node *node, * @mas: The maple state * @max: The maximum starting range * @empty: Can be empty + * @set_overflow: Should @mas->node be set to overflow when the limit is + * reached. * * Return: The entry in the next slot which is possibly NULL */ -static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty) +static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty, + bool set_overflow) { void __rcu **slots; unsigned long *pivots; @@ -4589,22 +4620,22 @@ static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty) if (unlikely(mas_rewalk_if_dead(mas, node, save_point))) goto retry; -again: if (mas->max >= max) { if (likely(mas->offset < data_end)) pivot = pivots[mas->offset]; else - return NULL; /* must be mas->max */ + goto overflow; if (unlikely(mas_rewalk_if_dead(mas, node, save_point))) goto retry; if (pivot >= max) - return NULL; + goto overflow; } if (likely(mas->offset < data_end)) { mas->index = pivots[mas->offset] + 1; +again: mas->offset++; if (likely(mas->offset < data_end)) mas->last = pivots[mas->offset]; @@ -4616,8 +4647,11 @@ static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty) goto retry; } - if (mas_is_none(mas)) + if (WARN_ON_ONCE(mas_is_none(mas))) { + mas->node = MAS_OVERFLOW; return NULL; + goto overflow; + } mas->offset = 0; mas->index = mas->min; @@ -4636,12 +4670,20 @@ static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty) return entry; if (!empty) { - if (!mas->offset) - data_end = 2; + if (mas->last >= max) + goto overflow; + + mas->index = mas->last + 1; + /* Node cannot end on NULL, so it's safe to short-cut here */ goto again; } return entry; + +overflow: + if (set_overflow) + mas->node = MAS_OVERFLOW; + return NULL; } /* @@ -4651,17 +4693,20 @@ static void *mas_next_slot(struct ma_state *mas, unsigned long max, bool empty) * * Set the @mas->node to the next entry and the range_start to * the beginning value for the entry. Does not check beyond @limit. - * Sets @mas->index and @mas->last to the limit if it is hit. + * Sets @mas->index and @mas->last to the range, Does not update @mas->index and + * @mas->last on overflow. * Restarts on dead nodes. * * Return: the next entry or %NULL. */ static inline void *mas_next_entry(struct ma_state *mas, unsigned long limit) { - if (mas->last >= limit) + if (mas->last >= limit) { + mas->node = MAS_OVERFLOW; return NULL; + } - return mas_next_slot(mas, limit, false); + return mas_next_slot(mas, limit, false, true); } /* @@ -4837,7 +4882,7 @@ void *mas_walk(struct ma_state *mas) { void *entry; - if (mas_is_none(mas) || mas_is_paused(mas) || mas_is_ptr(mas)) + if (!mas_is_active(mas) || !mas_is_start(mas)) mas->node = MAS_START; retry: entry = mas_state_walk(mas); @@ -5294,14 +5339,22 @@ static inline void mte_destroy_walk(struct maple_enode *enode, static void mas_wr_store_setup(struct ma_wr_state *wr_mas) { - if (mas_is_start(wr_mas->mas)) - return; + if (!mas_is_active(wr_mas->mas)) { + if (mas_is_start(wr_mas->mas)) + return; - if (unlikely(mas_is_paused(wr_mas->mas))) - goto reset; + if (unlikely(mas_is_paused(wr_mas->mas))) + goto reset; - if (unlikely(mas_is_none(wr_mas->mas))) - goto reset; + if (unlikely(mas_is_none(wr_mas->mas))) + goto reset; + + if (unlikely(mas_is_overflow(wr_mas->mas))) + goto reset; + + if (unlikely(mas_is_underflow(wr_mas->mas))) + goto reset; + } /* * A less strict version of mas_is_span_wr() where we allow spanning @@ -5595,8 +5648,25 @@ static inline bool mas_next_setup(struct ma_state *mas, unsigned long max, { bool was_none = mas_is_none(mas); - if (mas_is_none(mas) || mas_is_paused(mas)) + if (unlikely(mas->last >= max)) { + mas->node = MAS_OVERFLOW; + return true; + } + + if (mas_is_active(mas)) + return false; + + if (mas_is_none(mas) || mas_is_paused(mas)) { + mas->node = MAS_START; + } else if (mas_is_overflow(mas)) { + /* Overflowed before, but the max changed */ mas->node = MAS_START; + } else if (mas_is_underflow(mas)) { + mas->node = MAS_START; + *entry = mas_walk(mas); + if (*entry) + return true; + } if (mas_is_start(mas)) *entry = mas_walk(mas); /* Retries on dead nodes handled by mas_walk */ @@ -5615,6 +5685,7 @@ static inline bool mas_next_setup(struct ma_state *mas, unsigned long max, if (mas_is_none(mas)) return true; + return false; } @@ -5637,7 +5708,7 @@ void *mas_next(struct ma_state *mas, unsigned long max) return entry; /* Retries on dead nodes handled by mas_next_slot */ - return mas_next_slot(mas, max, false); + return mas_next_slot(mas, max, false, true); } EXPORT_SYMBOL_GPL(mas_next); @@ -5660,7 +5731,7 @@ void *mas_next_range(struct ma_state *mas, unsigned long max) return entry; /* Retries on dead nodes handled by mas_next_slot */ - return mas_next_slot(mas, max, true); + return mas_next_slot(mas, max, true, true); } EXPORT_SYMBOL_GPL(mas_next_range); @@ -5691,18 +5762,31 @@ EXPORT_SYMBOL_GPL(mt_next); static inline bool mas_prev_setup(struct ma_state *mas, unsigned long min, void **entry) { - if (mas->index <= min) - goto none; + if (unlikely(mas->index <= min)) { + mas->node = MAS_UNDERFLOW; + return true; + } - if (mas_is_none(mas) || mas_is_paused(mas)) + if (mas_is_active(mas)) + return false; + + if (mas_is_overflow(mas)) { mas->node = MAS_START; + *entry = mas_walk(mas); + if (*entry) + return true; + } - if (mas_is_start(mas)) { - mas_walk(mas); - if (!mas->index) - goto none; + if (mas_is_none(mas) || mas_is_paused(mas)) { + mas->node = MAS_START; + } else if (mas_is_underflow(mas)) { + /* underflowed before but the min changed */ + mas->node = MAS_START; } + if (mas_is_start(mas)) + mas_walk(mas); + if (unlikely(mas_is_ptr(mas))) { if (!mas->index) goto none; @@ -5747,7 +5831,7 @@ void *mas_prev(struct ma_state *mas, unsigned long min) if (mas_prev_setup(mas, min, &entry)) return entry; - return mas_prev_slot(mas, min, false); + return mas_prev_slot(mas, min, false, true); } EXPORT_SYMBOL_GPL(mas_prev); @@ -5770,7 +5854,7 @@ void *mas_prev_range(struct ma_state *mas, unsigned long min) if (mas_prev_setup(mas, min, &entry)) return entry; - return mas_prev_slot(mas, min, true); + return mas_prev_slot(mas, min, true, true); } EXPORT_SYMBOL_GPL(mas_prev_range); @@ -5828,24 +5912,35 @@ EXPORT_SYMBOL_GPL(mas_pause); static inline bool mas_find_setup(struct ma_state *mas, unsigned long max, void **entry) { - *entry = NULL; + if (mas_is_active(mas)) { + if (mas->last < max) + return false; - if (unlikely(mas_is_none(mas))) { + return true; + } + + if (mas_is_paused(mas)) { if (unlikely(mas->last >= max)) return true; - mas->index = mas->last; + mas->index = ++mas->last; mas->node = MAS_START; - } else if (unlikely(mas_is_paused(mas))) { + } else if (mas_is_none(mas)) { if (unlikely(mas->last >= max)) return true; + mas->index = mas->last; mas->node = MAS_START; - mas->index = ++mas->last; - } else if (unlikely(mas_is_ptr(mas))) - goto ptr_out_of_range; + } else if (mas_is_overflow(mas) || mas_is_underflow(mas)) { + if (mas->index > max) { + mas->node = MAS_OVERFLOW; + return true; + } + + mas->node = MAS_START; + } - if (unlikely(mas_is_start(mas))) { + if (mas_is_start(mas)) { /* First run or continue */ if (mas->index > max) return true; @@ -5895,7 +5990,7 @@ void *mas_find(struct ma_state *mas, unsigned long max) return entry; /* Retries on dead nodes handled by mas_next_slot */ - return mas_next_slot(mas, max, false); + return mas_next_slot(mas, max, false, false); } EXPORT_SYMBOL_GPL(mas_find); @@ -5913,13 +6008,13 @@ EXPORT_SYMBOL_GPL(mas_find); */ void *mas_find_range(struct ma_state *mas, unsigned long max) { - void *entry; + void *entry = NULL; if (mas_find_setup(mas, max, &entry)) return entry; /* Retries on dead nodes handled by mas_next_slot */ - return mas_next_slot(mas, max, true); + return mas_next_slot(mas, max, true, false); } EXPORT_SYMBOL_GPL(mas_find_range); @@ -5934,26 +6029,36 @@ EXPORT_SYMBOL_GPL(mas_find_range); static inline bool mas_find_rev_setup(struct ma_state *mas, unsigned long min, void **entry) { - *entry = NULL; - - if (unlikely(mas_is_none(mas))) { - if (mas->index <= min) - goto none; + if (mas_is_active(mas)) { + if (mas->index > min) + return false; - mas->last = mas->index; - mas->node = MAS_START; + return true; } - if (unlikely(mas_is_paused(mas))) { + if (mas_is_paused(mas)) { if (unlikely(mas->index <= min)) { mas->node = MAS_NONE; return true; } mas->node = MAS_START; mas->last = --mas->index; + } else if (mas_is_none(mas)) { + if (mas->index <= min) + goto none; + + mas->last = mas->index; + mas->node = MAS_START; + } else if (mas_is_underflow(mas) || mas_is_overflow(mas)) { + if (mas->last <= min) { + mas->node = MAS_UNDERFLOW; + return true; + } + + mas->node = MAS_START; } - if (unlikely(mas_is_start(mas))) { + if (mas_is_start(mas)) { /* First run or continue */ if (mas->index < min) return true; @@ -6004,13 +6109,13 @@ static inline bool mas_find_rev_setup(struct ma_state *mas, unsigned long min, */ void *mas_find_rev(struct ma_state *mas, unsigned long min) { - void *entry; + void *entry = NULL; if (mas_find_rev_setup(mas, min, &entry)) return entry; /* Retries on dead nodes handled by mas_prev_slot */ - return mas_prev_slot(mas, min, false); + return mas_prev_slot(mas, min, false, false); } EXPORT_SYMBOL_GPL(mas_find_rev); @@ -6030,13 +6135,13 @@ EXPORT_SYMBOL_GPL(mas_find_rev); */ void *mas_find_range_rev(struct ma_state *mas, unsigned long min) { - void *entry; + void *entry = NULL; if (mas_find_rev_setup(mas, min, &entry)) return entry; /* Retries on dead nodes handled by mas_prev_slot */ - return mas_prev_slot(mas, min, true); + return mas_prev_slot(mas, min, true, false); } EXPORT_SYMBOL_GPL(mas_find_range_rev); diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c index 5004463c4f9f1243674ca77c2aea8e7050edfbda..9073430dc8659c7e37aa262874b991c3677d6b06 100644 --- a/lib/percpu_counter.c +++ b/lib/percpu_counter.c @@ -151,48 +151,72 @@ s64 __percpu_counter_sum(struct percpu_counter *fbc) } EXPORT_SYMBOL(__percpu_counter_sum); -int __percpu_counter_init(struct percpu_counter *fbc, s64 amount, gfp_t gfp, - struct lock_class_key *key) +int __percpu_counter_init_many(struct percpu_counter *fbc, s64 amount, + gfp_t gfp, u32 nr_counters, + struct lock_class_key *key) { unsigned long flags __maybe_unused; - - raw_spin_lock_init(&fbc->lock); - lockdep_set_class(&fbc->lock, key); - fbc->count = amount; - fbc->counters = alloc_percpu_gfp(s32, gfp); - if (!fbc->counters) + size_t counter_size; + s32 __percpu *counters; + u32 i; + + counter_size = ALIGN(sizeof(*counters), __alignof__(*counters)); + counters = __alloc_percpu_gfp(nr_counters * counter_size, + __alignof__(*counters), gfp); + if (!counters) { + fbc[0].counters = NULL; return -ENOMEM; + } - debug_percpu_counter_activate(fbc); + for (i = 0; i < nr_counters; i++) { + raw_spin_lock_init(&fbc[i].lock); + lockdep_set_class(&fbc[i].lock, key); +#ifdef CONFIG_HOTPLUG_CPU + INIT_LIST_HEAD(&fbc[i].list); +#endif + fbc[i].count = amount; + fbc[i].counters = (void *)counters + (i * counter_size); + + debug_percpu_counter_activate(&fbc[i]); + } #ifdef CONFIG_HOTPLUG_CPU - INIT_LIST_HEAD(&fbc->list); spin_lock_irqsave(&percpu_counters_lock, flags); - list_add(&fbc->list, &percpu_counters); + for (i = 0; i < nr_counters; i++) + list_add(&fbc[i].list, &percpu_counters); spin_unlock_irqrestore(&percpu_counters_lock, flags); #endif return 0; } -EXPORT_SYMBOL(__percpu_counter_init); +EXPORT_SYMBOL(__percpu_counter_init_many); -void percpu_counter_destroy(struct percpu_counter *fbc) +void percpu_counter_destroy_many(struct percpu_counter *fbc, u32 nr_counters) { unsigned long flags __maybe_unused; + u32 i; + + if (WARN_ON_ONCE(!fbc)) + return; - if (!fbc->counters) + if (!fbc[0].counters) return; - debug_percpu_counter_deactivate(fbc); + for (i = 0; i < nr_counters; i++) + debug_percpu_counter_deactivate(&fbc[i]); #ifdef CONFIG_HOTPLUG_CPU spin_lock_irqsave(&percpu_counters_lock, flags); - list_del(&fbc->list); + for (i = 0; i < nr_counters; i++) + list_del(&fbc[i].list); spin_unlock_irqrestore(&percpu_counters_lock, flags); #endif - free_percpu(fbc->counters); - fbc->counters = NULL; + + free_percpu(fbc[0].counters); + + for (i = 0; i < nr_counters; i++) + fbc[i].counters = NULL; } -EXPORT_SYMBOL(percpu_counter_destroy); +EXPORT_SYMBOL(percpu_counter_destroy_many); int percpu_counter_batch __read_mostly = 32; EXPORT_SYMBOL(percpu_counter_batch); diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile index 45e17619422b4ef4b3c278b242806668d034b19f..035b0a4db476a1fba02620c2a422323dc5a1f560 100644 --- a/lib/raid6/Makefile +++ b/lib/raid6/Makefile @@ -9,6 +9,7 @@ raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o \ vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o +raid6_pq-$(CONFIG_LOONGARCH) += loongarch_simd.o recov_loongarch_simd.o hostprogs += mktables diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c index a22a05c9af8a3a75610fd2eca0c0b195009e2fe9..0ec534faf019bc966095f9cecf082720a834d412 100644 --- a/lib/raid6/algos.c +++ b/lib/raid6/algos.c @@ -73,6 +73,14 @@ const struct raid6_calls * const raid6_algos[] = { &raid6_neonx2, &raid6_neonx1, #endif +#ifdef CONFIG_LOONGARCH +#ifdef CONFIG_CPU_HAS_LASX + &raid6_lasx, +#endif +#ifdef CONFIG_CPU_HAS_LSX + &raid6_lsx, +#endif +#endif #if defined(__ia64__) &raid6_intx32, &raid6_intx16, @@ -103,6 +111,14 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = { #endif #if defined(CONFIG_KERNEL_MODE_NEON) &raid6_recov_neon, +#endif +#ifdef CONFIG_LOONGARCH +#ifdef CONFIG_CPU_HAS_LASX + &raid6_recov_lasx, +#endif +#ifdef CONFIG_CPU_HAS_LSX + &raid6_recov_lsx, +#endif #endif &raid6_recov_intx1, NULL diff --git a/lib/raid6/loongarch.h b/lib/raid6/loongarch.h new file mode 100644 index 0000000000000000000000000000000000000000..acfc33ce705625058131f4fd54bd4c5a64af0d30 --- /dev/null +++ b/lib/raid6/loongarch.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 WANG Xuerui + * + * raid6/loongarch.h + * + * Definitions common to LoongArch RAID-6 code only + */ + +#ifndef _LIB_RAID6_LOONGARCH_H +#define _LIB_RAID6_LOONGARCH_H + +#ifdef __KERNEL__ + +#include +#include + +#else /* for user-space testing */ + +#include + +/* have to supply these defines for glibc 2.37- and musl */ +#ifndef HWCAP_LOONGARCH_LSX +#define HWCAP_LOONGARCH_LSX (1 << 4) +#endif +#ifndef HWCAP_LOONGARCH_LASX +#define HWCAP_LOONGARCH_LASX (1 << 5) +#endif + +#define kernel_fpu_begin() +#define kernel_fpu_end() + +#define cpu_has_lsx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LSX) +#define cpu_has_lasx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LASX) + +#endif /* __KERNEL__ */ + +#endif /* _LIB_RAID6_LOONGARCH_H */ diff --git a/lib/raid6/loongarch_simd.c b/lib/raid6/loongarch_simd.c new file mode 100644 index 0000000000000000000000000000000000000000..aa5d9f924ca39694f7487cf3efc7295c78574c66 --- /dev/null +++ b/lib/raid6/loongarch_simd.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RAID6 syndrome calculations in LoongArch SIMD (LSX & LASX) + * + * Copyright 2023 WANG Xuerui + * + * Based on the generic RAID-6 code (int.uc): + * + * Copyright 2002-2004 H. Peter Anvin + */ + +#include +#include "loongarch.h" + +/* + * The vector algorithms are currently priority 0, which means the generic + * scalar algorithms are not being disabled if vector support is present. + * This is like the similar LoongArch RAID5 XOR code, with the main reason + * repeated here: it cannot be ruled out at this point of time, that some + * future (maybe reduced) models could run the vector algorithms slower than + * the scalar ones, maybe for errata or micro-op reasons. It may be + * appropriate to revisit this after one or two more uarch generations. + */ + +#ifdef CONFIG_CPU_HAS_LSX +#define NSIZE 16 + +static int raid6_has_lsx(void) +{ + return cpu_has_lsx; +} + +static void raid6_lsx_gen_syndrome(int disks, size_t bytes, void **ptrs) +{ + u8 **dptr = (u8 **)ptrs; + u8 *p, *q; + int d, z, z0; + + z0 = disks - 3; /* Highest data disk */ + p = dptr[z0+1]; /* XOR parity */ + q = dptr[z0+2]; /* RS syndrome */ + + kernel_fpu_begin(); + + /* + * $vr0, $vr1, $vr2, $vr3: wp + * $vr4, $vr5, $vr6, $vr7: wq + * $vr8, $vr9, $vr10, $vr11: wd + * $vr12, $vr13, $vr14, $vr15: w2 + * $vr16, $vr17, $vr18, $vr19: w1 + */ + for (d = 0; d < bytes; d += NSIZE*4) { + /* wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE]; */ + asm volatile("vld $vr0, %0" : : "m"(dptr[z0][d+0*NSIZE])); + asm volatile("vld $vr1, %0" : : "m"(dptr[z0][d+1*NSIZE])); + asm volatile("vld $vr2, %0" : : "m"(dptr[z0][d+2*NSIZE])); + asm volatile("vld $vr3, %0" : : "m"(dptr[z0][d+3*NSIZE])); + asm volatile("vori.b $vr4, $vr0, 0"); + asm volatile("vori.b $vr5, $vr1, 0"); + asm volatile("vori.b $vr6, $vr2, 0"); + asm volatile("vori.b $vr7, $vr3, 0"); + for (z = z0-1; z >= 0; z--) { + /* wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE]; */ + asm volatile("vld $vr8, %0" : : "m"(dptr[z][d+0*NSIZE])); + asm volatile("vld $vr9, %0" : : "m"(dptr[z][d+1*NSIZE])); + asm volatile("vld $vr10, %0" : : "m"(dptr[z][d+2*NSIZE])); + asm volatile("vld $vr11, %0" : : "m"(dptr[z][d+3*NSIZE])); + /* wp$$ ^= wd$$; */ + asm volatile("vxor.v $vr0, $vr0, $vr8"); + asm volatile("vxor.v $vr1, $vr1, $vr9"); + asm volatile("vxor.v $vr2, $vr2, $vr10"); + asm volatile("vxor.v $vr3, $vr3, $vr11"); + /* w2$$ = MASK(wq$$); */ + asm volatile("vslti.b $vr12, $vr4, 0"); + asm volatile("vslti.b $vr13, $vr5, 0"); + asm volatile("vslti.b $vr14, $vr6, 0"); + asm volatile("vslti.b $vr15, $vr7, 0"); + /* w1$$ = SHLBYTE(wq$$); */ + asm volatile("vslli.b $vr16, $vr4, 1"); + asm volatile("vslli.b $vr17, $vr5, 1"); + asm volatile("vslli.b $vr18, $vr6, 1"); + asm volatile("vslli.b $vr19, $vr7, 1"); + /* w2$$ &= NBYTES(0x1d); */ + asm volatile("vandi.b $vr12, $vr12, 0x1d"); + asm volatile("vandi.b $vr13, $vr13, 0x1d"); + asm volatile("vandi.b $vr14, $vr14, 0x1d"); + asm volatile("vandi.b $vr15, $vr15, 0x1d"); + /* w1$$ ^= w2$$; */ + asm volatile("vxor.v $vr16, $vr16, $vr12"); + asm volatile("vxor.v $vr17, $vr17, $vr13"); + asm volatile("vxor.v $vr18, $vr18, $vr14"); + asm volatile("vxor.v $vr19, $vr19, $vr15"); + /* wq$$ = w1$$ ^ wd$$; */ + asm volatile("vxor.v $vr4, $vr16, $vr8"); + asm volatile("vxor.v $vr5, $vr17, $vr9"); + asm volatile("vxor.v $vr6, $vr18, $vr10"); + asm volatile("vxor.v $vr7, $vr19, $vr11"); + } + /* *(unative_t *)&p[d+NSIZE*$$] = wp$$; */ + asm volatile("vst $vr0, %0" : "=m"(p[d+NSIZE*0])); + asm volatile("vst $vr1, %0" : "=m"(p[d+NSIZE*1])); + asm volatile("vst $vr2, %0" : "=m"(p[d+NSIZE*2])); + asm volatile("vst $vr3, %0" : "=m"(p[d+NSIZE*3])); + /* *(unative_t *)&q[d+NSIZE*$$] = wq$$; */ + asm volatile("vst $vr4, %0" : "=m"(q[d+NSIZE*0])); + asm volatile("vst $vr5, %0" : "=m"(q[d+NSIZE*1])); + asm volatile("vst $vr6, %0" : "=m"(q[d+NSIZE*2])); + asm volatile("vst $vr7, %0" : "=m"(q[d+NSIZE*3])); + } + + kernel_fpu_end(); +} + +static void raid6_lsx_xor_syndrome(int disks, int start, int stop, + size_t bytes, void **ptrs) +{ + u8 **dptr = (u8 **)ptrs; + u8 *p, *q; + int d, z, z0; + + z0 = stop; /* P/Q right side optimization */ + p = dptr[disks-2]; /* XOR parity */ + q = dptr[disks-1]; /* RS syndrome */ + + kernel_fpu_begin(); + + /* + * $vr0, $vr1, $vr2, $vr3: wp + * $vr4, $vr5, $vr6, $vr7: wq + * $vr8, $vr9, $vr10, $vr11: wd + * $vr12, $vr13, $vr14, $vr15: w2 + * $vr16, $vr17, $vr18, $vr19: w1 + */ + for (d = 0; d < bytes; d += NSIZE*4) { + /* P/Q data pages */ + /* wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE]; */ + asm volatile("vld $vr0, %0" : : "m"(dptr[z0][d+0*NSIZE])); + asm volatile("vld $vr1, %0" : : "m"(dptr[z0][d+1*NSIZE])); + asm volatile("vld $vr2, %0" : : "m"(dptr[z0][d+2*NSIZE])); + asm volatile("vld $vr3, %0" : : "m"(dptr[z0][d+3*NSIZE])); + asm volatile("vori.b $vr4, $vr0, 0"); + asm volatile("vori.b $vr5, $vr1, 0"); + asm volatile("vori.b $vr6, $vr2, 0"); + asm volatile("vori.b $vr7, $vr3, 0"); + for (z = z0-1; z >= start; z--) { + /* wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE]; */ + asm volatile("vld $vr8, %0" : : "m"(dptr[z][d+0*NSIZE])); + asm volatile("vld $vr9, %0" : : "m"(dptr[z][d+1*NSIZE])); + asm volatile("vld $vr10, %0" : : "m"(dptr[z][d+2*NSIZE])); + asm volatile("vld $vr11, %0" : : "m"(dptr[z][d+3*NSIZE])); + /* wp$$ ^= wd$$; */ + asm volatile("vxor.v $vr0, $vr0, $vr8"); + asm volatile("vxor.v $vr1, $vr1, $vr9"); + asm volatile("vxor.v $vr2, $vr2, $vr10"); + asm volatile("vxor.v $vr3, $vr3, $vr11"); + /* w2$$ = MASK(wq$$); */ + asm volatile("vslti.b $vr12, $vr4, 0"); + asm volatile("vslti.b $vr13, $vr5, 0"); + asm volatile("vslti.b $vr14, $vr6, 0"); + asm volatile("vslti.b $vr15, $vr7, 0"); + /* w1$$ = SHLBYTE(wq$$); */ + asm volatile("vslli.b $vr16, $vr4, 1"); + asm volatile("vslli.b $vr17, $vr5, 1"); + asm volatile("vslli.b $vr18, $vr6, 1"); + asm volatile("vslli.b $vr19, $vr7, 1"); + /* w2$$ &= NBYTES(0x1d); */ + asm volatile("vandi.b $vr12, $vr12, 0x1d"); + asm volatile("vandi.b $vr13, $vr13, 0x1d"); + asm volatile("vandi.b $vr14, $vr14, 0x1d"); + asm volatile("vandi.b $vr15, $vr15, 0x1d"); + /* w1$$ ^= w2$$; */ + asm volatile("vxor.v $vr16, $vr16, $vr12"); + asm volatile("vxor.v $vr17, $vr17, $vr13"); + asm volatile("vxor.v $vr18, $vr18, $vr14"); + asm volatile("vxor.v $vr19, $vr19, $vr15"); + /* wq$$ = w1$$ ^ wd$$; */ + asm volatile("vxor.v $vr4, $vr16, $vr8"); + asm volatile("vxor.v $vr5, $vr17, $vr9"); + asm volatile("vxor.v $vr6, $vr18, $vr10"); + asm volatile("vxor.v $vr7, $vr19, $vr11"); + } + + /* P/Q left side optimization */ + for (z = start-1; z >= 0; z--) { + /* w2$$ = MASK(wq$$); */ + asm volatile("vslti.b $vr12, $vr4, 0"); + asm volatile("vslti.b $vr13, $vr5, 0"); + asm volatile("vslti.b $vr14, $vr6, 0"); + asm volatile("vslti.b $vr15, $vr7, 0"); + /* w1$$ = SHLBYTE(wq$$); */ + asm volatile("vslli.b $vr16, $vr4, 1"); + asm volatile("vslli.b $vr17, $vr5, 1"); + asm volatile("vslli.b $vr18, $vr6, 1"); + asm volatile("vslli.b $vr19, $vr7, 1"); + /* w2$$ &= NBYTES(0x1d); */ + asm volatile("vandi.b $vr12, $vr12, 0x1d"); + asm volatile("vandi.b $vr13, $vr13, 0x1d"); + asm volatile("vandi.b $vr14, $vr14, 0x1d"); + asm volatile("vandi.b $vr15, $vr15, 0x1d"); + /* wq$$ = w1$$ ^ w2$$; */ + asm volatile("vxor.v $vr4, $vr16, $vr12"); + asm volatile("vxor.v $vr5, $vr17, $vr13"); + asm volatile("vxor.v $vr6, $vr18, $vr14"); + asm volatile("vxor.v $vr7, $vr19, $vr15"); + } + /* + * *(unative_t *)&p[d+NSIZE*$$] ^= wp$$; + * *(unative_t *)&q[d+NSIZE*$$] ^= wq$$; + */ + asm volatile( + "vld $vr20, %0\n\t" + "vld $vr21, %1\n\t" + "vld $vr22, %2\n\t" + "vld $vr23, %3\n\t" + "vld $vr24, %4\n\t" + "vld $vr25, %5\n\t" + "vld $vr26, %6\n\t" + "vld $vr27, %7\n\t" + "vxor.v $vr20, $vr20, $vr0\n\t" + "vxor.v $vr21, $vr21, $vr1\n\t" + "vxor.v $vr22, $vr22, $vr2\n\t" + "vxor.v $vr23, $vr23, $vr3\n\t" + "vxor.v $vr24, $vr24, $vr4\n\t" + "vxor.v $vr25, $vr25, $vr5\n\t" + "vxor.v $vr26, $vr26, $vr6\n\t" + "vxor.v $vr27, $vr27, $vr7\n\t" + "vst $vr20, %0\n\t" + "vst $vr21, %1\n\t" + "vst $vr22, %2\n\t" + "vst $vr23, %3\n\t" + "vst $vr24, %4\n\t" + "vst $vr25, %5\n\t" + "vst $vr26, %6\n\t" + "vst $vr27, %7\n\t" + : "+m"(p[d+NSIZE*0]), "+m"(p[d+NSIZE*1]), + "+m"(p[d+NSIZE*2]), "+m"(p[d+NSIZE*3]), + "+m"(q[d+NSIZE*0]), "+m"(q[d+NSIZE*1]), + "+m"(q[d+NSIZE*2]), "+m"(q[d+NSIZE*3]) + ); + } + + kernel_fpu_end(); +} + +const struct raid6_calls raid6_lsx = { + raid6_lsx_gen_syndrome, + raid6_lsx_xor_syndrome, + raid6_has_lsx, + "lsx", + .priority = 0 /* see the comment near the top of the file for reason */ +}; + +#undef NSIZE +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX +#define NSIZE 32 + +static int raid6_has_lasx(void) +{ + return cpu_has_lasx; +} + +static void raid6_lasx_gen_syndrome(int disks, size_t bytes, void **ptrs) +{ + u8 **dptr = (u8 **)ptrs; + u8 *p, *q; + int d, z, z0; + + z0 = disks - 3; /* Highest data disk */ + p = dptr[z0+1]; /* XOR parity */ + q = dptr[z0+2]; /* RS syndrome */ + + kernel_fpu_begin(); + + /* + * $xr0, $xr1: wp + * $xr2, $xr3: wq + * $xr4, $xr5: wd + * $xr6, $xr7: w2 + * $xr8, $xr9: w1 + */ + for (d = 0; d < bytes; d += NSIZE*2) { + /* wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE]; */ + asm volatile("xvld $xr0, %0" : : "m"(dptr[z0][d+0*NSIZE])); + asm volatile("xvld $xr1, %0" : : "m"(dptr[z0][d+1*NSIZE])); + asm volatile("xvori.b $xr2, $xr0, 0"); + asm volatile("xvori.b $xr3, $xr1, 0"); + for (z = z0-1; z >= 0; z--) { + /* wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE]; */ + asm volatile("xvld $xr4, %0" : : "m"(dptr[z][d+0*NSIZE])); + asm volatile("xvld $xr5, %0" : : "m"(dptr[z][d+1*NSIZE])); + /* wp$$ ^= wd$$; */ + asm volatile("xvxor.v $xr0, $xr0, $xr4"); + asm volatile("xvxor.v $xr1, $xr1, $xr5"); + /* w2$$ = MASK(wq$$); */ + asm volatile("xvslti.b $xr6, $xr2, 0"); + asm volatile("xvslti.b $xr7, $xr3, 0"); + /* w1$$ = SHLBYTE(wq$$); */ + asm volatile("xvslli.b $xr8, $xr2, 1"); + asm volatile("xvslli.b $xr9, $xr3, 1"); + /* w2$$ &= NBYTES(0x1d); */ + asm volatile("xvandi.b $xr6, $xr6, 0x1d"); + asm volatile("xvandi.b $xr7, $xr7, 0x1d"); + /* w1$$ ^= w2$$; */ + asm volatile("xvxor.v $xr8, $xr8, $xr6"); + asm volatile("xvxor.v $xr9, $xr9, $xr7"); + /* wq$$ = w1$$ ^ wd$$; */ + asm volatile("xvxor.v $xr2, $xr8, $xr4"); + asm volatile("xvxor.v $xr3, $xr9, $xr5"); + } + /* *(unative_t *)&p[d+NSIZE*$$] = wp$$; */ + asm volatile("xvst $xr0, %0" : "=m"(p[d+NSIZE*0])); + asm volatile("xvst $xr1, %0" : "=m"(p[d+NSIZE*1])); + /* *(unative_t *)&q[d+NSIZE*$$] = wq$$; */ + asm volatile("xvst $xr2, %0" : "=m"(q[d+NSIZE*0])); + asm volatile("xvst $xr3, %0" : "=m"(q[d+NSIZE*1])); + } + + kernel_fpu_end(); +} + +static void raid6_lasx_xor_syndrome(int disks, int start, int stop, + size_t bytes, void **ptrs) +{ + u8 **dptr = (u8 **)ptrs; + u8 *p, *q; + int d, z, z0; + + z0 = stop; /* P/Q right side optimization */ + p = dptr[disks-2]; /* XOR parity */ + q = dptr[disks-1]; /* RS syndrome */ + + kernel_fpu_begin(); + + /* + * $xr0, $xr1: wp + * $xr2, $xr3: wq + * $xr4, $xr5: wd + * $xr6, $xr7: w2 + * $xr8, $xr9: w1 + */ + for (d = 0; d < bytes; d += NSIZE*2) { + /* P/Q data pages */ + /* wq$$ = wp$$ = *(unative_t *)&dptr[z0][d+$$*NSIZE]; */ + asm volatile("xvld $xr0, %0" : : "m"(dptr[z0][d+0*NSIZE])); + asm volatile("xvld $xr1, %0" : : "m"(dptr[z0][d+1*NSIZE])); + asm volatile("xvori.b $xr2, $xr0, 0"); + asm volatile("xvori.b $xr3, $xr1, 0"); + for (z = z0-1; z >= start; z--) { + /* wd$$ = *(unative_t *)&dptr[z][d+$$*NSIZE]; */ + asm volatile("xvld $xr4, %0" : : "m"(dptr[z][d+0*NSIZE])); + asm volatile("xvld $xr5, %0" : : "m"(dptr[z][d+1*NSIZE])); + /* wp$$ ^= wd$$; */ + asm volatile("xvxor.v $xr0, $xr0, $xr4"); + asm volatile("xvxor.v $xr1, $xr1, $xr5"); + /* w2$$ = MASK(wq$$); */ + asm volatile("xvslti.b $xr6, $xr2, 0"); + asm volatile("xvslti.b $xr7, $xr3, 0"); + /* w1$$ = SHLBYTE(wq$$); */ + asm volatile("xvslli.b $xr8, $xr2, 1"); + asm volatile("xvslli.b $xr9, $xr3, 1"); + /* w2$$ &= NBYTES(0x1d); */ + asm volatile("xvandi.b $xr6, $xr6, 0x1d"); + asm volatile("xvandi.b $xr7, $xr7, 0x1d"); + /* w1$$ ^= w2$$; */ + asm volatile("xvxor.v $xr8, $xr8, $xr6"); + asm volatile("xvxor.v $xr9, $xr9, $xr7"); + /* wq$$ = w1$$ ^ wd$$; */ + asm volatile("xvxor.v $xr2, $xr8, $xr4"); + asm volatile("xvxor.v $xr3, $xr9, $xr5"); + } + + /* P/Q left side optimization */ + for (z = start-1; z >= 0; z--) { + /* w2$$ = MASK(wq$$); */ + asm volatile("xvslti.b $xr6, $xr2, 0"); + asm volatile("xvslti.b $xr7, $xr3, 0"); + /* w1$$ = SHLBYTE(wq$$); */ + asm volatile("xvslli.b $xr8, $xr2, 1"); + asm volatile("xvslli.b $xr9, $xr3, 1"); + /* w2$$ &= NBYTES(0x1d); */ + asm volatile("xvandi.b $xr6, $xr6, 0x1d"); + asm volatile("xvandi.b $xr7, $xr7, 0x1d"); + /* wq$$ = w1$$ ^ w2$$; */ + asm volatile("xvxor.v $xr2, $xr8, $xr6"); + asm volatile("xvxor.v $xr3, $xr9, $xr7"); + } + /* + * *(unative_t *)&p[d+NSIZE*$$] ^= wp$$; + * *(unative_t *)&q[d+NSIZE*$$] ^= wq$$; + */ + asm volatile( + "xvld $xr10, %0\n\t" + "xvld $xr11, %1\n\t" + "xvld $xr12, %2\n\t" + "xvld $xr13, %3\n\t" + "xvxor.v $xr10, $xr10, $xr0\n\t" + "xvxor.v $xr11, $xr11, $xr1\n\t" + "xvxor.v $xr12, $xr12, $xr2\n\t" + "xvxor.v $xr13, $xr13, $xr3\n\t" + "xvst $xr10, %0\n\t" + "xvst $xr11, %1\n\t" + "xvst $xr12, %2\n\t" + "xvst $xr13, %3\n\t" + : "+m"(p[d+NSIZE*0]), "+m"(p[d+NSIZE*1]), + "+m"(q[d+NSIZE*0]), "+m"(q[d+NSIZE*1]) + ); + } + + kernel_fpu_end(); +} + +const struct raid6_calls raid6_lasx = { + raid6_lasx_gen_syndrome, + raid6_lasx_xor_syndrome, + raid6_has_lasx, + "lasx", + .priority = 0 /* see the comment near the top of the file for reason */ +}; +#undef NSIZE +#endif /* CONFIG_CPU_HAS_LASX */ diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid6/recov_loongarch_simd.c new file mode 100644 index 0000000000000000000000000000000000000000..94aeac85e6f75e393363868c6306aff8d4521a5b --- /dev/null +++ b/lib/raid6/recov_loongarch_simd.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RAID6 recovery algorithms in LoongArch SIMD (LSX & LASX) + * + * Copyright (C) 2023 WANG Xuerui + * + * Originally based on recov_avx2.c and recov_ssse3.c: + * + * Copyright (C) 2012 Intel Corporation + * Author: Jim Kukunas + */ + +#include +#include "loongarch.h" + +/* + * Unlike with the syndrome calculation algorithms, there's no boot-time + * selection of recovery algorithms by benchmarking, so we have to specify + * the priorities and hope the future cores will all have decent vector + * support (i.e. no LASX slower than LSX, or even scalar code). + */ + +#ifdef CONFIG_CPU_HAS_LSX +static int raid6_has_lsx(void) +{ + return cpu_has_lsx; +} + +static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila, + int failb, void **ptrs) +{ + u8 *p, *q, *dp, *dq; + const u8 *pbmul; /* P multiplier table for B data */ + const u8 *qmul; /* Q multiplier table (for both) */ + + p = (u8 *)ptrs[disks - 2]; + q = (u8 *)ptrs[disks - 1]; + + /* + * Compute syndrome with zero for the missing data pages + * Use the dead data pages as temporary storage for + * delta p and delta q + */ + dp = (u8 *)ptrs[faila]; + ptrs[faila] = (void *)raid6_empty_zero_page; + ptrs[disks - 2] = dp; + dq = (u8 *)ptrs[failb]; + ptrs[failb] = (void *)raid6_empty_zero_page; + ptrs[disks - 1] = dq; + + raid6_call.gen_syndrome(disks, bytes, ptrs); + + /* Restore pointer table */ + ptrs[faila] = dp; + ptrs[failb] = dq; + ptrs[disks - 2] = p; + ptrs[disks - 1] = q; + + /* Now, pick the proper data tables */ + pbmul = raid6_vgfmul[raid6_gfexi[failb - faila]]; + qmul = raid6_vgfmul[raid6_gfinv[raid6_gfexp[faila] ^ raid6_gfexp[failb]]]; + + kernel_fpu_begin(); + + /* + * vr20, vr21: qmul + * vr22, vr23: pbmul + */ + asm volatile("vld $vr20, %0" : : "m" (qmul[0])); + asm volatile("vld $vr21, %0" : : "m" (qmul[16])); + asm volatile("vld $vr22, %0" : : "m" (pbmul[0])); + asm volatile("vld $vr23, %0" : : "m" (pbmul[16])); + + while (bytes) { + /* vr4 - vr7: Q */ + asm volatile("vld $vr4, %0" : : "m" (q[0])); + asm volatile("vld $vr5, %0" : : "m" (q[16])); + asm volatile("vld $vr6, %0" : : "m" (q[32])); + asm volatile("vld $vr7, %0" : : "m" (q[48])); + /* vr4 - vr7: Q + Qxy */ + asm volatile("vld $vr8, %0" : : "m" (dq[0])); + asm volatile("vld $vr9, %0" : : "m" (dq[16])); + asm volatile("vld $vr10, %0" : : "m" (dq[32])); + asm volatile("vld $vr11, %0" : : "m" (dq[48])); + asm volatile("vxor.v $vr4, $vr4, $vr8"); + asm volatile("vxor.v $vr5, $vr5, $vr9"); + asm volatile("vxor.v $vr6, $vr6, $vr10"); + asm volatile("vxor.v $vr7, $vr7, $vr11"); + /* vr0 - vr3: P */ + asm volatile("vld $vr0, %0" : : "m" (p[0])); + asm volatile("vld $vr1, %0" : : "m" (p[16])); + asm volatile("vld $vr2, %0" : : "m" (p[32])); + asm volatile("vld $vr3, %0" : : "m" (p[48])); + /* vr0 - vr3: P + Pxy */ + asm volatile("vld $vr8, %0" : : "m" (dp[0])); + asm volatile("vld $vr9, %0" : : "m" (dp[16])); + asm volatile("vld $vr10, %0" : : "m" (dp[32])); + asm volatile("vld $vr11, %0" : : "m" (dp[48])); + asm volatile("vxor.v $vr0, $vr0, $vr8"); + asm volatile("vxor.v $vr1, $vr1, $vr9"); + asm volatile("vxor.v $vr2, $vr2, $vr10"); + asm volatile("vxor.v $vr3, $vr3, $vr11"); + + /* vr8 - vr11: higher 4 bits of each byte of (Q + Qxy) */ + asm volatile("vsrli.b $vr8, $vr4, 4"); + asm volatile("vsrli.b $vr9, $vr5, 4"); + asm volatile("vsrli.b $vr10, $vr6, 4"); + asm volatile("vsrli.b $vr11, $vr7, 4"); + /* vr4 - vr7: lower 4 bits of each byte of (Q + Qxy) */ + asm volatile("vandi.b $vr4, $vr4, 0x0f"); + asm volatile("vandi.b $vr5, $vr5, 0x0f"); + asm volatile("vandi.b $vr6, $vr6, 0x0f"); + asm volatile("vandi.b $vr7, $vr7, 0x0f"); + /* lookup from qmul[0] */ + asm volatile("vshuf.b $vr4, $vr20, $vr20, $vr4"); + asm volatile("vshuf.b $vr5, $vr20, $vr20, $vr5"); + asm volatile("vshuf.b $vr6, $vr20, $vr20, $vr6"); + asm volatile("vshuf.b $vr7, $vr20, $vr20, $vr7"); + /* lookup from qmul[16] */ + asm volatile("vshuf.b $vr8, $vr21, $vr21, $vr8"); + asm volatile("vshuf.b $vr9, $vr21, $vr21, $vr9"); + asm volatile("vshuf.b $vr10, $vr21, $vr21, $vr10"); + asm volatile("vshuf.b $vr11, $vr21, $vr21, $vr11"); + /* vr16 - vr19: B(Q + Qxy) */ + asm volatile("vxor.v $vr16, $vr8, $vr4"); + asm volatile("vxor.v $vr17, $vr9, $vr5"); + asm volatile("vxor.v $vr18, $vr10, $vr6"); + asm volatile("vxor.v $vr19, $vr11, $vr7"); + + /* vr4 - vr7: higher 4 bits of each byte of (P + Pxy) */ + asm volatile("vsrli.b $vr4, $vr0, 4"); + asm volatile("vsrli.b $vr5, $vr1, 4"); + asm volatile("vsrli.b $vr6, $vr2, 4"); + asm volatile("vsrli.b $vr7, $vr3, 4"); + /* vr12 - vr15: lower 4 bits of each byte of (P + Pxy) */ + asm volatile("vandi.b $vr12, $vr0, 0x0f"); + asm volatile("vandi.b $vr13, $vr1, 0x0f"); + asm volatile("vandi.b $vr14, $vr2, 0x0f"); + asm volatile("vandi.b $vr15, $vr3, 0x0f"); + /* lookup from pbmul[0] */ + asm volatile("vshuf.b $vr12, $vr22, $vr22, $vr12"); + asm volatile("vshuf.b $vr13, $vr22, $vr22, $vr13"); + asm volatile("vshuf.b $vr14, $vr22, $vr22, $vr14"); + asm volatile("vshuf.b $vr15, $vr22, $vr22, $vr15"); + /* lookup from pbmul[16] */ + asm volatile("vshuf.b $vr4, $vr23, $vr23, $vr4"); + asm volatile("vshuf.b $vr5, $vr23, $vr23, $vr5"); + asm volatile("vshuf.b $vr6, $vr23, $vr23, $vr6"); + asm volatile("vshuf.b $vr7, $vr23, $vr23, $vr7"); + /* vr4 - vr7: A(P + Pxy) */ + asm volatile("vxor.v $vr4, $vr4, $vr12"); + asm volatile("vxor.v $vr5, $vr5, $vr13"); + asm volatile("vxor.v $vr6, $vr6, $vr14"); + asm volatile("vxor.v $vr7, $vr7, $vr15"); + + /* vr4 - vr7: A(P + Pxy) + B(Q + Qxy) = Dx */ + asm volatile("vxor.v $vr4, $vr4, $vr16"); + asm volatile("vxor.v $vr5, $vr5, $vr17"); + asm volatile("vxor.v $vr6, $vr6, $vr18"); + asm volatile("vxor.v $vr7, $vr7, $vr19"); + asm volatile("vst $vr4, %0" : "=m" (dq[0])); + asm volatile("vst $vr5, %0" : "=m" (dq[16])); + asm volatile("vst $vr6, %0" : "=m" (dq[32])); + asm volatile("vst $vr7, %0" : "=m" (dq[48])); + + /* vr0 - vr3: P + Pxy + Dx = Dy */ + asm volatile("vxor.v $vr0, $vr0, $vr4"); + asm volatile("vxor.v $vr1, $vr1, $vr5"); + asm volatile("vxor.v $vr2, $vr2, $vr6"); + asm volatile("vxor.v $vr3, $vr3, $vr7"); + asm volatile("vst $vr0, %0" : "=m" (dp[0])); + asm volatile("vst $vr1, %0" : "=m" (dp[16])); + asm volatile("vst $vr2, %0" : "=m" (dp[32])); + asm volatile("vst $vr3, %0" : "=m" (dp[48])); + + bytes -= 64; + p += 64; + q += 64; + dp += 64; + dq += 64; + } + + kernel_fpu_end(); +} + +static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila, + void **ptrs) +{ + u8 *p, *q, *dq; + const u8 *qmul; /* Q multiplier table */ + + p = (u8 *)ptrs[disks - 2]; + q = (u8 *)ptrs[disks - 1]; + + /* + * Compute syndrome with zero for the missing data page + * Use the dead data page as temporary storage for delta q + */ + dq = (u8 *)ptrs[faila]; + ptrs[faila] = (void *)raid6_empty_zero_page; + ptrs[disks - 1] = dq; + + raid6_call.gen_syndrome(disks, bytes, ptrs); + + /* Restore pointer table */ + ptrs[faila] = dq; + ptrs[disks - 1] = q; + + /* Now, pick the proper data tables */ + qmul = raid6_vgfmul[raid6_gfinv[raid6_gfexp[faila]]]; + + kernel_fpu_begin(); + + /* vr22, vr23: qmul */ + asm volatile("vld $vr22, %0" : : "m" (qmul[0])); + asm volatile("vld $vr23, %0" : : "m" (qmul[16])); + + while (bytes) { + /* vr0 - vr3: P + Dx */ + asm volatile("vld $vr0, %0" : : "m" (p[0])); + asm volatile("vld $vr1, %0" : : "m" (p[16])); + asm volatile("vld $vr2, %0" : : "m" (p[32])); + asm volatile("vld $vr3, %0" : : "m" (p[48])); + /* vr4 - vr7: Qx */ + asm volatile("vld $vr4, %0" : : "m" (dq[0])); + asm volatile("vld $vr5, %0" : : "m" (dq[16])); + asm volatile("vld $vr6, %0" : : "m" (dq[32])); + asm volatile("vld $vr7, %0" : : "m" (dq[48])); + /* vr4 - vr7: Q + Qx */ + asm volatile("vld $vr8, %0" : : "m" (q[0])); + asm volatile("vld $vr9, %0" : : "m" (q[16])); + asm volatile("vld $vr10, %0" : : "m" (q[32])); + asm volatile("vld $vr11, %0" : : "m" (q[48])); + asm volatile("vxor.v $vr4, $vr4, $vr8"); + asm volatile("vxor.v $vr5, $vr5, $vr9"); + asm volatile("vxor.v $vr6, $vr6, $vr10"); + asm volatile("vxor.v $vr7, $vr7, $vr11"); + + /* vr8 - vr11: higher 4 bits of each byte of (Q + Qx) */ + asm volatile("vsrli.b $vr8, $vr4, 4"); + asm volatile("vsrli.b $vr9, $vr5, 4"); + asm volatile("vsrli.b $vr10, $vr6, 4"); + asm volatile("vsrli.b $vr11, $vr7, 4"); + /* vr4 - vr7: lower 4 bits of each byte of (Q + Qx) */ + asm volatile("vandi.b $vr4, $vr4, 0x0f"); + asm volatile("vandi.b $vr5, $vr5, 0x0f"); + asm volatile("vandi.b $vr6, $vr6, 0x0f"); + asm volatile("vandi.b $vr7, $vr7, 0x0f"); + /* lookup from qmul[0] */ + asm volatile("vshuf.b $vr4, $vr22, $vr22, $vr4"); + asm volatile("vshuf.b $vr5, $vr22, $vr22, $vr5"); + asm volatile("vshuf.b $vr6, $vr22, $vr22, $vr6"); + asm volatile("vshuf.b $vr7, $vr22, $vr22, $vr7"); + /* lookup from qmul[16] */ + asm volatile("vshuf.b $vr8, $vr23, $vr23, $vr8"); + asm volatile("vshuf.b $vr9, $vr23, $vr23, $vr9"); + asm volatile("vshuf.b $vr10, $vr23, $vr23, $vr10"); + asm volatile("vshuf.b $vr11, $vr23, $vr23, $vr11"); + /* vr4 - vr7: qmul(Q + Qx) = Dx */ + asm volatile("vxor.v $vr4, $vr4, $vr8"); + asm volatile("vxor.v $vr5, $vr5, $vr9"); + asm volatile("vxor.v $vr6, $vr6, $vr10"); + asm volatile("vxor.v $vr7, $vr7, $vr11"); + asm volatile("vst $vr4, %0" : "=m" (dq[0])); + asm volatile("vst $vr5, %0" : "=m" (dq[16])); + asm volatile("vst $vr6, %0" : "=m" (dq[32])); + asm volatile("vst $vr7, %0" : "=m" (dq[48])); + + /* vr0 - vr3: P + Dx + Dx = P */ + asm volatile("vxor.v $vr0, $vr0, $vr4"); + asm volatile("vxor.v $vr1, $vr1, $vr5"); + asm volatile("vxor.v $vr2, $vr2, $vr6"); + asm volatile("vxor.v $vr3, $vr3, $vr7"); + asm volatile("vst $vr0, %0" : "=m" (p[0])); + asm volatile("vst $vr1, %0" : "=m" (p[16])); + asm volatile("vst $vr2, %0" : "=m" (p[32])); + asm volatile("vst $vr3, %0" : "=m" (p[48])); + + bytes -= 64; + p += 64; + q += 64; + dq += 64; + } + + kernel_fpu_end(); +} + +const struct raid6_recov_calls raid6_recov_lsx = { + .data2 = raid6_2data_recov_lsx, + .datap = raid6_datap_recov_lsx, + .valid = raid6_has_lsx, + .name = "lsx", + .priority = 1, +}; +#endif /* CONFIG_CPU_HAS_LSX */ + +#ifdef CONFIG_CPU_HAS_LASX +static int raid6_has_lasx(void) +{ + return cpu_has_lasx; +} + +static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila, + int failb, void **ptrs) +{ + u8 *p, *q, *dp, *dq; + const u8 *pbmul; /* P multiplier table for B data */ + const u8 *qmul; /* Q multiplier table (for both) */ + + p = (u8 *)ptrs[disks - 2]; + q = (u8 *)ptrs[disks - 1]; + + /* + * Compute syndrome with zero for the missing data pages + * Use the dead data pages as temporary storage for + * delta p and delta q + */ + dp = (u8 *)ptrs[faila]; + ptrs[faila] = (void *)raid6_empty_zero_page; + ptrs[disks - 2] = dp; + dq = (u8 *)ptrs[failb]; + ptrs[failb] = (void *)raid6_empty_zero_page; + ptrs[disks - 1] = dq; + + raid6_call.gen_syndrome(disks, bytes, ptrs); + + /* Restore pointer table */ + ptrs[faila] = dp; + ptrs[failb] = dq; + ptrs[disks - 2] = p; + ptrs[disks - 1] = q; + + /* Now, pick the proper data tables */ + pbmul = raid6_vgfmul[raid6_gfexi[failb - faila]]; + qmul = raid6_vgfmul[raid6_gfinv[raid6_gfexp[faila] ^ raid6_gfexp[failb]]]; + + kernel_fpu_begin(); + + /* + * xr20, xr21: qmul + * xr22, xr23: pbmul + */ + asm volatile("vld $vr20, %0" : : "m" (qmul[0])); + asm volatile("vld $vr21, %0" : : "m" (qmul[16])); + asm volatile("vld $vr22, %0" : : "m" (pbmul[0])); + asm volatile("vld $vr23, %0" : : "m" (pbmul[16])); + asm volatile("xvreplve0.q $xr20, $xr20"); + asm volatile("xvreplve0.q $xr21, $xr21"); + asm volatile("xvreplve0.q $xr22, $xr22"); + asm volatile("xvreplve0.q $xr23, $xr23"); + + while (bytes) { + /* xr0, xr1: Q */ + asm volatile("xvld $xr0, %0" : : "m" (q[0])); + asm volatile("xvld $xr1, %0" : : "m" (q[32])); + /* xr0, xr1: Q + Qxy */ + asm volatile("xvld $xr4, %0" : : "m" (dq[0])); + asm volatile("xvld $xr5, %0" : : "m" (dq[32])); + asm volatile("xvxor.v $xr0, $xr0, $xr4"); + asm volatile("xvxor.v $xr1, $xr1, $xr5"); + /* xr2, xr3: P */ + asm volatile("xvld $xr2, %0" : : "m" (p[0])); + asm volatile("xvld $xr3, %0" : : "m" (p[32])); + /* xr2, xr3: P + Pxy */ + asm volatile("xvld $xr4, %0" : : "m" (dp[0])); + asm volatile("xvld $xr5, %0" : : "m" (dp[32])); + asm volatile("xvxor.v $xr2, $xr2, $xr4"); + asm volatile("xvxor.v $xr3, $xr3, $xr5"); + + /* xr4, xr5: higher 4 bits of each byte of (Q + Qxy) */ + asm volatile("xvsrli.b $xr4, $xr0, 4"); + asm volatile("xvsrli.b $xr5, $xr1, 4"); + /* xr0, xr1: lower 4 bits of each byte of (Q + Qxy) */ + asm volatile("xvandi.b $xr0, $xr0, 0x0f"); + asm volatile("xvandi.b $xr1, $xr1, 0x0f"); + /* lookup from qmul[0] */ + asm volatile("xvshuf.b $xr0, $xr20, $xr20, $xr0"); + asm volatile("xvshuf.b $xr1, $xr20, $xr20, $xr1"); + /* lookup from qmul[16] */ + asm volatile("xvshuf.b $xr4, $xr21, $xr21, $xr4"); + asm volatile("xvshuf.b $xr5, $xr21, $xr21, $xr5"); + /* xr6, xr7: B(Q + Qxy) */ + asm volatile("xvxor.v $xr6, $xr4, $xr0"); + asm volatile("xvxor.v $xr7, $xr5, $xr1"); + + /* xr4, xr5: higher 4 bits of each byte of (P + Pxy) */ + asm volatile("xvsrli.b $xr4, $xr2, 4"); + asm volatile("xvsrli.b $xr5, $xr3, 4"); + /* xr0, xr1: lower 4 bits of each byte of (P + Pxy) */ + asm volatile("xvandi.b $xr0, $xr2, 0x0f"); + asm volatile("xvandi.b $xr1, $xr3, 0x0f"); + /* lookup from pbmul[0] */ + asm volatile("xvshuf.b $xr0, $xr22, $xr22, $xr0"); + asm volatile("xvshuf.b $xr1, $xr22, $xr22, $xr1"); + /* lookup from pbmul[16] */ + asm volatile("xvshuf.b $xr4, $xr23, $xr23, $xr4"); + asm volatile("xvshuf.b $xr5, $xr23, $xr23, $xr5"); + /* xr0, xr1: A(P + Pxy) */ + asm volatile("xvxor.v $xr0, $xr0, $xr4"); + asm volatile("xvxor.v $xr1, $xr1, $xr5"); + + /* xr0, xr1: A(P + Pxy) + B(Q + Qxy) = Dx */ + asm volatile("xvxor.v $xr0, $xr0, $xr6"); + asm volatile("xvxor.v $xr1, $xr1, $xr7"); + + /* xr2, xr3: P + Pxy + Dx = Dy */ + asm volatile("xvxor.v $xr2, $xr2, $xr0"); + asm volatile("xvxor.v $xr3, $xr3, $xr1"); + + asm volatile("xvst $xr0, %0" : "=m" (dq[0])); + asm volatile("xvst $xr1, %0" : "=m" (dq[32])); + asm volatile("xvst $xr2, %0" : "=m" (dp[0])); + asm volatile("xvst $xr3, %0" : "=m" (dp[32])); + + bytes -= 64; + p += 64; + q += 64; + dp += 64; + dq += 64; + } + + kernel_fpu_end(); +} + +static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila, + void **ptrs) +{ + u8 *p, *q, *dq; + const u8 *qmul; /* Q multiplier table */ + + p = (u8 *)ptrs[disks - 2]; + q = (u8 *)ptrs[disks - 1]; + + /* + * Compute syndrome with zero for the missing data page + * Use the dead data page as temporary storage for delta q + */ + dq = (u8 *)ptrs[faila]; + ptrs[faila] = (void *)raid6_empty_zero_page; + ptrs[disks - 1] = dq; + + raid6_call.gen_syndrome(disks, bytes, ptrs); + + /* Restore pointer table */ + ptrs[faila] = dq; + ptrs[disks - 1] = q; + + /* Now, pick the proper data tables */ + qmul = raid6_vgfmul[raid6_gfinv[raid6_gfexp[faila]]]; + + kernel_fpu_begin(); + + /* xr22, xr23: qmul */ + asm volatile("vld $vr22, %0" : : "m" (qmul[0])); + asm volatile("xvreplve0.q $xr22, $xr22"); + asm volatile("vld $vr23, %0" : : "m" (qmul[16])); + asm volatile("xvreplve0.q $xr23, $xr23"); + + while (bytes) { + /* xr0, xr1: P + Dx */ + asm volatile("xvld $xr0, %0" : : "m" (p[0])); + asm volatile("xvld $xr1, %0" : : "m" (p[32])); + /* xr2, xr3: Qx */ + asm volatile("xvld $xr2, %0" : : "m" (dq[0])); + asm volatile("xvld $xr3, %0" : : "m" (dq[32])); + /* xr2, xr3: Q + Qx */ + asm volatile("xvld $xr4, %0" : : "m" (q[0])); + asm volatile("xvld $xr5, %0" : : "m" (q[32])); + asm volatile("xvxor.v $xr2, $xr2, $xr4"); + asm volatile("xvxor.v $xr3, $xr3, $xr5"); + + /* xr4, xr5: higher 4 bits of each byte of (Q + Qx) */ + asm volatile("xvsrli.b $xr4, $xr2, 4"); + asm volatile("xvsrli.b $xr5, $xr3, 4"); + /* xr2, xr3: lower 4 bits of each byte of (Q + Qx) */ + asm volatile("xvandi.b $xr2, $xr2, 0x0f"); + asm volatile("xvandi.b $xr3, $xr3, 0x0f"); + /* lookup from qmul[0] */ + asm volatile("xvshuf.b $xr2, $xr22, $xr22, $xr2"); + asm volatile("xvshuf.b $xr3, $xr22, $xr22, $xr3"); + /* lookup from qmul[16] */ + asm volatile("xvshuf.b $xr4, $xr23, $xr23, $xr4"); + asm volatile("xvshuf.b $xr5, $xr23, $xr23, $xr5"); + /* xr2, xr3: qmul(Q + Qx) = Dx */ + asm volatile("xvxor.v $xr2, $xr2, $xr4"); + asm volatile("xvxor.v $xr3, $xr3, $xr5"); + + /* xr0, xr1: P + Dx + Dx = P */ + asm volatile("xvxor.v $xr0, $xr0, $xr2"); + asm volatile("xvxor.v $xr1, $xr1, $xr3"); + + asm volatile("xvst $xr2, %0" : "=m" (dq[0])); + asm volatile("xvst $xr3, %0" : "=m" (dq[32])); + asm volatile("xvst $xr0, %0" : "=m" (p[0])); + asm volatile("xvst $xr1, %0" : "=m" (p[32])); + + bytes -= 64; + p += 64; + q += 64; + dq += 64; + } + + kernel_fpu_end(); +} + +const struct raid6_recov_calls raid6_recov_lasx = { + .data2 = raid6_2data_recov_lasx, + .datap = raid6_datap_recov_lasx, + .valid = raid6_has_lasx, + .name = "lasx", + .priority = 2, +}; +#endif /* CONFIG_CPU_HAS_LASX */ diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile index 1f693ea3b980cef7f229ba19c5a794d4e24377b8..2abe0076a636c3816678d13df2b0c7d036da154a 100644 --- a/lib/raid6/test/Makefile +++ b/lib/raid6/test/Makefile @@ -41,6 +41,16 @@ ifeq ($(findstring ppc,$(ARCH)),ppc) gcc -c -x c - >/dev/null && rm ./-.o && echo yes) endif +ifeq ($(ARCH),loongarch64) + CFLAGS += -I../../../arch/loongarch/include -DCONFIG_LOONGARCH=1 + CFLAGS += $(shell echo 'vld $$vr0, $$zero, 0' | \ + gcc -c -x assembler - >/dev/null 2>&1 && \ + rm ./-.o && echo -DCONFIG_CPU_HAS_LSX=1) + CFLAGS += $(shell echo 'xvld $$xr0, $$zero, 0' | \ + gcc -c -x assembler - >/dev/null 2>&1 && \ + rm ./-.o && echo -DCONFIG_CPU_HAS_LASX=1) +endif + ifeq ($(IS_X86),yes) OBJS += mmx.o sse1.o sse2.o avx2.o recov_ssse3.o recov_avx2.o avx512.o recov_avx512.o CFLAGS += -DCONFIG_X86 @@ -54,6 +64,8 @@ else ifeq ($(HAS_ALTIVEC),yes) CFLAGS += -DCONFIG_ALTIVEC OBJS += altivec1.o altivec2.o altivec4.o altivec8.o \ vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o +else ifeq ($(ARCH),loongarch64) + OBJS += loongarch_simd.o recov_loongarch_simd.o endif .c.o: diff --git a/lib/scatterlist.c b/lib/scatterlist.c index c65566b4dc662afff966b773b78631de185c8851..68b45c82c37a6981bb72cc79ab4c4fcac8e2e489 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -265,7 +265,8 @@ EXPORT_SYMBOL(sg_free_table); * @table: The sg table header to use * @nents: Number of entries in sg list * @max_ents: The maximum number of entries the allocator returns per call - * @nents_first_chunk: Number of entries int the (preallocated) first + * @first_chunk: first SGL if preallocated (may be %NULL) + * @nents_first_chunk: Number of entries in the (preallocated) first * scatterlist chunk, 0 means no such preallocated chunk provided by user * @gfp_mask: GFP allocation mask * @alloc_fn: Allocator to use @@ -788,6 +789,7 @@ EXPORT_SYMBOL(__sg_page_iter_dma_next); * @miter: sg mapping iter to be started * @sgl: sg list to iterate over * @nents: number of sg entries + * @flags: sg iterator flags * * Description: * Starts mapping iterator @miter. diff --git a/lib/test_maple_tree.c b/lib/test_maple_tree.c index 0674aebd44230d94a12f6b8545bb6e67ae69c938..06959165e2f90f9a9a7cd41f138f90cb6dadc451 100644 --- a/lib/test_maple_tree.c +++ b/lib/test_maple_tree.c @@ -2166,7 +2166,7 @@ static noinline void __init next_prev_test(struct maple_tree *mt) MT_BUG_ON(mt, val != NULL); MT_BUG_ON(mt, mas.index != 0); MT_BUG_ON(mt, mas.last != 5); - MT_BUG_ON(mt, mas.node != MAS_NONE); + MT_BUG_ON(mt, mas.node != MAS_UNDERFLOW); mas.index = 0; mas.last = 5; @@ -2917,6 +2917,7 @@ static noinline void __init check_empty_area_fill(struct maple_tree *mt) * exists MAS_NONE active range * exists active active range * DNE active active set to last range + * ERANGE active MAS_OVERFLOW last range * * Function ENTRY Start Result index & last * mas_prev() @@ -2945,6 +2946,7 @@ static noinline void __init check_empty_area_fill(struct maple_tree *mt) * any MAS_ROOT MAS_NONE 0 * exists active active range * DNE active active last range + * ERANGE active MAS_UNDERFLOW last range * * Function ENTRY Start Result index & last * mas_find() @@ -2955,7 +2957,7 @@ static noinline void __init check_empty_area_fill(struct maple_tree *mt) * DNE MAS_START MAS_NONE 0 * DNE MAS_PAUSE MAS_NONE 0 * DNE MAS_ROOT MAS_NONE 0 - * DNE MAS_NONE MAS_NONE 0 + * DNE MAS_NONE MAS_NONE 1 * if index == 0 * exists MAS_START MAS_ROOT 0 * exists MAS_PAUSE MAS_ROOT 0 @@ -2967,7 +2969,7 @@ static noinline void __init check_empty_area_fill(struct maple_tree *mt) * DNE MAS_START active set to max * exists MAS_PAUSE active range * DNE MAS_PAUSE active set to max - * exists MAS_NONE active range + * exists MAS_NONE active range (start at last) * exists active active range * DNE active active last range (max < last) * @@ -2992,7 +2994,7 @@ static noinline void __init check_empty_area_fill(struct maple_tree *mt) * DNE MAS_START active set to min * exists MAS_PAUSE active range * DNE MAS_PAUSE active set to min - * exists MAS_NONE active range + * exists MAS_NONE active range (start at index) * exists active active range * DNE active active last range (min > index) * @@ -3039,10 +3041,10 @@ static noinline void __init check_state_handling(struct maple_tree *mt) mtree_store_range(mt, 0, 0, ptr, GFP_KERNEL); mas_lock(&mas); - /* prev: Start -> none */ + /* prev: Start -> underflow*/ entry = mas_prev(&mas, 0); MT_BUG_ON(mt, entry != NULL); - MT_BUG_ON(mt, mas.node != MAS_NONE); + MT_BUG_ON(mt, mas.node != MAS_UNDERFLOW); /* prev: Start -> root */ mas_set(&mas, 10); @@ -3069,7 +3071,7 @@ static noinline void __init check_state_handling(struct maple_tree *mt) MT_BUG_ON(mt, entry != NULL); MT_BUG_ON(mt, mas.node != MAS_NONE); - /* next: start -> none */ + /* next: start -> none*/ mas_set(&mas, 10); entry = mas_next(&mas, ULONG_MAX); MT_BUG_ON(mt, mas.index != 1); @@ -3268,25 +3270,46 @@ static noinline void __init check_state_handling(struct maple_tree *mt) MT_BUG_ON(mt, mas.last != 0x2500); MT_BUG_ON(mt, !mas_active(mas)); - /* next:active -> active out of range*/ + /* next:active -> active beyond data */ entry = mas_next(&mas, 0x2999); MT_BUG_ON(mt, entry != NULL); MT_BUG_ON(mt, mas.index != 0x2501); MT_BUG_ON(mt, mas.last != 0x2fff); MT_BUG_ON(mt, !mas_active(mas)); - /* Continue after out of range*/ + /* Continue after last range ends after max */ entry = mas_next(&mas, ULONG_MAX); MT_BUG_ON(mt, entry != ptr3); MT_BUG_ON(mt, mas.index != 0x3000); MT_BUG_ON(mt, mas.last != 0x3500); MT_BUG_ON(mt, !mas_active(mas)); - /* next:active -> active out of range*/ + /* next:active -> active continued */ + entry = mas_next(&mas, ULONG_MAX); + MT_BUG_ON(mt, entry != NULL); + MT_BUG_ON(mt, mas.index != 0x3501); + MT_BUG_ON(mt, mas.last != ULONG_MAX); + MT_BUG_ON(mt, !mas_active(mas)); + + /* next:active -> overflow */ entry = mas_next(&mas, ULONG_MAX); MT_BUG_ON(mt, entry != NULL); MT_BUG_ON(mt, mas.index != 0x3501); MT_BUG_ON(mt, mas.last != ULONG_MAX); + MT_BUG_ON(mt, mas.node != MAS_OVERFLOW); + + /* next:overflow -> overflow */ + entry = mas_next(&mas, ULONG_MAX); + MT_BUG_ON(mt, entry != NULL); + MT_BUG_ON(mt, mas.index != 0x3501); + MT_BUG_ON(mt, mas.last != ULONG_MAX); + MT_BUG_ON(mt, mas.node != MAS_OVERFLOW); + + /* prev:overflow -> active */ + entry = mas_prev(&mas, 0); + MT_BUG_ON(mt, entry != ptr3); + MT_BUG_ON(mt, mas.index != 0x3000); + MT_BUG_ON(mt, mas.last != 0x3500); MT_BUG_ON(mt, !mas_active(mas)); /* next: none -> active, skip value at location */ @@ -3307,11 +3330,46 @@ static noinline void __init check_state_handling(struct maple_tree *mt) MT_BUG_ON(mt, mas.last != 0x1500); MT_BUG_ON(mt, !mas_active(mas)); - /* prev:active -> active out of range*/ + /* prev:active -> active spanning end range */ + entry = mas_prev(&mas, 0x0100); + MT_BUG_ON(mt, entry != NULL); + MT_BUG_ON(mt, mas.index != 0); + MT_BUG_ON(mt, mas.last != 0x0FFF); + MT_BUG_ON(mt, !mas_active(mas)); + + /* prev:active -> underflow */ + entry = mas_prev(&mas, 0); + MT_BUG_ON(mt, entry != NULL); + MT_BUG_ON(mt, mas.index != 0); + MT_BUG_ON(mt, mas.last != 0x0FFF); + MT_BUG_ON(mt, mas.node != MAS_UNDERFLOW); + + /* prev:underflow -> underflow */ entry = mas_prev(&mas, 0); MT_BUG_ON(mt, entry != NULL); MT_BUG_ON(mt, mas.index != 0); MT_BUG_ON(mt, mas.last != 0x0FFF); + MT_BUG_ON(mt, mas.node != MAS_UNDERFLOW); + + /* next:underflow -> active */ + entry = mas_next(&mas, ULONG_MAX); + MT_BUG_ON(mt, entry != ptr); + MT_BUG_ON(mt, mas.index != 0x1000); + MT_BUG_ON(mt, mas.last != 0x1500); + MT_BUG_ON(mt, !mas_active(mas)); + + /* prev:first value -> underflow */ + entry = mas_prev(&mas, 0x1000); + MT_BUG_ON(mt, entry != NULL); + MT_BUG_ON(mt, mas.index != 0x1000); + MT_BUG_ON(mt, mas.last != 0x1500); + MT_BUG_ON(mt, mas.node != MAS_UNDERFLOW); + + /* find:underflow -> first value */ + entry = mas_find(&mas, ULONG_MAX); + MT_BUG_ON(mt, entry != ptr); + MT_BUG_ON(mt, mas.index != 0x1000); + MT_BUG_ON(mt, mas.last != 0x1500); MT_BUG_ON(mt, !mas_active(mas)); /* prev: pause ->active */ @@ -3325,14 +3383,14 @@ static noinline void __init check_state_handling(struct maple_tree *mt) MT_BUG_ON(mt, mas.last != 0x2500); MT_BUG_ON(mt, !mas_active(mas)); - /* prev:active -> active out of range*/ + /* prev:active -> active spanning min */ entry = mas_prev(&mas, 0x1600); MT_BUG_ON(mt, entry != NULL); MT_BUG_ON(mt, mas.index != 0x1501); MT_BUG_ON(mt, mas.last != 0x1FFF); MT_BUG_ON(mt, !mas_active(mas)); - /* prev: active ->active, continue*/ + /* prev: active ->active, continue */ entry = mas_prev(&mas, 0); MT_BUG_ON(mt, entry != ptr); MT_BUG_ON(mt, mas.index != 0x1000); @@ -3379,7 +3437,7 @@ static noinline void __init check_state_handling(struct maple_tree *mt) MT_BUG_ON(mt, mas.last != 0x2FFF); MT_BUG_ON(mt, !mas_active(mas)); - /* find: none ->active */ + /* find: overflow ->active */ entry = mas_find(&mas, 0x5000); MT_BUG_ON(mt, entry != ptr3); MT_BUG_ON(mt, mas.index != 0x3000); @@ -3778,7 +3836,6 @@ static int __init maple_tree_seed(void) check_empty_area_fill(&tree); mtree_destroy(&tree); - mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE); check_state_handling(&tree); mtree_destroy(&tree); diff --git a/lib/test_scanf.c b/lib/test_scanf.c index b620cf7de503542a98f38c45c76e9043b242746e..a2707af2951ab07ca7cd44aeceed15aff4b79cd1 100644 --- a/lib/test_scanf.c +++ b/lib/test_scanf.c @@ -606,7 +606,7 @@ static void __init numbers_slice(void) #define test_number_prefix(T, str, scan_fmt, expect0, expect1, n_args, fn) \ do { \ const T expect[2] = { expect0, expect1 }; \ - T result[2] = {~expect[0], ~expect[1]}; \ + T result[2] = { (T)~expect[0], (T)~expect[1] }; \ \ _test(fn, &expect, str, scan_fmt, n_args, &result[0], &result[1]); \ } while (0) diff --git a/lib/xarray.c b/lib/xarray.c index 2071a3718f4ed2d15a933aa633ecd955a0b53b41..39f07bfc4dccacd4da23f76bc0c553815728bee5 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -206,7 +206,7 @@ static void *xas_descend(struct xa_state *xas, struct xa_node *node) void *entry = xa_entry(xas->xa, node, offset); xas->xa_node = node; - if (xa_is_sibling(entry)) { + while (xa_is_sibling(entry)) { offset = xa_to_sibling(entry); entry = xa_entry(xas->xa, node, offset); if (node->shift && xa_is_node(entry)) @@ -1802,6 +1802,9 @@ EXPORT_SYMBOL(xa_get_order); * stores the index into the @id pointer, then stores the entry at * that index. A concurrent lookup will not see an uninitialised @id. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Any context. Expects xa_lock to be held on entry. May * release and reacquire xa_lock if @gfp flags permit. * Return: 0 on success, -ENOMEM if memory could not be allocated or @@ -1850,6 +1853,9 @@ EXPORT_SYMBOL(__xa_alloc); * The search for an empty entry will start at @next and will wrap * around if necessary. * + * Must only be operated on an xarray initialized with flag XA_FLAGS_ALLOC set + * in xa_init_flags(). + * * Context: Any context. Expects xa_lock to be held on entry. May * release and reacquire xa_lock if @gfp flags permit. * Return: 0 if the allocation succeeded without wrapping. 1 if the diff --git a/mm/damon/vaddr-test.h b/mm/damon/vaddr-test.h index c4b455b5ee30b867765da4bcff1d3c24e1398c8f..dcf1ca6b31cc4f8ef86ffe7ab6ea77f81a221bde 100644 --- a/mm/damon/vaddr-test.h +++ b/mm/damon/vaddr-test.h @@ -148,6 +148,8 @@ static void damon_do_test_apply_three_regions(struct kunit *test, KUNIT_EXPECT_EQ(test, r->ar.start, expected[i * 2]); KUNIT_EXPECT_EQ(test, r->ar.end, expected[i * 2 + 1]); } + + damon_destroy_target(t); } /* diff --git a/mm/damon/vaddr.c b/mm/damon/vaddr.c index 4c81a9dbd0444801a6578dd4e9fb2a47622da1af..cf8a9fc5c9d1a61770572a15c8e55738601b6e7a 100644 --- a/mm/damon/vaddr.c +++ b/mm/damon/vaddr.c @@ -341,13 +341,14 @@ static void damon_hugetlb_mkold(pte_t *pte, struct mm_struct *mm, bool referenced = false; pte_t entry = huge_ptep_get(pte); struct folio *folio = pfn_folio(pte_pfn(entry)); + unsigned long psize = huge_page_size(hstate_vma(vma)); folio_get(folio); if (pte_young(entry)) { referenced = true; entry = pte_mkold(entry); - set_huge_pte_at(mm, addr, pte, entry); + set_huge_pte_at(mm, addr, pte, entry, psize); } #ifdef CONFIG_MMU_NOTIFIER diff --git a/mm/filemap.c b/mm/filemap.c index bf6219d9aaacc52f30466bd8ad27a447ec57d5fe..f0a15ce1bd1ba1cca4856e05e70d9e44128709b4 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -121,9 +121,6 @@ * bdi.wb->list_lock (zap_pte_range->set_page_dirty) * ->inode->i_lock (zap_pte_range->set_page_dirty) * ->private_lock (zap_pte_range->block_dirty_folio) - * - * ->i_mmap_rwsem - * ->tasklist_lock (memory_failure, collect_procs_ao) */ static void page_cache_delete(struct address_space *mapping, @@ -3478,13 +3475,11 @@ static struct folio *next_uptodate_folio(struct xa_state *xas, */ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, struct folio *folio, unsigned long start, - unsigned long addr, unsigned int nr_pages) + unsigned long addr, unsigned int nr_pages, + unsigned int *mmap_miss) { vm_fault_t ret = 0; - struct vm_area_struct *vma = vmf->vma; - struct file *file = vma->vm_file; struct page *page = folio_page(folio, start); - unsigned int mmap_miss = READ_ONCE(file->f_ra.mmap_miss); unsigned int count = 0; pte_t *old_ptep = vmf->pte; @@ -3492,8 +3487,7 @@ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, if (PageHWPoison(page + count)) goto skip; - if (mmap_miss > 0) - mmap_miss--; + (*mmap_miss)++; /* * NOTE: If there're PTE markers, we'll leave them to be @@ -3509,7 +3503,7 @@ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, if (count) { set_pte_range(vmf, folio, page, count, addr); folio_ref_add(folio, count); - if (in_range(vmf->address, addr, count)) + if (in_range(vmf->address, addr, count * PAGE_SIZE)) ret = VM_FAULT_NOPAGE; } @@ -3523,12 +3517,40 @@ static vm_fault_t filemap_map_folio_range(struct vm_fault *vmf, if (count) { set_pte_range(vmf, folio, page, count, addr); folio_ref_add(folio, count); - if (in_range(vmf->address, addr, count)) + if (in_range(vmf->address, addr, count * PAGE_SIZE)) ret = VM_FAULT_NOPAGE; } vmf->pte = old_ptep; - WRITE_ONCE(file->f_ra.mmap_miss, mmap_miss); + + return ret; +} + +static vm_fault_t filemap_map_order0_folio(struct vm_fault *vmf, + struct folio *folio, unsigned long addr, + unsigned int *mmap_miss) +{ + vm_fault_t ret = 0; + struct page *page = &folio->page; + + if (PageHWPoison(page)) + return ret; + + (*mmap_miss)++; + + /* + * NOTE: If there're PTE markers, we'll leave them to be + * handled in the specific fault path, and it'll prohibit + * the fault-around logic. + */ + if (!pte_none(ptep_get(vmf->pte))) + return ret; + + if (vmf->address == addr) + ret = VM_FAULT_NOPAGE; + + set_pte_range(vmf, folio, page, 1, addr); + folio_ref_inc(folio); return ret; } @@ -3544,7 +3566,7 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, XA_STATE(xas, &mapping->i_pages, start_pgoff); struct folio *folio; vm_fault_t ret = 0; - int nr_pages = 0; + unsigned int nr_pages = 0, mmap_miss = 0, mmap_miss_saved; rcu_read_lock(); folio = next_uptodate_folio(&xas, mapping, end_pgoff); @@ -3572,25 +3594,27 @@ vm_fault_t filemap_map_pages(struct vm_fault *vmf, end = folio->index + folio_nr_pages(folio) - 1; nr_pages = min(end, end_pgoff) - xas.xa_index + 1; - /* - * NOTE: If there're PTE markers, we'll leave them to be - * handled in the specific fault path, and it'll prohibit the - * fault-around logic. - */ - if (!pte_none(ptep_get(vmf->pte))) - goto unlock; - - ret |= filemap_map_folio_range(vmf, folio, - xas.xa_index - folio->index, addr, nr_pages); + if (!folio_test_large(folio)) + ret |= filemap_map_order0_folio(vmf, + folio, addr, &mmap_miss); + else + ret |= filemap_map_folio_range(vmf, folio, + xas.xa_index - folio->index, addr, + nr_pages, &mmap_miss); -unlock: folio_unlock(folio); folio_put(folio); - folio = next_uptodate_folio(&xas, mapping, end_pgoff); - } while (folio); + } while ((folio = next_uptodate_folio(&xas, mapping, end_pgoff)) != NULL); pte_unmap_unlock(vmf->pte, vmf->ptl); out: rcu_read_unlock(); + + mmap_miss_saved = READ_ONCE(file->f_ra.mmap_miss); + if (mmap_miss >= mmap_miss_saved) + WRITE_ONCE(file->f_ra.mmap_miss, 0); + else + WRITE_ONCE(file->f_ra.mmap_miss, mmap_miss_saved - mmap_miss); + return ret; } EXPORT_SYMBOL(filemap_map_pages); diff --git a/mm/hugetlb.c b/mm/hugetlb.c index ba6d39b71cb14326393f53d7c6068e2648932ffb..52d26072dfda55ddefa97457cdb11f9d716eb20f 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -4980,7 +4980,7 @@ static bool is_hugetlb_entry_hwpoisoned(pte_t pte) static void hugetlb_install_folio(struct vm_area_struct *vma, pte_t *ptep, unsigned long addr, - struct folio *new_folio, pte_t old) + struct folio *new_folio, pte_t old, unsigned long sz) { pte_t newpte = make_huge_pte(vma, &new_folio->page, 1); @@ -4988,7 +4988,7 @@ hugetlb_install_folio(struct vm_area_struct *vma, pte_t *ptep, unsigned long add hugepage_add_new_anon_rmap(new_folio, vma, addr); if (userfaultfd_wp(vma) && huge_pte_uffd_wp(old)) newpte = huge_pte_mkuffd_wp(newpte); - set_huge_pte_at(vma->vm_mm, addr, ptep, newpte); + set_huge_pte_at(vma->vm_mm, addr, ptep, newpte, sz); hugetlb_count_add(pages_per_huge_page(hstate_vma(vma)), vma->vm_mm); folio_set_hugetlb_migratable(new_folio); } @@ -5065,7 +5065,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, } else if (unlikely(is_hugetlb_entry_hwpoisoned(entry))) { if (!userfaultfd_wp(dst_vma)) entry = huge_pte_clear_uffd_wp(entry); - set_huge_pte_at(dst, addr, dst_pte, entry); + set_huge_pte_at(dst, addr, dst_pte, entry, sz); } else if (unlikely(is_hugetlb_entry_migration(entry))) { swp_entry_t swp_entry = pte_to_swp_entry(entry); bool uffd_wp = pte_swp_uffd_wp(entry); @@ -5080,18 +5080,18 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, entry = swp_entry_to_pte(swp_entry); if (userfaultfd_wp(src_vma) && uffd_wp) entry = pte_swp_mkuffd_wp(entry); - set_huge_pte_at(src, addr, src_pte, entry); + set_huge_pte_at(src, addr, src_pte, entry, sz); } if (!userfaultfd_wp(dst_vma)) entry = huge_pte_clear_uffd_wp(entry); - set_huge_pte_at(dst, addr, dst_pte, entry); + set_huge_pte_at(dst, addr, dst_pte, entry, sz); } else if (unlikely(is_pte_marker(entry))) { pte_marker marker = copy_pte_marker( pte_to_swp_entry(entry), dst_vma); if (marker) set_huge_pte_at(dst, addr, dst_pte, - make_pte_marker(marker)); + make_pte_marker(marker), sz); } else { entry = huge_ptep_get(src_pte); pte_folio = page_folio(pte_page(entry)); @@ -5145,7 +5145,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, goto again; } hugetlb_install_folio(dst_vma, dst_pte, addr, - new_folio, src_pte_old); + new_folio, src_pte_old, sz); spin_unlock(src_ptl); spin_unlock(dst_ptl); continue; @@ -5166,7 +5166,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, if (!userfaultfd_wp(dst_vma)) entry = huge_pte_clear_uffd_wp(entry); - set_huge_pte_at(dst, addr, dst_pte, entry); + set_huge_pte_at(dst, addr, dst_pte, entry, sz); hugetlb_count_add(npages, dst); } spin_unlock(src_ptl); @@ -5184,7 +5184,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, } static void move_huge_pte(struct vm_area_struct *vma, unsigned long old_addr, - unsigned long new_addr, pte_t *src_pte, pte_t *dst_pte) + unsigned long new_addr, pte_t *src_pte, pte_t *dst_pte, + unsigned long sz) { struct hstate *h = hstate_vma(vma); struct mm_struct *mm = vma->vm_mm; @@ -5202,7 +5203,7 @@ static void move_huge_pte(struct vm_area_struct *vma, unsigned long old_addr, spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING); pte = huge_ptep_get_and_clear(mm, old_addr, src_pte); - set_huge_pte_at(mm, new_addr, dst_pte, pte); + set_huge_pte_at(mm, new_addr, dst_pte, pte, sz); if (src_ptl != dst_ptl) spin_unlock(src_ptl); @@ -5259,7 +5260,7 @@ int move_hugetlb_page_tables(struct vm_area_struct *vma, if (!dst_pte) break; - move_huge_pte(vma, old_addr, new_addr, src_pte, dst_pte); + move_huge_pte(vma, old_addr, new_addr, src_pte, dst_pte, sz); } if (shared_pmd) @@ -5337,7 +5338,8 @@ static void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct if (pte_swp_uffd_wp_any(pte) && !(zap_flags & ZAP_FLAG_DROP_MARKER)) set_huge_pte_at(mm, address, ptep, - make_pte_marker(PTE_MARKER_UFFD_WP)); + make_pte_marker(PTE_MARKER_UFFD_WP), + sz); else huge_pte_clear(mm, address, ptep, sz); spin_unlock(ptl); @@ -5371,7 +5373,8 @@ static void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct if (huge_pte_uffd_wp(pte) && !(zap_flags & ZAP_FLAG_DROP_MARKER)) set_huge_pte_at(mm, address, ptep, - make_pte_marker(PTE_MARKER_UFFD_WP)); + make_pte_marker(PTE_MARKER_UFFD_WP), + sz); hugetlb_count_sub(pages_per_huge_page(h), mm); page_remove_rmap(page, vma, true); @@ -5676,7 +5679,7 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma, hugepage_add_new_anon_rmap(new_folio, vma, haddr); if (huge_pte_uffd_wp(pte)) newpte = huge_pte_mkuffd_wp(newpte); - set_huge_pte_at(mm, haddr, ptep, newpte); + set_huge_pte_at(mm, haddr, ptep, newpte, huge_page_size(h)); folio_set_hugetlb_migratable(new_folio); /* Make the old page be freed below */ new_folio = old_folio; @@ -5972,7 +5975,7 @@ static vm_fault_t hugetlb_no_page(struct mm_struct *mm, */ if (unlikely(pte_marker_uffd_wp(old_pte))) new_pte = huge_pte_mkuffd_wp(new_pte); - set_huge_pte_at(mm, haddr, ptep, new_pte); + set_huge_pte_at(mm, haddr, ptep, new_pte, huge_page_size(h)); hugetlb_count_add(pages_per_huge_page(h), mm); if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) { @@ -6261,7 +6264,8 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte, } _dst_pte = make_pte_marker(PTE_MARKER_POISONED); - set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); + set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte, + huge_page_size(h)); /* No need to invalidate - it was non-present before */ update_mmu_cache(dst_vma, dst_addr, dst_pte); @@ -6412,7 +6416,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte, if (wp_enabled) _dst_pte = huge_pte_mkuffd_wp(_dst_pte); - set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte); + set_huge_pte_at(dst_mm, dst_addr, dst_pte, _dst_pte, huge_page_size(h)); hugetlb_count_add(pages_per_huge_page(h), dst_mm); @@ -6598,7 +6602,7 @@ long hugetlb_change_protection(struct vm_area_struct *vma, else if (uffd_wp_resolve) newpte = pte_swp_clear_uffd_wp(newpte); if (!pte_same(pte, newpte)) - set_huge_pte_at(mm, address, ptep, newpte); + set_huge_pte_at(mm, address, ptep, newpte, psize); } else if (unlikely(is_pte_marker(pte))) { /* No other markers apply for now. */ WARN_ON_ONCE(!pte_marker_uffd_wp(pte)); @@ -6623,7 +6627,8 @@ long hugetlb_change_protection(struct vm_area_struct *vma, if (unlikely(uffd_wp)) /* Safe to modify directly (none->non-present). */ set_huge_pte_at(mm, address, ptep, - make_pte_marker(PTE_MARKER_UFFD_WP)); + make_pte_marker(PTE_MARKER_UFFD_WP), + psize); } spin_unlock(ptl); } diff --git a/mm/kasan/init.c b/mm/kasan/init.c index dcfec277e8394be8312058c3cab2dcdacd5b105b..89895f38f722423c02744f359799db4bb7f12dc2 100644 --- a/mm/kasan/init.c +++ b/mm/kasan/init.c @@ -139,6 +139,10 @@ static int __ref zero_pmd_populate(pud_t *pud, unsigned long addr, return 0; } +void __weak __meminit pmd_init(void *addr) +{ +} + static int __ref zero_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long end) { @@ -166,8 +170,9 @@ static int __ref zero_pud_populate(p4d_t *p4d, unsigned long addr, if (!p) return -ENOMEM; } else { - pud_populate(&init_mm, pud, - early_alloc(PAGE_SIZE, NUMA_NO_NODE)); + p = early_alloc(PAGE_SIZE, NUMA_NO_NODE); + pmd_init(p); + pud_populate(&init_mm, pud, p); } } zero_pmd_populate(pud, addr, next); @@ -176,6 +181,10 @@ static int __ref zero_pud_populate(p4d_t *p4d, unsigned long addr, return 0; } +void __weak __meminit pud_init(void *addr) +{ +} + static int __ref zero_p4d_populate(pgd_t *pgd, unsigned long addr, unsigned long end) { @@ -207,8 +216,9 @@ static int __ref zero_p4d_populate(pgd_t *pgd, unsigned long addr, if (!p) return -ENOMEM; } else { - p4d_populate(&init_mm, p4d, - early_alloc(PAGE_SIZE, NUMA_NO_NODE)); + p = early_alloc(PAGE_SIZE, NUMA_NO_NODE); + pud_init(p); + p4d_populate(&init_mm, p4d, p); } } zero_pud_populate(p4d, addr, next); diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h index 2e973b36fe0725339c04eda6f10446b8b28244ca..d37831b8511c611a43dbe6f0a1bea1ccf6e7c0e4 100644 --- a/mm/kasan/kasan.h +++ b/mm/kasan/kasan.h @@ -291,17 +291,21 @@ struct kasan_stack_ring { #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) +#ifndef kasan_shadow_to_mem static inline const void *kasan_shadow_to_mem(const void *shadow_addr) { return (void *)(((unsigned long)shadow_addr - KASAN_SHADOW_OFFSET) << KASAN_SHADOW_SCALE_SHIFT); } +#endif +#ifndef addr_has_metadata static __always_inline bool addr_has_metadata(const void *addr) { return (kasan_reset_tag(addr) >= kasan_shadow_to_mem((void *)KASAN_SHADOW_START)); } +#endif /** * kasan_check_range - Check memory region, and report if invalid access. diff --git a/mm/kfence/core.c b/mm/kfence/core.c index 96fd0411f5c5866d69913f6e6e8a87285e830aad..3872528d096380b0c412897e7cc7b56b4cb59eb9 100644 --- a/mm/kfence/core.c +++ b/mm/kfence/core.c @@ -574,13 +574,14 @@ static void rcu_guarded_free(struct rcu_head *h) */ static unsigned long kfence_init_pool(void) { - unsigned long addr = (unsigned long)__kfence_pool; + unsigned long addr; struct page *pages; int i; if (!arch_kfence_init_pool()) - return addr; + return (unsigned long)__kfence_pool; + addr = (unsigned long)__kfence_pool; pages = virt_to_page(__kfence_pool); /* diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 2918150e31bd9c442d428e91d53dfb467823d2f8..54c2c90d3abc9de0ae7b3b4308eea5011794e322 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -1584,6 +1584,9 @@ static void kmemleak_scan(void) for (pfn = start_pfn; pfn < end_pfn; pfn++) { struct page *page = pfn_to_online_page(pfn); + if (!(pfn & 63)) + cond_resched(); + if (!page) continue; @@ -1594,8 +1597,6 @@ static void kmemleak_scan(void) if (page_count(page) == 0) continue; scan_block(page, page + 1, NULL); - if (!(pfn & 63)) - cond_resched(); } } put_online_mems(); diff --git a/mm/ksm.c b/mm/ksm.c index 8d6aee05421da62de48ddd53262d32f74673f391..981af9c72e7a3ea754f374007a828de97d22ff93 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -2925,7 +2925,7 @@ void collect_procs_ksm(struct page *page, struct list_head *to_kill, struct anon_vma *av = rmap_item->anon_vma; anon_vma_lock_read(av); - read_lock(&tasklist_lock); + rcu_read_lock(); for_each_process(tsk) { struct anon_vma_chain *vmac; unsigned long addr; @@ -2944,7 +2944,7 @@ void collect_procs_ksm(struct page *page, struct list_head *to_kill, } } } - read_unlock(&tasklist_lock); + rcu_read_unlock(); anon_vma_unlock_read(av); } } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index b29b850cf3994e0d665eac67863582591c21f9bd..5b009b233ab8921110148de54b6b96e69d5ab638 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2555,7 +2555,7 @@ static unsigned long calculate_high_delay(struct mem_cgroup *memcg, * Scheduled by try_charge() to be executed from the userland return path * and reclaims memory over the high limit. */ -void mem_cgroup_handle_over_high(void) +void mem_cgroup_handle_over_high(gfp_t gfp_mask) { unsigned long penalty_jiffies; unsigned long pflags; @@ -2583,7 +2583,7 @@ void mem_cgroup_handle_over_high(void) */ nr_reclaimed = reclaim_high(memcg, in_retry ? SWAP_CLUSTER_MAX : nr_pages, - GFP_KERNEL); + gfp_mask); /* * memory.high is breached and reclaim is unable to keep up. Throttle @@ -2819,7 +2819,7 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask, if (current->memcg_nr_pages_over_high > MEMCG_CHARGE_BATCH && !(current->flags & PF_MEMALLOC) && gfpflags_allow_blocking(gfp_mask)) { - mem_cgroup_handle_over_high(); + mem_cgroup_handle_over_high(gfp_mask); } return 0; } @@ -3867,6 +3867,13 @@ static ssize_t mem_cgroup_write(struct kernfs_open_file *of, case _MEMSWAP: ret = mem_cgroup_resize_max(memcg, nr_pages, true); break; + case _KMEM: + pr_warn_once("kmem.limit_in_bytes is deprecated and will be removed. " + "Writing any value to this file has no effect. " + "Please report your usecase to linux-mm@kvack.org if you " + "depend on this functionality.\n"); + ret = 0; + break; case _TCP: ret = memcg_update_tcp_max(memcg, nr_pages); break; @@ -5077,6 +5084,12 @@ static struct cftype mem_cgroup_legacy_files[] = { .seq_show = memcg_numa_stat_show, }, #endif + { + .name = "kmem.limit_in_bytes", + .private = MEMFILE_PRIVATE(_KMEM, RES_LIMIT), + .write = mem_cgroup_write, + .read_u64 = mem_cgroup_read_u64, + }, { .name = "kmem.usage_in_bytes", .private = MEMFILE_PRIVATE(_KMEM, RES_USAGE), @@ -5326,7 +5339,6 @@ static struct mem_cgroup *mem_cgroup_alloc(void) INIT_LIST_HEAD(&memcg->deferred_split_queue.split_queue); memcg->deferred_split_queue.split_queue_len = 0; #endif - idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); lru_gen_init_memcg(memcg); return memcg; fail: @@ -5398,14 +5410,27 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css) if (alloc_shrinker_info(memcg)) goto offline_kmem; - /* Online state pins memcg ID, memcg ID pins CSS */ - refcount_set(&memcg->id.ref, 1); - css_get(css); - if (unlikely(mem_cgroup_is_root(memcg))) queue_delayed_work(system_unbound_wq, &stats_flush_dwork, FLUSH_TIME); lru_gen_online_memcg(memcg); + + /* Online state pins memcg ID, memcg ID pins CSS */ + refcount_set(&memcg->id.ref, 1); + css_get(css); + + /* + * Ensure mem_cgroup_from_id() works once we're fully online. + * + * We could do this earlier and require callers to filter with + * css_tryget_online(). But right now there are no users that + * need earlier access, and the workingset code relies on the + * cgroup tree linkage (mem_cgroup_get_nr_swap_pages()). So + * publish it here at the end of onlining. This matches the + * regular ID destruction during offlining. + */ + idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); + return 0; offline_kmem: memcg_offline_kmem(memcg); diff --git a/mm/memfd.c b/mm/memfd.c index 1cad1904fc26b0be71e1370173e111f3b58b9931..2dba2cb6f0d0f8ba5c2c8d9b8a7297c22bbd62a6 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -316,7 +316,7 @@ SYSCALL_DEFINE2(memfd_create, return -EINVAL; if (!(flags & (MFD_EXEC | MFD_NOEXEC_SEAL))) { - pr_info_ratelimited( + pr_warn_once( "%s[%d]: memfd_create() called without MFD_EXEC or MFD_NOEXEC_SEAL set\n", current->comm, task_pid_nr(current)); } diff --git a/mm/memory-failure.c b/mm/memory-failure.c index 881c35ef1daa88144714ad7640341aeb3286291d..4d6e43c88489a0ef1755656a7512577902374496 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -547,8 +547,8 @@ static void kill_procs(struct list_head *to_kill, int forcekill, bool fail, * on behalf of the thread group. Return task_struct of the (first found) * dedicated thread if found, and return NULL otherwise. * - * We already hold read_lock(&tasklist_lock) in the caller, so we don't - * have to call rcu_read_lock/unlock() in this function. + * We already hold rcu lock in the caller, so we don't have to call + * rcu_read_lock/unlock() in this function. */ static struct task_struct *find_early_kill_thread(struct task_struct *tsk) { @@ -609,7 +609,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill, return; pgoff = page_to_pgoff(page); - read_lock(&tasklist_lock); + rcu_read_lock(); for_each_process(tsk) { struct anon_vma_chain *vmac; struct task_struct *t = task_early_kill(tsk, force_early); @@ -626,7 +626,7 @@ static void collect_procs_anon(struct page *page, struct list_head *to_kill, add_to_kill_anon_file(t, page, vma, to_kill); } } - read_unlock(&tasklist_lock); + rcu_read_unlock(); anon_vma_unlock_read(av); } @@ -642,7 +642,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill, pgoff_t pgoff; i_mmap_lock_read(mapping); - read_lock(&tasklist_lock); + rcu_read_lock(); pgoff = page_to_pgoff(page); for_each_process(tsk) { struct task_struct *t = task_early_kill(tsk, force_early); @@ -662,7 +662,7 @@ static void collect_procs_file(struct page *page, struct list_head *to_kill, add_to_kill_anon_file(t, page, vma, to_kill); } } - read_unlock(&tasklist_lock); + rcu_read_unlock(); i_mmap_unlock_read(mapping); } @@ -685,7 +685,7 @@ static void collect_procs_fsdax(struct page *page, struct task_struct *tsk; i_mmap_lock_read(mapping); - read_lock(&tasklist_lock); + rcu_read_lock(); for_each_process(tsk) { struct task_struct *t = task_early_kill(tsk, true); @@ -696,7 +696,7 @@ static void collect_procs_fsdax(struct page *page, add_to_kill_fsdax(t, page, vma, to_kill, pgoff); } } - read_unlock(&tasklist_lock); + rcu_read_unlock(); i_mmap_unlock_read(mapping); } #endif /* CONFIG_FS_DAX */ @@ -717,7 +717,7 @@ static void collect_procs(struct page *page, struct list_head *tokill, collect_procs_file(page, tokill, force_early); } -struct hwp_walk { +struct hwpoison_walk { struct to_kill tk; unsigned long pfn; int flags; @@ -752,7 +752,7 @@ static int check_hwpoisoned_entry(pte_t pte, unsigned long addr, short shift, #ifdef CONFIG_TRANSPARENT_HUGEPAGE static int check_hwpoisoned_pmd_entry(pmd_t *pmdp, unsigned long addr, - struct hwp_walk *hwp) + struct hwpoison_walk *hwp) { pmd_t pmd = *pmdp; unsigned long pfn; @@ -770,7 +770,7 @@ static int check_hwpoisoned_pmd_entry(pmd_t *pmdp, unsigned long addr, } #else static int check_hwpoisoned_pmd_entry(pmd_t *pmdp, unsigned long addr, - struct hwp_walk *hwp) + struct hwpoison_walk *hwp) { return 0; } @@ -779,7 +779,7 @@ static int check_hwpoisoned_pmd_entry(pmd_t *pmdp, unsigned long addr, static int hwpoison_pte_range(pmd_t *pmdp, unsigned long addr, unsigned long end, struct mm_walk *walk) { - struct hwp_walk *hwp = walk->private; + struct hwpoison_walk *hwp = walk->private; int ret = 0; pte_t *ptep, *mapped_pte; spinlock_t *ptl; @@ -813,7 +813,7 @@ static int hwpoison_hugetlb_range(pte_t *ptep, unsigned long hmask, unsigned long addr, unsigned long end, struct mm_walk *walk) { - struct hwp_walk *hwp = walk->private; + struct hwpoison_walk *hwp = walk->private; pte_t pte = huge_ptep_get(ptep); struct hstate *h = hstate_vma(walk->vma); @@ -824,7 +824,7 @@ static int hwpoison_hugetlb_range(pte_t *ptep, unsigned long hmask, #define hwpoison_hugetlb_range NULL #endif -static const struct mm_walk_ops hwp_walk_ops = { +static const struct mm_walk_ops hwpoison_walk_ops = { .pmd_entry = hwpoison_pte_range, .hugetlb_entry = hwpoison_hugetlb_range, .walk_lock = PGWALK_RDLOCK, @@ -847,7 +847,7 @@ static int kill_accessing_process(struct task_struct *p, unsigned long pfn, int flags) { int ret; - struct hwp_walk priv = { + struct hwpoison_walk priv = { .pfn = pfn, }; priv.tk.tsk = p; @@ -856,7 +856,7 @@ static int kill_accessing_process(struct task_struct *p, unsigned long pfn, return -EFAULT; mmap_read_lock(p->mm); - ret = walk_page_range(p->mm, 0, TASK_SIZE, &hwp_walk_ops, + ret = walk_page_range(p->mm, 0, TASK_SIZE, &hwpoison_walk_ops, (void *)&priv); if (ret == 1 && priv.tk.addr) kill_proc(&priv.tk, pfn, flags); @@ -1562,7 +1562,7 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn, * Here we are interested only in user-mapped pages, so skip any * other types of pages. */ - if (PageReserved(p) || PageSlab(p) || PageTable(p)) + if (PageReserved(p) || PageSlab(p) || PageTable(p) || PageOffline(p)) return true; if (!(PageLRU(hpage) || PageHuge(p))) return true; @@ -2533,7 +2533,8 @@ int unpoison_memory(unsigned long pfn) goto unlock_mutex; } - if (folio_test_slab(folio) || PageTable(&folio->page) || folio_test_reserved(folio)) + if (folio_test_slab(folio) || PageTable(&folio->page) || + folio_test_reserved(folio) || PageOffline(&folio->page)) goto unlock_mutex; /* diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 42b5567e37738d20ae67fa3b826677d97afe4609..f1b00d6ac7ee5cface6b0d4fd6c44210ab9ae90f 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -426,6 +426,7 @@ struct queue_pages { unsigned long start; unsigned long end; struct vm_area_struct *first; + bool has_unmovable; }; /* @@ -446,9 +447,8 @@ static inline bool queue_folio_required(struct folio *folio, /* * queue_folios_pmd() has three possible return values: * 0 - folios are placed on the right node or queued successfully, or - * special page is met, i.e. huge zero page. - * 1 - there is unmovable folio, and MPOL_MF_MOVE* & MPOL_MF_STRICT were - * specified. + * special page is met, i.e. zero page, or unmovable page is found + * but continue walking (indicated by queue_pages.has_unmovable). * -EIO - is migration entry or only MPOL_MF_STRICT was specified and an * existing folio was already on a node that does not follow the * policy. @@ -479,7 +479,7 @@ static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr, if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) { if (!vma_migratable(walk->vma) || migrate_folio_add(folio, qp->pagelist, flags)) { - ret = 1; + qp->has_unmovable = true; goto unlock; } } else @@ -495,9 +495,8 @@ static int queue_folios_pmd(pmd_t *pmd, spinlock_t *ptl, unsigned long addr, * * queue_folios_pte_range() has three possible return values: * 0 - folios are placed on the right node or queued successfully, or - * special page is met, i.e. zero page. - * 1 - there is unmovable folio, and MPOL_MF_MOVE* & MPOL_MF_STRICT were - * specified. + * special page is met, i.e. zero page, or unmovable page is found + * but continue walking (indicated by queue_pages.has_unmovable). * -EIO - only MPOL_MF_STRICT was specified and an existing folio was already * on a node that does not follow the policy. */ @@ -508,7 +507,6 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr, struct folio *folio; struct queue_pages *qp = walk->private; unsigned long flags = qp->flags; - bool has_unmovable = false; pte_t *pte, *mapped_pte; pte_t ptent; spinlock_t *ptl; @@ -538,11 +536,12 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr, if (!queue_folio_required(folio, qp)) continue; if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) { - /* MPOL_MF_STRICT must be specified if we get here */ - if (!vma_migratable(vma)) { - has_unmovable = true; - break; - } + /* + * MPOL_MF_STRICT must be specified if we get here. + * Continue walking vmas due to MPOL_MF_MOVE* flags. + */ + if (!vma_migratable(vma)) + qp->has_unmovable = true; /* * Do not abort immediately since there may be @@ -550,16 +549,13 @@ static int queue_folios_pte_range(pmd_t *pmd, unsigned long addr, * need migrate other LRU pages. */ if (migrate_folio_add(folio, qp->pagelist, flags)) - has_unmovable = true; + qp->has_unmovable = true; } else break; } pte_unmap_unlock(mapped_pte, ptl); cond_resched(); - if (has_unmovable) - return 1; - return addr != end ? -EIO : 0; } @@ -599,7 +595,7 @@ static int queue_folios_hugetlb(pte_t *pte, unsigned long hmask, * Detecting misplaced folio but allow migrating folios which * have been queued. */ - ret = 1; + qp->has_unmovable = true; goto unlock; } @@ -620,7 +616,7 @@ static int queue_folios_hugetlb(pte_t *pte, unsigned long hmask, * Failed to isolate folio but allow migrating pages * which have been queued. */ - ret = 1; + qp->has_unmovable = true; } unlock: spin_unlock(ptl); @@ -756,12 +752,15 @@ queue_pages_range(struct mm_struct *mm, unsigned long start, unsigned long end, .start = start, .end = end, .first = NULL, + .has_unmovable = false, }; const struct mm_walk_ops *ops = lock_vma ? &queue_pages_lock_vma_walk_ops : &queue_pages_walk_ops; err = walk_page_range(mm, start, end, ops, &qp); + if (qp.has_unmovable) + err = 1; if (!qp.first) /* whole range in hole */ err = -EFAULT; @@ -1358,7 +1357,7 @@ static long do_mbind(unsigned long start, unsigned long len, putback_movable_pages(&pagelist); } - if ((ret > 0) || (nr_failed && (flags & MPOL_MF_STRICT))) + if (((ret > 0) || nr_failed) && (flags & MPOL_MF_STRICT)) err = -EIO; } else { up_out: diff --git a/mm/migrate.c b/mm/migrate.c index b7fa020003f34e4f539491d3755735607469b1c1..2053b54556ca5b558dbdba1b02c0081cd348d113 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -243,7 +243,9 @@ static bool remove_migration_pte(struct folio *folio, #ifdef CONFIG_HUGETLB_PAGE if (folio_test_hugetlb(folio)) { - unsigned int shift = huge_page_shift(hstate_vma(vma)); + struct hstate *h = hstate_vma(vma); + unsigned int shift = huge_page_shift(h); + unsigned long psize = huge_page_size(h); pte = arch_make_huge_pte(pte, shift, vma->vm_flags); if (folio_test_anon(folio)) @@ -251,7 +253,8 @@ static bool remove_migration_pte(struct folio *folio, rmap_flags); else page_dup_file_rmap(new, true); - set_huge_pte_at(vma->vm_mm, pvmw.address, pvmw.pte, pte); + set_huge_pte_at(vma->vm_mm, pvmw.address, pvmw.pte, pte, + psize); } else #endif { diff --git a/mm/mremap.c b/mm/mremap.c index 056478c106eec6894b26b57d847ca785cb6b2faa..382e81c33fc437f2e11e49a1f8a7d68e99e5c845 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -715,7 +715,7 @@ static unsigned long move_vma(struct vm_area_struct *vma, } vma_iter_init(&vmi, mm, old_addr); - if (!do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false)) { + if (do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false) < 0) { /* OOM: unable to split vma, just get accounts right */ if (vm_flags & VM_ACCOUNT && !(flags & MREMAP_DONTUNMAP)) vm_acct_memory(old_len >> PAGE_SHIFT); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 452459836b7118954de3e346ee602aff7f84207e..95546f3763021af4d86098fcb780a034e3e7b8b2 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2400,7 +2400,7 @@ void free_unref_page(struct page *page, unsigned int order) struct per_cpu_pages *pcp; struct zone *zone; unsigned long pfn = page_to_pfn(page); - int migratetype; + int migratetype, pcpmigratetype; if (!free_unref_page_prepare(page, pfn, order)) return; @@ -2408,24 +2408,24 @@ void free_unref_page(struct page *page, unsigned int order) /* * We only track unmovable, reclaimable and movable on pcp lists. * Place ISOLATE pages on the isolated list because they are being - * offlined but treat HIGHATOMIC as movable pages so we can get those - * areas back if necessary. Otherwise, we may have to free + * offlined but treat HIGHATOMIC and CMA as movable pages so we can + * get those areas back if necessary. Otherwise, we may have to free * excessively into the page allocator */ - migratetype = get_pcppage_migratetype(page); + migratetype = pcpmigratetype = get_pcppage_migratetype(page); if (unlikely(migratetype >= MIGRATE_PCPTYPES)) { if (unlikely(is_migrate_isolate(migratetype))) { free_one_page(page_zone(page), page, pfn, order, migratetype, FPI_NONE); return; } - migratetype = MIGRATE_MOVABLE; + pcpmigratetype = MIGRATE_MOVABLE; } zone = page_zone(page); pcp_trylock_prepare(UP_flags); pcp = pcp_spin_trylock(zone->per_cpu_pageset); if (pcp) { - free_unref_page_commit(zone, pcp, page, migratetype, order); + free_unref_page_commit(zone, pcp, page, pcpmigratetype, order); pcp_spin_unlock(pcp); } else { free_one_page(zone, page, pfn, order, migratetype, FPI_NONE); @@ -2641,12 +2641,6 @@ struct page *rmqueue_buddy(struct zone *preferred_zone, struct zone *zone, do { page = NULL; spin_lock_irqsave(&zone->lock, flags); - /* - * order-0 request can reach here when the pcplist is skipped - * due to non-CMA allocation context. HIGHATOMIC area is - * reserved for high-order atomic allocation, so order-0 - * request should skip it. - */ if (alloc_flags & ALLOC_HIGHATOMIC) page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC); if (!page) { @@ -2780,17 +2774,10 @@ struct page *rmqueue(struct zone *preferred_zone, WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1)); if (likely(pcp_allowed_order(order))) { - /* - * MIGRATE_MOVABLE pcplist could have the pages on CMA area and - * we need to skip it when CMA area isn't allowed. - */ - if (!IS_ENABLED(CONFIG_CMA) || alloc_flags & ALLOC_CMA || - migratetype != MIGRATE_MOVABLE) { - page = rmqueue_pcplist(preferred_zone, zone, order, - migratetype, alloc_flags); - if (likely(page)) - goto out; - } + page = rmqueue_pcplist(preferred_zone, zone, order, + migratetype, alloc_flags); + if (likely(page)) + goto out; } page = rmqueue_buddy(preferred_zone, zone, order, alloc_flags, diff --git a/mm/pagewalk.c b/mm/pagewalk.c index 9b2d23fbf4d358caf508851b037d3bd6fa0e6bb9..b7d7e4fcfad7aa5c4ce2af9dc036d0169769c72f 100644 --- a/mm/pagewalk.c +++ b/mm/pagewalk.c @@ -58,7 +58,7 @@ static int walk_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, pte = pte_offset_map(pmd, addr); if (pte) { err = walk_pte_range_inner(pte, addr, end, walk); - if (walk->mm != &init_mm) + if (walk->mm != &init_mm && addr < TASK_SIZE) pte_unmap(pte); } } else { diff --git a/mm/percpu.c b/mm/percpu.c index 28e07ede46f6649eb3e69368d8d09d806bf09d43..a7665de8485fd9594c523c68ad579b1303e1c4ba 100644 --- a/mm/percpu.c +++ b/mm/percpu.c @@ -1890,13 +1890,15 @@ static void __percpu *pcpu_alloc(size_t size, size_t align, bool reserved, fail: trace_percpu_alloc_percpu_fail(reserved, is_atomic, size, align); - if (!is_atomic && do_warn && warn_limit) { + if (do_warn && warn_limit) { pr_warn("allocation failed, size=%zu align=%zu atomic=%d, %s\n", size, align, is_atomic, err); - dump_stack(); + if (!is_atomic) + dump_stack(); if (!--warn_limit) pr_info("limit reached, disable warning\n"); } + if (is_atomic) { /* see the flag handling in pcpu_balance_workfn() */ pcpu_atomic_alloc_failed = true; @@ -2581,14 +2583,12 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, { size_t size_sum = ai->static_size + ai->reserved_size + ai->dyn_size; size_t static_size, dyn_size; - struct pcpu_chunk *chunk; unsigned long *group_offsets; size_t *group_sizes; unsigned long *unit_off; unsigned int cpu; int *unit_map; int group, unit, i; - int map_size; unsigned long tmp_addr; size_t alloc_size; @@ -2615,7 +2615,6 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, PCPU_SETUP_BUG_ON(ai->unit_size < PCPU_MIN_UNIT_SIZE); PCPU_SETUP_BUG_ON(!IS_ALIGNED(ai->unit_size, PCPU_BITMAP_BLOCK_SIZE)); PCPU_SETUP_BUG_ON(ai->dyn_size < PERCPU_DYNAMIC_EARLY_SIZE); - PCPU_SETUP_BUG_ON(!ai->dyn_size); PCPU_SETUP_BUG_ON(!IS_ALIGNED(ai->reserved_size, PCPU_MIN_ALLOC_SIZE)); PCPU_SETUP_BUG_ON(!(IS_ALIGNED(PCPU_BITMAP_BLOCK_SIZE, PAGE_SIZE) || IS_ALIGNED(PAGE_SIZE, PCPU_BITMAP_BLOCK_SIZE))); @@ -2698,7 +2697,7 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, pcpu_unit_pages = ai->unit_size >> PAGE_SHIFT; pcpu_unit_size = pcpu_unit_pages << PAGE_SHIFT; pcpu_atom_size = ai->atom_size; - pcpu_chunk_struct_size = struct_size(chunk, populated, + pcpu_chunk_struct_size = struct_size((struct pcpu_chunk *)0, populated, BITS_TO_LONGS(pcpu_unit_pages)); pcpu_stats_save_ai(ai); @@ -2735,29 +2734,23 @@ void __init pcpu_setup_first_chunk(const struct pcpu_alloc_info *ai, dyn_size = ai->dyn_size - (static_size - ai->static_size); /* - * Initialize first chunk. - * If the reserved_size is non-zero, this initializes the reserved - * chunk. If the reserved_size is zero, the reserved chunk is NULL - * and the dynamic region is initialized here. The first chunk, - * pcpu_first_chunk, will always point to the chunk that serves - * the dynamic region. + * Initialize first chunk: + * This chunk is broken up into 3 parts: + * < static | [reserved] | dynamic > + * - static - there is no backing chunk because these allocations can + * never be freed. + * - reserved (pcpu_reserved_chunk) - exists primarily to serve + * allocations from module load. + * - dynamic (pcpu_first_chunk) - serves the dynamic part of the first + * chunk. */ tmp_addr = (unsigned long)base_addr + static_size; - map_size = ai->reserved_size ?: dyn_size; - chunk = pcpu_alloc_first_chunk(tmp_addr, map_size); - - /* init dynamic chunk if necessary */ - if (ai->reserved_size) { - pcpu_reserved_chunk = chunk; + if (ai->reserved_size) + pcpu_reserved_chunk = pcpu_alloc_first_chunk(tmp_addr, + ai->reserved_size); + tmp_addr = (unsigned long)base_addr + static_size + ai->reserved_size; + pcpu_first_chunk = pcpu_alloc_first_chunk(tmp_addr, dyn_size); - tmp_addr = (unsigned long)base_addr + static_size + - ai->reserved_size; - map_size = dyn_size; - chunk = pcpu_alloc_first_chunk(tmp_addr, map_size); - } - - /* link the first chunk in */ - pcpu_first_chunk = chunk; pcpu_nr_empty_pop_pages = pcpu_first_chunk->nr_empty_pop_pages; pcpu_chunk_relocate(pcpu_first_chunk, -1); @@ -3189,32 +3182,26 @@ void __init __weak pcpu_populate_pte(unsigned long addr) pmd_t *pmd; if (pgd_none(*pgd)) { - p4d_t *new; - - new = memblock_alloc(P4D_TABLE_SIZE, P4D_TABLE_SIZE); - if (!new) + p4d = memblock_alloc(P4D_TABLE_SIZE, P4D_TABLE_SIZE); + if (!p4d) goto err_alloc; - pgd_populate(&init_mm, pgd, new); + pgd_populate(&init_mm, pgd, p4d); } p4d = p4d_offset(pgd, addr); if (p4d_none(*p4d)) { - pud_t *new; - - new = memblock_alloc(PUD_TABLE_SIZE, PUD_TABLE_SIZE); - if (!new) + pud = memblock_alloc(PUD_TABLE_SIZE, PUD_TABLE_SIZE); + if (!pud) goto err_alloc; - p4d_populate(&init_mm, p4d, new); + p4d_populate(&init_mm, p4d, pud); } pud = pud_offset(p4d, addr); if (pud_none(*pud)) { - pmd_t *new; - - new = memblock_alloc(PMD_TABLE_SIZE, PMD_TABLE_SIZE); - if (!new) + pmd = memblock_alloc(PMD_TABLE_SIZE, PMD_TABLE_SIZE); + if (!pmd) goto err_alloc; - pud_populate(&init_mm, pud, new); + pud_populate(&init_mm, pud, pmd); } pmd = pmd_offset(pud, addr); diff --git a/mm/rmap.c b/mm/rmap.c index ec7f8e6c9e483a6ff768272d4e89221044974bee..9f795b93cf40f5fa57c3dc38f7f18c4d4020d17d 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1480,6 +1480,7 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, struct mmu_notifier_range range; enum ttu_flags flags = (enum ttu_flags)(long)arg; unsigned long pfn; + unsigned long hsz = 0; /* * When racing against e.g. zap_pte_range() on another cpu, @@ -1511,6 +1512,9 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, */ adjust_range_if_pmd_sharing_possible(vma, &range.start, &range.end); + + /* We need the huge page size for set_huge_pte_at() */ + hsz = huge_page_size(hstate_vma(vma)); } mmu_notifier_invalidate_range_start(&range); @@ -1628,7 +1632,8 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, pteval = swp_entry_to_pte(make_hwpoison_entry(subpage)); if (folio_test_hugetlb(folio)) { hugetlb_count_sub(folio_nr_pages(folio), mm); - set_huge_pte_at(mm, address, pvmw.pte, pteval); + set_huge_pte_at(mm, address, pvmw.pte, pteval, + hsz); } else { dec_mm_counter(mm, mm_counter(&folio->page)); set_pte_at(mm, address, pvmw.pte, pteval); @@ -1820,6 +1825,7 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, struct mmu_notifier_range range; enum ttu_flags flags = (enum ttu_flags)(long)arg; unsigned long pfn; + unsigned long hsz = 0; /* * When racing against e.g. zap_pte_range() on another cpu, @@ -1855,6 +1861,9 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, */ adjust_range_if_pmd_sharing_possible(vma, &range.start, &range.end); + + /* We need the huge page size for set_huge_pte_at() */ + hsz = huge_page_size(hstate_vma(vma)); } mmu_notifier_invalidate_range_start(&range); @@ -2020,7 +2029,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, pteval = swp_entry_to_pte(make_hwpoison_entry(subpage)); if (folio_test_hugetlb(folio)) { hugetlb_count_sub(folio_nr_pages(folio), mm); - set_huge_pte_at(mm, address, pvmw.pte, pteval); + set_huge_pte_at(mm, address, pvmw.pte, pteval, + hsz); } else { dec_mm_counter(mm, mm_counter(&folio->page)); set_pte_at(mm, address, pvmw.pte, pteval); @@ -2044,7 +2054,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, if (arch_unmap_one(mm, vma, address, pteval) < 0) { if (folio_test_hugetlb(folio)) - set_huge_pte_at(mm, address, pvmw.pte, pteval); + set_huge_pte_at(mm, address, pvmw.pte, + pteval, hsz); else set_pte_at(mm, address, pvmw.pte, pteval); ret = false; @@ -2058,7 +2069,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, if (anon_exclusive && page_try_share_anon_rmap(subpage)) { if (folio_test_hugetlb(folio)) - set_huge_pte_at(mm, address, pvmw.pte, pteval); + set_huge_pte_at(mm, address, pvmw.pte, + pteval, hsz); else set_pte_at(mm, address, pvmw.pte, pteval); ret = false; @@ -2090,7 +2102,8 @@ static bool try_to_migrate_one(struct folio *folio, struct vm_area_struct *vma, if (pte_uffd_wp(pteval)) swp_pte = pte_swp_mkuffd_wp(swp_pte); if (folio_test_hugetlb(folio)) - set_huge_pte_at(mm, address, pvmw.pte, swp_pte); + set_huge_pte_at(mm, address, pvmw.pte, swp_pte, + hsz); else set_pte_at(mm, address, pvmw.pte, swp_pte); trace_set_migration_pte(address, pte_val(swp_pte), diff --git a/mm/shmem.c b/mm/shmem.c index 02e62fccc80d4973b6626988eed07ff2482980d1..69595d3418829f08d05829f3ad5166b7421a30fc 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4586,7 +4586,7 @@ static struct file_system_type shmem_fs_type = { #endif .kill_sb = kill_litter_super, #ifdef CONFIG_SHMEM - .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP | FS_MGTIME, + .fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP, #else .fs_flags = FS_USERNS_MOUNT, #endif diff --git a/mm/slab_common.c b/mm/slab_common.c index cd71f9581e672258872eeb369f69ce0c58f4c3da..8fda308e400db80d031a702b58d7ae4980badc43 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -479,7 +479,7 @@ void slab_kmem_cache_release(struct kmem_cache *s) void kmem_cache_destroy(struct kmem_cache *s) { - int refcnt; + int err = -EBUSY; bool rcu_set; if (unlikely(!s) || !kasan_check_byte(s)) @@ -490,17 +490,17 @@ void kmem_cache_destroy(struct kmem_cache *s) rcu_set = s->flags & SLAB_TYPESAFE_BY_RCU; - refcnt = --s->refcount; - if (refcnt) + s->refcount--; + if (s->refcount) goto out_unlock; - WARN(shutdown_cache(s), - "%s %s: Slab cache still has objects when called from %pS", + err = shutdown_cache(s); + WARN(err, "%s %s: Slab cache still has objects when called from %pS", __func__, s->name, (void *)_RET_IP_); out_unlock: mutex_unlock(&slab_mutex); cpus_read_unlock(); - if (!refcnt && !rcu_set) + if (!err && !rcu_set) kmem_cache_release(s); } EXPORT_SYMBOL(kmem_cache_destroy); @@ -745,24 +745,24 @@ struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags, unsigned long caller) size_t kmalloc_size_roundup(size_t size) { - struct kmem_cache *c; + if (size && size <= KMALLOC_MAX_CACHE_SIZE) { + /* + * The flags don't matter since size_index is common to all. + * Neither does the caller for just getting ->object_size. + */ + return kmalloc_slab(size, GFP_KERNEL, 0)->object_size; + } - /* Short-circuit the 0 size case. */ - if (unlikely(size == 0)) - return 0; - /* Short-circuit saturated "too-large" case. */ - if (unlikely(size == SIZE_MAX)) - return SIZE_MAX; /* Above the smaller buckets, size is a multiple of page size. */ - if (size > KMALLOC_MAX_CACHE_SIZE) + if (size && size <= KMALLOC_MAX_SIZE) return PAGE_SIZE << get_order(size); /* - * The flags don't matter since size_index is common to all. - * Neither does the caller for just getting ->object_size. + * Return 'size' for 0 - kmalloc() returns ZERO_SIZE_PTR + * and very large size - kmalloc() may fail. */ - c = kmalloc_slab(size, GFP_KERNEL, 0); - return c ? c->object_size : 0; + return size; + } EXPORT_SYMBOL(kmalloc_size_roundup); diff --git a/mm/util.c b/mm/util.c index f08b655da917e368895f01bccb73a49bcd2773bf..8cbbfd3a3d5984016c47716b372c52b83feb9ab0 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1068,7 +1068,9 @@ void mem_dump_obj(void *object) if (vmalloc_dump_obj(object)) return; - if (virt_addr_valid(object)) + if (is_vmalloc_addr(object)) + type = "vmalloc memory"; + else if (virt_addr_valid(object)) type = "non-slab/vmalloc memory"; else if (object == NULL) type = "NULL pointer"; diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 228a4a5312f2213f8bb2cfdef6d07cb94f2abdd3..a3fedb3ee0dbd48a3bae9c712bb2602a94dd3fbf 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -111,7 +111,7 @@ static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end, pte_t entry = pfn_pte(pfn, prot); entry = arch_make_huge_pte(entry, ilog2(size), 0); - set_huge_pte_at(&init_mm, addr, pte, entry); + set_huge_pte_at(&init_mm, addr, pte, entry, size); pfn += PFN_DOWN(size); continue; } @@ -4278,14 +4278,32 @@ void pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms) #ifdef CONFIG_PRINTK bool vmalloc_dump_obj(void *object) { - struct vm_struct *vm; void *objp = (void *)PAGE_ALIGN((unsigned long)object); + const void *caller; + struct vm_struct *vm; + struct vmap_area *va; + unsigned long addr; + unsigned int nr_pages; - vm = find_vm_area(objp); - if (!vm) + if (!spin_trylock(&vmap_area_lock)) + return false; + va = __find_vmap_area((unsigned long)objp, &vmap_area_root); + if (!va) { + spin_unlock(&vmap_area_lock); return false; + } + + vm = va->vm; + if (!vm) { + spin_unlock(&vmap_area_lock); + return false; + } + addr = (unsigned long)vm->addr; + caller = vm->caller; + nr_pages = vm->nr_pages; + spin_unlock(&vmap_area_lock); pr_cont(" %u-page vmalloc region starting at %#lx allocated at %pS\n", - vm->nr_pages, (unsigned long)vm->addr, vm->caller); + nr_pages, addr, caller); return true; } #endif diff --git a/mm/zswap.c b/mm/zswap.c index 412b1409a0d78afbeba2aabc640084e83b39602b..083c693602b8209a7cfc95ca5d43cdb2a7639ff8 100644 --- a/mm/zswap.c +++ b/mm/zswap.c @@ -1218,6 +1218,19 @@ bool zswap_store(struct folio *folio) if (!zswap_enabled || !tree) return false; + /* + * If this is a duplicate, it must be removed before attempting to store + * it, otherwise, if the store fails the old page won't be removed from + * the tree, and it might be written back overriding the new data. + */ + spin_lock(&tree->lock); + dupentry = zswap_rb_search(&tree->rbroot, offset); + if (dupentry) { + zswap_duplicate_entry++; + zswap_invalidate_entry(tree, dupentry); + } + spin_unlock(&tree->lock); + /* * XXX: zswap reclaim does not work with cgroups yet. Without a * cgroup-aware entry LRU, we will push out entries system-wide based on @@ -1333,7 +1346,14 @@ bool zswap_store(struct folio *folio) /* map */ spin_lock(&tree->lock); + /* + * A duplicate entry should have been removed at the beginning of this + * function. Since the swap entry should be pinned, if a duplicate is + * found again here it means that something went wrong in the swap + * cache. + */ while (zswap_rb_insert(&tree->rbroot, entry, &dupentry) == -EEXIST) { + WARN_ON(1); zswap_duplicate_entry++; zswap_invalidate_entry(tree, dupentry); } diff --git a/net/ax25/Kconfig b/net/ax25/Kconfig index d3a9843a043d7a0a731c3d8d89396b2bf5513fa0..fdb666607f10ac748a59daf46189cd1cba158c43 100644 --- a/net/ax25/Kconfig +++ b/net/ax25/Kconfig @@ -10,7 +10,7 @@ menuconfig HAMRADIO If you want to connect your Linux box to an amateur radio, answer Y here. You want to read and more specifically about AX.25 on Linux - . + . Note that the answer to this question won't directly affect the kernel: saying N will just cause the configurator to skip all @@ -61,7 +61,7 @@ config AX25_DAMA_SLAVE configuration. Linux cannot yet act as a DAMA server. This option only compiles DAMA slave support into the kernel. It still needs to be enabled at runtime. For more about DAMA see - . If unsure, say Y. + . If unsure, say Y. # placeholder until implemented config AX25_DAMA_MASTER @@ -87,9 +87,9 @@ config NETROM A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the Linux Ham Wiki, available from - . You also might want to check out the - file . More information about - digital amateur radio in general is on the WWW at + . You also might want to check out + the file . More information + about digital amateur radio in general is on the WWW at . To compile this driver as a module, choose M here: the @@ -106,9 +106,9 @@ config ROSE A comprehensive listing of all the software for Linux amateur radio users as well as information about how to configure an AX.25 port is contained in the Linux Ham Wiki, available from - . You also might want to check out the - file . More information about - digital amateur radio in general is on the WWW at + . You also might want to check out + the file . More information + about digital amateur radio in general is on the WWW at . To compile this driver as a module, choose M here: the diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 9d5057cef30a303abf099f1f7eff05afe56abaf3..7a6f20338db8efe42a6f3ec44bb08a37eec35ed3 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -2413,34 +2413,41 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type, if (!test_bit(HCI_CONN_AUTH, &conn->flags)) goto auth; - /* An authenticated FIPS approved combination key has sufficient - * security for security level 4. */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256 && - sec_level == BT_SECURITY_FIPS) - goto encrypt; - - /* An authenticated combination key has sufficient security for - security level 3. */ - if ((conn->key_type == HCI_LK_AUTH_COMBINATION_P192 || - conn->key_type == HCI_LK_AUTH_COMBINATION_P256) && - sec_level == BT_SECURITY_HIGH) - goto encrypt; - - /* An unauthenticated combination key has sufficient security for - security level 1 and 2. */ - if ((conn->key_type == HCI_LK_UNAUTH_COMBINATION_P192 || - conn->key_type == HCI_LK_UNAUTH_COMBINATION_P256) && - (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW)) - goto encrypt; - - /* A combination key has always sufficient security for the security - levels 1 or 2. High security level requires the combination key - is generated using maximum PIN code length (16). - For pre 2.1 units. */ - if (conn->key_type == HCI_LK_COMBINATION && - (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW || - conn->pin_length == 16)) - goto encrypt; + switch (conn->key_type) { + case HCI_LK_AUTH_COMBINATION_P256: + /* An authenticated FIPS approved combination key has + * sufficient security for security level 4 or lower. + */ + if (sec_level <= BT_SECURITY_FIPS) + goto encrypt; + break; + case HCI_LK_AUTH_COMBINATION_P192: + /* An authenticated combination key has sufficient security for + * security level 3 or lower. + */ + if (sec_level <= BT_SECURITY_HIGH) + goto encrypt; + break; + case HCI_LK_UNAUTH_COMBINATION_P192: + case HCI_LK_UNAUTH_COMBINATION_P256: + /* An unauthenticated combination key has sufficient security + * for security level 2 or lower. + */ + if (sec_level <= BT_SECURITY_MEDIUM) + goto encrypt; + break; + case HCI_LK_COMBINATION: + /* A combination key has always sufficient security for the + * security levels 2 or lower. High security level requires the + * combination key is generated using maximum PIN code length + * (16). For pre 2.1 units. + */ + if (sec_level <= BT_SECURITY_MEDIUM || conn->pin_length == 16) + goto encrypt; + break; + default: + break; + } auth: if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a5992f1b3c9b74438986f4f3a7289a1e9ddec873..195aea2198a963ebeecaec40f74e03be7e3a70b5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2617,7 +2617,11 @@ int hci_register_dev(struct hci_dev *hdev) if (id < 0) return id; - snprintf(hdev->name, sizeof(hdev->name), "hci%d", id); + error = dev_set_name(&hdev->dev, "hci%u", id); + if (error) + return error; + + hdev->name = dev_name(&hdev->dev); hdev->id = id; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); @@ -2639,8 +2643,6 @@ int hci_register_dev(struct hci_dev *hdev) if (!IS_ERR_OR_NULL(bt_debugfs)) hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); - dev_set_name(&hdev->dev, "%s", hdev->name); - error = device_add(&hdev->dev); if (error < 0) goto err_wqueue; @@ -2784,6 +2786,7 @@ void hci_release_dev(struct hci_dev *hdev) hci_conn_params_clear_all(hdev); hci_discovery_filter_clear(hdev); hci_blocked_keys_clear(hdev); + hci_codec_list_clear(&hdev->local_codecs); hci_dev_unlock(hdev); ida_simple_remove(&hci_index_ida, hdev->id); @@ -3418,7 +3421,12 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) if (c->type == type && c->sent) { bt_dev_err(hdev, "killing stalled connection %pMR", &c->dst); + /* hci_disconnect might sleep, so, we have to release + * the RCU read lock before calling it. + */ + rcu_read_unlock(); hci_disconnect(c, HCI_ERROR_REMOTE_USER_TERM); + rcu_read_lock(); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 35f251041eeb9b58f2ffce8696a3c84a6f5e8355..31d02b54eea119f0a03f3de45a33475a982ca529 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -33,6 +33,7 @@ #include "hci_request.h" #include "hci_debugfs.h" +#include "hci_codec.h" #include "a2mp.h" #include "amp.h" #include "smp.h" diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index b9c5a98238374cccb4a59fbc3d41c7c465db1afd..0be75cf0efed86c450f5765c8f7b8b2b66c1c608 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h @@ -71,7 +71,5 @@ struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, void hci_req_add_le_scan_disable(struct hci_request *req, bool rpa_le_conn); void hci_req_add_le_passive_scan(struct hci_request *req); -void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next); - void hci_request_setup(struct hci_dev *hdev); void hci_request_cancel_all(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 9b93653c6197300eb5f4c8d55dd3f38557b1976d..d06e07a0ea5a983fd3dabc155f8ff8ce5b5ada4c 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -413,11 +413,6 @@ static int hci_le_scan_restart_sync(struct hci_dev *hdev) LE_SCAN_FILTER_DUP_ENABLE); } -static int le_scan_restart_sync(struct hci_dev *hdev, void *data) -{ - return hci_le_scan_restart_sync(hdev); -} - static void le_scan_restart(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, @@ -427,15 +422,15 @@ static void le_scan_restart(struct work_struct *work) bt_dev_dbg(hdev, ""); - hci_dev_lock(hdev); - - status = hci_cmd_sync_queue(hdev, le_scan_restart_sync, NULL, NULL); + status = hci_le_scan_restart_sync(hdev); if (status) { bt_dev_err(hdev, "failed to restart LE scan: status %d", status); - goto unlock; + return; } + hci_dev_lock(hdev); + if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) || !hdev->discovery.scan_start) goto unlock; @@ -5079,6 +5074,7 @@ int hci_dev_close_sync(struct hci_dev *hdev) memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); bacpy(&hdev->random_addr, BDADDR_ANY); + hci_codec_list_clear(&hdev->local_codecs); hci_dev_put(hdev); return err; diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 16da946f5881bab7d3b3da2ab5046eb372c6953d..71248163ce9a5c76582037e7ad3ac1a3f2f7e008 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -502,7 +502,7 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb) } /* -------- Socket interface ---------- */ -static struct sock *__iso_get_sock_listen_by_addr(bdaddr_t *ba) +static struct sock *__iso_get_sock_listen_by_addr(bdaddr_t *src, bdaddr_t *dst) { struct sock *sk; @@ -510,7 +510,10 @@ static struct sock *__iso_get_sock_listen_by_addr(bdaddr_t *ba) if (sk->sk_state != BT_LISTEN) continue; - if (!bacmp(&iso_pi(sk)->src, ba)) + if (bacmp(&iso_pi(sk)->dst, dst)) + continue; + + if (!bacmp(&iso_pi(sk)->src, src)) return sk; } @@ -952,7 +955,7 @@ static int iso_listen_cis(struct sock *sk) write_lock(&iso_sk_list.lock); - if (__iso_get_sock_listen_by_addr(&iso_pi(sk)->src)) + if (__iso_get_sock_listen_by_addr(&iso_pi(sk)->src, &iso_pi(sk)->dst)) err = -EADDRINUSE; write_unlock(&iso_sk_list.lock); diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 57a7a64b84ede57b842c04a4adef34725858d5d2..0841f8d824198d0409d68aa178f2fba4d8ebb11d 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -543,6 +543,7 @@ struct bpf_fentry_test_t { int noinline bpf_fentry_test7(struct bpf_fentry_test_t *arg) { + asm volatile (""); return (long)arg; } diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 9d7bc8b96b53a911d5add8d0b21736358441755f..7431f89e897b9549a0c35d9431e36b6de2e80022 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -124,7 +124,7 @@ static int deliver_clone(const struct net_bridge_port *prev, skb = skb_clone(skb, GFP_ATOMIC); if (!skb) { - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return -ENOMEM; } @@ -268,7 +268,7 @@ static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb, skb = skb_copy(skb, GFP_ATOMIC); if (!skb) { - dev->stats.tx_dropped++; + DEV_STATS_INC(dev, tx_dropped); return; } diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index c34a0b0901b07de9e2a3f172527a2bf1c0b6f0b7..c729528b5e85f3a28eff6bd346c18bc11288e7a8 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -181,12 +181,12 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb if ((mdst && mdst->host_joined) || br_multicast_is_router(brmctx, skb)) { local_rcv = true; - br->dev->stats.multicast++; + DEV_STATS_INC(br->dev, multicast); } mcast_hit = true; } else { local_rcv = true; - br->dev->stats.multicast++; + DEV_STATS_INC(br->dev, multicast); } break; case BR_PKT_UNICAST: diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 15186247b59af52062907cacec994e736ddbc01b..033034d68f1f057349acdd2f127c427195be6b62 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -294,7 +294,7 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_ /* tell br_dev_xmit to continue with forwarding */ nf_bridge->bridged_dnat = 1; /* FIXME Need to refragment */ - ret = neigh->output(neigh, skb); + ret = READ_ONCE(neigh->output)(neigh, skb); } neigh_release(neigh); return ret; diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c index feaec4ad6d163d6c898f7e63671a3e264bb2cd1b..b28c976f52a0a16e13e23aee7c6fe4a7a8c844af 100644 --- a/net/can/j1939/socket.c +++ b/net/can/j1939/socket.c @@ -974,6 +974,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, struct sock_exterr_skb *serr; struct sk_buff *skb; char *state = "UNK"; + u32 tsflags; int err; jsk = j1939_sk(sk); @@ -981,13 +982,14 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, if (!(jsk->state & J1939_SOCK_ERRQUEUE)) return; + tsflags = READ_ONCE(sk->sk_tsflags); switch (type) { case J1939_ERRQUEUE_TX_ACK: - if (!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_ACK)) + if (!(tsflags & SOF_TIMESTAMPING_TX_ACK)) return; break; case J1939_ERRQUEUE_TX_SCHED: - if (!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_SCHED)) + if (!(tsflags & SOF_TIMESTAMPING_TX_SCHED)) return; break; case J1939_ERRQUEUE_TX_ABORT: @@ -997,7 +999,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, case J1939_ERRQUEUE_RX_DPO: fallthrough; case J1939_ERRQUEUE_RX_ABORT: - if (!(sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE)) + if (!(tsflags & SOF_TIMESTAMPING_RX_SOFTWARE)) return; break; default: @@ -1054,7 +1056,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk, } serr->opt_stats = true; - if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) + if (tsflags & SOF_TIMESTAMPING_OPT_ID) serr->ee.ee_data = session->tskey; netdev_dbg(session->priv->ndev, "%s: 0x%p tskey: %i, state: %s\n", diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 5eb4898cccd4cc01e44b40952d2ddc888d224f53..10a41cd9c52353bf10db342d885afaa193e8c68e 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -969,6 +969,62 @@ static bool ceph_msg_data_pagelist_advance(struct ceph_msg_data_cursor *cursor, return true; } +static void ceph_msg_data_iter_cursor_init(struct ceph_msg_data_cursor *cursor, + size_t length) +{ + struct ceph_msg_data *data = cursor->data; + + cursor->iov_iter = data->iter; + cursor->lastlen = 0; + iov_iter_truncate(&cursor->iov_iter, length); + cursor->resid = iov_iter_count(&cursor->iov_iter); +} + +static struct page *ceph_msg_data_iter_next(struct ceph_msg_data_cursor *cursor, + size_t *page_offset, size_t *length) +{ + struct page *page; + ssize_t len; + + if (cursor->lastlen) + iov_iter_revert(&cursor->iov_iter, cursor->lastlen); + + len = iov_iter_get_pages2(&cursor->iov_iter, &page, PAGE_SIZE, + 1, page_offset); + BUG_ON(len < 0); + + cursor->lastlen = len; + + /* + * FIXME: The assumption is that the pages represented by the iov_iter + * are pinned, with the references held by the upper-level + * callers, or by virtue of being under writeback. Eventually, + * we'll get an iov_iter_get_pages2 variant that doesn't take + * page refs. Until then, just put the page ref. + */ + VM_BUG_ON_PAGE(!PageWriteback(page) && page_count(page) < 2, page); + put_page(page); + + *length = min_t(size_t, len, cursor->resid); + return page; +} + +static bool ceph_msg_data_iter_advance(struct ceph_msg_data_cursor *cursor, + size_t bytes) +{ + BUG_ON(bytes > cursor->resid); + cursor->resid -= bytes; + + if (bytes < cursor->lastlen) { + cursor->lastlen -= bytes; + } else { + iov_iter_advance(&cursor->iov_iter, bytes - cursor->lastlen); + cursor->lastlen = 0; + } + + return cursor->resid; +} + /* * Message data is handled (sent or received) in pieces, where each * piece resides on a single page. The network layer might not @@ -996,6 +1052,9 @@ static void __ceph_msg_data_cursor_init(struct ceph_msg_data_cursor *cursor) case CEPH_MSG_DATA_BVECS: ceph_msg_data_bvecs_cursor_init(cursor, length); break; + case CEPH_MSG_DATA_ITER: + ceph_msg_data_iter_cursor_init(cursor, length); + break; case CEPH_MSG_DATA_NONE: default: /* BUG(); */ @@ -1013,6 +1072,7 @@ void ceph_msg_data_cursor_init(struct ceph_msg_data_cursor *cursor, cursor->total_resid = length; cursor->data = msg->data; + cursor->sr_resid = 0; __ceph_msg_data_cursor_init(cursor); } @@ -1042,6 +1102,9 @@ struct page *ceph_msg_data_next(struct ceph_msg_data_cursor *cursor, case CEPH_MSG_DATA_BVECS: page = ceph_msg_data_bvecs_next(cursor, page_offset, length); break; + case CEPH_MSG_DATA_ITER: + page = ceph_msg_data_iter_next(cursor, page_offset, length); + break; case CEPH_MSG_DATA_NONE: default: page = NULL; @@ -1080,6 +1143,9 @@ void ceph_msg_data_advance(struct ceph_msg_data_cursor *cursor, size_t bytes) case CEPH_MSG_DATA_BVECS: new_piece = ceph_msg_data_bvecs_advance(cursor, bytes); break; + case CEPH_MSG_DATA_ITER: + new_piece = ceph_msg_data_iter_advance(cursor, bytes); + break; case CEPH_MSG_DATA_NONE: default: BUG(); @@ -1879,6 +1945,18 @@ void ceph_msg_data_add_bvecs(struct ceph_msg *msg, } EXPORT_SYMBOL(ceph_msg_data_add_bvecs); +void ceph_msg_data_add_iter(struct ceph_msg *msg, + struct iov_iter *iter) +{ + struct ceph_msg_data *data; + + data = ceph_msg_data_add(msg); + data->type = CEPH_MSG_DATA_ITER; + data->iter = *iter; + + msg->data_length += iov_iter_count(&data->iter); +} + /* * construct a new message with given type, size * the new msg has a ref count of 1. diff --git a/net/ceph/messenger_v1.c b/net/ceph/messenger_v1.c index 3d57bb48a2b49a584858422ae7dbf205bf5d2f66..f9a50d7f0d204639f821835d341bb87c13a80333 100644 --- a/net/ceph/messenger_v1.c +++ b/net/ceph/messenger_v1.c @@ -159,9 +159,9 @@ static size_t sizeof_footer(struct ceph_connection *con) static void prepare_message_data(struct ceph_msg *msg, u32 data_len) { - /* Initialize data cursor */ - - ceph_msg_data_cursor_init(&msg->cursor, msg, data_len); + /* Initialize data cursor if it's not a sparse read */ + if (!msg->sparse_read) + ceph_msg_data_cursor_init(&msg->cursor, msg, data_len); } /* @@ -960,9 +960,9 @@ static void process_ack(struct ceph_connection *con) prepare_read_tag(con); } -static int read_partial_message_section(struct ceph_connection *con, - struct kvec *section, - unsigned int sec_len, u32 *crc) +static int read_partial_message_chunk(struct ceph_connection *con, + struct kvec *section, + unsigned int sec_len, u32 *crc) { int ret, left; @@ -978,11 +978,91 @@ static int read_partial_message_section(struct ceph_connection *con, section->iov_len += ret; } if (section->iov_len == sec_len) - *crc = crc32c(0, section->iov_base, section->iov_len); + *crc = crc32c(*crc, section->iov_base, section->iov_len); return 1; } +static inline int read_partial_message_section(struct ceph_connection *con, + struct kvec *section, + unsigned int sec_len, u32 *crc) +{ + *crc = 0; + return read_partial_message_chunk(con, section, sec_len, crc); +} + +static int read_sparse_msg_extent(struct ceph_connection *con, u32 *crc) +{ + struct ceph_msg_data_cursor *cursor = &con->in_msg->cursor; + bool do_bounce = ceph_test_opt(from_msgr(con->msgr), RXBOUNCE); + + if (do_bounce && unlikely(!con->bounce_page)) { + con->bounce_page = alloc_page(GFP_NOIO); + if (!con->bounce_page) { + pr_err("failed to allocate bounce page\n"); + return -ENOMEM; + } + } + + while (cursor->sr_resid > 0) { + struct page *page, *rpage; + size_t off, len; + int ret; + + page = ceph_msg_data_next(cursor, &off, &len); + rpage = do_bounce ? con->bounce_page : page; + + /* clamp to what remains in extent */ + len = min_t(int, len, cursor->sr_resid); + ret = ceph_tcp_recvpage(con->sock, rpage, (int)off, len); + if (ret <= 0) + return ret; + *crc = ceph_crc32c_page(*crc, rpage, off, ret); + ceph_msg_data_advance(cursor, (size_t)ret); + cursor->sr_resid -= ret; + if (do_bounce) + memcpy_page(page, off, rpage, off, ret); + } + return 1; +} + +static int read_sparse_msg_data(struct ceph_connection *con) +{ + struct ceph_msg_data_cursor *cursor = &con->in_msg->cursor; + bool do_datacrc = !ceph_test_opt(from_msgr(con->msgr), NOCRC); + u32 crc = 0; + int ret = 1; + + if (do_datacrc) + crc = con->in_data_crc; + + do { + if (con->v1.in_sr_kvec.iov_base) + ret = read_partial_message_chunk(con, + &con->v1.in_sr_kvec, + con->v1.in_sr_len, + &crc); + else if (cursor->sr_resid > 0) + ret = read_sparse_msg_extent(con, &crc); + + if (ret <= 0) { + if (do_datacrc) + con->in_data_crc = crc; + return ret; + } + + memset(&con->v1.in_sr_kvec, 0, sizeof(con->v1.in_sr_kvec)); + ret = con->ops->sparse_read(con, cursor, + (char **)&con->v1.in_sr_kvec.iov_base); + con->v1.in_sr_len = ret; + } while (ret > 0); + + if (do_datacrc) + con->in_data_crc = crc; + + return ret < 0 ? ret : 1; /* must return > 0 to indicate success */ +} + static int read_partial_msg_data(struct ceph_connection *con) { struct ceph_msg_data_cursor *cursor = &con->in_msg->cursor; @@ -1173,7 +1253,9 @@ static int read_partial_message(struct ceph_connection *con) if (!m->num_data_items) return -EIO; - if (ceph_test_opt(from_msgr(con->msgr), RXBOUNCE)) + if (m->sparse_read) + ret = read_sparse_msg_data(con); + else if (ceph_test_opt(from_msgr(con->msgr), RXBOUNCE)) ret = read_partial_msg_data_bounce(con); else ret = read_partial_msg_data(con); diff --git a/net/ceph/messenger_v2.c b/net/ceph/messenger_v2.c index 1df1d29dee920bddbaff384fd36783191e699d51..d09a39ff2cf0419db2e66a34b61738a2fd98fc3b 100644 --- a/net/ceph/messenger_v2.c +++ b/net/ceph/messenger_v2.c @@ -8,9 +8,9 @@ #include #include -#include /* for crypto_memneq() */ #include #include +#include #include #include #include @@ -52,14 +52,16 @@ #define FRAME_LATE_STATUS_COMPLETE 0xe #define FRAME_LATE_STATUS_ABORTED_MASK 0xf -#define IN_S_HANDLE_PREAMBLE 1 -#define IN_S_HANDLE_CONTROL 2 -#define IN_S_HANDLE_CONTROL_REMAINDER 3 -#define IN_S_PREPARE_READ_DATA 4 -#define IN_S_PREPARE_READ_DATA_CONT 5 -#define IN_S_PREPARE_READ_ENC_PAGE 6 -#define IN_S_HANDLE_EPILOGUE 7 -#define IN_S_FINISH_SKIP 8 +#define IN_S_HANDLE_PREAMBLE 1 +#define IN_S_HANDLE_CONTROL 2 +#define IN_S_HANDLE_CONTROL_REMAINDER 3 +#define IN_S_PREPARE_READ_DATA 4 +#define IN_S_PREPARE_READ_DATA_CONT 5 +#define IN_S_PREPARE_READ_ENC_PAGE 6 +#define IN_S_PREPARE_SPARSE_DATA 7 +#define IN_S_PREPARE_SPARSE_DATA_CONT 8 +#define IN_S_HANDLE_EPILOGUE 9 +#define IN_S_FINISH_SKIP 10 #define OUT_S_QUEUE_DATA 1 #define OUT_S_QUEUE_DATA_CONT 2 @@ -967,12 +969,48 @@ static void init_sgs_cursor(struct scatterlist **sg, } } +/** + * init_sgs_pages: set up scatterlist on an array of page pointers + * @sg: scatterlist to populate + * @pages: pointer to page array + * @dpos: position in the array to start (bytes) + * @dlen: len to add to sg (bytes) + * @pad: pointer to pad destination (if any) + * + * Populate the scatterlist from the page array, starting at an arbitrary + * byte in the array and running for a specified length. + */ +static void init_sgs_pages(struct scatterlist **sg, struct page **pages, + int dpos, int dlen, u8 *pad) +{ + int idx = dpos >> PAGE_SHIFT; + int off = offset_in_page(dpos); + int resid = dlen; + + do { + int len = min(resid, (int)PAGE_SIZE - off); + + sg_set_page(*sg, pages[idx], len, off); + *sg = sg_next(*sg); + off = 0; + ++idx; + resid -= len; + } while (resid); + + if (need_padding(dlen)) { + sg_set_buf(*sg, pad, padding_len(dlen)); + *sg = sg_next(*sg); + } +} + static int setup_message_sgs(struct sg_table *sgt, struct ceph_msg *msg, u8 *front_pad, u8 *middle_pad, u8 *data_pad, - void *epilogue, bool add_tag) + void *epilogue, struct page **pages, int dpos, + bool add_tag) { struct ceph_msg_data_cursor cursor; struct scatterlist *cur_sg; + int dlen = data_len(msg); int sg_cnt; int ret; @@ -986,9 +1024,15 @@ static int setup_message_sgs(struct sg_table *sgt, struct ceph_msg *msg, if (middle_len(msg)) sg_cnt += calc_sg_cnt(msg->middle->vec.iov_base, middle_len(msg)); - if (data_len(msg)) { - ceph_msg_data_cursor_init(&cursor, msg, data_len(msg)); - sg_cnt += calc_sg_cnt_cursor(&cursor); + if (dlen) { + if (pages) { + sg_cnt += calc_pages_for(dpos, dlen); + if (need_padding(dlen)) + sg_cnt++; + } else { + ceph_msg_data_cursor_init(&cursor, msg, dlen); + sg_cnt += calc_sg_cnt_cursor(&cursor); + } } ret = sg_alloc_table(sgt, sg_cnt, GFP_NOIO); @@ -1002,9 +1046,13 @@ static int setup_message_sgs(struct sg_table *sgt, struct ceph_msg *msg, if (middle_len(msg)) init_sgs(&cur_sg, msg->middle->vec.iov_base, middle_len(msg), middle_pad); - if (data_len(msg)) { - ceph_msg_data_cursor_init(&cursor, msg, data_len(msg)); - init_sgs_cursor(&cur_sg, &cursor, data_pad); + if (dlen) { + if (pages) { + init_sgs_pages(&cur_sg, pages, dpos, dlen, data_pad); + } else { + ceph_msg_data_cursor_init(&cursor, msg, dlen); + init_sgs_cursor(&cur_sg, &cursor, data_pad); + } } WARN_ON(!sg_is_last(cur_sg)); @@ -1039,10 +1087,53 @@ static int decrypt_control_remainder(struct ceph_connection *con) padded_len(rem_len) + CEPH_GCM_TAG_LEN); } +/* Process sparse read data that lives in a buffer */ +static int process_v2_sparse_read(struct ceph_connection *con, + struct page **pages, int spos) +{ + struct ceph_msg_data_cursor *cursor = &con->v2.in_cursor; + int ret; + + for (;;) { + char *buf = NULL; + + ret = con->ops->sparse_read(con, cursor, &buf); + if (ret <= 0) + return ret; + + dout("%s: sparse_read return %x buf %p\n", __func__, ret, buf); + + do { + int idx = spos >> PAGE_SHIFT; + int soff = offset_in_page(spos); + struct page *spage = con->v2.in_enc_pages[idx]; + int len = min_t(int, ret, PAGE_SIZE - soff); + + if (buf) { + memcpy_from_page(buf, spage, soff, len); + buf += len; + } else { + struct bio_vec bv; + + get_bvec_at(cursor, &bv); + len = min_t(int, len, bv.bv_len); + memcpy_page(bv.bv_page, bv.bv_offset, + spage, soff, len); + ceph_msg_data_advance(cursor, len); + } + spos += len; + ret -= len; + } while (ret); + } +} + static int decrypt_tail(struct ceph_connection *con) { struct sg_table enc_sgt = {}; struct sg_table sgt = {}; + struct page **pages = NULL; + bool sparse = con->in_msg->sparse_read; + int dpos = 0; int tail_len; int ret; @@ -1053,9 +1144,14 @@ static int decrypt_tail(struct ceph_connection *con) if (ret) goto out; + if (sparse) { + dpos = padded_len(front_len(con->in_msg) + padded_len(middle_len(con->in_msg))); + pages = con->v2.in_enc_pages; + } + ret = setup_message_sgs(&sgt, con->in_msg, FRONT_PAD(con->v2.in_buf), - MIDDLE_PAD(con->v2.in_buf), DATA_PAD(con->v2.in_buf), - con->v2.in_buf, true); + MIDDLE_PAD(con->v2.in_buf), DATA_PAD(con->v2.in_buf), + con->v2.in_buf, pages, dpos, true); if (ret) goto out; @@ -1065,6 +1161,12 @@ static int decrypt_tail(struct ceph_connection *con) if (ret) goto out; + if (sparse && data_len(con->in_msg)) { + ret = process_v2_sparse_read(con, con->v2.in_enc_pages, dpos); + if (ret) + goto out; + } + WARN_ON(!con->v2.in_enc_page_cnt); ceph_release_page_vector(con->v2.in_enc_pages, con->v2.in_enc_page_cnt); @@ -1588,7 +1690,7 @@ static int prepare_message_secure(struct ceph_connection *con) encode_epilogue_secure(con, false); ret = setup_message_sgs(&sgt, con->out_msg, zerop, zerop, zerop, - &con->v2.out_epil, false); + &con->v2.out_epil, NULL, 0, false); if (ret) goto out; @@ -1825,6 +1927,123 @@ static void prepare_read_data_cont(struct ceph_connection *con) con->v2.in_state = IN_S_HANDLE_EPILOGUE; } +static int prepare_sparse_read_cont(struct ceph_connection *con) +{ + int ret; + struct bio_vec bv; + char *buf = NULL; + struct ceph_msg_data_cursor *cursor = &con->v2.in_cursor; + + WARN_ON(con->v2.in_state != IN_S_PREPARE_SPARSE_DATA_CONT); + + if (iov_iter_is_bvec(&con->v2.in_iter)) { + if (ceph_test_opt(from_msgr(con->msgr), RXBOUNCE)) { + con->in_data_crc = crc32c(con->in_data_crc, + page_address(con->bounce_page), + con->v2.in_bvec.bv_len); + get_bvec_at(cursor, &bv); + memcpy_to_page(bv.bv_page, bv.bv_offset, + page_address(con->bounce_page), + con->v2.in_bvec.bv_len); + } else { + con->in_data_crc = ceph_crc32c_page(con->in_data_crc, + con->v2.in_bvec.bv_page, + con->v2.in_bvec.bv_offset, + con->v2.in_bvec.bv_len); + } + + ceph_msg_data_advance(cursor, con->v2.in_bvec.bv_len); + cursor->sr_resid -= con->v2.in_bvec.bv_len; + dout("%s: advance by 0x%x sr_resid 0x%x\n", __func__, + con->v2.in_bvec.bv_len, cursor->sr_resid); + WARN_ON_ONCE(cursor->sr_resid > cursor->total_resid); + if (cursor->sr_resid) { + get_bvec_at(cursor, &bv); + if (bv.bv_len > cursor->sr_resid) + bv.bv_len = cursor->sr_resid; + if (ceph_test_opt(from_msgr(con->msgr), RXBOUNCE)) { + bv.bv_page = con->bounce_page; + bv.bv_offset = 0; + } + set_in_bvec(con, &bv); + con->v2.data_len_remain -= bv.bv_len; + return 0; + } + } else if (iov_iter_is_kvec(&con->v2.in_iter)) { + /* On first call, we have no kvec so don't compute crc */ + if (con->v2.in_kvec_cnt) { + WARN_ON_ONCE(con->v2.in_kvec_cnt > 1); + con->in_data_crc = crc32c(con->in_data_crc, + con->v2.in_kvecs[0].iov_base, + con->v2.in_kvecs[0].iov_len); + } + } else { + return -EIO; + } + + /* get next extent */ + ret = con->ops->sparse_read(con, cursor, &buf); + if (ret <= 0) { + if (ret < 0) + return ret; + + reset_in_kvecs(con); + add_in_kvec(con, con->v2.in_buf, CEPH_EPILOGUE_PLAIN_LEN); + con->v2.in_state = IN_S_HANDLE_EPILOGUE; + return 0; + } + + if (buf) { + /* receive into buffer */ + reset_in_kvecs(con); + add_in_kvec(con, buf, ret); + con->v2.data_len_remain -= ret; + return 0; + } + + if (ret > cursor->total_resid) { + pr_warn("%s: ret 0x%x total_resid 0x%zx resid 0x%zx\n", + __func__, ret, cursor->total_resid, cursor->resid); + return -EIO; + } + get_bvec_at(cursor, &bv); + if (bv.bv_len > cursor->sr_resid) + bv.bv_len = cursor->sr_resid; + if (ceph_test_opt(from_msgr(con->msgr), RXBOUNCE)) { + if (unlikely(!con->bounce_page)) { + con->bounce_page = alloc_page(GFP_NOIO); + if (!con->bounce_page) { + pr_err("failed to allocate bounce page\n"); + return -ENOMEM; + } + } + + bv.bv_page = con->bounce_page; + bv.bv_offset = 0; + } + set_in_bvec(con, &bv); + con->v2.data_len_remain -= ret; + return ret; +} + +static int prepare_sparse_read_data(struct ceph_connection *con) +{ + struct ceph_msg *msg = con->in_msg; + + dout("%s: starting sparse read\n", __func__); + + if (WARN_ON_ONCE(!con->ops->sparse_read)) + return -EOPNOTSUPP; + + if (!con_secure(con)) + con->in_data_crc = -1; + + reset_in_kvecs(con); + con->v2.in_state = IN_S_PREPARE_SPARSE_DATA_CONT; + con->v2.data_len_remain = data_len(msg); + return prepare_sparse_read_cont(con); +} + static int prepare_read_tail_plain(struct ceph_connection *con) { struct ceph_msg *msg = con->in_msg; @@ -1845,7 +2064,10 @@ static int prepare_read_tail_plain(struct ceph_connection *con) } if (data_len(msg)) { - con->v2.in_state = IN_S_PREPARE_READ_DATA; + if (msg->sparse_read) + con->v2.in_state = IN_S_PREPARE_SPARSE_DATA; + else + con->v2.in_state = IN_S_PREPARE_READ_DATA; } else { add_in_kvec(con, con->v2.in_buf, CEPH_EPILOGUE_PLAIN_LEN); con->v2.in_state = IN_S_HANDLE_EPILOGUE; @@ -2898,6 +3120,12 @@ static int populate_in_iter(struct ceph_connection *con) prepare_read_enc_page(con); ret = 0; break; + case IN_S_PREPARE_SPARSE_DATA: + ret = prepare_sparse_read_data(con); + break; + case IN_S_PREPARE_SPARSE_DATA_CONT: + ret = prepare_sparse_read_cont(con); + break; case IN_S_HANDLE_EPILOGUE: ret = handle_epilogue(con); break; @@ -3489,6 +3717,23 @@ static void revoke_at_prepare_read_enc_page(struct ceph_connection *con) con->v2.in_state = IN_S_FINISH_SKIP; } +static void revoke_at_prepare_sparse_data(struct ceph_connection *con) +{ + int resid; /* current piece of data */ + int remaining; + + WARN_ON(con_secure(con)); + WARN_ON(!data_len(con->in_msg)); + WARN_ON(!iov_iter_is_bvec(&con->v2.in_iter)); + resid = iov_iter_count(&con->v2.in_iter); + dout("%s con %p resid %d\n", __func__, con, resid); + + remaining = CEPH_EPILOGUE_PLAIN_LEN + con->v2.data_len_remain; + con->v2.in_iter.count -= resid; + set_in_skip(con, resid + remaining); + con->v2.in_state = IN_S_FINISH_SKIP; +} + static void revoke_at_handle_epilogue(struct ceph_connection *con) { int resid; @@ -3505,6 +3750,7 @@ static void revoke_at_handle_epilogue(struct ceph_connection *con) void ceph_con_v2_revoke_incoming(struct ceph_connection *con) { switch (con->v2.in_state) { + case IN_S_PREPARE_SPARSE_DATA: case IN_S_PREPARE_READ_DATA: revoke_at_prepare_read_data(con); break; @@ -3514,6 +3760,9 @@ void ceph_con_v2_revoke_incoming(struct ceph_connection *con) case IN_S_PREPARE_READ_ENC_PAGE: revoke_at_prepare_read_enc_page(con); break; + case IN_S_PREPARE_SPARSE_DATA_CONT: + revoke_at_prepare_sparse_data(con); + break; case IN_S_HANDLE_EPILOGUE: revoke_at_handle_epilogue(con); break; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index 658a6f2320cfa17721a6bc395a278729506aef6d..d3a759e052c81f066710a467cc9d9baf3dbf8e20 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -171,6 +171,13 @@ static void ceph_osd_data_bvecs_init(struct ceph_osd_data *osd_data, osd_data->num_bvecs = num_bvecs; } +static void ceph_osd_iter_init(struct ceph_osd_data *osd_data, + struct iov_iter *iter) +{ + osd_data->type = CEPH_OSD_DATA_TYPE_ITER; + osd_data->iter = *iter; +} + static struct ceph_osd_data * osd_req_op_raw_data_in(struct ceph_osd_request *osd_req, unsigned int which) { @@ -264,6 +271,22 @@ void osd_req_op_extent_osd_data_bvec_pos(struct ceph_osd_request *osd_req, } EXPORT_SYMBOL(osd_req_op_extent_osd_data_bvec_pos); +/** + * osd_req_op_extent_osd_iter - Set up an operation with an iterator buffer + * @osd_req: The request to set up + * @which: Index of the operation in which to set the iter + * @iter: The buffer iterator + */ +void osd_req_op_extent_osd_iter(struct ceph_osd_request *osd_req, + unsigned int which, struct iov_iter *iter) +{ + struct ceph_osd_data *osd_data; + + osd_data = osd_req_op_data(osd_req, which, extent, osd_data); + ceph_osd_iter_init(osd_data, iter); +} +EXPORT_SYMBOL(osd_req_op_extent_osd_iter); + static void osd_req_op_cls_request_info_pagelist( struct ceph_osd_request *osd_req, unsigned int which, struct ceph_pagelist *pagelist) @@ -346,6 +369,8 @@ static u64 ceph_osd_data_length(struct ceph_osd_data *osd_data) #endif /* CONFIG_BLOCK */ case CEPH_OSD_DATA_TYPE_BVECS: return osd_data->bvec_pos.iter.bi_size; + case CEPH_OSD_DATA_TYPE_ITER: + return iov_iter_count(&osd_data->iter); default: WARN(true, "unrecognized data type %d\n", (int)osd_data->type); return 0; @@ -376,8 +401,10 @@ static void osd_req_op_data_release(struct ceph_osd_request *osd_req, switch (op->op) { case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_SPARSE_READ: case CEPH_OSD_OP_WRITE: case CEPH_OSD_OP_WRITEFULL: + kfree(op->extent.sparse_ext); ceph_osd_data_release(&op->extent.osd_data); break; case CEPH_OSD_OP_CALL: @@ -669,6 +696,7 @@ static void get_num_data_items(struct ceph_osd_request *req, /* reply */ case CEPH_OSD_OP_STAT: case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_SPARSE_READ: case CEPH_OSD_OP_LIST_WATCHERS: *num_reply_data_items += 1; break; @@ -738,7 +766,7 @@ void osd_req_op_extent_init(struct ceph_osd_request *osd_req, BUG_ON(opcode != CEPH_OSD_OP_READ && opcode != CEPH_OSD_OP_WRITE && opcode != CEPH_OSD_OP_WRITEFULL && opcode != CEPH_OSD_OP_ZERO && - opcode != CEPH_OSD_OP_TRUNCATE); + opcode != CEPH_OSD_OP_TRUNCATE && opcode != CEPH_OSD_OP_SPARSE_READ); op->extent.offset = offset; op->extent.length = length; @@ -951,6 +979,8 @@ static void ceph_osdc_msg_data_add(struct ceph_msg *msg, #endif } else if (osd_data->type == CEPH_OSD_DATA_TYPE_BVECS) { ceph_msg_data_add_bvecs(msg, &osd_data->bvec_pos); + } else if (osd_data->type == CEPH_OSD_DATA_TYPE_ITER) { + ceph_msg_data_add_iter(msg, &osd_data->iter); } else { BUG_ON(osd_data->type != CEPH_OSD_DATA_TYPE_NONE); } @@ -963,6 +993,7 @@ static u32 osd_req_encode_op(struct ceph_osd_op *dst, case CEPH_OSD_OP_STAT: break; case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_SPARSE_READ: case CEPH_OSD_OP_WRITE: case CEPH_OSD_OP_WRITEFULL: case CEPH_OSD_OP_ZERO: @@ -1017,6 +1048,10 @@ static u32 osd_req_encode_op(struct ceph_osd_op *dst, dst->copy_from.src_fadvise_flags = cpu_to_le32(src->copy_from.src_fadvise_flags); break; + case CEPH_OSD_OP_ASSERT_VER: + dst->assert_ver.unused = cpu_to_le64(0); + dst->assert_ver.ver = cpu_to_le64(src->assert_ver.ver); + break; default: pr_err("unsupported osd opcode %s\n", ceph_osd_op_name(src->op)); @@ -1059,7 +1094,8 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, BUG_ON(opcode != CEPH_OSD_OP_READ && opcode != CEPH_OSD_OP_WRITE && opcode != CEPH_OSD_OP_ZERO && opcode != CEPH_OSD_OP_TRUNCATE && - opcode != CEPH_OSD_OP_CREATE && opcode != CEPH_OSD_OP_DELETE); + opcode != CEPH_OSD_OP_CREATE && opcode != CEPH_OSD_OP_DELETE && + opcode != CEPH_OSD_OP_SPARSE_READ); req = ceph_osdc_alloc_request(osdc, snapc, num_ops, use_mempool, GFP_NOFS); @@ -1100,15 +1136,30 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, if (flags & CEPH_OSD_FLAG_WRITE) req->r_data_offset = off; - if (num_ops > 1) + if (num_ops > 1) { + int num_req_ops, num_rep_ops; + /* - * This is a special case for ceph_writepages_start(), but it - * also covers ceph_uninline_data(). If more multi-op request - * use cases emerge, we will need a separate helper. + * If this is a multi-op write request, assume that we'll need + * request ops. If it's a multi-op read then assume we'll need + * reply ops. Anything else and call it -EINVAL. */ - r = __ceph_osdc_alloc_messages(req, GFP_NOFS, num_ops, 0); - else + if (flags & CEPH_OSD_FLAG_WRITE) { + num_req_ops = num_ops; + num_rep_ops = 0; + } else if (flags & CEPH_OSD_FLAG_READ) { + num_req_ops = 0; + num_rep_ops = num_ops; + } else { + r = -EINVAL; + goto fail; + } + + r = __ceph_osdc_alloc_messages(req, GFP_NOFS, num_req_ops, + num_rep_ops); + } else { r = ceph_osdc_alloc_messages(req, GFP_NOFS); + } if (r) goto fail; @@ -1120,6 +1171,18 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc, } EXPORT_SYMBOL(ceph_osdc_new_request); +int __ceph_alloc_sparse_ext_map(struct ceph_osd_req_op *op, int cnt) +{ + op->extent.sparse_ext_cnt = cnt; + op->extent.sparse_ext = kmalloc_array(cnt, + sizeof(*op->extent.sparse_ext), + GFP_NOFS); + if (!op->extent.sparse_ext) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(__ceph_alloc_sparse_ext_map); + /* * We keep osd requests in an rbtree, sorted by ->r_tid. */ @@ -1177,6 +1240,7 @@ static void osd_init(struct ceph_osd *osd) { refcount_set(&osd->o_ref, 1); RB_CLEAR_NODE(&osd->o_node); + spin_lock_init(&osd->o_requests_lock); osd->o_requests = RB_ROOT; osd->o_linger_requests = RB_ROOT; osd->o_backoff_mappings = RB_ROOT; @@ -1187,6 +1251,13 @@ static void osd_init(struct ceph_osd *osd) mutex_init(&osd->lock); } +static void ceph_init_sparse_read(struct ceph_sparse_read *sr) +{ + kfree(sr->sr_extent); + memset(sr, '\0', sizeof(*sr)); + sr->sr_state = CEPH_SPARSE_READ_HDR; +} + static void osd_cleanup(struct ceph_osd *osd) { WARN_ON(!RB_EMPTY_NODE(&osd->o_node)); @@ -1197,6 +1268,8 @@ static void osd_cleanup(struct ceph_osd *osd) WARN_ON(!list_empty(&osd->o_osd_lru)); WARN_ON(!list_empty(&osd->o_keepalive_item)); + ceph_init_sparse_read(&osd->o_sparse_read); + if (osd->o_auth.authorizer) { WARN_ON(osd_homeless(osd)); ceph_auth_destroy_authorizer(osd->o_auth.authorizer); @@ -1216,6 +1289,9 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum) osd_init(osd); osd->o_osdc = osdc; osd->o_osd = onum; + osd->o_sparse_op_idx = -1; + + ceph_init_sparse_read(&osd->o_sparse_read); ceph_con_init(&osd->o_con, osd, &osd_con_ops, &osdc->client->msgr); @@ -1406,7 +1482,9 @@ static void link_request(struct ceph_osd *osd, struct ceph_osd_request *req) atomic_inc(&osd->o_osdc->num_homeless); get_osd(osd); + spin_lock(&osd->o_requests_lock); insert_request(&osd->o_requests, req); + spin_unlock(&osd->o_requests_lock); req->r_osd = osd; } @@ -1418,7 +1496,9 @@ static void unlink_request(struct ceph_osd *osd, struct ceph_osd_request *req) req, req->r_tid); req->r_osd = NULL; + spin_lock(&osd->o_requests_lock); erase_request(&osd->o_requests, req); + spin_unlock(&osd->o_requests_lock); put_osd(osd); if (!osd_homeless(osd)) @@ -2016,6 +2096,7 @@ static void setup_request_data(struct ceph_osd_request *req) &op->raw_data_in); break; case CEPH_OSD_OP_READ: + case CEPH_OSD_OP_SPARSE_READ: ceph_osdc_msg_data_add(reply_msg, &op->extent.osd_data); break; @@ -2435,8 +2516,10 @@ static void finish_request(struct ceph_osd_request *req) req->r_end_latency = ktime_get(); - if (req->r_osd) + if (req->r_osd) { + ceph_init_sparse_read(&req->r_osd->o_sparse_read); unlink_request(req->r_osd, req); + } atomic_dec(&osdc->num_requests); /* @@ -3795,6 +3878,7 @@ static void handle_reply(struct ceph_osd *osd, struct ceph_msg *msg) * one (type of) reply back. */ WARN_ON(!(m.flags & CEPH_OSD_FLAG_ONDISK)); + req->r_version = m.user_version; req->r_result = m.result ?: data_len; finish_request(req); mutex_unlock(&osd->lock); @@ -5348,6 +5432,24 @@ static void osd_dispatch(struct ceph_connection *con, struct ceph_msg *msg) ceph_msg_put(msg); } +/* How much sparse data was requested? */ +static u64 sparse_data_requested(struct ceph_osd_request *req) +{ + u64 len = 0; + + if (req->r_flags & CEPH_OSD_FLAG_READ) { + int i; + + for (i = 0; i < req->r_num_ops; ++i) { + struct ceph_osd_req_op *op = &req->r_ops[i]; + + if (op->op == CEPH_OSD_OP_SPARSE_READ) + len += op->extent.length; + } + } + return len; +} + /* * Lookup and return message for incoming reply. Don't try to do * anything about a larger than preallocated data portion of the @@ -5364,6 +5466,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, int front_len = le32_to_cpu(hdr->front_len); int data_len = le32_to_cpu(hdr->data_len); u64 tid = le64_to_cpu(hdr->tid); + u64 srlen; down_read(&osdc->lock); if (!osd_registered(osd)) { @@ -5396,7 +5499,8 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, req->r_reply = m; } - if (data_len > req->r_reply->data_length) { + srlen = sparse_data_requested(req); + if (!srlen && data_len > req->r_reply->data_length) { pr_warn("%s osd%d tid %llu data %d > preallocated %zu, skipping\n", __func__, osd->o_osd, req->r_tid, data_len, req->r_reply->data_length); @@ -5406,6 +5510,8 @@ static struct ceph_msg *get_reply(struct ceph_connection *con, } m = ceph_msg_get(req->r_reply); + m->sparse_read = (bool)srlen; + dout("get_reply tid %lld %p\n", tid, m); out_unlock_session: @@ -5638,9 +5744,217 @@ static int osd_check_message_signature(struct ceph_msg *msg) return ceph_auth_check_message_signature(auth, msg); } +static void advance_cursor(struct ceph_msg_data_cursor *cursor, size_t len, + bool zero) +{ + while (len) { + struct page *page; + size_t poff, plen; + + page = ceph_msg_data_next(cursor, &poff, &plen); + if (plen > len) + plen = len; + if (zero) + zero_user_segment(page, poff, poff + plen); + len -= plen; + ceph_msg_data_advance(cursor, plen); + } +} + +static int prep_next_sparse_read(struct ceph_connection *con, + struct ceph_msg_data_cursor *cursor) +{ + struct ceph_osd *o = con->private; + struct ceph_sparse_read *sr = &o->o_sparse_read; + struct ceph_osd_request *req; + struct ceph_osd_req_op *op; + + spin_lock(&o->o_requests_lock); + req = lookup_request(&o->o_requests, le64_to_cpu(con->in_msg->hdr.tid)); + if (!req) { + spin_unlock(&o->o_requests_lock); + return -EBADR; + } + + if (o->o_sparse_op_idx < 0) { + u64 srlen = sparse_data_requested(req); + + dout("%s: [%d] starting new sparse read req. srlen=0x%llx\n", + __func__, o->o_osd, srlen); + ceph_msg_data_cursor_init(cursor, con->in_msg, srlen); + } else { + u64 end; + + op = &req->r_ops[o->o_sparse_op_idx]; + + WARN_ON_ONCE(op->extent.sparse_ext); + + /* hand back buffer we took earlier */ + op->extent.sparse_ext = sr->sr_extent; + sr->sr_extent = NULL; + op->extent.sparse_ext_cnt = sr->sr_count; + sr->sr_ext_len = 0; + dout("%s: [%d] completed extent array len %d cursor->resid %zd\n", + __func__, o->o_osd, op->extent.sparse_ext_cnt, cursor->resid); + /* Advance to end of data for this operation */ + end = ceph_sparse_ext_map_end(op); + if (end < sr->sr_req_len) + advance_cursor(cursor, sr->sr_req_len - end, false); + } + + ceph_init_sparse_read(sr); + + /* find next op in this request (if any) */ + while (++o->o_sparse_op_idx < req->r_num_ops) { + op = &req->r_ops[o->o_sparse_op_idx]; + if (op->op == CEPH_OSD_OP_SPARSE_READ) + goto found; + } + + /* reset for next sparse read request */ + spin_unlock(&o->o_requests_lock); + o->o_sparse_op_idx = -1; + return 0; +found: + sr->sr_req_off = op->extent.offset; + sr->sr_req_len = op->extent.length; + sr->sr_pos = sr->sr_req_off; + dout("%s: [%d] new sparse read op at idx %d 0x%llx~0x%llx\n", __func__, + o->o_osd, o->o_sparse_op_idx, sr->sr_req_off, sr->sr_req_len); + + /* hand off request's sparse extent map buffer */ + sr->sr_ext_len = op->extent.sparse_ext_cnt; + op->extent.sparse_ext_cnt = 0; + sr->sr_extent = op->extent.sparse_ext; + op->extent.sparse_ext = NULL; + + spin_unlock(&o->o_requests_lock); + return 1; +} + +#ifdef __BIG_ENDIAN +static inline void convert_extent_map(struct ceph_sparse_read *sr) +{ + int i; + + for (i = 0; i < sr->sr_count; i++) { + struct ceph_sparse_extent *ext = &sr->sr_extent[i]; + + ext->off = le64_to_cpu((__force __le64)ext->off); + ext->len = le64_to_cpu((__force __le64)ext->len); + } +} +#else +static inline void convert_extent_map(struct ceph_sparse_read *sr) +{ +} +#endif + +#define MAX_EXTENTS 4096 + +static int osd_sparse_read(struct ceph_connection *con, + struct ceph_msg_data_cursor *cursor, + char **pbuf) +{ + struct ceph_osd *o = con->private; + struct ceph_sparse_read *sr = &o->o_sparse_read; + u32 count = sr->sr_count; + u64 eoff, elen; + int ret; + + switch (sr->sr_state) { + case CEPH_SPARSE_READ_HDR: +next_op: + ret = prep_next_sparse_read(con, cursor); + if (ret <= 0) + return ret; + + /* number of extents */ + ret = sizeof(sr->sr_count); + *pbuf = (char *)&sr->sr_count; + sr->sr_state = CEPH_SPARSE_READ_EXTENTS; + break; + case CEPH_SPARSE_READ_EXTENTS: + /* Convert sr_count to host-endian */ + count = le32_to_cpu((__force __le32)sr->sr_count); + sr->sr_count = count; + dout("[%d] got %u extents\n", o->o_osd, count); + + if (count > 0) { + if (!sr->sr_extent || count > sr->sr_ext_len) { + /* + * Apply a hard cap to the number of extents. + * If we have more, assume something is wrong. + */ + if (count > MAX_EXTENTS) { + dout("%s: OSD returned 0x%x extents in a single reply!\n", + __func__, count); + return -EREMOTEIO; + } + + /* no extent array provided, or too short */ + kfree(sr->sr_extent); + sr->sr_extent = kmalloc_array(count, + sizeof(*sr->sr_extent), + GFP_NOIO); + if (!sr->sr_extent) + return -ENOMEM; + sr->sr_ext_len = count; + } + ret = count * sizeof(*sr->sr_extent); + *pbuf = (char *)sr->sr_extent; + sr->sr_state = CEPH_SPARSE_READ_DATA_LEN; + break; + } + /* No extents? Read data len */ + fallthrough; + case CEPH_SPARSE_READ_DATA_LEN: + convert_extent_map(sr); + ret = sizeof(sr->sr_datalen); + *pbuf = (char *)&sr->sr_datalen; + sr->sr_state = CEPH_SPARSE_READ_DATA; + break; + case CEPH_SPARSE_READ_DATA: + if (sr->sr_index >= count) { + sr->sr_state = CEPH_SPARSE_READ_HDR; + goto next_op; + } + + eoff = sr->sr_extent[sr->sr_index].off; + elen = sr->sr_extent[sr->sr_index].len; + + dout("[%d] ext %d off 0x%llx len 0x%llx\n", + o->o_osd, sr->sr_index, eoff, elen); + + if (elen > INT_MAX) { + dout("Sparse read extent length too long (0x%llx)\n", + elen); + return -EREMOTEIO; + } + + /* zero out anything from sr_pos to start of extent */ + if (sr->sr_pos < eoff) + advance_cursor(cursor, eoff - sr->sr_pos, true); + + /* Set position to end of extent */ + sr->sr_pos = eoff + elen; + + /* send back the new length and nullify the ptr */ + cursor->sr_resid = elen; + ret = elen; + *pbuf = NULL; + + /* Bump the array index */ + ++sr->sr_index; + break; + } + return ret; +} + static const struct ceph_connection_operations osd_con_ops = { .get = osd_get_con, .put = osd_put_con, + .sparse_read = osd_sparse_read, .alloc_msg = osd_alloc_msg, .dispatch = osd_dispatch, .fault = osd_fault, diff --git a/net/core/dev.c b/net/core/dev.c index ccff2b6ef9583eb2f284111b7294794779f7ba35..85df22f05c38b663f050410b9f7bcd32dd781951 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -69,7 +69,7 @@ */ #include -#include +#include #include #include #include @@ -1080,7 +1080,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) return -EINVAL; /* Use one page as a bit array of possible slots */ - inuse = (unsigned long *) get_zeroed_page(GFP_ATOMIC); + inuse = bitmap_zalloc(max_netdevices, GFP_ATOMIC); if (!inuse) return -ENOMEM; @@ -1109,7 +1109,7 @@ static int __dev_alloc_name(struct net *net, const char *name, char *buf) } i = find_first_zero_bit(inuse, max_netdevices); - free_page((unsigned long) inuse); + bitmap_free(inuse); } snprintf(buf, IFNAMSIZ, name, i); diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 89d15ceaf9afd40cbcb4e2dbf8ab0f9bff14c7d8..272f09251343da46a1b3dbc6b1499c52c83d7725 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -1446,7 +1446,7 @@ bool __skb_flow_dissect(const struct net *net, break; } - nhoff += ntohs(hdr->message_length); + nhoff += sizeof(struct ptp_header); fdret = FLOW_DISSECT_RET_OUT_GOOD; break; } @@ -1831,8 +1831,7 @@ u32 __skb_get_hash_symmetric(const struct sk_buff *skb) memset(&keys, 0, sizeof(keys)); __skb_flow_dissect(NULL, skb, &flow_keys_dissector_symmetric, - &keys, NULL, 0, 0, 0, - FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL); + &keys, NULL, 0, 0, 0, 0); return __flow_hash_from_keys(&keys, &hashrnd); } diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 6b76cd103195374f73786b7bb94fb2f4051a3c73..9c09f091cbffe59043ce4614423d7dce7d4ec42a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -410,7 +410,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, */ __skb_queue_purge(&n->arp_queue); n->arp_queue_len_bytes = 0; - n->output = neigh_blackhole; + WRITE_ONCE(n->output, neigh_blackhole); if (n->nud_state & NUD_VALID) n->nud_state = NUD_NOARP; else @@ -920,7 +920,7 @@ static void neigh_suspect(struct neighbour *neigh) { neigh_dbg(2, "neigh %p is suspected\n", neigh); - neigh->output = neigh->ops->output; + WRITE_ONCE(neigh->output, neigh->ops->output); } /* Neighbour state is OK; @@ -932,7 +932,7 @@ static void neigh_connect(struct neighbour *neigh) { neigh_dbg(2, "neigh %p is connected\n", neigh); - neigh->output = neigh->ops->connected_output; + WRITE_ONCE(neigh->output, neigh->ops->connected_output); } static void neigh_periodic_work(struct work_struct *work) @@ -988,7 +988,9 @@ static void neigh_periodic_work(struct work_struct *work) (state == NUD_FAILED || !time_in_range_open(jiffies, n->used, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { - *np = n->next; + rcu_assign_pointer(*np, + rcu_dereference_protected(n->next, + lockdep_is_held(&tbl->lock))); neigh_mark_dead(n); write_unlock(&n->lock); neigh_cleanup_and_release(n); @@ -1447,7 +1449,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, if (n2) n1 = n2; } - n1->output(n1, skb); + READ_ONCE(n1->output)(n1, skb); if (n2) neigh_release(n2); rcu_read_unlock(); @@ -3153,7 +3155,7 @@ int neigh_xmit(int index, struct net_device *dev, rcu_read_unlock(); goto out_kfree_skb; } - err = neigh->output(neigh, skb); + err = READ_ONCE(neigh->output)(neigh, skb); rcu_read_unlock(); } else if (index == NEIGH_LINK_TABLE) { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 45707059082f2d706a9820f4484d0d4636aaa930..4eaf7ed0d1f44e8305109da2da2835013786a857 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -550,7 +550,7 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node, bool *pfmemalloc) { bool ret_pfmemalloc = false; - unsigned int obj_size; + size_t obj_size; void *obj; obj_size = SKB_HEAD_ALIGN(*size); @@ -567,7 +567,13 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node, obj = kmem_cache_alloc_node(skb_small_head_cache, flags, node); goto out; } - *size = obj_size = kmalloc_size_roundup(obj_size); + + obj_size = kmalloc_size_roundup(obj_size); + /* The following cast might truncate high-order bits of obj_size, this + * is harmless because kmalloc(obj_size >= 2^32) will fail anyway. + */ + *size = (unsigned int)obj_size; + /* * Try a regular allocation, when that fails and we're not entitled * to the reserves, fail. @@ -4423,21 +4429,20 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, struct sk_buff *segs = NULL; struct sk_buff *tail = NULL; struct sk_buff *list_skb = skb_shinfo(head_skb)->frag_list; - skb_frag_t *frag = skb_shinfo(head_skb)->frags; unsigned int mss = skb_shinfo(head_skb)->gso_size; unsigned int doffset = head_skb->data - skb_mac_header(head_skb); - struct sk_buff *frag_skb = head_skb; unsigned int offset = doffset; unsigned int tnl_hlen = skb_tnl_header_len(head_skb); unsigned int partial_segs = 0; unsigned int headroom; unsigned int len = head_skb->len; + struct sk_buff *frag_skb; + skb_frag_t *frag; __be16 proto; bool csum, sg; - int nfrags = skb_shinfo(head_skb)->nr_frags; int err = -ENOMEM; int i = 0; - int pos; + int nfrags, pos; if ((skb_shinfo(head_skb)->gso_type & SKB_GSO_DODGY) && mss != GSO_BY_FRAGS && mss != skb_headlen(head_skb)) { @@ -4514,6 +4519,13 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, headroom = skb_headroom(head_skb); pos = skb_headlen(head_skb); + if (skb_orphan_frags(head_skb, GFP_ATOMIC)) + return ERR_PTR(-ENOMEM); + + nfrags = skb_shinfo(head_skb)->nr_frags; + frag = skb_shinfo(head_skb)->frags; + frag_skb = head_skb; + do { struct sk_buff *nskb; skb_frag_t *nskb_frag; @@ -4534,6 +4546,10 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, (skb_headlen(list_skb) == len || sg)) { BUG_ON(skb_headlen(list_skb) > len); + nskb = skb_clone(list_skb, GFP_ATOMIC); + if (unlikely(!nskb)) + goto err; + i = 0; nfrags = skb_shinfo(list_skb)->nr_frags; frag = skb_shinfo(list_skb)->frags; @@ -4552,12 +4568,8 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, frag++; } - nskb = skb_clone(list_skb, GFP_ATOMIC); list_skb = list_skb->next; - if (unlikely(!nskb)) - goto err; - if (unlikely(pskb_trim(nskb, len))) { kfree_skb(nskb); goto err; @@ -4633,12 +4645,16 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, skb_shinfo(nskb)->flags |= skb_shinfo(head_skb)->flags & SKBFL_SHARED_FRAG; - if (skb_orphan_frags(frag_skb, GFP_ATOMIC) || - skb_zerocopy_clone(nskb, frag_skb, GFP_ATOMIC)) + if (skb_zerocopy_clone(nskb, frag_skb, GFP_ATOMIC)) goto err; while (pos < offset + len) { if (i >= nfrags) { + if (skb_orphan_frags(list_skb, GFP_ATOMIC) || + skb_zerocopy_clone(nskb, list_skb, + GFP_ATOMIC)) + goto err; + i = 0; nfrags = skb_shinfo(list_skb)->nr_frags; frag = skb_shinfo(list_skb)->frags; @@ -4652,10 +4668,6 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb, i--; frag--; } - if (skb_orphan_frags(frag_skb, GFP_ATOMIC) || - skb_zerocopy_clone(nskb, frag_skb, - GFP_ATOMIC)) - goto err; list_skb = list_skb->next; } @@ -5207,7 +5219,7 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb, serr->ee.ee_info = tstype; serr->opt_stats = opt_stats; serr->header.h4.iif = skb->dev ? skb->dev->ifindex : 0; - if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) { + if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) { serr->ee.ee_data = skb_shinfo(skb)->tskey; if (sk_is_tcp(sk)) serr->ee.ee_data -= atomic_read(&sk->sk_tskey); @@ -5263,21 +5275,23 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb, { struct sk_buff *skb; bool tsonly, opt_stats = false; + u32 tsflags; if (!sk) return; - if (!hwtstamps && !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) && + tsflags = READ_ONCE(sk->sk_tsflags); + if (!hwtstamps && !(tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) && skb_shinfo(orig_skb)->tx_flags & SKBTX_IN_PROGRESS) return; - tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY; + tsonly = tsflags & SOF_TIMESTAMPING_OPT_TSONLY; if (!skb_may_tx_timestamp(sk, tsonly)) return; if (tsonly) { #ifdef CONFIG_INET - if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS) && + if ((tsflags & SOF_TIMESTAMPING_OPT_STATS) && sk_is_tcp(sk)) { skb = tcp_get_timestamping_opt_stats(sk, orig_skb, ack_skb); diff --git a/net/core/skmsg.c b/net/core/skmsg.c index a0659fc29bcca4eb3a2e1344a72b5033664115b4..6c31eefbd77786ba3651f79313af867883210654 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -612,12 +612,18 @@ static int sk_psock_skb_ingress_self(struct sk_psock *psock, struct sk_buff *skb static int sk_psock_handle_skb(struct sk_psock *psock, struct sk_buff *skb, u32 off, u32 len, bool ingress) { + int err = 0; + if (!ingress) { if (!sock_writeable(psock->sk)) return -EAGAIN; return skb_send_sock(psock->sk, skb, off, len); } - return sk_psock_skb_ingress(psock, skb, off, len); + skb_get(skb); + err = sk_psock_skb_ingress(psock, skb, off, len); + if (err < 0) + kfree_skb(skb); + return err; } static void sk_psock_skb_state(struct sk_psock *psock, @@ -685,9 +691,7 @@ static void sk_psock_backlog(struct work_struct *work) } while (len); skb = skb_dequeue(&psock->ingress_skb); - if (!ingress) { - kfree_skb(skb); - } + kfree_skb(skb); } end: mutex_unlock(&psock->work_mutex); diff --git a/net/core/sock.c b/net/core/sock.c index 666a17cab4f5605076af3f929864d03bc5e90058..16584e2dd6481a3fc28d796db785439f0446703b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -765,7 +765,8 @@ bool sk_mc_loop(struct sock *sk) return false; if (!sk) return true; - switch (sk->sk_family) { + /* IPV6_ADDRFORM can change sk->sk_family under us. */ + switch (READ_ONCE(sk->sk_family)) { case AF_INET: return inet_test_bit(MC_LOOP, sk); #if IS_ENABLED(CONFIG_IPV6) @@ -893,7 +894,7 @@ static int sock_timestamping_bind_phc(struct sock *sk, int phc_index) if (!match) return -EINVAL; - sk->sk_bind_phc = phc_index; + WRITE_ONCE(sk->sk_bind_phc, phc_index); return 0; } @@ -936,7 +937,7 @@ int sock_set_timestamping(struct sock *sk, int optname, return ret; } - sk->sk_tsflags = val; + WRITE_ONCE(sk->sk_tsflags, val); sock_valbool_flag(sk, SOCK_TSTAMP_NEW, optname == SO_TIMESTAMPING_NEW); if (val & SOF_TIMESTAMPING_RX_SOFTWARE) @@ -1044,7 +1045,7 @@ static int sock_reserve_memory(struct sock *sk, int bytes) mem_cgroup_uncharge_skmem(sk->sk_memcg, pages); return -ENOMEM; } - sk->sk_forward_alloc += pages << PAGE_SHIFT; + sk_forward_alloc_add(sk, pages << PAGE_SHIFT); WRITE_ONCE(sk->sk_reserved_mem, sk->sk_reserved_mem + (pages << PAGE_SHIFT)); @@ -1718,8 +1719,8 @@ int sk_getsockopt(struct sock *sk, int level, int optname, case SO_TIMESTAMPING_OLD: lv = sizeof(v.timestamping); - v.timestamping.flags = sk->sk_tsflags; - v.timestamping.bind_phc = sk->sk_bind_phc; + v.timestamping.flags = READ_ONCE(sk->sk_tsflags); + v.timestamping.bind_phc = READ_ONCE(sk->sk_bind_phc); break; case SO_RCVTIMEO_OLD: @@ -2746,9 +2747,9 @@ static long sock_wait_for_wmem(struct sock *sk, long timeo) prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); if (refcount_read(&sk->sk_wmem_alloc) < READ_ONCE(sk->sk_sndbuf)) break; - if (sk->sk_shutdown & SEND_SHUTDOWN) + if (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) break; - if (sk->sk_err) + if (READ_ONCE(sk->sk_err)) break; timeo = schedule_timeout(timeo); } @@ -2776,7 +2777,7 @@ struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, goto failure; err = -EPIPE; - if (sk->sk_shutdown & SEND_SHUTDOWN) + if (READ_ONCE(sk->sk_shutdown) & SEND_SHUTDOWN) goto failure; if (sk_wmem_alloc_get(sk) < READ_ONCE(sk->sk_sndbuf)) @@ -3138,10 +3139,10 @@ int __sk_mem_schedule(struct sock *sk, int size, int kind) { int ret, amt = sk_mem_pages(size); - sk->sk_forward_alloc += amt << PAGE_SHIFT; + sk_forward_alloc_add(sk, amt << PAGE_SHIFT); ret = __sk_mem_raise_allocated(sk, size, amt, kind); if (!ret) - sk->sk_forward_alloc -= amt << PAGE_SHIFT; + sk_forward_alloc_add(sk, -(amt << PAGE_SHIFT)); return ret; } EXPORT_SYMBOL(__sk_mem_schedule); @@ -3173,7 +3174,7 @@ void __sk_mem_reduce_allocated(struct sock *sk, int amount) void __sk_mem_reclaim(struct sock *sk, int amount) { amount >>= PAGE_SHIFT; - sk->sk_forward_alloc -= amount << PAGE_SHIFT; + sk_forward_alloc_add(sk, -(amount << PAGE_SHIFT)); __sk_mem_reduce_allocated(sk, amount); } EXPORT_SYMBOL(__sk_mem_reclaim); @@ -3742,7 +3743,7 @@ void sk_get_meminfo(const struct sock *sk, u32 *mem) mem[SK_MEMINFO_RCVBUF] = READ_ONCE(sk->sk_rcvbuf); mem[SK_MEMINFO_WMEM_ALLOC] = sk_wmem_alloc_get(sk); mem[SK_MEMINFO_SNDBUF] = READ_ONCE(sk->sk_sndbuf); - mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; + mem[SK_MEMINFO_FWD_ALLOC] = sk_forward_alloc_get(sk); mem[SK_MEMINFO_WMEM_QUEUED] = READ_ONCE(sk->sk_wmem_queued); mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); mem[SK_MEMINFO_BACKLOG] = READ_ONCE(sk->sk_backlog.len); diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 8f07fea39d9ea471adc141a8ab2de395de59cd72..4292c2ed18286683bd2dc536b9d0d2dec5866fba 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -18,7 +18,7 @@ struct bpf_stab { struct bpf_map map; struct sock **sks; struct sk_psock_progs progs; - raw_spinlock_t lock; + spinlock_t lock; }; #define SOCK_CREATE_FLAG_MASK \ @@ -44,7 +44,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr) return ERR_PTR(-ENOMEM); bpf_map_init_from_attr(&stab->map, attr); - raw_spin_lock_init(&stab->lock); + spin_lock_init(&stab->lock); stab->sks = bpf_map_area_alloc((u64) stab->map.max_entries * sizeof(struct sock *), @@ -411,7 +411,7 @@ static int __sock_map_delete(struct bpf_stab *stab, struct sock *sk_test, struct sock *sk; int err = 0; - raw_spin_lock_bh(&stab->lock); + spin_lock_bh(&stab->lock); sk = *psk; if (!sk_test || sk_test == sk) sk = xchg(psk, NULL); @@ -421,7 +421,7 @@ static int __sock_map_delete(struct bpf_stab *stab, struct sock *sk_test, else err = -EINVAL; - raw_spin_unlock_bh(&stab->lock); + spin_unlock_bh(&stab->lock); return err; } @@ -487,7 +487,7 @@ static int sock_map_update_common(struct bpf_map *map, u32 idx, psock = sk_psock(sk); WARN_ON_ONCE(!psock); - raw_spin_lock_bh(&stab->lock); + spin_lock_bh(&stab->lock); osk = stab->sks[idx]; if (osk && flags == BPF_NOEXIST) { ret = -EEXIST; @@ -501,10 +501,10 @@ static int sock_map_update_common(struct bpf_map *map, u32 idx, stab->sks[idx] = sk; if (osk) sock_map_unref(osk, &stab->sks[idx]); - raw_spin_unlock_bh(&stab->lock); + spin_unlock_bh(&stab->lock); return 0; out_unlock: - raw_spin_unlock_bh(&stab->lock); + spin_unlock_bh(&stab->lock); if (psock) sk_psock_put(sk, psock); out_free: @@ -668,6 +668,8 @@ BPF_CALL_4(bpf_msg_redirect_map, struct sk_msg *, msg, sk = __sock_map_lookup_elem(map, key); if (unlikely(!sk || !sock_map_redirect_allowed(sk))) return SK_DROP; + if (!(flags & BPF_F_INGRESS) && !sk_is_tcp(sk)) + return SK_DROP; msg->flags = flags; msg->sk_redir = sk; @@ -835,7 +837,7 @@ struct bpf_shtab_elem { struct bpf_shtab_bucket { struct hlist_head head; - raw_spinlock_t lock; + spinlock_t lock; }; struct bpf_shtab { @@ -910,7 +912,7 @@ static void sock_hash_delete_from_link(struct bpf_map *map, struct sock *sk, * is okay since it's going away only after RCU grace period. * However, we need to check whether it's still present. */ - raw_spin_lock_bh(&bucket->lock); + spin_lock_bh(&bucket->lock); elem_probe = sock_hash_lookup_elem_raw(&bucket->head, elem->hash, elem->key, map->key_size); if (elem_probe && elem_probe == elem) { @@ -918,7 +920,7 @@ static void sock_hash_delete_from_link(struct bpf_map *map, struct sock *sk, sock_map_unref(elem->sk, elem); sock_hash_free_elem(htab, elem); } - raw_spin_unlock_bh(&bucket->lock); + spin_unlock_bh(&bucket->lock); } static long sock_hash_delete_elem(struct bpf_map *map, void *key) @@ -932,7 +934,7 @@ static long sock_hash_delete_elem(struct bpf_map *map, void *key) hash = sock_hash_bucket_hash(key, key_size); bucket = sock_hash_select_bucket(htab, hash); - raw_spin_lock_bh(&bucket->lock); + spin_lock_bh(&bucket->lock); elem = sock_hash_lookup_elem_raw(&bucket->head, hash, key, key_size); if (elem) { hlist_del_rcu(&elem->node); @@ -940,7 +942,7 @@ static long sock_hash_delete_elem(struct bpf_map *map, void *key) sock_hash_free_elem(htab, elem); ret = 0; } - raw_spin_unlock_bh(&bucket->lock); + spin_unlock_bh(&bucket->lock); return ret; } @@ -1000,7 +1002,7 @@ static int sock_hash_update_common(struct bpf_map *map, void *key, hash = sock_hash_bucket_hash(key, key_size); bucket = sock_hash_select_bucket(htab, hash); - raw_spin_lock_bh(&bucket->lock); + spin_lock_bh(&bucket->lock); elem = sock_hash_lookup_elem_raw(&bucket->head, hash, key, key_size); if (elem && flags == BPF_NOEXIST) { ret = -EEXIST; @@ -1026,10 +1028,10 @@ static int sock_hash_update_common(struct bpf_map *map, void *key, sock_map_unref(elem->sk, elem); sock_hash_free_elem(htab, elem); } - raw_spin_unlock_bh(&bucket->lock); + spin_unlock_bh(&bucket->lock); return 0; out_unlock: - raw_spin_unlock_bh(&bucket->lock); + spin_unlock_bh(&bucket->lock); sk_psock_put(sk, psock); out_free: sk_psock_free_link(link); @@ -1115,7 +1117,7 @@ static struct bpf_map *sock_hash_alloc(union bpf_attr *attr) for (i = 0; i < htab->buckets_num; i++) { INIT_HLIST_HEAD(&htab->buckets[i].head); - raw_spin_lock_init(&htab->buckets[i].lock); + spin_lock_init(&htab->buckets[i].lock); } return &htab->map; @@ -1147,11 +1149,11 @@ static void sock_hash_free(struct bpf_map *map) * exists, psock exists and holds a ref to socket. That * lets us to grab a socket ref too. */ - raw_spin_lock_bh(&bucket->lock); + spin_lock_bh(&bucket->lock); hlist_for_each_entry(elem, &bucket->head, node) sock_hold(elem->sk); hlist_move_list(&bucket->head, &unlink_list); - raw_spin_unlock_bh(&bucket->lock); + spin_unlock_bh(&bucket->lock); /* Process removed entries out of atomic context to * block for socket lock before deleting the psock's @@ -1267,6 +1269,8 @@ BPF_CALL_4(bpf_msg_redirect_hash, struct sk_msg *, msg, sk = __sock_hash_lookup_elem(map, key); if (unlikely(!sk || !sock_map_redirect_allowed(sk))) return SK_DROP; + if (!(flags & BPF_F_INGRESS) && !sk_is_tcp(sk)) + return SK_DROP; msg->flags = flags; msg->sk_redir = sk; diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 8f56e8723c7386c9f9344f1376823bfd0077c8c2..69453b936bd557c77a790a27ff64cc91e5a58296 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -254,13 +254,8 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info) int err; struct net *net = dev_net(skb->dev); - /* For the first __dccp_basic_hdr_len() check, we only need dh->dccph_x, - * which is in byte 7 of the dccp header. - * Our caller (icmp_socket_deliver()) already pulled 8 bytes for us. - * - * Later on, we want to access the sequence number fields, which are - * beyond 8 bytes, so we have to pskb_may_pull() ourselves. - */ + if (!pskb_may_pull(skb, offset + sizeof(*dh))) + return -EINVAL; dh = (struct dccp_hdr *)(skb->data + offset); if (!pskb_may_pull(skb, offset + __dccp_basic_hdr_len(dh))) return -EINVAL; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 33f6ccf6ba77b9bcc24054b09857aaee4bb71acf..c693a570682fba2ad93c7bceb8788bd9d51a0b41 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -83,13 +83,8 @@ static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, __u64 seq; struct net *net = dev_net(skb->dev); - /* For the first __dccp_basic_hdr_len() check, we only need dh->dccph_x, - * which is in byte 7 of the dccp header. - * Our caller (icmpv6_notify()) already pulled 8 bytes for us. - * - * Later on, we want to access the sequence number fields, which are - * beyond 8 bytes, so we have to pskb_may_pull() ourselves. - */ + if (!pskb_may_pull(skb, offset + sizeof(*dh))) + return -EINVAL; dh = (struct dccp_hdr *)(skb->data + offset); if (!pskb_may_pull(skb, offset + __dccp_basic_hdr_len(dh))) return -EINVAL; diff --git a/net/ethtool/plca.c b/net/ethtool/plca.c index b238a1afe9aeddf094046ba056cb8cd6a1660e77..b1e2e3b5027fc7b9a26c9bbdf61006b771b457a3 100644 --- a/net/ethtool/plca.c +++ b/net/ethtool/plca.c @@ -21,16 +21,6 @@ struct plca_reply_data { #define PLCA_REPDATA(__reply_base) \ container_of(__reply_base, struct plca_reply_data, base) -static void plca_update_sint(int *dst, const struct nlattr *attr, - bool *mod) -{ - if (!attr) - return; - - *dst = nla_get_u32(attr); - *mod = true; -} - // PLCA get configuration message ------------------------------------------- // const struct nla_policy ethnl_plca_get_cfg_policy[] = { @@ -38,6 +28,29 @@ const struct nla_policy ethnl_plca_get_cfg_policy[] = { NLA_POLICY_NESTED(ethnl_header_policy), }; +static void plca_update_sint(int *dst, struct nlattr **tb, u32 attrid, + bool *mod) +{ + const struct nlattr *attr = tb[attrid]; + + if (!attr || + WARN_ON_ONCE(attrid >= ARRAY_SIZE(ethnl_plca_set_cfg_policy))) + return; + + switch (ethnl_plca_set_cfg_policy[attrid].type) { + case NLA_U8: + *dst = nla_get_u8(attr); + break; + case NLA_U32: + *dst = nla_get_u32(attr); + break; + default: + WARN_ON_ONCE(1); + } + + *mod = true; +} + static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base, struct ethnl_reply_data *reply_base, const struct genl_info *info) @@ -144,13 +157,13 @@ ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info) return -EOPNOTSUPP; memset(&plca_cfg, 0xff, sizeof(plca_cfg)); - plca_update_sint(&plca_cfg.enabled, tb[ETHTOOL_A_PLCA_ENABLED], &mod); - plca_update_sint(&plca_cfg.node_id, tb[ETHTOOL_A_PLCA_NODE_ID], &mod); - plca_update_sint(&plca_cfg.node_cnt, tb[ETHTOOL_A_PLCA_NODE_CNT], &mod); - plca_update_sint(&plca_cfg.to_tmr, tb[ETHTOOL_A_PLCA_TO_TMR], &mod); - plca_update_sint(&plca_cfg.burst_cnt, tb[ETHTOOL_A_PLCA_BURST_CNT], + plca_update_sint(&plca_cfg.enabled, tb, ETHTOOL_A_PLCA_ENABLED, &mod); + plca_update_sint(&plca_cfg.node_id, tb, ETHTOOL_A_PLCA_NODE_ID, &mod); + plca_update_sint(&plca_cfg.node_cnt, tb, ETHTOOL_A_PLCA_NODE_CNT, &mod); + plca_update_sint(&plca_cfg.to_tmr, tb, ETHTOOL_A_PLCA_TO_TMR, &mod); + plca_update_sint(&plca_cfg.burst_cnt, tb, ETHTOOL_A_PLCA_BURST_CNT, &mod); - plca_update_sint(&plca_cfg.burst_tmr, tb[ETHTOOL_A_PLCA_BURST_TMR], + plca_update_sint(&plca_cfg.burst_tmr, tb, ETHTOOL_A_PLCA_BURST_TMR, &mod); if (!mod) return 0; diff --git a/net/handshake/handshake-test.c b/net/handshake/handshake-test.c index 6d37bab35c8fcc42e77d1fc081a87d61658751c3..16ed7bfd29e4fbf39e204278950834b9e69b9f97 100644 --- a/net/handshake/handshake-test.c +++ b/net/handshake/handshake-test.c @@ -235,7 +235,7 @@ static void handshake_req_submit_test4(struct kunit *test) KUNIT_EXPECT_PTR_EQ(test, req, result); handshake_req_cancel(sock->sk); - sock_release(sock); + fput(filp); } static void handshake_req_submit_test5(struct kunit *test) @@ -272,7 +272,7 @@ static void handshake_req_submit_test5(struct kunit *test) /* Assert */ KUNIT_EXPECT_EQ(test, err, -EAGAIN); - sock_release(sock); + fput(filp); hn->hn_pending = saved; } @@ -306,7 +306,7 @@ static void handshake_req_submit_test6(struct kunit *test) KUNIT_EXPECT_EQ(test, err, -EBUSY); handshake_req_cancel(sock->sk); - sock_release(sock); + fput(filp); } static void handshake_req_cancel_test1(struct kunit *test) @@ -340,7 +340,7 @@ static void handshake_req_cancel_test1(struct kunit *test) /* Assert */ KUNIT_EXPECT_TRUE(test, result); - sock_release(sock); + fput(filp); } static void handshake_req_cancel_test2(struct kunit *test) @@ -382,7 +382,7 @@ static void handshake_req_cancel_test2(struct kunit *test) /* Assert */ KUNIT_EXPECT_TRUE(test, result); - sock_release(sock); + fput(filp); } static void handshake_req_cancel_test3(struct kunit *test) @@ -427,7 +427,7 @@ static void handshake_req_cancel_test3(struct kunit *test) /* Assert */ KUNIT_EXPECT_FALSE(test, result); - sock_release(sock); + fput(filp); } static struct handshake_req *handshake_req_destroy_test; @@ -471,7 +471,7 @@ static void handshake_req_destroy_test1(struct kunit *test) handshake_req_cancel(sock->sk); /* Act */ - sock_release(sock); + fput(filp); /* Assert */ KUNIT_EXPECT_PTR_EQ(test, handshake_req_destroy_test, req); diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 1086653e1fada1724f98ccbc81fbcf7741ef9bc9..d0bc1dd8e65a8201751fddcc2356da89cd2c65e7 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -157,26 +157,24 @@ int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info) int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info) { struct net *net = sock_net(skb->sk); - struct handshake_req *req = NULL; - struct socket *sock = NULL; + struct handshake_req *req; + struct socket *sock; int fd, status, err; if (GENL_REQ_ATTR_CHECK(info, HANDSHAKE_A_DONE_SOCKFD)) return -EINVAL; fd = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_SOCKFD]); - err = 0; sock = sockfd_lookup(fd, &err); - if (err) { - err = -EBADF; - goto out_status; - } + if (!sock) + return err; req = handshake_req_hash_lookup(sock->sk); if (!req) { err = -EBUSY; + trace_handshake_cmd_done_err(net, req, sock->sk, err); fput(sock->file); - goto out_status; + return err; } trace_handshake_cmd_done(net, req, sock->sk, fd); @@ -188,10 +186,6 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info) handshake_complete(req, status, info); fput(sock->file); return 0; - -out_status: - trace_handshake_cmd_done_err(net, req, sock->sk, err); - return err; } static unsigned int handshake_net_id; diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 629daacc96071f40555939e590f9763c96553672..b71dab630a8732de385fb07694149d7e69a7151c 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -594,6 +594,7 @@ static int fill_frame_info(struct hsr_frame_info *frame, proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto; /* FIXME: */ netdev_warn_once(skb->dev, "VLAN not yet supported"); + return -EINVAL; } frame->is_from_san = false; diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c index b77f1189d19d1ff5dc76f4a7345efc7a65918399..6d14d935ee828d025d40ea237884d244cb56eb8e 100644 --- a/net/hsr/hsr_framereg.c +++ b/net/hsr/hsr_framereg.c @@ -288,13 +288,13 @@ void hsr_handle_sup_frame(struct hsr_frame_info *frame) /* And leave the HSR tag. */ if (ethhdr->h_proto == htons(ETH_P_HSR)) { - pull_size = sizeof(struct ethhdr); + pull_size = sizeof(struct hsr_tag); skb_pull(skb, pull_size); total_pull_size += pull_size; } /* And leave the HSR sup tag. */ - pull_size = sizeof(struct hsr_tag); + pull_size = sizeof(struct hsr_sup_tag); skb_pull(skb, pull_size); total_pull_size += pull_size; diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h index 6851e33df7d14607034807c64ebe3a0b860af7f1..18e01791ad799d9562923d4359df2aafab2abd08 100644 --- a/net/hsr/hsr_main.h +++ b/net/hsr/hsr_main.h @@ -83,7 +83,7 @@ struct hsr_vlan_ethhdr { struct hsr_sup_tlv { u8 HSR_TLV_type; u8 HSR_TLV_length; -}; +} __packed; /* HSR/PRP Supervision Frame data types. * Field names as defined in the IEC:2010 standard for HSR. diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 9cf64ee47dd2b4fddf7be62c7110a1ad41b53fe8..ca0ff15dc8fa358b81a804eda7398ecd10f00743 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -355,14 +355,14 @@ static void __inet_del_ifa(struct in_device *in_dev, { struct in_ifaddr *promote = NULL; struct in_ifaddr *ifa, *ifa1; - struct in_ifaddr *last_prim; + struct in_ifaddr __rcu **last_prim; struct in_ifaddr *prev_prom = NULL; int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); ASSERT_RTNL(); ifa1 = rtnl_dereference(*ifap); - last_prim = rtnl_dereference(in_dev->ifa_list); + last_prim = ifap; if (in_dev->dead) goto no_promotions; @@ -376,7 +376,7 @@ static void __inet_del_ifa(struct in_device *in_dev, while ((ifa = rtnl_dereference(*ifap1)) != NULL) { if (!(ifa->ifa_flags & IFA_F_SECONDARY) && ifa1->ifa_scope <= ifa->ifa_scope) - last_prim = ifa; + last_prim = &ifa->ifa_next; if (!(ifa->ifa_flags & IFA_F_SECONDARY) || ifa1->ifa_mask != ifa->ifa_mask || @@ -440,9 +440,9 @@ static void __inet_del_ifa(struct in_device *in_dev, rcu_assign_pointer(prev_prom->ifa_next, next_sec); - last_sec = rtnl_dereference(last_prim->ifa_next); + last_sec = rtnl_dereference(*last_prim); rcu_assign_pointer(promote->ifa_next, last_sec); - rcu_assign_pointer(last_prim->ifa_next, promote); + rcu_assign_pointer(*last_prim, promote); } promote->ifa_flags &= ~IFA_F_SECONDARY; diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 65ba18a91865ae9a9bd850052811509b8fb3a79b..1ea82bc33ef14bd4411204c58ea5fece02783b35 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -278,7 +278,8 @@ void fib_release_info(struct fib_info *fi) hlist_del(&nexthop_nh->nh_hash); } endfor_nexthops(fi) } - fi->fib_dead = 1; + /* Paired with READ_ONCE() from fib_table_lookup() */ + WRITE_ONCE(fi->fib_dead, 1); fib_info_put(fi); } spin_unlock_bh(&fib_info_lock); @@ -1581,6 +1582,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, link_it: ofi = fib_find_info(fi); if (ofi) { + /* fib_table_lookup() should not see @fi yet. */ fi->fib_dead = 1; free_fib_info(fi); refcount_inc(&ofi->fib_treeref); @@ -1619,6 +1621,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, failure: if (fi) { + /* fib_table_lookup() should not see @fi yet. */ fi->fib_dead = 1; free_fib_info(fi); } @@ -1884,6 +1887,7 @@ int fib_sync_down_addr(struct net_device *dev, __be32 local) continue; if (fi->fib_prefsrc == local) { fi->fib_flags |= RTNH_F_DEAD; + fi->pfsrc_removed = true; ret++; } } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 74d403dbd2b4e6128dacd1bfc3579a1fc3d635aa..9bdfdab906fe00cb048718eef932892181723a40 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1582,7 +1582,8 @@ int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, if (fa->fa_dscp && inet_dscp_to_dsfield(fa->fa_dscp) != flp->flowi4_tos) continue; - if (fi->fib_dead) + /* Paired with WRITE_ONCE() in fib_release_info() */ + if (READ_ONCE(fi->fib_dead)) continue; if (fa->fa_info->fib_scope < flp->flowi4_scope) continue; @@ -2026,6 +2027,7 @@ void fib_table_flush_external(struct fib_table *tb) int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) { struct trie *t = (struct trie *)tb->tb_data; + struct nl_info info = { .nl_net = net }; struct key_vector *pn = t->kv; unsigned long cindex = 1; struct hlist_node *tmp; @@ -2088,6 +2090,9 @@ int fib_table_flush(struct net *net, struct fib_table *tb, bool flush_all) fib_notify_alias_delete(net, n->key, &n->leaf, fa, NULL); + if (fi->pfsrc_removed) + rtmsg_fib(RTM_DELROUTE, htonl(n->key), fa, + KEYLENGTH - fa->fa_slen, tb->tb_id, &info, 0); hlist_del_rcu(&fa->fa_list); fib_release_info(fa->fa_info); alias_free_mem_rcu(fa); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 0c9e768e5628b1c8fd7e87bebe528762ea4a6e1e..418e5fb58fd3f2443f3c88fde5c0776805a832ef 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -353,8 +353,9 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu) struct flowi4 fl4; int hlen = LL_RESERVED_SPACE(dev); int tlen = dev->needed_tailroom; - unsigned int size = mtu; + unsigned int size; + size = min(mtu, IP_MAX_MTU); while (1) { skb = alloc_skb(size + hlen + tlen, GFP_ATOMIC | __GFP_NOWARN); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 7876b7d703cb5647086c45ca547c4caadc00c091..c32f5e28758be76d72cc33c754a10303e0905dbc 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -815,41 +815,45 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, const struct net *net, unsigned short port, int l3mdev, const struct sock *sk) { + if (!net_eq(ib2_net(tb), net) || tb->port != port || + tb->l3mdev != l3mdev) + return false; + #if IS_ENABLED(CONFIG_IPV6) - if (sk->sk_family != tb->family) + if (sk->sk_family != tb->family) { + if (sk->sk_family == AF_INET) + return ipv6_addr_v4mapped(&tb->v6_rcv_saddr) && + tb->v6_rcv_saddr.s6_addr32[3] == sk->sk_rcv_saddr; + return false; + } if (sk->sk_family == AF_INET6) - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && - ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr); - else + return ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr); #endif - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr; + return tb->rcv_saddr == sk->sk_rcv_saddr; } bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net, unsigned short port, int l3mdev, const struct sock *sk) { + if (!net_eq(ib2_net(tb), net) || tb->port != port || + tb->l3mdev != l3mdev) + return false; + #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family != tb->family) { if (sk->sk_family == AF_INET) - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && - ipv6_addr_any(&tb->v6_rcv_saddr); + return ipv6_addr_any(&tb->v6_rcv_saddr) || + ipv6_addr_v4mapped_any(&tb->v6_rcv_saddr); return false; } if (sk->sk_family == AF_INET6) - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && - ipv6_addr_any(&tb->v6_rcv_saddr); - else + return ipv6_addr_any(&tb->v6_rcv_saddr); #endif - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && tb->rcv_saddr == 0; + return tb->rcv_saddr == 0; } /* The socket's bhash2 hashbucket spinlock must be held when this is called */ diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index e18931a6d15341d7115e608d614b16ad93f91273..66fac1216d467a5709e8048f4ea9d4172ba85f67 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -67,7 +67,6 @@ static int ip_forward_finish(struct net *net, struct sock *sk, struct sk_buff *s struct ip_options *opt = &(IPCB(skb)->opt); __IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); - __IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len); #ifdef CONFIG_NET_SWITCHDEV if (skb->offload_l3_fwd_mark) { diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index fe9ead9ee863d6d7da1ac78643e9527ae5ddff1e..5e9c8156656a7642acc7a1bb08488b7482e52ce7 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -584,7 +584,8 @@ static void ip_sublist_rcv_finish(struct list_head *head) static struct sk_buff *ip_extract_route_hint(const struct net *net, struct sk_buff *skb, int rt_type) { - if (fib4_has_custom_rules(net) || rt_type == RTN_BROADCAST) + if (fib4_has_custom_rules(net) || rt_type == RTN_BROADCAST || + IPCB(skb)->flags & IPSKB_MULTIPATH) return NULL; return skb; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 43ba4b77b2484a38031a919c9be29eba9af3f031..4ab877cf6d35f229761986d5c6a17eb2a3ad4043 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -207,6 +207,9 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s } else if (rt->rt_type == RTN_BROADCAST) IP_UPD_PO_STATS(net, IPSTATS_MIB_OUTBCAST, skb->len); + /* OUTOCTETS should be counted after fragment */ + IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); + if (unlikely(skb_headroom(skb) < hh_len && dev->header_ops)) { skb = skb_expand_head(skb, hh_len); if (!skb) @@ -366,8 +369,6 @@ int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb) /* * If the indicated interface is up and running, send the packet. */ - IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); - skb->dev = dev; skb->protocol = htons(ETH_P_IP); @@ -424,8 +425,6 @@ int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb) { struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev; - IP_UPD_PO_STATS(net, IPSTATS_MIB_OUT, skb->len); - skb->dev = dev; skb->protocol = htons(ETH_P_IP); @@ -982,7 +981,7 @@ static int __ip_append_data(struct sock *sk, paged = !!cork->gso_size; if (cork->tx_flags & SKBTX_ANY_TSTAMP && - sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) + READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) tskey = atomic_inc_return(&sk->sk_tskey) - 1; hh_len = LL_RESERVED_SPACE(rt->dst.dev); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index d1c73660b844949b57960630e0467112da4f0abd..cce9cb25f3b31cd57fa883ae0dedb6829d8da2fa 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -511,7 +511,7 @@ static bool ipv4_datagram_support_cmsg(const struct sock *sk, * or without payload (SOF_TIMESTAMPING_OPT_TSONLY). */ info = PKTINFO_SKB_CB(skb); - if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) || + if (!(READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_CMSG) || !info->ipi_ifindex) return false; diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 3f0c6d602fb7760ea01b07e8f81592ee0eceb4a1..9e222a57bc2b47a2f89ca068fc5b5f4217df3725 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1804,7 +1804,6 @@ static inline int ipmr_forward_finish(struct net *net, struct sock *sk, struct ip_options *opt = &(IPCB(skb)->opt); IP_INC_STATS(net, IPSTATS_MIB_OUTFORWDATAGRAMS); - IP_ADD_STATS(net, IPSTATS_MIB_OUTOCTETS, skb->len); if (unlikely(opt->optlen)) ip_forward_options(skb); diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d8c99bdc617061b4790f87dce138348ae4cfa3c7..b214b5a2e045fec0513f4a7db69d048e355c52ae 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1213,6 +1213,7 @@ EXPORT_INDIRECT_CALLABLE(ipv4_dst_check); static void ipv4_send_dest_unreach(struct sk_buff *skb) { + struct net_device *dev; struct ip_options opt; int res; @@ -1230,7 +1231,8 @@ static void ipv4_send_dest_unreach(struct sk_buff *skb) opt.optlen = ip_hdr(skb)->ihl * 4 - sizeof(struct iphdr); rcu_read_lock(); - res = __ip_options_compile(dev_net(skb->dev), &opt, skb, NULL); + dev = skb->dev ? skb->dev : skb_rtable(skb)->dst.dev; + res = __ip_options_compile(dev_net(dev), &opt, skb, NULL); rcu_read_unlock(); if (res) @@ -2144,6 +2146,7 @@ static int ip_mkroute_input(struct sk_buff *skb, int h = fib_multipath_hash(res->fi->fib_net, NULL, skb, hkeys); fib_select_multipath(res, h); + IPCB(skb)->flags |= IPSKB_MULTIPATH; } #endif @@ -3414,6 +3417,8 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, fa->fa_type == fri.type) { fri.offload = READ_ONCE(fa->offload); fri.trap = READ_ONCE(fa->trap); + fri.offload_failed = + READ_ONCE(fa->offload_failed); break; } } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b1559481898de4555869428e28cf5d8f1872e0b0..3f66cdeef7decb5b5d2b84212c623781b8ce63db 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1621,16 +1621,13 @@ EXPORT_SYMBOL(tcp_read_sock); int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) { - struct tcp_sock *tp = tcp_sk(sk); - u32 seq = tp->copied_seq; struct sk_buff *skb; int copied = 0; - u32 offset; if (sk->sk_state == TCP_LISTEN) return -ENOTCONN; - while ((skb = tcp_recv_skb(sk, seq, &offset)) != NULL) { + while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) { u8 tcp_flags; int used; @@ -1643,13 +1640,10 @@ int tcp_read_skb(struct sock *sk, skb_read_actor_t recv_actor) copied = used; break; } - seq += used; copied += used; - if (tcp_flags & TCPHDR_FIN) { - ++seq; + if (tcp_flags & TCPHDR_FIN) break; - } } return copied; } @@ -2256,14 +2250,14 @@ void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk, } } - if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) + if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_SOFTWARE) has_timestamping = true; else tss->ts[0] = (struct timespec64) {0}; } if (tss->ts[2].tv_sec || tss->ts[2].tv_nsec) { - if (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) + if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_RAW_HARDWARE) has_timestamping = true; else tss->ts[2] = (struct timespec64) {0}; diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 81f0dff69e0b69bcc03bd932dcc42a8578939ecb..32726820300156322168e7696e38adae322ab43a 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -222,6 +222,7 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, int *addr_len) { struct tcp_sock *tcp = tcp_sk(sk); + int peek = flags & MSG_PEEK; u32 seq = tcp->copied_seq; struct sk_psock *psock; int copied = 0; @@ -311,7 +312,8 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk, copied = -EAGAIN; } out: - WRITE_ONCE(tcp->copied_seq, seq); + if (!peek) + WRITE_ONCE(tcp->copied_seq, seq); tcp_rcv_space_adjust(sk); if (copied > 0) __tcp_cleanup_rbuf(sk, copied); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 06fe1cf645d5a386331548484de2beb68e846404..8afb0950a69790fdb3b82449eed086f713a211fb 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -253,6 +253,19 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb) if (unlikely(len > icsk->icsk_ack.rcv_mss + MAX_TCP_OPTION_SPACE)) tcp_gro_dev_warn(sk, skb, len); + /* If the skb has a len of exactly 1*MSS and has the PSH bit + * set then it is likely the end of an application write. So + * more data may not be arriving soon, and yet the data sender + * may be waiting for an ACK if cwnd-bound or using TX zero + * copy. So we set ICSK_ACK_PUSHED here so that + * tcp_cleanup_rbuf() will send an ACK immediately if the app + * reads all of the data and is not ping-pong. If len > MSS + * then this logic does not matter (and does not hurt) because + * tcp_cleanup_rbuf() will always ACK immediately if the app + * reads data and there is more than an MSS of unACKed data. + */ + if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_PSH) + icsk->icsk_ack.pending |= ICSK_ACK_PUSHED; } else { /* Otherwise, we make more careful check taking into account, * that SACKs block is variable. diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index e6b4fbd642f7e4e3c986fef54e416628c8981aff..aa0fc8c766e509c0424b989edbb02322c681eb53 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -177,8 +177,7 @@ static void tcp_event_data_sent(struct tcp_sock *tp, } /* Account for an ACK we sent. */ -static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts, - u32 rcv_nxt) +static inline void tcp_event_ack_sent(struct sock *sk, u32 rcv_nxt) { struct tcp_sock *tp = tcp_sk(sk); @@ -192,7 +191,7 @@ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts, if (unlikely(rcv_nxt != tp->rcv_nxt)) return; /* Special ACK sent by DCTCP to reflect ECN */ - tcp_dec_quickack_mode(sk, pkts); + tcp_dec_quickack_mode(sk); inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK); } @@ -1387,7 +1386,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, sk, skb); if (likely(tcb->tcp_flags & TCPHDR_ACK)) - tcp_event_ack_sent(sk, tcp_skb_pcount(skb), rcv_nxt); + tcp_event_ack_sent(sk, rcv_nxt); if (skb->len != tcp_header_size) { tcp_event_data_sent(tp, sk); @@ -3474,7 +3473,7 @@ void sk_forced_mem_schedule(struct sock *sk, int size) if (delta <= 0) return; amt = sk_mem_pages(delta); - sk->sk_forward_alloc += amt << PAGE_SHIFT; + sk_forward_alloc_add(sk, amt << PAGE_SHIFT); sk_memory_allocated_add(sk, amt); if (mem_cgroup_sockets_enabled && sk->sk_memcg) diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0794a2c46a568d644cc488c1d7f6ee676180a5bd..f39b9c8445808deee2c777cbb828474ff105d322 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1414,9 +1414,9 @@ static void udp_rmem_release(struct sock *sk, int size, int partial, spin_lock(&sk_queue->lock); - sk->sk_forward_alloc += size; + sk_forward_alloc_add(sk, size); amt = (sk->sk_forward_alloc - partial) & ~(PAGE_SIZE - 1); - sk->sk_forward_alloc -= amt; + sk_forward_alloc_add(sk, -amt); if (amt) __sk_mem_reduce_allocated(sk, amt >> PAGE_SHIFT); @@ -1527,7 +1527,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) goto uncharge_drop; } - sk->sk_forward_alloc -= size; + sk_forward_alloc_add(sk, -size); /* no need to setup a destructor, we will explicitly release the * forward allocated memory on dequeue diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 967913ad65e54ae3cd63925dbc5846bfb040c850..0b6ee962c84e27906a1bc0dcd01d7088abb44cb2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1378,7 +1378,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block) * idev->desync_factor if it's larger */ cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft); - max_desync_factor = min_t(__u32, + max_desync_factor = min_t(long, idev->cnf.max_desync_factor, cnf_temp_preferred_lft - regen_advance); diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index d94041bb42872f02eaaa5bd2f64ae2301a8a48a4..b8378814532cead0275e8b7a656f78450993f619 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -99,7 +99,8 @@ static bool ip6_can_use_hint(const struct sk_buff *skb, static struct sk_buff *ip6_extract_route_hint(const struct net *net, struct sk_buff *skb) { - if (fib6_routes_require_src(net) || fib6_has_custom_rules(net)) + if (fib6_routes_require_src(net) || fib6_has_custom_rules(net) || + IP6CB(skb)->flags & IP6SKB_MULTIPATH) return NULL; return skb; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 0665e8b099680c9bea605da25b06a3d03ced1c78..54fc4c711f2c545f2ca625d6b0e09f2bb8e6d513 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -451,7 +451,6 @@ static inline int ip6_forward_finish(struct net *net, struct sock *sk, struct dst_entry *dst = skb_dst(skb); __IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTFORWDATAGRAMS); - __IP6_ADD_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTOCTETS, skb->len); #ifdef CONFIG_NET_SWITCHDEV if (skb->offload_l3_fwd_mark) { @@ -1502,7 +1501,7 @@ static int __ip6_append_data(struct sock *sk, orig_mtu = mtu; if (cork->tx_flags & SKBTX_ANY_TSTAMP && - sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) + READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) tskey = atomic_inc_return(&sk->sk_tskey) - 1; hh_len = LL_RESERVED_SPACE(rt->dst.dev); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 67a3b8f6e72b115e8fae07d7c7d358a7ac1659a5..30ca064b76ef1798d249fa4266bef34668d6a811 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -2010,8 +2010,6 @@ static inline int ip6mr_forward2_finish(struct net *net, struct sock *sk, struct { IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTFORWDATAGRAMS); - IP6_ADD_STATS(net, ip6_dst_idev(skb_dst(skb)), - IPSTATS_MIB_OUTOCTETS, skb->len); return dst_output(net, sk, skb); } diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 1b27728349725e212ab5d988dd96cf87464c9531..5831aaa53d75eae7b764d54ab52da65db4030d73 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -119,7 +119,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) return -EINVAL; ipcm6_init_sk(&ipc6, np); - ipc6.sockc.tsflags = sk->sk_tsflags; + ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); ipc6.sockc.mark = READ_ONCE(sk->sk_mark); fl6.flowi6_oif = oif; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 0eae7661a85c4487a64384c6054a3fb827387ce7..42fcec3ecf5e171a5ebe724b8c971d90885abe41 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -772,7 +772,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) fl6.flowi6_uid = sk->sk_uid; ipcm6_init(&ipc6); - ipc6.sockc.tsflags = sk->sk_tsflags; + ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); ipc6.sockc.mark = fl6.flowi6_mark; if (sin6) { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d15a9e3aa24aedd386eb05498340400236870816..9c687b357e6a41dcbc1011d43766ab1fb0e55a39 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -423,6 +423,9 @@ void fib6_select_path(const struct net *net, struct fib6_result *res, if (match->nh && have_oif_match && res->nh) return; + if (skb) + IP6CB(skb)->flags |= IP6SKB_MULTIPATH; + /* We might have already computed the hash for ICMPv6 errors. In such * case it will always be non-zero. Otherwise now is the time to do it. */ diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 3a88545a265d6bd064ecc41d33c9541a75fe0f4d..44b6949d72b2216b4d3934cd302bfff94ecae23d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1640,9 +1640,12 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) struct sock *nsk; sk = req->rsk_listener; - drop_reason = tcp_inbound_md5_hash(sk, skb, - &hdr->saddr, &hdr->daddr, - AF_INET6, dif, sdif); + if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) + drop_reason = SKB_DROP_REASON_XFRM_POLICY; + else + drop_reason = tcp_inbound_md5_hash(sk, skb, + &hdr->saddr, &hdr->daddr, + AF_INET6, dif, sdif); if (drop_reason) { sk_drops_add(sk, skb); reqsk_put(req); @@ -1689,6 +1692,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } goto discard_and_relse; } + nf_reset_ct(skb); if (nsk == sk) { reqsk_put(req); tcp_v6_restore_cb(skb); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index ebc6ae47cfeadc699e3f5a1f46be85803ff37fdd..86b5d509a4688cacb2f40667c9ddc10f81ade2fe 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1339,7 +1339,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ipcm6_init(&ipc6); ipc6.gso_size = READ_ONCE(up->gso_size); - ipc6.sockc.tsflags = sk->sk_tsflags; + ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); ipc6.sockc.mark = READ_ONCE(sk->sk_mark); /* destination address check */ diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 393f01b2a7e6d4dbd47645d58e5d146548dacc16..dd1d8ffd5f5941a423a4ea43c1867709a101d0a0 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -930,15 +930,18 @@ static int kcm_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) out_error: kcm_push(kcm); - if (copied && sock->type == SOCK_SEQPACKET) { + if (sock->type == SOCK_SEQPACKET) { /* Wrote some bytes before encountering an * error, return partial success. */ - goto partial_message; - } - - if (head != kcm->seq_skb) + if (copied) + goto partial_message; + if (head != kcm->seq_skb) + kfree_skb(head); + } else { kfree_skb(head); + kcm->seq_skb = NULL; + } err = sk_stream_error(sk, msg->msg_flags, err); @@ -1859,6 +1862,8 @@ static __net_exit void kcm_exit_net(struct net *net) * that all multiplexors and psocks have been destroyed. */ WARN_ON(!list_empty(&knet->mux_list)); + + mutex_destroy(&knet->mutex); } static struct pernet_operations kcm_net_ops = { diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index ed8ebb6f59097ac18bb284d1c48f9e801e9a92c2..11f3d375cec0003338c15e1e19cf8e4c99127958 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -507,7 +507,6 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) */ if (len > INT_MAX - transhdrlen) return -EMSGSIZE; - ulen = len + transhdrlen; /* Mirror BSD error message compatibility */ if (msg->msg_flags & MSG_OOB) @@ -628,6 +627,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) back_from_confirm: lock_sock(sk); + ulen = len + skb_queue_empty(&sk->sk_write_queue) ? transhdrlen : 0; err = ip6_append_data(sk, ip_generic_getfrag, msg, ulen, transhdrlen, &ipc6, &fl6, (struct rt6_info *)dst, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 45e7a5d9c7d94206f1af8f5ecf142d156deb6797..0e3a1753a51c6dcc904b004ec15aa2f9f2e1a413 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -566,6 +566,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, } err = ieee80211_key_link(key, link, sta); + /* KRACK protection, shouldn't happen but just silently accept key */ + if (err == -EALREADY) + err = 0; out_unlock: mutex_unlock(&local->sta_mtx); @@ -1857,7 +1860,8 @@ static int sta_link_apply_parameters(struct ieee80211_local *local, /* VHT can override some HT caps such as the A-MSDU max length */ if (params->vht_capa) ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, - params->vht_capa, link_sta); + params->vht_capa, NULL, + link_sta); if (params->he_capa) ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index e1900077bc4b94fbb52d112d837319cc1e3eceee..5542c93edfba0b0bdb9d167fbab633c103697cde 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1072,7 +1072,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, &chandef); memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie)); ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, - &cap_ie, + &cap_ie, NULL, &sta->deflink); if (memcmp(&cap, &sta->sta.deflink.vht_cap, sizeof(cap))) rates_updated |= true; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 06bd406846d2984c3acbdadcaf1cc9c465678b66..98ef1fe1226e72f8e19f785f8489537635c39799 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -676,7 +676,7 @@ struct ieee80211_if_mesh { struct timer_list mesh_path_root_timer; unsigned long wrkq_flags; - unsigned long mbss_changed; + unsigned long mbss_changed[64 / BITS_PER_LONG]; bool userspace_handles_dfs; @@ -2141,6 +2141,7 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, const struct ieee80211_vht_cap *vht_cap_ie, + const struct ieee80211_vht_cap *vht_cap_ie2, struct link_sta_info *link_sta); enum ieee80211_sta_rx_bandwidth ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 13050dc9321f197d6aaa1c55235302bfa2ecf6f1..0665ff5e456eb43605393ef193c9342760c71381 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -802,6 +802,9 @@ static void ieee80211_key_destroy(struct ieee80211_key *key, void ieee80211_key_free_unused(struct ieee80211_key *key) { + if (!key) + return; + WARN_ON(key->sdata || key->local); ieee80211_key_free_common(key); } @@ -854,7 +857,7 @@ int ieee80211_key_link(struct ieee80211_key *key, * can cause warnings to appear. */ bool delay_tailroom = sdata->vif.type == NL80211_IFTYPE_STATION; - int ret = -EOPNOTSUPP; + int ret; mutex_lock(&sdata->local->key_mtx); @@ -868,8 +871,10 @@ int ieee80211_key_link(struct ieee80211_key *key, * the same cipher. Enforce the assumption for pairwise keys. */ if ((alt_key && alt_key->conf.cipher != key->conf.cipher) || - (old_key && old_key->conf.cipher != key->conf.cipher)) + (old_key && old_key->conf.cipher != key->conf.cipher)) { + ret = -EOPNOTSUPP; goto out; + } } else if (sta) { struct link_sta_info *link_sta = &sta->deflink; int link_id = key->conf.link_id; @@ -895,8 +900,10 @@ int ieee80211_key_link(struct ieee80211_key *key, /* Non-pairwise keys must also not switch the cipher on rekey */ if (!pairwise) { - if (old_key && old_key->conf.cipher != key->conf.cipher) + if (old_key && old_key->conf.cipher != key->conf.cipher) { + ret = -EOPNOTSUPP; goto out; + } } /* @@ -904,9 +911,8 @@ int ieee80211_key_link(struct ieee80211_key *key, * new version of the key to avoid nonce reuse or replay issues. */ if (ieee80211_key_identical(sdata, old_key, key)) { - ieee80211_key_free_unused(key); - ret = 0; - goto out; + ret = -EALREADY; + goto unlock; } key->local = sdata->local; @@ -930,7 +936,11 @@ int ieee80211_key_link(struct ieee80211_key *key, ieee80211_key_free(key, delay_tailroom); } + key = NULL; + out: + ieee80211_key_free_unused(key); + unlock: mutex_unlock(&sdata->local->key_mtx); return ret; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index af8c5fc2db1497eb9f0fcfeb59491080a74f43ec..e31c312c124a1a9f81bc75779dcd8a5317f51b5a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1175,7 +1175,7 @@ void ieee80211_mbss_info_change_notify(struct ieee80211_sub_if_data *sdata, /* if we race with running work, worst case this work becomes a noop */ for_each_set_bit(bit, &bits, sizeof(changed) * BITS_PER_BYTE) - set_bit(bit, &ifmsh->mbss_changed); + set_bit(bit, ifmsh->mbss_changed); set_bit(MESH_WORK_MBSS_CHANGED, &ifmsh->wrkq_flags); wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work); } @@ -1257,7 +1257,7 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) /* clear any mesh work (for next join) we may have accrued */ ifmsh->wrkq_flags = 0; - ifmsh->mbss_changed = 0; + memset(ifmsh->mbss_changed, 0, sizeof(ifmsh->mbss_changed)); local->fif_other_bss--; atomic_dec(&local->iff_allmultis); @@ -1724,9 +1724,9 @@ static void mesh_bss_info_changed(struct ieee80211_sub_if_data *sdata) u32 bit; u64 changed = 0; - for_each_set_bit(bit, &ifmsh->mbss_changed, + for_each_set_bit(bit, ifmsh->mbss_changed, sizeof(changed) * BITS_PER_BYTE) { - clear_bit(bit, &ifmsh->mbss_changed); + clear_bit(bit, ifmsh->mbss_changed); changed |= BIT(bit); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index f3d5bb0a59f1022ff7b48e5888fbb092dbf0a928..a1e526419e9d25ba4e95b53edbfdc94217e09851 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -451,7 +451,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, changed |= IEEE80211_RC_BW_CHANGED; ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, - elems->vht_cap_elem, + elems->vht_cap_elem, NULL, &sta->deflink); ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f93eb38ae0b8dbc819c88290c643e885ad57f955..0c9198997482bcfa41b9b4781d3419d7e4b5bb53 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4202,10 +4202,33 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link, elems->ht_cap_elem, link_sta); - if (elems->vht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) + if (elems->vht_cap_elem && + !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) { + const struct ieee80211_vht_cap *bss_vht_cap = NULL; + const struct cfg80211_bss_ies *ies; + + /* + * Cisco AP module 9115 with FW 17.3 has a bug and sends a + * too large maximum MPDU length in the association response + * (indicating 12k) that it cannot actually process ... + * Work around that. + */ + rcu_read_lock(); + ies = rcu_dereference(cbss->ies); + if (ies) { + const struct element *elem; + + elem = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY, + ies->data, ies->len); + if (elem && elem->datalen >= sizeof(*bss_vht_cap)) + bss_vht_cap = (const void *)elem->data; + } + ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, elems->vht_cap_elem, - link_sta); + bss_vht_cap, link_sta); + rcu_read_unlock(); + } if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) && elems->he_cap) { @@ -5107,9 +5130,10 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, continue; valid_links |= BIT(link_id); - if (assoc_data->link[link_id].disabled) { + if (assoc_data->link[link_id].disabled) dormant_links |= BIT(link_id); - } else if (link_id != assoc_data->assoc_link_id) { + + if (link_id != assoc_data->assoc_link_id) { err = ieee80211_sta_allocate_link(sta, link_id); if (err) goto out_err; @@ -5124,7 +5148,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, struct ieee80211_link_data *link; struct link_sta_info *link_sta; - if (!cbss || assoc_data->link[link_id].disabled) + if (!cbss) continue; link = sdata_dereference(sdata->link[link_id], sdata); @@ -5429,17 +5453,18 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { struct ieee80211_link_data *link; - link = sdata_dereference(sdata->link[link_id], sdata); - if (!link) - continue; - if (!assoc_data->link[link_id].bss) continue; resp.links[link_id].bss = assoc_data->link[link_id].bss; - resp.links[link_id].addr = link->conf->addr; + ether_addr_copy(resp.links[link_id].addr, + assoc_data->link[link_id].addr); resp.links[link_id].status = assoc_data->link[link_id].status; + link = sdata_dereference(sdata->link[link_id], sdata); + if (!link) + continue; + /* get uapsd queues configuration - same for all links */ resp.uapsd_queues = 0; for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7fe7280e8437458015a3a9addfb5ff42a51cfb4f..d45d4be63dd877e52ebf61cc5d1c624724b4077b 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -665,7 +665,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) } if (unlikely(tx->key && tx->key->flags & KEY_FLAG_TAINTED && - !ieee80211_is_deauth(hdr->frame_control))) + !ieee80211_is_deauth(hdr->frame_control)) && + tx->skb->protocol != tx->sdata->control_port_protocol) return TX_DROP; if (!skip_hw && tx->key && diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index c1250aa478083888697dd346a03838c251e1d635..b3a5c3e96a7205354692d86eb022ecebefa05d64 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -4,7 +4,7 @@ * * Portions of this file * Copyright(c) 2015 - 2016 Intel Deutschland GmbH - * Copyright (C) 2018 - 2022 Intel Corporation + * Copyright (C) 2018 - 2023 Intel Corporation */ #include @@ -116,12 +116,14 @@ void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, const struct ieee80211_vht_cap *vht_cap_ie, + const struct ieee80211_vht_cap *vht_cap_ie2, struct link_sta_info *link_sta) { struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap; struct ieee80211_sta_vht_cap own_cap; u32 cap_info, i; bool have_80mhz; + u32 mpdu_len; memset(vht_cap, 0, sizeof(*vht_cap)); @@ -317,11 +319,21 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta); + /* + * Work around the Cisco 9115 FW 17.3 bug by taking the min of + * both reported MPDU lengths. + */ + mpdu_len = vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK; + if (vht_cap_ie2) + mpdu_len = min_t(u32, mpdu_len, + le32_get_bits(vht_cap_ie2->vht_cap_info, + IEEE80211_VHT_CAP_MAX_MPDU_MASK)); + /* * FIXME - should the amsdu len be per link? store per link * and maintain a minimum? */ - switch (vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) { + switch (mpdu_len) { case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454: link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; break; diff --git a/net/mptcp/options.c b/net/mptcp/options.c index c254accb14dee6ef4b692c3f4bc96bfa161e2d84..cd15ec73073e05a133a3130f90e45e33394e759f 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -1269,12 +1269,13 @@ static void mptcp_set_rwin(struct tcp_sock *tp, struct tcphdr *th) if (rcv_wnd == rcv_wnd_old) break; - if (before64(rcv_wnd_new, rcv_wnd)) { + + rcv_wnd_old = rcv_wnd; + if (before64(rcv_wnd_new, rcv_wnd_old)) { MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_RCVWNDCONFLICTUPDATE); goto raise_win; } MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_RCVWNDCONFLICT); - rcv_wnd_old = rcv_wnd; } return; } diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index b5a8aa4c1ebd6d6f30b312cb0e13d31ce3aea9cc..d042d32beb4df0955aec27e14bef72e63dc6361a 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -307,12 +307,6 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info) goto create_err; } - if (addr_l.id == 0) { - NL_SET_ERR_MSG_ATTR(info->extack, laddr, "missing local addr id"); - err = -EINVAL; - goto create_err; - } - err = mptcp_pm_parse_addr(raddr, info, &addr_r); if (err < 0) { NL_SET_ERR_MSG_ATTR(info->extack, raddr, "error parsing remote addr"); diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 933b257eee0242d9864ca9202cea69f2592663be..c3b83cb390d9d82a7b8dec49b6bf5090636689c4 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -134,9 +134,15 @@ static void mptcp_drop(struct sock *sk, struct sk_buff *skb) __kfree_skb(skb); } +static void mptcp_rmem_fwd_alloc_add(struct sock *sk, int size) +{ + WRITE_ONCE(mptcp_sk(sk)->rmem_fwd_alloc, + mptcp_sk(sk)->rmem_fwd_alloc + size); +} + static void mptcp_rmem_charge(struct sock *sk, int size) { - mptcp_sk(sk)->rmem_fwd_alloc -= size; + mptcp_rmem_fwd_alloc_add(sk, -size); } static bool mptcp_try_coalesce(struct sock *sk, struct sk_buff *to, @@ -177,7 +183,7 @@ static bool mptcp_ooo_try_coalesce(struct mptcp_sock *msk, struct sk_buff *to, static void __mptcp_rmem_reclaim(struct sock *sk, int amount) { amount >>= PAGE_SHIFT; - mptcp_sk(sk)->rmem_fwd_alloc -= amount << PAGE_SHIFT; + mptcp_rmem_charge(sk, amount << PAGE_SHIFT); __sk_mem_reduce_allocated(sk, amount); } @@ -186,7 +192,7 @@ static void mptcp_rmem_uncharge(struct sock *sk, int size) struct mptcp_sock *msk = mptcp_sk(sk); int reclaimable; - msk->rmem_fwd_alloc += size; + mptcp_rmem_fwd_alloc_add(sk, size); reclaimable = msk->rmem_fwd_alloc - sk_unused_reserved_mem(sk); /* see sk_mem_uncharge() for the rationale behind the following schema */ @@ -341,7 +347,7 @@ static bool mptcp_rmem_schedule(struct sock *sk, struct sock *ssk, int size) if (!__sk_mem_raise_allocated(sk, size, amt, SK_MEM_RECV)) return false; - msk->rmem_fwd_alloc += amount; + mptcp_rmem_fwd_alloc_add(sk, amount); return true; } @@ -399,7 +405,7 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk, return false; } -static void mptcp_stop_timer(struct sock *sk) +static void mptcp_stop_rtx_timer(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); @@ -764,6 +770,46 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk) return moved; } +static bool __mptcp_subflow_error_report(struct sock *sk, struct sock *ssk) +{ + int err = sock_error(ssk); + int ssk_state; + + if (!err) + return false; + + /* only propagate errors on fallen-back sockets or + * on MPC connect + */ + if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(mptcp_sk(sk))) + return false; + + /* We need to propagate only transition to CLOSE state. + * Orphaned socket will see such state change via + * subflow_sched_work_if_closed() and that path will properly + * destroy the msk as needed. + */ + ssk_state = inet_sk_state_load(ssk); + if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD)) + inet_sk_state_store(sk, ssk_state); + WRITE_ONCE(sk->sk_err, -err); + + /* This barrier is coupled with smp_rmb() in mptcp_poll() */ + smp_wmb(); + sk_error_report(sk); + return true; +} + +void __mptcp_error_report(struct sock *sk) +{ + struct mptcp_subflow_context *subflow; + struct mptcp_sock *msk = mptcp_sk(sk); + + mptcp_for_each_subflow(msk, subflow) + if (__mptcp_subflow_error_report(sk, mptcp_subflow_tcp_sock(subflow))) + break; +} + /* In most cases we will be able to lock the mptcp socket. If its already * owned, we need to defer to the work queue to avoid ABBA deadlock. */ @@ -846,6 +892,7 @@ static bool __mptcp_finish_join(struct mptcp_sock *msk, struct sock *ssk) mptcp_subflow_ctx(ssk)->subflow_id = msk->subflow_id++; mptcp_sockopt_sync_locked(msk, ssk); mptcp_subflow_joined(msk, ssk); + mptcp_stop_tout_timer(sk); return true; } @@ -865,12 +912,12 @@ static void __mptcp_flush_join_list(struct sock *sk, struct list_head *join_list } } -static bool mptcp_timer_pending(struct sock *sk) +static bool mptcp_rtx_timer_pending(struct sock *sk) { return timer_pending(&inet_csk(sk)->icsk_retransmit_timer); } -static void mptcp_reset_timer(struct sock *sk) +static void mptcp_reset_rtx_timer(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); unsigned long tout; @@ -1004,10 +1051,10 @@ static void __mptcp_clean_una(struct sock *sk) out: if (snd_una == READ_ONCE(msk->snd_nxt) && snd_una == READ_ONCE(msk->write_seq)) { - if (mptcp_timer_pending(sk) && !mptcp_data_fin_enabled(msk)) - mptcp_stop_timer(sk); + if (mptcp_rtx_timer_pending(sk) && !mptcp_data_fin_enabled(msk)) + mptcp_stop_rtx_timer(sk); } else { - mptcp_reset_timer(sk); + mptcp_reset_rtx_timer(sk); } } @@ -1580,8 +1627,8 @@ void __mptcp_push_pending(struct sock *sk, unsigned int flags) mptcp_push_release(ssk, &info); /* ensure the rtx timer is running */ - if (!mptcp_timer_pending(sk)) - mptcp_reset_timer(sk); + if (!mptcp_rtx_timer_pending(sk)) + mptcp_reset_rtx_timer(sk); if (do_check_data_fin) mptcp_check_send_data_fin(sk); } @@ -1644,8 +1691,8 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk, bool if (copied) { tcp_push(ssk, 0, info.mss_now, tcp_sk(ssk)->nonagle, info.size_goal); - if (!mptcp_timer_pending(sk)) - mptcp_reset_timer(sk); + if (!mptcp_rtx_timer_pending(sk)) + mptcp_reset_rtx_timer(sk); if (msk->snd_data_fin_enable && msk->snd_nxt + 1 == msk->write_seq) @@ -1800,7 +1847,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) } /* data successfully copied into the write queue */ - sk->sk_forward_alloc -= total_ts; + sk_forward_alloc_add(sk, -total_ts); copied += psize; dfrag->data_len += psize; frag_truesize += psize; @@ -2214,7 +2261,7 @@ static void mptcp_retransmit_timer(struct timer_list *t) sock_put(sk); } -static void mptcp_timeout_timer(struct timer_list *t) +static void mptcp_tout_timer(struct timer_list *t) { struct sock *sk = from_timer(sk, t, sk_timer); @@ -2323,18 +2370,14 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, bool dispose_it, need_push = false; /* If the first subflow moved to a close state before accept, e.g. due - * to an incoming reset, mptcp either: - * - if either the subflow or the msk are dead, destroy the context - * (the subflow socket is deleted by inet_child_forget) and the msk - * - otherwise do nothing at the moment and take action at accept and/or - * listener shutdown - user-space must be able to accept() the closed - * socket. + * to an incoming reset or listener shutdown, the subflow socket is + * already deleted by inet_child_forget() and the mptcp socket can't + * survive too. */ - if (msk->in_accept_queue && msk->first == ssk) { - if (!sock_flag(sk, SOCK_DEAD) && !sock_flag(ssk, SOCK_DEAD)) - return; - + if (msk->in_accept_queue && msk->first == ssk && + (sock_flag(sk, SOCK_DEAD) || sock_flag(ssk, SOCK_DEAD))) { /* ensure later check in mptcp_worker() will dispose the msk */ + mptcp_set_close_tout(sk, tcp_jiffies32 - (TCP_TIMEWAIT_LEN + 1)); sock_set_flag(sk, SOCK_DEAD); lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); mptcp_subflow_drop_ctx(ssk); @@ -2386,6 +2429,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, } out_release: + __mptcp_subflow_error_report(sk, ssk); release_sock(ssk); sock_put(ssk); @@ -2396,6 +2440,22 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, out: if (need_push) __mptcp_push_pending(sk, 0); + + /* Catch every 'all subflows closed' scenario, including peers silently + * closing them, e.g. due to timeout. + * For established sockets, allow an additional timeout before closing, + * as the protocol can still create more subflows. + */ + if (list_is_singular(&msk->conn_list) && msk->first && + inet_sk_state_load(msk->first) == TCP_CLOSE) { + if (sk->sk_state != TCP_ESTABLISHED || + msk->in_accept_queue || sock_flag(sk, SOCK_DEAD)) { + inet_sk_state_store(sk, TCP_CLOSE); + mptcp_close_wake_up(sk); + } else { + mptcp_start_tout_timer(sk); + } + } } void mptcp_close_ssk(struct sock *sk, struct sock *ssk, @@ -2439,23 +2499,14 @@ static void __mptcp_close_subflow(struct sock *sk) } -static bool mptcp_should_close(const struct sock *sk) +static bool mptcp_close_tout_expired(const struct sock *sk) { - s32 delta = tcp_jiffies32 - inet_csk(sk)->icsk_mtup.probe_timestamp; - struct mptcp_subflow_context *subflow; - - if (delta >= TCP_TIMEWAIT_LEN || mptcp_sk(sk)->in_accept_queue) - return true; + if (!inet_csk(sk)->icsk_mtup.probe_timestamp || + sk->sk_state == TCP_CLOSE) + return false; - /* if all subflows are in closed status don't bother with additional - * timeout - */ - mptcp_for_each_subflow(mptcp_sk(sk), subflow) { - if (inet_sk_state_load(mptcp_subflow_tcp_sock(subflow)) != - TCP_CLOSE) - return false; - } - return true; + return time_after32(tcp_jiffies32, + inet_csk(sk)->icsk_mtup.probe_timestamp + TCP_TIMEWAIT_LEN); } static void mptcp_check_fastclose(struct mptcp_sock *msk) @@ -2582,27 +2633,28 @@ static void __mptcp_retrans(struct sock *sk) reset_timer: mptcp_check_and_set_pending(sk); - if (!mptcp_timer_pending(sk)) - mptcp_reset_timer(sk); + if (!mptcp_rtx_timer_pending(sk)) + mptcp_reset_rtx_timer(sk); } /* schedule the timeout timer for the relevant event: either close timeout * or mp_fail timeout. The close timeout takes precedence on the mp_fail one */ -void mptcp_reset_timeout(struct mptcp_sock *msk, unsigned long fail_tout) +void mptcp_reset_tout_timer(struct mptcp_sock *msk, unsigned long fail_tout) { struct sock *sk = (struct sock *)msk; unsigned long timeout, close_timeout; - if (!fail_tout && !sock_flag(sk, SOCK_DEAD)) + if (!fail_tout && !inet_csk(sk)->icsk_mtup.probe_timestamp) return; - close_timeout = inet_csk(sk)->icsk_mtup.probe_timestamp - tcp_jiffies32 + jiffies + TCP_TIMEWAIT_LEN; + close_timeout = inet_csk(sk)->icsk_mtup.probe_timestamp - tcp_jiffies32 + jiffies + + TCP_TIMEWAIT_LEN; /* the close timeout takes precedence on the fail one, and here at least one of * them is active */ - timeout = sock_flag(sk, SOCK_DEAD) ? close_timeout : fail_tout; + timeout = inet_csk(sk)->icsk_mtup.probe_timestamp ? close_timeout : fail_tout; sk_reset_timer(sk, &sk->sk_timer, timeout); } @@ -2621,8 +2673,6 @@ static void mptcp_mp_fail_no_response(struct mptcp_sock *msk) mptcp_subflow_reset(ssk); WRITE_ONCE(mptcp_subflow_ctx(ssk)->fail_tout, 0); unlock_sock_fast(ssk, slow); - - mptcp_reset_timeout(msk, 0); } static void mptcp_do_fastclose(struct sock *sk) @@ -2659,18 +2709,14 @@ static void mptcp_worker(struct work_struct *work) if (test_and_clear_bit(MPTCP_WORK_CLOSE_SUBFLOW, &msk->flags)) __mptcp_close_subflow(sk); - /* There is no point in keeping around an orphaned sk timedout or - * closed, but we need the msk around to reply to incoming DATA_FIN, - * even if it is orphaned and in FIN_WAIT2 state - */ - if (sock_flag(sk, SOCK_DEAD)) { - if (mptcp_should_close(sk)) - mptcp_do_fastclose(sk); + if (mptcp_close_tout_expired(sk)) { + mptcp_do_fastclose(sk); + mptcp_close_wake_up(sk); + } - if (sk->sk_state == TCP_CLOSE) { - __mptcp_destroy_sock(sk); - goto unlock; - } + if (sock_flag(sk, SOCK_DEAD) && sk->sk_state == TCP_CLOSE) { + __mptcp_destroy_sock(sk); + goto unlock; } if (test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags)) @@ -2711,7 +2757,7 @@ static void __mptcp_init_sock(struct sock *sk) /* re-use the csk retrans timer for MPTCP-level retrans */ timer_setup(&msk->sk.icsk_retransmit_timer, mptcp_retransmit_timer, 0); - timer_setup(&sk->sk_timer, mptcp_timeout_timer, 0); + timer_setup(&sk->sk_timer, mptcp_tout_timer, 0); } static void mptcp_ca_reset(struct sock *sk) @@ -2802,8 +2848,8 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how) } else { pr_debug("Sending DATA_FIN on subflow %p", ssk); tcp_send_ack(ssk); - if (!mptcp_timer_pending(sk)) - mptcp_reset_timer(sk); + if (!mptcp_rtx_timer_pending(sk)) + mptcp_reset_rtx_timer(sk); } break; } @@ -2886,7 +2932,7 @@ static void __mptcp_destroy_sock(struct sock *sk) might_sleep(); - mptcp_stop_timer(sk); + mptcp_stop_rtx_timer(sk); sk_stop_timer(sk, &sk->sk_timer); msk->pm.status = 0; mptcp_release_sched(msk); @@ -2969,7 +3015,6 @@ bool __mptcp_close(struct sock *sk, long timeout) cleanup: /* orphan all the subflows */ - inet_csk(sk)->icsk_mtup.probe_timestamp = tcp_jiffies32; mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); bool slow = lock_sock_fast_nested(ssk); @@ -3006,7 +3051,7 @@ bool __mptcp_close(struct sock *sk, long timeout) __mptcp_destroy_sock(sk); do_cancel_work = true; } else { - mptcp_reset_timeout(msk, 0); + mptcp_start_tout_timer(sk); } return do_cancel_work; @@ -3069,8 +3114,8 @@ static int mptcp_disconnect(struct sock *sk, int flags) mptcp_check_listen_stop(sk); inet_sk_state_store(sk, TCP_CLOSE); - mptcp_stop_timer(sk); - sk_stop_timer(sk, &sk->sk_timer); + mptcp_stop_rtx_timer(sk); + mptcp_stop_tout_timer(sk); if (msk->token) mptcp_event(MPTCP_EVENT_CLOSED, msk, NULL, GFP_KERNEL); @@ -3257,8 +3302,8 @@ void mptcp_destroy_common(struct mptcp_sock *msk, unsigned int flags) /* move all the rx fwd alloc into the sk_mem_reclaim_final in * inet_sock_destruct() will dispose it */ - sk->sk_forward_alloc += msk->rmem_fwd_alloc; - msk->rmem_fwd_alloc = 0; + sk_forward_alloc_add(sk, msk->rmem_fwd_alloc); + WRITE_ONCE(msk->rmem_fwd_alloc, 0); mptcp_token_destroy(msk); mptcp_pm_free_anno_list(msk); mptcp_free_local_addr_list(msk); @@ -3380,24 +3425,21 @@ static void schedule_3rdack_retransmission(struct sock *ssk) sk_reset_timer(ssk, &icsk->icsk_delack_timer, timeout); } -void mptcp_subflow_process_delegated(struct sock *ssk) +void mptcp_subflow_process_delegated(struct sock *ssk, long status) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); struct sock *sk = subflow->conn; - if (test_bit(MPTCP_DELEGATE_SEND, &subflow->delegated_status)) { + if (status & BIT(MPTCP_DELEGATE_SEND)) { mptcp_data_lock(sk); if (!sock_owned_by_user(sk)) __mptcp_subflow_push_pending(sk, ssk, true); else __set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->cb_flags); mptcp_data_unlock(sk); - mptcp_subflow_delegated_done(subflow, MPTCP_DELEGATE_SEND); } - if (test_bit(MPTCP_DELEGATE_ACK, &subflow->delegated_status)) { + if (status & BIT(MPTCP_DELEGATE_ACK)) schedule_3rdack_retransmission(ssk); - mptcp_subflow_delegated_done(subflow, MPTCP_DELEGATE_ACK); - } } static int mptcp_hash(struct sock *sk) @@ -3522,7 +3564,8 @@ static void mptcp_shutdown(struct sock *sk, int how) static int mptcp_forward_alloc_get(const struct sock *sk) { - return sk->sk_forward_alloc + mptcp_sk(sk)->rmem_fwd_alloc; + return READ_ONCE(sk->sk_forward_alloc) + + READ_ONCE(mptcp_sk(sk)->rmem_fwd_alloc); } static int mptcp_ioctl_outq(const struct mptcp_sock *msk, u64 v) @@ -3922,14 +3965,17 @@ static int mptcp_napi_poll(struct napi_struct *napi, int budget) struct sock *ssk = mptcp_subflow_tcp_sock(subflow); bh_lock_sock_nested(ssk); - if (!sock_owned_by_user(ssk) && - mptcp_subflow_has_delegated_action(subflow)) - mptcp_subflow_process_delegated(ssk); - /* ... elsewhere tcp_release_cb_override already processed - * the action or will do at next release_sock(). - * In both case must dequeue the subflow here - on the same - * CPU that scheduled it. - */ + if (!sock_owned_by_user(ssk)) { + mptcp_subflow_process_delegated(ssk, xchg(&subflow->delegated_status, 0)); + } else { + /* tcp_release_cb_override already processed + * the action or will do at next release_sock(). + * In both case must dequeue the subflow here - on the same + * CPU that scheduled it. + */ + smp_wmb(); + clear_bit(MPTCP_DELEGATE_SCHEDULED, &subflow->delegated_status); + } bh_unlock_sock(ssk); sock_put(ssk); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 7254b35625756b24fbe33a2139add079ee98fd63..3612545fa62e046b62a743fba72f9bc0fd7a4233 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -444,9 +444,11 @@ struct mptcp_delegated_action { DECLARE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions); -#define MPTCP_DELEGATE_SEND 0 -#define MPTCP_DELEGATE_ACK 1 +#define MPTCP_DELEGATE_SCHEDULED 0 +#define MPTCP_DELEGATE_SEND 1 +#define MPTCP_DELEGATE_ACK 2 +#define MPTCP_DELEGATE_ACTIONS_MASK (~BIT(MPTCP_DELEGATE_SCHEDULED)) /* MPTCP subflow context */ struct mptcp_subflow_context { struct list_head node;/* conn_list of subflows */ @@ -564,23 +566,24 @@ mptcp_subflow_get_mapped_dsn(const struct mptcp_subflow_context *subflow) return subflow->map_seq + mptcp_subflow_get_map_offset(subflow); } -void mptcp_subflow_process_delegated(struct sock *ssk); +void mptcp_subflow_process_delegated(struct sock *ssk, long actions); static inline void mptcp_subflow_delegate(struct mptcp_subflow_context *subflow, int action) { + long old, set_bits = BIT(MPTCP_DELEGATE_SCHEDULED) | BIT(action); struct mptcp_delegated_action *delegated; bool schedule; /* the caller held the subflow bh socket lock */ lockdep_assert_in_softirq(); - /* The implied barrier pairs with mptcp_subflow_delegated_done(), and - * ensures the below list check sees list updates done prior to status - * bit changes + /* The implied barrier pairs with tcp_release_cb_override() + * mptcp_napi_poll(), and ensures the below list check sees list + * updates done prior to delegated status bits changes */ - if (!test_and_set_bit(action, &subflow->delegated_status)) { - /* still on delegated list from previous scheduling */ - if (!list_empty(&subflow->delegated_node)) + old = set_mask_bits(&subflow->delegated_status, 0, set_bits); + if (!(old & BIT(MPTCP_DELEGATE_SCHEDULED))) { + if (WARN_ON_ONCE(!list_empty(&subflow->delegated_node))) return; delegated = this_cpu_ptr(&mptcp_delegated_actions); @@ -605,20 +608,6 @@ mptcp_subflow_delegated_next(struct mptcp_delegated_action *delegated) return ret; } -static inline bool mptcp_subflow_has_delegated_action(const struct mptcp_subflow_context *subflow) -{ - return !!READ_ONCE(subflow->delegated_status); -} - -static inline void mptcp_subflow_delegated_done(struct mptcp_subflow_context *subflow, int action) -{ - /* pairs with mptcp_subflow_delegate, ensures delegate_node is updated before - * touching the status bit - */ - smp_wmb(); - clear_bit(action, &subflow->delegated_status); -} - int mptcp_is_enabled(const struct net *net); unsigned int mptcp_get_add_addr_timeout(const struct net *net); int mptcp_is_checksum_enabled(const struct net *net); @@ -718,7 +707,29 @@ void mptcp_get_options(const struct sk_buff *skb, void mptcp_finish_connect(struct sock *sk); void __mptcp_set_connected(struct sock *sk); -void mptcp_reset_timeout(struct mptcp_sock *msk, unsigned long fail_tout); +void mptcp_reset_tout_timer(struct mptcp_sock *msk, unsigned long fail_tout); + +static inline void mptcp_stop_tout_timer(struct sock *sk) +{ + if (!inet_csk(sk)->icsk_mtup.probe_timestamp) + return; + + sk_stop_timer(sk, &sk->sk_timer); + inet_csk(sk)->icsk_mtup.probe_timestamp = 0; +} + +static inline void mptcp_set_close_tout(struct sock *sk, unsigned long tout) +{ + /* avoid 0 timestamp, as that means no close timeout */ + inet_csk(sk)->icsk_mtup.probe_timestamp = tout ? : 1; +} + +static inline void mptcp_start_tout_timer(struct sock *sk) +{ + mptcp_set_close_tout(sk, tcp_jiffies32); + mptcp_reset_tout_timer(mptcp_sk(sk), 0); +} + static inline bool mptcp_is_fully_established(struct sock *sk) { return inet_sk_state_load(sk) == TCP_ESTABLISHED && diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 9bf3c7bc1762a0990401f7e07db1e0f395f4e567..9c1f8d1d63d24abad84605837c5b67b827aec1af 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1226,7 +1226,7 @@ static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk) WRITE_ONCE(subflow->fail_tout, fail_tout); tcp_send_ack(ssk); - mptcp_reset_timeout(msk, subflow->fail_tout); + mptcp_reset_tout_timer(msk, subflow->fail_tout); } static bool subflow_check_data_avail(struct sock *ssk) @@ -1362,42 +1362,6 @@ void mptcp_space(const struct sock *ssk, int *space, int *full_space) *full_space = mptcp_win_from_space(sk, READ_ONCE(sk->sk_rcvbuf)); } -void __mptcp_error_report(struct sock *sk) -{ - struct mptcp_subflow_context *subflow; - struct mptcp_sock *msk = mptcp_sk(sk); - - mptcp_for_each_subflow(msk, subflow) { - struct sock *ssk = mptcp_subflow_tcp_sock(subflow); - int err = sock_error(ssk); - int ssk_state; - - if (!err) - continue; - - /* only propagate errors on fallen-back sockets or - * on MPC connect - */ - if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(msk)) - continue; - - /* We need to propagate only transition to CLOSE state. - * Orphaned socket will see such state change via - * subflow_sched_work_if_closed() and that path will properly - * destroy the msk as needed. - */ - ssk_state = inet_sk_state_load(ssk); - if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD)) - inet_sk_state_store(sk, ssk_state); - WRITE_ONCE(sk->sk_err, -err); - - /* This barrier is coupled with smp_rmb() in mptcp_poll() */ - smp_wmb(); - sk_error_report(sk); - break; - } -} - static void subflow_error_report(struct sock *ssk) { struct sock *sk = mptcp_subflow_ctx(ssk)->conn; @@ -1588,6 +1552,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc, mptcp_sock_graft(ssk, sk->sk_socket); iput(SOCK_INODE(sf)); WRITE_ONCE(msk->allow_infinite_fallback, false); + mptcp_stop_tout_timer(sk); return 0; failed_unlink: @@ -1991,9 +1956,15 @@ static void subflow_ulp_clone(const struct request_sock *req, static void tcp_release_cb_override(struct sock *ssk) { struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); + long status; - if (mptcp_subflow_has_delegated_action(subflow)) - mptcp_subflow_process_delegated(ssk); + /* process and clear all the pending actions, but leave the subflow into + * the napi queue. To respect locking, only the same CPU that originated + * the action can touch the list. mptcp_napi_poll will take care of it. + */ + status = set_mask_bits(&subflow->delegated_status, MPTCP_DELEGATE_ACTIONS_MASK, 0); + if (status) + mptcp_subflow_process_delegated(ssk, status); tcp_release_cb(ssk); } diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c index 62fb1031763d14f82dcfee50191a21cb38ad8cf9..f8854bff286cbd7229e0c543689f892f40e80cab 100644 --- a/net/ncsi/ncsi-aen.c +++ b/net/ncsi/ncsi-aen.c @@ -89,6 +89,11 @@ static int ncsi_aen_handler_lsc(struct ncsi_dev_priv *ndp, if ((had_link == has_link) || chained) return 0; + if (had_link) + netif_carrier_off(ndp->ndev.dev); + else + netif_carrier_on(ndp->ndev.dev); + if (!ndp->multi_package && !nc->package->multi_channel) { if (had_link) { ndp->flags |= NCSI_DEV_RESHUFFLE; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index e564b5174261159173bfda0756480ca554243b7d..35d2f9c9ada0252e01af3bee1faa1f63a0c94498 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -682,6 +682,14 @@ __ip_set_put(struct ip_set *set) /* set->ref can be swapped out by ip_set_swap, netlink events (like dump) need * a separate reference counter */ +static void +__ip_set_get_netlink(struct ip_set *set) +{ + write_lock_bh(&ip_set_ref_lock); + set->ref_netlink++; + write_unlock_bh(&ip_set_ref_lock); +} + static void __ip_set_put_netlink(struct ip_set *set) { @@ -1693,11 +1701,11 @@ call_ad(struct net *net, struct sock *ctnl, struct sk_buff *skb, do { if (retried) { - __ip_set_get(set); + __ip_set_get_netlink(set); nfnl_unlock(NFNL_SUBSYS_IPSET); cond_resched(); nfnl_lock(NFNL_SUBSYS_IPSET); - __ip_set_put(set); + __ip_set_put_netlink(set); } ip_set_lock(set); diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c index 005a7ce87217e24c292e27eddb03f1320fed4a6a..bf4f91b78e1dcb93288d4f87471a00ac0a4ae387 100644 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -36,6 +36,7 @@ MODULE_ALIAS("ip_set_hash:net,port,net"); #define IP_SET_HASH_WITH_PROTO #define IP_SET_HASH_WITH_NETS #define IPSET_NET_COUNT 2 +#define IP_SET_HASH_WITH_NET0 /* IPv4 variant */ diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index da5af28ff57b5254c0ec8976c4180113037c96a0..4174076c66fa7795ecbdef18ca2b30f92765cd4d 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -1439,7 +1439,7 @@ static int bind_mcastif_addr(struct socket *sock, struct net_device *dev) sin.sin_addr.s_addr = addr; sin.sin_port = 0; - return sock->ops->bind(sock, (struct sockaddr*)&sin, sizeof(sin)); + return kernel_bind(sock, (struct sockaddr *)&sin, sizeof(sin)); } static void get_mcast_sockaddr(union ipvs_sockaddr *sa, int *salen, @@ -1505,8 +1505,8 @@ static int make_send_sock(struct netns_ipvs *ipvs, int id, } get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->mcfg, id); - result = sock->ops->connect(sock, (struct sockaddr *) &mcast_addr, - salen, 0); + result = kernel_connect(sock, (struct sockaddr *)&mcast_addr, + salen, 0); if (result < 0) { pr_err("Error connecting to the multicast addr\n"); goto error; @@ -1546,7 +1546,7 @@ static int make_receive_sock(struct netns_ipvs *ipvs, int id, get_mcast_sockaddr(&mcast_addr, &salen, &ipvs->bcfg, id); sock->sk->sk_bound_dev_if = dev->ifindex; - result = sock->ops->bind(sock, (struct sockaddr *)&mcast_addr, salen); + result = kernel_bind(sock, (struct sockaddr *)&mcast_addr, salen); if (result < 0) { pr_err("Error binding to the multicast addr\n"); goto error; diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index c7a6114091ae59718f494f8bf10346f0632b761c..b21799d468d2815413cc1334cb6c5e5df7fe231d 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -381,6 +381,8 @@ __bpf_kfunc struct nf_conn *bpf_ct_insert_entry(struct nf_conn___init *nfct_i) struct nf_conn *nfct = (struct nf_conn *)nfct_i; int err; + if (!nf_ct_is_confirmed(nfct)) + nfct->timeout += nfct_time_stamp; nfct->status |= IPS_CONFIRMED; err = nf_conntrack_hash_check_insert(nfct); if (err < 0) { diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 0b513f7bf9f391e7f94a3b4ebce9d47a51fa6cb2..dd62cc12e7750734fec9be8a90fd0defcbc815e0 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -40,10 +40,10 @@ static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = { [NF_CT_EXT_ECACHE] = sizeof(struct nf_conntrack_ecache), #endif #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP - [NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_acct), + [NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_tstamp), #endif #ifdef CONFIG_NF_CONNTRACK_TIMEOUT - [NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_tstamp), + [NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_timeout), #endif #ifdef CONFIG_NF_CONNTRACK_LABELS [NF_CT_EXT_LABELS] = sizeof(struct nf_conn_labels), diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index b6bcc8f2f46b7dc0c93e4e75ac9122809fc54be2..c6bd533983c1ff275796b789cdb973c09f646984 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -112,7 +112,7 @@ static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = { /* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA}, /* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL},/* Can't have Stale cookie*/ /* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL},/* 5.2.4 - Big TODO */ -/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL},/* Can't come in orig dir */ +/* cookie_ack */ {sCL, sCL, sCW, sES, sES, sSS, sSR, sSA, sCL},/* Can't come in orig dir */ /* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL}, /* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS}, /* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS}, @@ -126,7 +126,7 @@ static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = { /* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV}, /* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV}, /* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV}, -/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV},/* Can't come in reply dir */ +/* cookie_echo */ {sIV, sCL, sCE, sCE, sES, sSS, sSR, sSA, sIV},/* Can't come in reply dir */ /* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV}, /* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV}, /* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS}, @@ -412,6 +412,9 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, /* (D) vtag must be same as init_vtag as found in INIT_ACK */ if (sh->vtag != ct->proto.sctp.vtag[dir]) goto out_unlock; + } else if (sch->type == SCTP_CID_COOKIE_ACK) { + ct->proto.sctp.init[dir] = 0; + ct->proto.sctp.init[!dir] = 0; } else if (sch->type == SCTP_CID_HEARTBEAT) { if (ct->proto.sctp.vtag[dir] == 0) { pr_debug("Setting %d vtag %x for dir %d\n", sch->type, sh->vtag, dir); @@ -461,16 +464,18 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, } /* If it is an INIT or an INIT ACK note down the vtag */ - if (sch->type == SCTP_CID_INIT || - sch->type == SCTP_CID_INIT_ACK) { - struct sctp_inithdr _inithdr, *ih; + if (sch->type == SCTP_CID_INIT) { + struct sctp_inithdr _ih, *ih; - ih = skb_header_pointer(skb, offset + sizeof(_sch), - sizeof(_inithdr), &_inithdr); - if (ih == NULL) + ih = skb_header_pointer(skb, offset + sizeof(_sch), sizeof(*ih), &_ih); + if (!ih) goto out_unlock; - pr_debug("Setting vtag %x for dir %d\n", - ih->init_tag, !dir); + + if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir]) + ct->proto.sctp.init[!dir] = 0; + ct->proto.sctp.init[dir] = 1; + + pr_debug("Setting vtag %x for dir %d\n", ih->init_tag, !dir); ct->proto.sctp.vtag[!dir] = ih->init_tag; /* don't renew timeout on init retransmit so @@ -481,6 +486,24 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct, old_state == SCTP_CONNTRACK_CLOSED && nf_ct_is_confirmed(ct)) ignore = true; + } else if (sch->type == SCTP_CID_INIT_ACK) { + struct sctp_inithdr _ih, *ih; + __be32 vtag; + + ih = skb_header_pointer(skb, offset + sizeof(_sch), sizeof(*ih), &_ih); + if (!ih) + goto out_unlock; + + vtag = ct->proto.sctp.vtag[!dir]; + if (!ct->proto.sctp.init[!dir] && vtag && vtag != ih->init_tag) + goto out_unlock; + /* collision */ + if (ct->proto.sctp.init[dir] && ct->proto.sctp.init[!dir] && + vtag != ih->init_tag) + goto out_unlock; + + pr_debug("Setting vtag %x for dir %d\n", ih->init_tag, !dir); + ct->proto.sctp.vtag[!dir] = ih->init_tag; } ct->proto.sctp.state = new_state; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 41b826dff6f5cb8f7ccbfad843f970dcba97e090..a72b6aeefb1b5de353a89876ccc2635aa60515dd 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -102,6 +102,7 @@ static const u8 nft2audit_op[NFT_MSG_MAX] = { // enum nf_tables_msg_types [NFT_MSG_NEWFLOWTABLE] = AUDIT_NFT_OP_FLOWTABLE_REGISTER, [NFT_MSG_GETFLOWTABLE] = AUDIT_NFT_OP_INVALID, [NFT_MSG_DELFLOWTABLE] = AUDIT_NFT_OP_FLOWTABLE_UNREGISTER, + [NFT_MSG_GETSETELEM_RESET] = AUDIT_NFT_OP_SETELEM_RESET, }; static void nft_validate_state_update(struct nft_table *table, u8 new_validate_state) @@ -1218,6 +1219,10 @@ static int nf_tables_updtable(struct nft_ctx *ctx) flags & NFT_TABLE_F_OWNER)) return -EOPNOTSUPP; + /* No dormant off/on/off/on games in single transaction */ + if (ctx->table->flags & __NFT_TABLE_F_UPDATE) + return -EINVAL; + trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE, sizeof(struct nft_trans_table)); if (trans == NULL) @@ -1431,7 +1436,7 @@ static int nft_flush_table(struct nft_ctx *ctx) if (!nft_is_active_next(ctx->net, chain)) continue; - if (nft_chain_is_bound(chain)) + if (nft_chain_binding(chain)) continue; ctx->chain = chain; @@ -1445,8 +1450,7 @@ static int nft_flush_table(struct nft_ctx *ctx) if (!nft_is_active_next(ctx->net, set)) continue; - if (nft_set_is_anonymous(set) && - !list_empty(&set->bindings)) + if (nft_set_is_anonymous(set)) continue; err = nft_delset(ctx, set); @@ -1476,7 +1480,7 @@ static int nft_flush_table(struct nft_ctx *ctx) if (!nft_is_active_next(ctx->net, chain)) continue; - if (nft_chain_is_bound(chain)) + if (nft_chain_binding(chain)) continue; ctx->chain = chain; @@ -2909,6 +2913,9 @@ static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info, return PTR_ERR(chain); } + if (nft_chain_binding(chain)) + return -EOPNOTSUPP; + nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla); if (nla[NFTA_CHAIN_HOOK]) { @@ -3421,6 +3428,18 @@ static void nf_tables_rule_notify(const struct nft_ctx *ctx, nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES, -ENOBUFS); } +static void audit_log_rule_reset(const struct nft_table *table, + unsigned int base_seq, + unsigned int nentries) +{ + char *buf = kasprintf(GFP_ATOMIC, "%s:%u", + table->name, base_seq); + + audit_log_nfcfg(buf, table->family, nentries, + AUDIT_NFT_OP_RULE_RESET, GFP_ATOMIC); + kfree(buf); +} + struct nft_rule_dump_ctx { char *table; char *chain; @@ -3436,6 +3455,8 @@ static int __nf_tables_dump_rules(struct sk_buff *skb, struct net *net = sock_net(skb->sk); const struct nft_rule *rule, *prule; unsigned int s_idx = cb->args[0]; + unsigned int entries = 0; + int ret = 0; u64 handle; prule = NULL; @@ -3458,16 +3479,22 @@ static int __nf_tables_dump_rules(struct sk_buff *skb, NFT_MSG_NEWRULE, NLM_F_MULTI | NLM_F_APPEND, table->family, - table, chain, rule, handle, reset) < 0) - return 1; - + table, chain, rule, handle, reset) < 0) { + ret = 1; + break; + } + entries++; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: prule = rule; cont_skip: (*idx)++; } - return 0; + + if (reset && entries) + audit_log_rule_reset(table, cb->seq, entries); + + return ret; } static int nf_tables_dump_rules(struct sk_buff *skb, @@ -3634,6 +3661,9 @@ static int nf_tables_getrule(struct sk_buff *skb, const struct nfnl_info *info, if (err < 0) goto err_fill_rule_info; + if (reset) + audit_log_rule_reset(table, nft_pernet(net)->base_seq, 1); + return nfnetlink_unicast(skb2, net, NETLINK_CB(skb).portid); err_fill_rule_info: @@ -3951,6 +3981,11 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, } if (info->nlh->nlmsg_flags & NLM_F_REPLACE) { + if (nft_chain_binding(chain)) { + err = -EOPNOTSUPP; + goto err_destroy_flow_rule; + } + err = nft_delrule(&ctx, old_rule); if (err < 0) goto err_destroy_flow_rule; @@ -4058,7 +4093,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info, NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN]); return PTR_ERR(chain); } - if (nft_chain_is_bound(chain)) + if (nft_chain_binding(chain)) return -EOPNOTSUPP; } @@ -4092,7 +4127,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info, list_for_each_entry(chain, &table->chains, list) { if (!nft_is_active_next(net, chain)) continue; - if (nft_chain_is_bound(chain)) + if (nft_chain_binding(chain)) continue; ctx.chain = chain; @@ -5624,13 +5659,25 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx, return nf_tables_fill_setelem(args->skb, set, elem, args->reset); } +static void audit_log_nft_set_reset(const struct nft_table *table, + unsigned int base_seq, + unsigned int nentries) +{ + char *buf = kasprintf(GFP_ATOMIC, "%s:%u", table->name, base_seq); + + audit_log_nfcfg(buf, table->family, nentries, + AUDIT_NFT_OP_SETELEM_RESET, GFP_ATOMIC); + kfree(buf); +} + struct nft_set_dump_ctx { const struct nft_set *set; struct nft_ctx ctx; }; static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb, - const struct nft_set *set, bool reset) + const struct nft_set *set, bool reset, + unsigned int base_seq) { struct nft_set_elem_catchall *catchall; u8 genmask = nft_genmask_cur(net); @@ -5646,6 +5693,8 @@ static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb, elem.priv = catchall->elem; ret = nf_tables_fill_setelem(skb, set, &elem, reset); + if (reset && !ret) + audit_log_nft_set_reset(set->table, base_seq, 1); break; } @@ -5725,12 +5774,17 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) set->ops->walk(&dump_ctx->ctx, set, &args.iter); if (!args.iter.err && args.iter.count == cb->args[0]) - args.iter.err = nft_set_catchall_dump(net, skb, set, reset); - rcu_read_unlock(); - + args.iter.err = nft_set_catchall_dump(net, skb, set, + reset, cb->seq); nla_nest_end(skb, nest); nlmsg_end(skb, nlh); + if (reset && args.iter.count > args.iter.skip) + audit_log_nft_set_reset(table, cb->seq, + args.iter.count - args.iter.skip); + + rcu_read_unlock(); + if (args.iter.err && args.iter.err != -EMSGSIZE) return args.iter.err; if (args.iter.count == cb->args[0]) @@ -5955,13 +6009,13 @@ static int nf_tables_getsetelem(struct sk_buff *skb, struct netlink_ext_ack *extack = info->extack; u8 genmask = nft_genmask_cur(info->net); u8 family = info->nfmsg->nfgen_family; + int rem, err = 0, nelems = 0; struct net *net = info->net; struct nft_table *table; struct nft_set *set; struct nlattr *attr; struct nft_ctx ctx; bool reset = false; - int rem, err = 0; table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family, genmask, 0); @@ -6004,8 +6058,13 @@ static int nf_tables_getsetelem(struct sk_buff *skb, NL_SET_BAD_ATTR(extack, attr); break; } + nelems++; } + if (reset) + audit_log_nft_set_reset(table, nft_pernet(net)->base_seq, + nelems); + return err; } @@ -7139,8 +7198,10 @@ static int nf_tables_delsetelem(struct sk_buff *skb, if (IS_ERR(set)) return PTR_ERR(set); - if (!list_empty(&set->bindings) && - (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS))) + if (nft_set_is_anonymous(set)) + return -EOPNOTSUPP; + + if (!list_empty(&set->bindings) && (set->flags & NFT_SET_CONSTANT)) return -EBUSY; nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla); @@ -7810,24 +7871,14 @@ static int nf_tables_delobj(struct sk_buff *skb, const struct nfnl_info *info, return nft_delobj(&ctx, obj); } -void nft_obj_notify(struct net *net, const struct nft_table *table, - struct nft_object *obj, u32 portid, u32 seq, int event, - u16 flags, int family, int report, gfp_t gfp) +static void +__nft_obj_notify(struct net *net, const struct nft_table *table, + struct nft_object *obj, u32 portid, u32 seq, int event, + u16 flags, int family, int report, gfp_t gfp) { struct nftables_pernet *nft_net = nft_pernet(net); struct sk_buff *skb; int err; - char *buf = kasprintf(gfp, "%s:%u", - table->name, nft_net->base_seq); - - audit_log_nfcfg(buf, - family, - obj->handle, - event == NFT_MSG_NEWOBJ ? - AUDIT_NFT_OP_OBJ_REGISTER : - AUDIT_NFT_OP_OBJ_UNREGISTER, - gfp); - kfree(buf); if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) @@ -7850,13 +7901,35 @@ void nft_obj_notify(struct net *net, const struct nft_table *table, err: nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, -ENOBUFS); } + +void nft_obj_notify(struct net *net, const struct nft_table *table, + struct nft_object *obj, u32 portid, u32 seq, int event, + u16 flags, int family, int report, gfp_t gfp) +{ + struct nftables_pernet *nft_net = nft_pernet(net); + char *buf = kasprintf(gfp, "%s:%u", + table->name, nft_net->base_seq); + + audit_log_nfcfg(buf, + family, + obj->handle, + event == NFT_MSG_NEWOBJ ? + AUDIT_NFT_OP_OBJ_REGISTER : + AUDIT_NFT_OP_OBJ_UNREGISTER, + gfp); + kfree(buf); + + __nft_obj_notify(net, table, obj, portid, seq, event, + flags, family, report, gfp); +} EXPORT_SYMBOL_GPL(nft_obj_notify); static void nf_tables_obj_notify(const struct nft_ctx *ctx, struct nft_object *obj, int event) { - nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, ctx->seq, event, - ctx->flags, ctx->family, ctx->report, GFP_KERNEL); + __nft_obj_notify(ctx->net, ctx->table, obj, ctx->portid, + ctx->seq, event, ctx->flags, ctx->family, + ctx->report, GFP_KERNEL); } /* @@ -9518,12 +9591,15 @@ static int nft_trans_gc_space(struct nft_trans_gc *trans) struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc, unsigned int gc_seq, gfp_t gfp) { + struct nft_set *set; + if (nft_trans_gc_space(gc)) return gc; + set = gc->set; nft_trans_gc_queue_work(gc); - return nft_trans_gc_alloc(gc->set, gc_seq, gfp); + return nft_trans_gc_alloc(set, gc_seq, gfp); } void nft_trans_gc_queue_async_done(struct nft_trans_gc *trans) @@ -9538,15 +9614,18 @@ void nft_trans_gc_queue_async_done(struct nft_trans_gc *trans) struct nft_trans_gc *nft_trans_gc_queue_sync(struct nft_trans_gc *gc, gfp_t gfp) { + struct nft_set *set; + if (WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net))) return NULL; if (nft_trans_gc_space(gc)) return gc; + set = gc->set; call_rcu(&gc->rcu, nft_trans_gc_trans_free); - return nft_trans_gc_alloc(gc->set, 0, gfp); + return nft_trans_gc_alloc(set, 0, gfp); } void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans) @@ -9561,8 +9640,9 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans) call_rcu(&trans->rcu, nft_trans_gc_trans_free); } -struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, - unsigned int gc_seq) +static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, + unsigned int gc_seq, + bool sync) { struct nft_set_elem_catchall *catchall; const struct nft_set *set = gc->set; @@ -9578,7 +9658,11 @@ struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, nft_set_elem_dead(ext); dead_elem: - gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); + if (sync) + gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); + else + gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC); + if (!gc) return NULL; @@ -9588,6 +9672,17 @@ struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, return gc; } +struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc, + unsigned int gc_seq) +{ + return nft_trans_gc_catchall(gc, gc_seq, false); +} + +struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc) +{ + return nft_trans_gc_catchall(gc, 0, true); +} + static void nf_tables_module_autoload_cleanup(struct net *net) { struct nftables_pernet *nft_net = nft_pernet(net); @@ -11010,7 +11105,7 @@ static void __nft_release_table(struct net *net, struct nft_table *table) ctx.family = table->family; ctx.table = table; list_for_each_entry(chain, &table->chains, list) { - if (nft_chain_is_bound(chain)) + if (nft_chain_binding(chain)) continue; ctx.chain = chain; diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index 8f1bfa6ccc2d9a09ab4a6132cd9b7df777316cd9..50723ba0828905737c71dd842be39e28e039218e 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -315,6 +315,14 @@ static int nfnl_osf_add_callback(struct sk_buff *skb, f = nla_data(osf_attrs[OSF_ATTR_FINGER]); + if (f->opt_num > ARRAY_SIZE(f->opt)) + return -EINVAL; + + if (!memchr(f->genre, 0, MAXGENRELEN) || + !memchr(f->subtype, 0, MAXGENRELEN) || + !memchr(f->version, 0, MAXGENRELEN)) + return -EINVAL; + kf = kmalloc(sizeof(struct nf_osf_finger), GFP_KERNEL); if (!kf) return -ENOMEM; diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 7f856ceb3a668268205882643b39f56aa2588f97..3fbaa7bf41f9c74956c826ee63797406bff9f297 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -35,6 +35,14 @@ static unsigned int optlen(const u8 *opt, unsigned int offset) return opt[offset + 1]; } +static int nft_skb_copy_to_reg(const struct sk_buff *skb, int offset, u32 *dest, unsigned int len) +{ + if (len % NFT_REG32_SIZE) + dest[len / NFT_REG32_SIZE] = 0; + + return skb_copy_bits(skb, offset, dest, len); +} + static void nft_exthdr_ipv6_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -56,8 +64,7 @@ static void nft_exthdr_ipv6_eval(const struct nft_expr *expr, } offset += priv->offset; - dest[priv->len / NFT_REG32_SIZE] = 0; - if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0) + if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0) goto err; return; err: @@ -153,8 +160,7 @@ static void nft_exthdr_ipv4_eval(const struct nft_expr *expr, } offset += priv->offset; - dest[priv->len / NFT_REG32_SIZE] = 0; - if (skb_copy_bits(pkt->skb, offset, dest, priv->len) < 0) + if (nft_skb_copy_to_reg(pkt->skb, offset, dest, priv->len) < 0) goto err; return; err: @@ -210,7 +216,8 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr, if (priv->flags & NFT_EXTHDR_F_PRESENT) { *dest = 1; } else { - dest[priv->len / NFT_REG32_SIZE] = 0; + if (priv->len % NFT_REG32_SIZE) + dest[priv->len / NFT_REG32_SIZE] = 0; memcpy(dest, opt + offset, priv->len); } @@ -238,7 +245,12 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, if (!tcph) goto err; + if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len)) + goto err; + + tcph = (struct tcphdr *)(pkt->skb->data + nft_thoff(pkt)); opt = (u8 *)tcph; + for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { union { __be16 v16; @@ -253,15 +265,6 @@ static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, if (i + optl > tcphdr_len || priv->len + priv->offset > optl) goto err; - if (skb_ensure_writable(pkt->skb, - nft_thoff(pkt) + i + priv->len)) - goto err; - - tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, - &tcphdr_len); - if (!tcph) - goto err; - offset = i + priv->offset; switch (priv->len) { @@ -325,9 +328,9 @@ static void nft_exthdr_tcp_strip_eval(const struct nft_expr *expr, if (skb_ensure_writable(pkt->skb, nft_thoff(pkt) + tcphdr_len)) goto drop; - opt = (u8 *)nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); - if (!opt) - goto err; + tcph = (struct tcphdr *)(pkt->skb->data + nft_thoff(pkt)); + opt = (u8 *)tcph; + for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { unsigned int j; @@ -392,9 +395,8 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr, offset + ntohs(sch->length) > pkt->skb->len) break; - dest[priv->len / NFT_REG32_SIZE] = 0; - if (skb_copy_bits(pkt->skb, offset + priv->offset, - dest, priv->len) < 0) + if (nft_skb_copy_to_reg(pkt->skb, offset + priv->offset, + dest, priv->len) < 0) break; return; } diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 8cb80098994793fb49c2ef43a2644d0c4425067b..120f6d395b98b3ddf9d73d56ecbfb46e1e408bc0 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -154,6 +154,17 @@ int nft_payload_inner_offset(const struct nft_pktinfo *pkt) return pkt->inneroff; } +static bool nft_payload_need_vlan_copy(const struct nft_payload *priv) +{ + unsigned int len = priv->offset + priv->len; + + /* data past ether src/dst requested, copy needed */ + if (len > offsetof(struct ethhdr, h_proto)) + return true; + + return false; +} + void nft_payload_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -172,7 +183,7 @@ void nft_payload_eval(const struct nft_expr *expr, goto err; if (skb_vlan_tag_present(skb) && - priv->offset >= offsetof(struct ethhdr, h_proto)) { + nft_payload_need_vlan_copy(priv)) { if (!nft_payload_copy_vlan(dest, skb, priv->offset, priv->len)) goto err; diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 524763659f2510c2678e013b19a12a929f7630c5..2013de934cef096ac8fa512c1cd4bc18c7c52c10 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -338,12 +338,9 @@ static void nft_rhash_gc(struct work_struct *work) while ((he = rhashtable_walk_next(&hti))) { if (IS_ERR(he)) { - if (PTR_ERR(he) != -EAGAIN) { - nft_trans_gc_destroy(gc); - gc = NULL; - goto try_later; - } - continue; + nft_trans_gc_destroy(gc); + gc = NULL; + goto try_later; } /* Ruleset has been updated, try later. */ @@ -372,7 +369,7 @@ static void nft_rhash_gc(struct work_struct *work) nft_trans_gc_elem_add(gc, he); } - gc = nft_trans_gc_catchall(gc, gc_seq); + gc = nft_trans_gc_catchall_async(gc, gc_seq); try_later: /* catchall list iteration requires rcu read side lock. */ diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 6af9c9ed4b5c3049353fd50b16080f34cd660a6f..c0dcc40de358fb2f364ed15f05a5b166279887aa 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -1596,7 +1596,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m) gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); if (!gc) - break; + return; nft_pipapo_gc_deactivate(net, set, e); pipapo_drop(m, rulemap); @@ -1610,7 +1610,7 @@ static void pipapo_gc(const struct nft_set *_set, struct nft_pipapo_match *m) } } - gc = nft_trans_gc_catchall(gc, 0); + gc = nft_trans_gc_catchall_sync(gc); if (gc) { nft_trans_gc_queue_sync_done(gc); priv->last_gc = jiffies; diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index c6435e70923197d838d765bad016f7d929c353cc..2660ceab3759dbac18eb2693a8f6d87447cc7e4e 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -233,10 +233,9 @@ static void nft_rbtree_gc_remove(struct net *net, struct nft_set *set, rb_erase(&rbe->node, &priv->root); } -static int nft_rbtree_gc_elem(const struct nft_set *__set, - struct nft_rbtree *priv, - struct nft_rbtree_elem *rbe, - u8 genmask) +static const struct nft_rbtree_elem * +nft_rbtree_gc_elem(const struct nft_set *__set, struct nft_rbtree *priv, + struct nft_rbtree_elem *rbe, u8 genmask) { struct nft_set *set = (struct nft_set *)__set; struct rb_node *prev = rb_prev(&rbe->node); @@ -246,7 +245,7 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, gc = nft_trans_gc_alloc(set, 0, GFP_ATOMIC); if (!gc) - return -ENOMEM; + return ERR_PTR(-ENOMEM); /* search for end interval coming before this element. * end intervals don't carry a timeout extension, they @@ -261,6 +260,7 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, prev = rb_prev(prev); } + rbe_prev = NULL; if (prev) { rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node); nft_rbtree_gc_remove(net, set, priv, rbe_prev); @@ -272,7 +272,7 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, */ gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); if (WARN_ON_ONCE(!gc)) - return -ENOMEM; + return ERR_PTR(-ENOMEM); nft_trans_gc_elem_add(gc, rbe_prev); } @@ -280,13 +280,13 @@ static int nft_rbtree_gc_elem(const struct nft_set *__set, nft_rbtree_gc_remove(net, set, priv, rbe); gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC); if (WARN_ON_ONCE(!gc)) - return -ENOMEM; + return ERR_PTR(-ENOMEM); nft_trans_gc_elem_add(gc, rbe); nft_trans_gc_queue_sync_done(gc); - return 0; + return rbe_prev; } static bool nft_rbtree_update_first(const struct nft_set *set, @@ -312,8 +312,9 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL; struct rb_node *node, *next, *parent, **p, *first = NULL; struct nft_rbtree *priv = nft_set_priv(set); + u8 cur_genmask = nft_genmask_cur(net); u8 genmask = nft_genmask_next(net); - int d, err; + int d; /* Descend the tree to search for an existing element greater than the * key value to insert that is greater than the new element. This is the @@ -357,11 +358,19 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, if (!nft_set_elem_active(&rbe->ext, genmask)) continue; - /* perform garbage collection to avoid bogus overlap reports. */ - if (nft_set_elem_expired(&rbe->ext)) { - err = nft_rbtree_gc_elem(set, priv, rbe, genmask); - if (err < 0) - return err; + /* perform garbage collection to avoid bogus overlap reports + * but skip new elements in this transaction. + */ + if (nft_set_elem_expired(&rbe->ext) && + nft_set_elem_active(&rbe->ext, cur_genmask)) { + const struct nft_rbtree_elem *removed_end; + + removed_end = nft_rbtree_gc_elem(set, priv, rbe, genmask); + if (IS_ERR(removed_end)) + return PTR_ERR(removed_end); + + if (removed_end == rbe_le || removed_end == rbe_ge) + return -EAGAIN; continue; } @@ -482,11 +491,18 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_rbtree_elem *rbe = elem->priv; int err; - write_lock_bh(&priv->lock); - write_seqcount_begin(&priv->count); - err = __nft_rbtree_insert(net, set, rbe, ext); - write_seqcount_end(&priv->count); - write_unlock_bh(&priv->lock); + do { + if (fatal_signal_pending(current)) + return -EINTR; + + cond_resched(); + + write_lock_bh(&priv->lock); + write_seqcount_begin(&priv->count); + err = __nft_rbtree_insert(net, set, rbe, ext); + write_seqcount_end(&priv->count); + write_unlock_bh(&priv->lock); + } while (err == -EAGAIN); return err; } @@ -618,8 +634,7 @@ static void nft_rbtree_gc(struct work_struct *work) if (!gc) goto done; - write_lock_bh(&priv->lock); - write_seqcount_begin(&priv->count); + read_lock_bh(&priv->lock); for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { /* Ruleset has been updated, try later. */ @@ -666,11 +681,10 @@ static void nft_rbtree_gc(struct work_struct *work) nft_trans_gc_elem_add(gc, rbe); } - gc = nft_trans_gc_catchall(gc, gc_seq); + gc = nft_trans_gc_catchall_async(gc, gc_seq); try_later: - write_seqcount_end(&priv->count); - write_unlock_bh(&priv->lock); + read_unlock_bh(&priv->lock); if (gc) nft_trans_gc_queue_async_done(gc); diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index e8961094a2822d991868a318d1ab88a814b45578..b46a6a51205833bb6688d400755a76c3c744bb14 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -149,6 +149,8 @@ static int sctp_mt_check(const struct xt_mtchk_param *par) { const struct xt_sctp_info *info = par->matchinfo; + if (info->flag_count > ARRAY_SIZE(info->flag_info)) + return -EINVAL; if (info->flags & ~XT_SCTP_VALID_FLAGS) return -EINVAL; if (info->invflags & ~XT_SCTP_VALID_FLAGS) diff --git a/net/netfilter/xt_u32.c b/net/netfilter/xt_u32.c index 177b40d08098b22de5e5335cfc3833e68f6ee457..117d4615d6684c53b3a13225d2222a0993df0002 100644 --- a/net/netfilter/xt_u32.c +++ b/net/netfilter/xt_u32.c @@ -96,11 +96,32 @@ static bool u32_mt(const struct sk_buff *skb, struct xt_action_param *par) return ret ^ data->invert; } +static int u32_mt_checkentry(const struct xt_mtchk_param *par) +{ + const struct xt_u32 *data = par->matchinfo; + const struct xt_u32_test *ct; + unsigned int i; + + if (data->ntests > ARRAY_SIZE(data->tests)) + return -EINVAL; + + for (i = 0; i < data->ntests; ++i) { + ct = &data->tests[i]; + + if (ct->nnums > ARRAY_SIZE(ct->location) || + ct->nvalues > ARRAY_SIZE(ct->value)) + return -EINVAL; + } + + return 0; +} + static struct xt_match xt_u32_mt_reg __read_mostly = { .name = "u32", .revision = 0, .family = NFPROTO_UNSPEC, .match = u32_mt, + .checkentry = u32_mt_checkentry, .matchsize = sizeof(struct xt_u32), .me = THIS_MODULE, }; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 642b9d382fb46ddbc3523584c98e07da6860951a..eb086b06d60da48646739226ca084fa6f6f31b74 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -352,7 +352,7 @@ static void netlink_overrun(struct sock *sk) if (!nlk_test_bit(RECV_NO_ENOBUFS, sk)) { if (!test_and_set_bit(NETLINK_S_CONGESTED, &nlk_sk(sk)->state)) { - sk->sk_err = ENOBUFS; + WRITE_ONCE(sk->sk_err, ENOBUFS); sk_error_report(sk); } } @@ -1605,7 +1605,7 @@ static int do_one_set_err(struct sock *sk, struct netlink_set_err_data *p) goto out; } - sk->sk_err = p->code; + WRITE_ONCE(sk->sk_err, p->code); sk_error_report(sk); out: return ret; @@ -1991,7 +1991,7 @@ static int netlink_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { ret = netlink_dump(sk); if (ret) { - sk->sk_err = -ret; + WRITE_ONCE(sk->sk_err, -ret); sk_error_report(sk); } } @@ -2511,7 +2511,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err, err_bad_put: nlmsg_free(skb); err_skb: - NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; + WRITE_ONCE(NETLINK_CB(in_skb).sk->sk_err, ENOBUFS); sk_error_report(NETLINK_CB(in_skb).sk); } EXPORT_SYMBOL(netlink_ack); diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index f60e424e060764916b6bd37e117c22ddea1b00ea..6705bb895e23930fad97715214d7b6e56c8c2829 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -1636,7 +1636,9 @@ int nfc_llcp_register_device(struct nfc_dev *ndev) timer_setup(&local->sdreq_timer, nfc_llcp_sdreq_timer, 0); INIT_WORK(&local->sdreq_timeout_work, nfc_llcp_sdreq_timeout_work); + spin_lock(&llcp_devices_lock); list_add(&local->list, &llcp_devices); + spin_unlock(&llcp_devices_lock); return 0; } diff --git a/net/rds/rdma_transport.c b/net/rds/rdma_transport.c index d36f3f6b43510dde1ef2e98515b41c6890d76ebf..b15cf316b23a221d47eaf0e7c38200840cc2442b 100644 --- a/net/rds/rdma_transport.c +++ b/net/rds/rdma_transport.c @@ -86,11 +86,13 @@ static int rds_rdma_cm_event_handler_cmn(struct rdma_cm_id *cm_id, break; case RDMA_CM_EVENT_ADDR_RESOLVED: - rdma_set_service_type(cm_id, conn->c_tos); - rdma_set_min_rnr_timer(cm_id, IB_RNR_TIMER_000_32); - /* XXX do we need to clean up if this fails? */ - ret = rdma_resolve_route(cm_id, - RDS_RDMA_RESOLVE_TIMEOUT_MS); + if (conn) { + rdma_set_service_type(cm_id, conn->c_tos); + rdma_set_min_rnr_timer(cm_id, IB_RNR_TIMER_000_32); + /* XXX do we need to clean up if this fails? */ + ret = rdma_resolve_route(cm_id, + RDS_RDMA_RESOLVE_TIMEOUT_MS); + } break; case RDMA_CM_EVENT_ROUTE_RESOLVED: diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c index f0c477c5d1db4e355afc370b563652bad4b52905..a0046e99d6df71987707a655cee0def985328b01 100644 --- a/net/rds/tcp_connect.c +++ b/net/rds/tcp_connect.c @@ -145,7 +145,7 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp) addrlen = sizeof(sin); } - ret = sock->ops->bind(sock, addr, addrlen); + ret = kernel_bind(sock, addr, addrlen); if (ret) { rdsdebug("bind failed with %d at address %pI6c\n", ret, &conn->c_laddr); @@ -173,7 +173,7 @@ int rds_tcp_conn_path_connect(struct rds_conn_path *cp) * own the socket */ rds_tcp_set_callbacks(sock, cp); - ret = sock->ops->connect(sock, addr, addrlen, O_NONBLOCK); + ret = kernel_connect(sock, addr, addrlen, O_NONBLOCK); rdsdebug("connect to address %pI6c returned %d\n", &conn->c_faddr, ret); if (ret == -EINPROGRESS) diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c index 014fa24418c12ea29d7be7fb72028f44bf6bd9ce..53b3535a1e4a84c3e5ae9dee110af2df376c7f20 100644 --- a/net/rds/tcp_listen.c +++ b/net/rds/tcp_listen.c @@ -306,7 +306,7 @@ struct socket *rds_tcp_listen_init(struct net *net, bool isv6) addr_len = sizeof(*sin); } - ret = sock->ops->bind(sock, (struct sockaddr *)&ss, addr_len); + ret = kernel_bind(sock, (struct sockaddr *)&ss, addr_len); if (ret < 0) { rdsdebug("could not bind %s listener socket: %d\n", isv6 ? "IPv6" : "IPv4", ret); diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 01fca7a10b4bb0d4108d2096a4dc30ae56f861b9..08630896b6c887645df9e75c97a1fba90935c3e6 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -48,6 +48,7 @@ struct rfkill { bool persistent; bool polling_paused; bool suspended; + bool need_sync; const struct rfkill_ops *ops; void *data; @@ -368,6 +369,17 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked) rfkill_event(rfkill); } +static void rfkill_sync(struct rfkill *rfkill) +{ + lockdep_assert_held(&rfkill_global_mutex); + + if (!rfkill->need_sync) + return; + + rfkill_set_block(rfkill, rfkill_global_states[rfkill->type].cur); + rfkill->need_sync = false; +} + static void rfkill_update_global_state(enum rfkill_type type, bool blocked) { int i; @@ -730,6 +742,10 @@ static ssize_t soft_show(struct device *dev, struct device_attribute *attr, { struct rfkill *rfkill = to_rfkill(dev); + mutex_lock(&rfkill_global_mutex); + rfkill_sync(rfkill); + mutex_unlock(&rfkill_global_mutex); + return sysfs_emit(buf, "%d\n", (rfkill->state & RFKILL_BLOCK_SW) ? 1 : 0); } @@ -751,6 +767,7 @@ static ssize_t soft_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&rfkill_global_mutex); + rfkill_sync(rfkill); rfkill_set_block(rfkill, state); mutex_unlock(&rfkill_global_mutex); @@ -783,6 +800,10 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, { struct rfkill *rfkill = to_rfkill(dev); + mutex_lock(&rfkill_global_mutex); + rfkill_sync(rfkill); + mutex_unlock(&rfkill_global_mutex); + return sysfs_emit(buf, "%d\n", user_state_from_blocked(rfkill->state)); } @@ -805,6 +826,7 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&rfkill_global_mutex); + rfkill_sync(rfkill); rfkill_set_block(rfkill, state == RFKILL_USER_STATE_SOFT_BLOCKED); mutex_unlock(&rfkill_global_mutex); @@ -1032,14 +1054,10 @@ static void rfkill_uevent_work(struct work_struct *work) static void rfkill_sync_work(struct work_struct *work) { - struct rfkill *rfkill; - bool cur; - - rfkill = container_of(work, struct rfkill, sync_work); + struct rfkill *rfkill = container_of(work, struct rfkill, sync_work); mutex_lock(&rfkill_global_mutex); - cur = rfkill_global_states[rfkill->type].cur; - rfkill_set_block(rfkill, cur); + rfkill_sync(rfkill); mutex_unlock(&rfkill_global_mutex); } @@ -1087,6 +1105,7 @@ int __must_check rfkill_register(struct rfkill *rfkill) round_jiffies_relative(POLL_INTERVAL)); if (!rfkill->persistent || rfkill_epo_lock_active) { + rfkill->need_sync = true; schedule_work(&rfkill->sync_work); } else { #ifdef CONFIG_RFKILL_INPUT @@ -1171,6 +1190,7 @@ static int rfkill_fop_open(struct inode *inode, struct file *file) ev = kzalloc(sizeof(*ev), GFP_KERNEL); if (!ev) goto free; + rfkill_sync(rfkill); rfkill_fill_event(&ev->ev, rfkill, RFKILL_OP_ADD); list_add_tail(&ev->list, &data->events); } diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 591d87d5e5c0f10379645da9eb0f4e7309d74916..68e6acd0f130d93de42b75f8af40513899b5e2bd 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -61,6 +61,7 @@ struct fq_pie_sched_data { struct pie_params p_params; u32 ecn_prob; u32 flows_cnt; + u32 flows_cursor; u32 quantum; u32 memory_limit; u32 new_flow_count; @@ -375,22 +376,32 @@ static int fq_pie_change(struct Qdisc *sch, struct nlattr *opt, static void fq_pie_timer(struct timer_list *t) { struct fq_pie_sched_data *q = from_timer(q, t, adapt_timer); + unsigned long next, tupdate; struct Qdisc *sch = q->sch; spinlock_t *root_lock; /* to lock qdisc for probability calculations */ - u32 idx; + int max_cnt, i; rcu_read_lock(); root_lock = qdisc_lock(qdisc_root_sleeping(sch)); spin_lock(root_lock); - for (idx = 0; idx < q->flows_cnt; idx++) - pie_calculate_probability(&q->p_params, &q->flows[idx].vars, - q->flows[idx].backlog); - - /* reset the timer to fire after 'tupdate' jiffies. */ - if (q->p_params.tupdate) - mod_timer(&q->adapt_timer, jiffies + q->p_params.tupdate); + /* Limit this expensive loop to 2048 flows per round. */ + max_cnt = min_t(int, q->flows_cnt - q->flows_cursor, 2048); + for (i = 0; i < max_cnt; i++) { + pie_calculate_probability(&q->p_params, + &q->flows[q->flows_cursor].vars, + q->flows[q->flows_cursor].backlog); + q->flows_cursor++; + } + tupdate = q->p_params.tupdate; + next = 0; + if (q->flows_cursor >= q->flows_cnt) { + q->flows_cursor = 0; + next = tupdate; + } + if (tupdate) + mod_timer(&q->adapt_timer, jiffies + next); spin_unlock(root_lock); rcu_read_unlock(); } diff --git a/net/sched/sch_plug.c b/net/sched/sch_plug.c index ea8c4a7174bba0880851be8631f9234bdecc3ada..35f49edf63dbf2cbdb7010871d7e430cd9d6ed97 100644 --- a/net/sched/sch_plug.c +++ b/net/sched/sch_plug.c @@ -207,7 +207,7 @@ static struct Qdisc_ops plug_qdisc_ops __read_mostly = { .priv_size = sizeof(struct plug_sched_data), .enqueue = plug_enqueue, .dequeue = plug_dequeue, - .peek = qdisc_peek_head, + .peek = qdisc_peek_dequeued, .init = plug_init, .change = plug_change, .reset = qdisc_reset_queue, diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 1a25752f1a9a61dd61445ffef63f03d6e28a01a9..546c10adcacdeaabffbef55f9f992bc239f2fd37 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -974,10 +974,13 @@ static void qfq_update_eligible(struct qfq_sched *q) } /* Dequeue head packet of the head class in the DRR queue of the aggregate. */ -static void agg_dequeue(struct qfq_aggregate *agg, - struct qfq_class *cl, unsigned int len) +static struct sk_buff *agg_dequeue(struct qfq_aggregate *agg, + struct qfq_class *cl, unsigned int len) { - qdisc_dequeue_peeked(cl->qdisc); + struct sk_buff *skb = qdisc_dequeue_peeked(cl->qdisc); + + if (!skb) + return NULL; cl->deficit -= (int) len; @@ -987,6 +990,8 @@ static void agg_dequeue(struct qfq_aggregate *agg, cl->deficit += agg->lmax; list_move_tail(&cl->alist, &agg->active); } + + return skb; } static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg, @@ -1132,11 +1137,18 @@ static struct sk_buff *qfq_dequeue(struct Qdisc *sch) if (!skb) return NULL; - qdisc_qstats_backlog_dec(sch, skb); sch->q.qlen--; + + skb = agg_dequeue(in_serv_agg, cl, len); + + if (!skb) { + sch->q.qlen++; + return NULL; + } + + qdisc_qstats_backlog_dec(sch, skb); qdisc_bstats_update(sch, skb); - agg_dequeue(in_serv_agg, cl, len); /* If lmax is lowered, through qfq_change_class, for a class * owning pending packets with larger size than the new value * of lmax, then the following condition may hold. diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 796529167e8d2fd23bc9e0afd5e0a0ff7e92848e..c45c192b787873bdfa57e78022b415511a39f3a5 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1159,8 +1159,7 @@ int sctp_assoc_update(struct sctp_association *asoc, /* Add any peer addresses from the new association. */ list_for_each_entry(trans, &new->peer.transport_addr_list, transports) - if (!sctp_assoc_lookup_paddr(asoc, &trans->ipaddr) && - !sctp_assoc_add_peer(asoc, &trans->ipaddr, + if (!sctp_assoc_add_peer(asoc, &trans->ipaddr, GFP_ATOMIC, trans->state)) return -ENOMEM; diff --git a/net/sctp/proc.c b/net/sctp/proc.c index f13d6a34f32f2f733df83ac82766466234cda42f..ec00ee75d59a658b7ad0086314f7e82a49ffc876 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -282,7 +282,7 @@ static int sctp_assocs_seq_show(struct seq_file *seq, void *v) assoc->init_retries, assoc->shutdown_retries, assoc->rtx_data_chunks, refcount_read(&sk->sk_wmem_alloc), - sk->sk_wmem_queued, + READ_ONCE(sk->sk_wmem_queued), sk->sk_sndbuf, sk->sk_rcvbuf); seq_printf(seq, "\n"); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index fd0631e70d46a282be382e4edb5bcf91ff7798cc..7f89e43154c091f6f7a3c995c1ba8abb62a8e767 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -69,7 +69,7 @@ #include /* Forward declarations for internal helper functions. */ -static bool sctp_writeable(struct sock *sk); +static bool sctp_writeable(const struct sock *sk); static void sctp_wfree(struct sk_buff *skb); static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, size_t msg_len); @@ -140,7 +140,7 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk) refcount_add(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc); asoc->sndbuf_used += chunk->skb->truesize + sizeof(struct sctp_chunk); - sk->sk_wmem_queued += chunk->skb->truesize + sizeof(struct sctp_chunk); + sk_wmem_queued_add(sk, chunk->skb->truesize + sizeof(struct sctp_chunk)); sk_mem_charge(sk, chunk->skb->truesize); } @@ -2450,6 +2450,7 @@ static int sctp_apply_peer_addr_params(struct sctp_paddrparams *params, if (trans) { trans->hbinterval = msecs_to_jiffies(params->spp_hbinterval); + sctp_transport_reset_hb_timer(trans); } else if (asoc) { asoc->hbinterval = msecs_to_jiffies(params->spp_hbinterval); @@ -9144,7 +9145,7 @@ static void sctp_wfree(struct sk_buff *skb) struct sock *sk = asoc->base.sk; sk_mem_uncharge(sk, skb->truesize); - sk->sk_wmem_queued -= skb->truesize + sizeof(struct sctp_chunk); + sk_wmem_queued_add(sk, -(skb->truesize + sizeof(struct sctp_chunk))); asoc->sndbuf_used -= skb->truesize + sizeof(struct sctp_chunk); WARN_ON(refcount_sub_and_test(sizeof(struct sctp_chunk), &sk->sk_wmem_alloc)); @@ -9299,9 +9300,9 @@ void sctp_write_space(struct sock *sk) * UDP-style sockets or TCP-style sockets, this code should work. * - Daisy */ -static bool sctp_writeable(struct sock *sk) +static bool sctp_writeable(const struct sock *sk) { - return sk->sk_sndbuf > sk->sk_wmem_queued; + return READ_ONCE(sk->sk_sndbuf) > READ_ONCE(sk->sk_wmem_queued); } /* Wait for an association to go into ESTABLISHED state. If timeout is 0, diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index bd01dd31e4bd736e86d20a849fe432b8c3a6aa6d..d520ee62c8ecd6cc6f172fbd953a5d102aab4e74 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1662,6 +1662,7 @@ void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport) { struct smc_link_group *lgr, *n; + spin_lock_bh(&smc_lgr_list.lock); list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) { struct smc_link *link; @@ -1680,6 +1681,7 @@ void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport) if (link) smc_llc_add_link_local(link); } + spin_unlock_bh(&smc_lgr_list.lock); } /* link is down - switch connections to alternate link, diff --git a/net/smc/smc_stats.h b/net/smc/smc_stats.h index b60fe1eb37ab66031474033988f7457129b3d290..aa8928975cc633cf169059713226d737e1634293 100644 --- a/net/smc/smc_stats.h +++ b/net/smc/smc_stats.h @@ -243,8 +243,9 @@ while (0) #define SMC_STAT_SERV_SUCC_INC(net, _ini) \ do { \ typeof(_ini) i = (_ini); \ - bool is_v2 = (i->smcd_version & SMC_V2); \ bool is_smcd = (i->is_smcd); \ + u8 version = is_smcd ? i->smcd_version : i->smcr_version; \ + bool is_v2 = (version & SMC_V2); \ typeof(net->smc.smc_stats) smc_stats = (net)->smc.smc_stats; \ if (is_v2 && is_smcd) \ this_cpu_inc(smc_stats->smc[SMC_TYPE_D].srv_v2_succ_cnt); \ diff --git a/net/socket.c b/net/socket.c index 77f28328e3878bb93f2955868ef019a013834a1e..c4a6f55329552d9c80a41b1b691ce305c22f09f8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -737,6 +737,14 @@ static inline int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg) return ret; } +static int __sock_sendmsg(struct socket *sock, struct msghdr *msg) +{ + int err = security_socket_sendmsg(sock, msg, + msg_data_left(msg)); + + return err ?: sock_sendmsg_nosec(sock, msg); +} + /** * sock_sendmsg - send a message through @sock * @sock: socket @@ -747,10 +755,19 @@ static inline int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg) */ int sock_sendmsg(struct socket *sock, struct msghdr *msg) { - int err = security_socket_sendmsg(sock, msg, - msg_data_left(msg)); + struct sockaddr_storage *save_addr = (struct sockaddr_storage *)msg->msg_name; + struct sockaddr_storage address; + int ret; - return err ?: sock_sendmsg_nosec(sock, msg); + if (msg->msg_name) { + memcpy(&address, msg->msg_name, msg->msg_namelen); + msg->msg_name = &address; + } + + ret = __sock_sendmsg(sock, msg); + msg->msg_name = save_addr; + + return ret; } EXPORT_SYMBOL(sock_sendmsg); @@ -827,7 +844,7 @@ static bool skb_is_swtx_tstamp(const struct sk_buff *skb, int false_tstamp) static ktime_t get_timestamp(struct sock *sk, struct sk_buff *skb, int *if_index) { - bool cycles = sk->sk_tsflags & SOF_TIMESTAMPING_BIND_PHC; + bool cycles = READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC; struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); struct net_device *orig_dev; ktime_t hwtstamp; @@ -879,12 +896,12 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP); int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW); struct scm_timestamping_internal tss; - int empty = 1, false_tstamp = 0; struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); int if_index; ktime_t hwtstamp; + u32 tsflags; /* Race occurred between timestamp enabling and packet receiving. Fill in the current time for now. */ @@ -926,11 +943,12 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, } memset(&tss, 0, sizeof(tss)); - if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) && + tsflags = READ_ONCE(sk->sk_tsflags); + if ((tsflags & SOF_TIMESTAMPING_SOFTWARE) && ktime_to_timespec64_cond(skb->tstamp, tss.ts + 0)) empty = 0; if (shhwtstamps && - (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) && + (tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) && !skb_is_swtx_tstamp(skb, false_tstamp)) { if_index = 0; if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV) @@ -938,14 +956,14 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, else hwtstamp = shhwtstamps->hwtstamp; - if (sk->sk_tsflags & SOF_TIMESTAMPING_BIND_PHC) + if (tsflags & SOF_TIMESTAMPING_BIND_PHC) hwtstamp = ptp_convert_timestamp(&hwtstamp, - sk->sk_bind_phc); + READ_ONCE(sk->sk_bind_phc)); if (ktime_to_timespec64_cond(hwtstamp, tss.ts + 2)) { empty = 0; - if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) && + if ((tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) && !skb_is_err_queue(skb)) put_ts_pktinfo(msg, skb, if_index); } @@ -1137,7 +1155,7 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from) if (sock->type == SOCK_SEQPACKET) msg.msg_flags |= MSG_EOR; - res = sock_sendmsg(sock, &msg); + res = __sock_sendmsg(sock, &msg); *from = msg.msg_iter; return res; } @@ -2173,7 +2191,7 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; msg.msg_flags = flags; - err = sock_sendmsg(sock, &msg); + err = __sock_sendmsg(sock, &msg); out_put: fput_light(sock->file, fput_needed); @@ -2537,7 +2555,7 @@ static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys, err = sock_sendmsg_nosec(sock, msg_sys); goto out_freectl; } - err = sock_sendmsg(sock, msg_sys); + err = __sock_sendmsg(sock, msg_sys); /* * If this is sendmmsg() and sending to current destination address was * successful, remember it. @@ -3498,7 +3516,12 @@ static long compat_sock_ioctl(struct file *file, unsigned int cmd, int kernel_bind(struct socket *sock, struct sockaddr *addr, int addrlen) { - return READ_ONCE(sock->ops)->bind(sock, addr, addrlen); + struct sockaddr_storage address; + + memcpy(&address, addr, addrlen); + + return READ_ONCE(sock->ops)->bind(sock, (struct sockaddr *)&address, + addrlen); } EXPORT_SYMBOL(kernel_bind); diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 2f16f9d179662442b94bb729f772fdb1b39c2a39..814b0169f972304e105e1a63dfcf6ce59f3428cd 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -769,9 +769,14 @@ int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) * @task: controlling RPC task * @xdr: xdr_stream containing RPC Reply header * - * On success, @xdr is updated to point past the verifier and - * zero is returned. Otherwise, @xdr is in an undefined state - * and a negative errno is returned. + * Return values: + * %0: Verifier is valid. @xdr now points past the verifier. + * %-EIO: Verifier is corrupted or message ended early. + * %-EACCES: Verifier is intact but not valid. + * %-EPROTONOSUPPORT: Server does not support the requested auth type. + * + * When a negative errno is returned, @xdr is left in an undefined + * state. */ int rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr) diff --git a/net/sunrpc/auth_tls.c b/net/sunrpc/auth_tls.c index de7678f8a23d26ff25acb5ff338181a44d5727d2..87f570fd3b00ead957f7e8541affb37be43ef3f8 100644 --- a/net/sunrpc/auth_tls.c +++ b/net/sunrpc/auth_tls.c @@ -129,9 +129,9 @@ static int tls_validate(struct rpc_task *task, struct xdr_stream *xdr) if (*p != rpc_auth_null) return -EIO; if (xdr_stream_decode_opaque_inline(xdr, &str, starttls_len) != starttls_len) - return -EIO; + return -EPROTONOSUPPORT; if (memcmp(str, starttls_token, starttls_len)) - return -EIO; + return -EPROTONOSUPPORT; return 0; } diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 8d75290f1a31d23d5f61439c18549ed76c36ced8..9c210273d06b7f51184c08d38c71929a87eef1c2 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -2476,8 +2476,7 @@ call_status(struct rpc_task *task) goto out_exit; } task->tk_action = call_encode; - if (status != -ECONNRESET && status != -ECONNABORTED) - rpc_check_timeout(task); + rpc_check_timeout(task); return; out_exit: rpc_call_rpcerror(task, status); @@ -2725,7 +2724,15 @@ rpc_decode_header(struct rpc_task *task, struct xdr_stream *xdr) out_verifier: trace_rpc_bad_verifier(task); - goto out_err; + switch (error) { + case -EPROTONOSUPPORT: + goto out_err; + case -EACCES: + /* Re-encode with a fresh cred */ + fallthrough; + default: + goto out_garbage; + } out_msg_denied: error = -EACCES; @@ -2751,6 +2758,7 @@ rpc_decode_header(struct rpc_task *task, struct xdr_stream *xdr) case rpc_autherr_rejectedverf: case rpcsec_gsserr_credproblem: case rpcsec_gsserr_ctxproblem: + rpcauth_invalcred(task); if (!task->tk_cred_retry) break; task->tk_cred_retry--; @@ -2907,19 +2915,22 @@ static const struct rpc_call_ops rpc_cb_add_xprt_call_ops = { * @clnt: pointer to struct rpc_clnt * @xps: pointer to struct rpc_xprt_switch, * @xprt: pointer struct rpc_xprt - * @dummy: unused + * @in_max_connect: pointer to the max_connect value for the passed in xprt transport */ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt_switch *xps, struct rpc_xprt *xprt, - void *dummy) + void *in_max_connect) { struct rpc_cb_add_xprt_calldata *data; struct rpc_task *task; + int max_connect = clnt->cl_max_connect; - if (xps->xps_nunique_destaddr_xprts + 1 > clnt->cl_max_connect) { + if (in_max_connect) + max_connect = *(int *)in_max_connect; + if (xps->xps_nunique_destaddr_xprts + 1 > max_connect) { rcu_read_lock(); pr_warn("SUNRPC: reached max allowed number (%d) did not add " - "transport to server: %s\n", clnt->cl_max_connect, + "transport to server: %s\n", max_connect, rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR)); rcu_read_unlock(); return -EINVAL; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 71cd916e384f173b30d2e96bfc504474d9e832d5..a15bf2ede89bf5f09843022b7b24004b433b4832 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2672,6 +2672,10 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) rcu_read_lock(); lower_xprt = rcu_dereference(lower_clnt->cl_xprt); rcu_read_unlock(); + + if (wait_on_bit_lock(&lower_xprt->state, XPRT_LOCKED, TASK_KILLABLE)) + goto out_unlock; + status = xs_tls_handshake_sync(lower_xprt, &upper_xprt->xprtsec); if (status) { trace_rpc_tls_not_started(upper_clnt, upper_xprt); @@ -2681,6 +2685,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) status = xs_tcp_tls_finish_connecting(lower_xprt, upper_transport); if (status) goto out_close; + xprt_release_write(lower_xprt, NULL); trace_rpc_socket_connect(upper_xprt, upper_transport->sock, 0); if (!xprt_test_and_set_connected(upper_xprt)) { @@ -2702,6 +2707,7 @@ static void xs_tcp_tls_setup_socket(struct work_struct *work) return; out_close: + xprt_release_write(lower_xprt, NULL); rpc_shutdown_client(lower_clnt); /* xprt_force_disconnect() wakes tasks with a fixed tk_status code. diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c index 302fd749c4249a9644dbc907ca5f9d11f8014d2b..43c3f1c971b8fdf43e93b1616483fb2d8fef3d57 100644 --- a/net/tipc/crypto.c +++ b/net/tipc/crypto.c @@ -1441,14 +1441,14 @@ static int tipc_crypto_key_revoke(struct net *net, u8 tx_key) struct tipc_crypto *tx = tipc_net(net)->crypto_tx; struct tipc_key key; - spin_lock(&tx->lock); + spin_lock_bh(&tx->lock); key = tx->key; WARN_ON(!key.active || tx_key != key.active); /* Free the active key */ tipc_crypto_key_set_state(tx, key.passive, 0, key.pending); tipc_crypto_key_detach(tx->aead[key.active], &tx->lock); - spin_unlock(&tx->lock); + spin_unlock_bh(&tx->lock); pr_warn("%s: key is revoked\n", tx->name); return -EKEYREVOKED; diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 1ed4a611631fe4e01d6646b6c2d7fc0d505611fc..d1fc295b83b59a03a472f8b9d3d740765bad7fbb 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -817,7 +817,7 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk, psock = sk_psock_get(sk); if (!psock || !policy) { err = tls_push_record(sk, flags, record_type); - if (err && sk->sk_err == EBADMSG) { + if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { *copied -= sk_msg_free(sk, msg); tls_free_open_rec(sk); err = -sk->sk_err; @@ -846,7 +846,7 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk, switch (psock->eval) { case __SK_PASS: err = tls_push_record(sk, flags, record_type); - if (err && sk->sk_err == EBADMSG) { + if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { *copied -= sk_msg_free(sk, msg); tls_free_open_rec(sk); err = -sk->sk_err; diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 86930a8ed012bfad9a407db985782934f52b9698..3e8a04a136688341da1dd49255f740f9fba371ff 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -680,7 +680,7 @@ static void unix_release_sock(struct sock *sk, int embrion) * What the above comment does talk about? --ANK(980817) */ - if (unix_tot_inflight) + if (READ_ONCE(unix_tot_inflight)) unix_gc(); /* Garbage collect fds */ } diff --git a/net/unix/scm.c b/net/unix/scm.c index e9dde7176c8a3a1100aaf659991ceeedf8ef223d..6ff628f2349f571fa8f1e86454a0339a78f7b0f0 100644 --- a/net/unix/scm.c +++ b/net/unix/scm.c @@ -64,7 +64,7 @@ void unix_inflight(struct user_struct *user, struct file *fp) /* Paired with READ_ONCE() in wait_for_unix_gc() */ WRITE_ONCE(unix_tot_inflight, unix_tot_inflight + 1); } - user->unix_inflight++; + WRITE_ONCE(user->unix_inflight, user->unix_inflight + 1); spin_unlock(&unix_gc_lock); } @@ -85,7 +85,7 @@ void unix_notinflight(struct user_struct *user, struct file *fp) /* Paired with READ_ONCE() in wait_for_unix_gc() */ WRITE_ONCE(unix_tot_inflight, unix_tot_inflight - 1); } - user->unix_inflight--; + WRITE_ONCE(user->unix_inflight, user->unix_inflight - 1); spin_unlock(&unix_gc_lock); } @@ -99,7 +99,7 @@ static inline bool too_many_unix_fds(struct task_struct *p) { struct user_struct *user = current_user(); - if (unlikely(user->unix_inflight > task_rlimit(p, RLIMIT_NOFILE))) + if (unlikely(READ_ONCE(user->unix_inflight) > task_rlimit(p, RLIMIT_NOFILE))) return !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN); return false; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 25bc2e50a061593cebf2af38ffa5764cb5c37378..64e8616171104f97571aca6bb6fe9a7e64763444 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -1181,16 +1181,11 @@ void wiphy_rfkill_set_hw_state_reason(struct wiphy *wiphy, bool blocked, } EXPORT_SYMBOL(wiphy_rfkill_set_hw_state_reason); -void cfg80211_cqm_config_free(struct wireless_dev *wdev) -{ - kfree(wdev->cqm_config); - wdev->cqm_config = NULL; -} - static void _cfg80211_unregister_wdev(struct wireless_dev *wdev, bool unregister_netdev) { struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct cfg80211_cqm_config *cqm_config; unsigned int link_id; ASSERT_RTNL(); @@ -1227,7 +1222,10 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev, kfree_sensitive(wdev->wext.keys); wdev->wext.keys = NULL; #endif - cfg80211_cqm_config_free(wdev); + wiphy_work_cancel(wdev->wiphy, &wdev->cqm_rssi_work); + /* deleted from the list, so can't be found from nl80211 any more */ + cqm_config = rcu_access_pointer(wdev->cqm_config); + kfree_rcu(cqm_config, rcu_head); /* * Ensure that all events have been processed and @@ -1379,6 +1377,8 @@ void cfg80211_init_wdev(struct wireless_dev *wdev) wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; #endif + wiphy_work_init(&wdev->cqm_rssi_work, cfg80211_cqm_rssi_notify_work); + if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) wdev->ps = true; else diff --git a/net/wireless/core.h b/net/wireless/core.h index 507d184b8b40bc31666b1da510bdca215c33dc94..ba9c7170afa44e1a37c11d750cf6fd6c4998ba7c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -295,12 +295,17 @@ struct cfg80211_beacon_registration { }; struct cfg80211_cqm_config { + struct rcu_head rcu_head; u32 rssi_hyst; s32 last_rssi_event_value; + enum nl80211_cqm_rssi_threshold_event last_rssi_event_type; int n_rssi_thresholds; s32 rssi_thresholds[] __counted_by(n_rssi_thresholds); }; +void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, + struct wiphy_work *work); + void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev); /* free object */ @@ -566,8 +571,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, #define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) #endif -void cfg80211_cqm_config_free(struct wireless_dev *wdev); - void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid); void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev); void cfg80211_pmsr_free_wk(struct work_struct *work); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 775cac4d61006f0221559e21232f913365cf529b..3e2c398abddccb3ae0ac9926a374d8dacfe6d1f0 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -52,7 +52,8 @@ void cfg80211_rx_assoc_resp(struct net_device *dev, cr.links[link_id].bssid = data->links[link_id].bss->bssid; cr.links[link_id].addr = data->links[link_id].addr; /* need to have local link addresses for MLO connections */ - WARN_ON(cr.ap_mld_addr && !cr.links[link_id].addr); + WARN_ON(cr.ap_mld_addr && + !is_valid_ether_addr(cr.links[link_id].addr)); BUG_ON(!cr.links[link_id].bss->channel); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index de47838aca4f25af9fc6872225004441ca809cc1..931a03f4549c9fc1e85e20870742c935baca83a2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5909,6 +5909,21 @@ static void nl80211_send_ap_started(struct wireless_dev *wdev, nlmsg_free(msg); } +static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params) +{ + struct ieee80211_channel *channel = params->chandef.chan; + + if ((params->he_cap || params->he_oper) && + (channel->flags & IEEE80211_CHAN_NO_HE)) + return -EOPNOTSUPP; + + if ((params->eht_cap || params->eht_oper) && + (channel->flags & IEEE80211_CHAN_NO_EHT)) + return -EOPNOTSUPP; + + return 0; +} + static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6178,6 +6193,10 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) if (err) goto out_unlock; + err = nl80211_validate_ap_phy_operation(params); + if (err) + goto out_unlock; + if (info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]) params->flags = nla_get_u32( info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]); @@ -8482,7 +8501,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; - struct mesh_config cfg; + struct mesh_config cfg = {}; u32 mask; int err; @@ -12796,7 +12815,8 @@ static int nl80211_set_cqm_txe(struct genl_info *info, } static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, - struct net_device *dev) + struct net_device *dev, + struct cfg80211_cqm_config *cqm_config) { struct wireless_dev *wdev = dev->ieee80211_ptr; s32 last, low, high; @@ -12805,7 +12825,7 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, int err; /* RSSI reporting disabled? */ - if (!wdev->cqm_config) + if (!cqm_config) return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0); /* @@ -12814,7 +12834,7 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, * connection is established and enough beacons received to calculate * the average. */ - if (!wdev->cqm_config->last_rssi_event_value && + if (!cqm_config->last_rssi_event_value && wdev->links[0].client.current_bss && rdev->ops->get_station) { struct station_info sinfo = {}; @@ -12828,30 +12848,30 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev, cfg80211_sinfo_release_content(&sinfo); if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) - wdev->cqm_config->last_rssi_event_value = + cqm_config->last_rssi_event_value = (s8) sinfo.rx_beacon_signal_avg; } - last = wdev->cqm_config->last_rssi_event_value; - hyst = wdev->cqm_config->rssi_hyst; - n = wdev->cqm_config->n_rssi_thresholds; + last = cqm_config->last_rssi_event_value; + hyst = cqm_config->rssi_hyst; + n = cqm_config->n_rssi_thresholds; for (i = 0; i < n; i++) { i = array_index_nospec(i, n); - if (last < wdev->cqm_config->rssi_thresholds[i]) + if (last < cqm_config->rssi_thresholds[i]) break; } low_index = i - 1; if (low_index >= 0) { low_index = array_index_nospec(low_index, n); - low = wdev->cqm_config->rssi_thresholds[low_index] - hyst; + low = cqm_config->rssi_thresholds[low_index] - hyst; } else { low = S32_MIN; } if (i < n) { i = array_index_nospec(i, n); - high = wdev->cqm_config->rssi_thresholds[i] + hyst - 1; + high = cqm_config->rssi_thresholds[i] + hyst - 1; } else { high = S32_MAX; } @@ -12864,6 +12884,7 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, u32 hysteresis) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_cqm_config *cqm_config = NULL, *old; struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; int i, err; @@ -12881,10 +12902,6 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) return -EOPNOTSUPP; - wdev_lock(wdev); - cfg80211_cqm_config_free(wdev); - wdev_unlock(wdev); - if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) { if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */ return rdev_set_cqm_rssi_config(rdev, dev, 0, 0); @@ -12901,9 +12918,10 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, n_thresholds = 0; wdev_lock(wdev); - if (n_thresholds) { - struct cfg80211_cqm_config *cqm_config; + old = rcu_dereference_protected(wdev->cqm_config, + lockdep_is_held(&wdev->mtx)); + if (n_thresholds) { cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds, n_thresholds), GFP_KERNEL); @@ -12918,11 +12936,18 @@ static int nl80211_set_cqm_rssi(struct genl_info *info, flex_array_size(cqm_config, rssi_thresholds, n_thresholds)); - wdev->cqm_config = cqm_config; + rcu_assign_pointer(wdev->cqm_config, cqm_config); + } else { + RCU_INIT_POINTER(wdev->cqm_config, NULL); } - err = cfg80211_cqm_rssi_update(rdev, dev); - + err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); + if (err) { + rcu_assign_pointer(wdev->cqm_config, old); + kfree_rcu(cqm_config, rcu_head); + } else { + kfree_rcu(old, rcu_head); + } unlock: wdev_unlock(wdev); @@ -19073,9 +19098,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, enum nl80211_cqm_rssi_threshold_event rssi_event, s32 rssi_level, gfp_t gfp) { - struct sk_buff *msg; struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct cfg80211_cqm_config *cqm_config; trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level); @@ -19083,18 +19107,41 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)) return; - if (wdev->cqm_config) { - wdev->cqm_config->last_rssi_event_value = rssi_level; + rcu_read_lock(); + cqm_config = rcu_dereference(wdev->cqm_config); + if (cqm_config) { + cqm_config->last_rssi_event_value = rssi_level; + cqm_config->last_rssi_event_type = rssi_event; + wiphy_work_queue(wdev->wiphy, &wdev->cqm_rssi_work); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); - cfg80211_cqm_rssi_update(rdev, dev); +void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct wireless_dev *wdev = container_of(work, struct wireless_dev, + cqm_rssi_work); + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + enum nl80211_cqm_rssi_threshold_event rssi_event; + struct cfg80211_cqm_config *cqm_config; + struct sk_buff *msg; + s32 rssi_level; - if (rssi_level == 0) - rssi_level = wdev->cqm_config->last_rssi_event_value; - } + wdev_lock(wdev); + cqm_config = rcu_dereference_protected(wdev->cqm_config, + lockdep_is_held(&wdev->mtx)); + if (!wdev->cqm_config) + goto unlock; - msg = cfg80211_prepare_cqm(dev, NULL, gfp); + cfg80211_cqm_rssi_update(rdev, wdev->netdev, cqm_config); + + rssi_level = cqm_config->last_rssi_event_value; + rssi_event = cqm_config->last_rssi_event_type; + + msg = cfg80211_prepare_cqm(wdev->netdev, NULL, GFP_KERNEL); if (!msg) - return; + goto unlock; if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, rssi_event)) @@ -19104,14 +19151,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, rssi_level)) goto nla_put_failure; - cfg80211_send_cqm(msg, gfp); + cfg80211_send_cqm(msg, GFP_KERNEL); - return; + goto unlock; nla_put_failure: nlmsg_free(msg); + unlock: + wdev_unlock(wdev); } -EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer, u32 num_packets, diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0cf1ce7b6934238626a9f3173e74af89ee0822cb..939deecf0bbeffd550b2f571f7da4897881d534c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -908,6 +908,10 @@ static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) !cfg80211_find_ssid_match(ap, request)) continue; + if (!is_broadcast_ether_addr(request->bssid) && + !ether_addr_equal(request->bssid, ap->bssid)) + continue; + if (!request->n_ssids && ap->multi_bss && !ap->transmitted_bssid) continue; diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index fcfc8472f73da8500201473ce9960ab838c0428b..55f8b9b0e06d1ff88e918de56dce9635f3a19f08 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -602,7 +602,7 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs, for (copied = 0, i = skb_shinfo(skb)->nr_frags; copied < len; i++) { if (unlikely(i >= MAX_SKB_FRAGS)) - return ERR_PTR(-EFAULT); + return ERR_PTR(-EOVERFLOW); page = pool->umem->pgs[addr >> PAGE_SHIFT]; get_page(page); @@ -655,15 +655,17 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, skb_put(skb, len); err = skb_store_bits(skb, 0, buffer, len); - if (unlikely(err)) + if (unlikely(err)) { + kfree_skb(skb); goto free_err; + } } else { int nr_frags = skb_shinfo(skb)->nr_frags; struct page *page; u8 *vaddr; if (unlikely(nr_frags == (MAX_SKB_FRAGS - 1) && xp_mb_desc(desc))) { - err = -EFAULT; + err = -EOVERFLOW; goto free_err; } @@ -690,12 +692,14 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs, return skb; free_err: - if (err == -EAGAIN) { - xsk_cq_cancel_locked(xs, 1); - } else { - xsk_set_destructor_arg(skb); - xsk_drop_skb(skb); + if (err == -EOVERFLOW) { + /* Drop the packet */ + xsk_set_destructor_arg(xs->skb); + xsk_drop_skb(xs->skb); xskq_cons_release(xs->tx); + } else { + /* Let application retry */ + xsk_cq_cancel_locked(xs, 1); } return ERR_PTR(err); @@ -738,7 +742,7 @@ static int __xsk_generic_xmit(struct sock *sk) skb = xsk_build_skb(xs, &desc); if (IS_ERR(skb)) { err = PTR_ERR(skb); - if (err == -EAGAIN) + if (err != -EOVERFLOW) goto out; err = 0; continue; diff --git a/net/xdp/xsk_diag.c b/net/xdp/xsk_diag.c index c014217f5fa7d92aba92b2682416d208f8a53fdf..22b36c8143cfd5a96ccb7e1934880a98f16f179c 100644 --- a/net/xdp/xsk_diag.c +++ b/net/xdp/xsk_diag.c @@ -111,6 +111,9 @@ static int xsk_diag_fill(struct sock *sk, struct sk_buff *nlskb, sock_diag_save_cookie(sk, msg->xdiag_cookie); mutex_lock(&xs->mutex); + if (READ_ONCE(xs->state) == XSK_UNBOUND) + goto out_nlmsg_trim; + if ((req->xdiag_show & XDP_SHOW_INFO) && xsk_diag_put_info(xs, nlskb)) goto out_nlmsg_trim; diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 40cd13eca82e80f6de41e8d273a739d28c9c8e49..2fe6f2828d3769a88da2a8ffcae91a64442fc9fb 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -6,7 +6,79 @@ # They are independent, and can be combined like W=12 or W=123e. # ========================================================================== -KBUILD_CFLAGS += $(call cc-disable-warning, packed-not-aligned) +# Default set of warnings, always enabled +KBUILD_CFLAGS += -Wall +KBUILD_CFLAGS += -Wundef +KBUILD_CFLAGS += -Werror=implicit-function-declaration +KBUILD_CFLAGS += -Werror=implicit-int +KBUILD_CFLAGS += -Werror=return-type +KBUILD_CFLAGS += -Werror=strict-prototypes +KBUILD_CFLAGS += -Wno-format-security +KBUILD_CFLAGS += -Wno-trigraphs +KBUILD_CFLAGS += $(call cc-disable-warning,frame-address,) +KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member) + +ifneq ($(CONFIG_FRAME_WARN),0) +KBUILD_CFLAGS += -Wframe-larger-than=$(CONFIG_FRAME_WARN) +endif + +KBUILD_CPPFLAGS-$(CONFIG_WERROR) += -Werror +KBUILD_CPPFLAGS += $(KBUILD_CPPFLAGS-y) +KBUILD_CFLAGS-$(CONFIG_CC_NO_ARRAY_BOUNDS) += -Wno-array-bounds + +ifdef CONFIG_CC_IS_CLANG +# The kernel builds with '-std=gnu11' so use of GNU extensions is acceptable. +KBUILD_CFLAGS += -Wno-gnu +else + +# gcc inanely warns about local variables called 'main' +KBUILD_CFLAGS += -Wno-main +endif + +# These warnings generated too much noise in a regular build. +# Use make W=1 to enable them (see scripts/Makefile.extrawarn) +KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) +KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable) + +# These result in bogus false positives +KBUILD_CFLAGS += $(call cc-disable-warning, dangling-pointer) + +# Variable Length Arrays (VLAs) should not be used anywhere in the kernel +KBUILD_CFLAGS += -Wvla + +# disable pointer signed / unsigned warnings in gcc 4.0 +KBUILD_CFLAGS += -Wno-pointer-sign + +# In order to make sure new function cast mismatches are not introduced +# in the kernel (to avoid tripping CFI checking), the kernel should be +# globally built with -Wcast-function-type. +KBUILD_CFLAGS += $(call cc-option, -Wcast-function-type) + +# The allocators already balk at large sizes, so silence the compiler +# warnings for bounds checks involving those possible values. While +# -Wno-alloc-size-larger-than would normally be used here, earlier versions +# of gcc (<9.1) weirdly don't handle the option correctly when _other_ +# warnings are produced (?!). Using -Walloc-size-larger-than=SIZE_MAX +# doesn't work (as it is documented to), silently resolving to "0" prior to +# version 9.1 (and producing an error more recently). Numeric values larger +# than PTRDIFF_MAX also don't work prior to version 9.1, which are silently +# ignored, continuing to default to PTRDIFF_MAX. So, left with no other +# choice, we must perform a versioned check to disable this warning. +# https://lore.kernel.org/lkml/20210824115859.187f272f@canb.auug.org.au +KBUILD_CFLAGS-$(call gcc-min-version, 90100) += -Wno-alloc-size-larger-than +KBUILD_CFLAGS += $(KBUILD_CFLAGS-y) $(CONFIG_CC_IMPLICIT_FALLTHROUGH) + +# Prohibit date/time macros, which would make the build non-deterministic +KBUILD_CFLAGS += -Werror=date-time + +# enforce correct pointer usage +KBUILD_CFLAGS += $(call cc-option,-Werror=incompatible-pointer-types) + +# Require designated initializers for all marked structures +KBUILD_CFLAGS += $(call cc-option,-Werror=designated-init) + +# Warn if there is an enum types mismatch +KBUILD_CFLAGS += $(call cc-option,-Wenum-conversion) # backward compatibility KBUILD_EXTRA_WARN ?= $(KBUILD_ENABLE_EXTRA_GCC_CHECKS) @@ -24,6 +96,7 @@ ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),) KBUILD_CFLAGS += -Wextra -Wunused -Wno-unused-parameter KBUILD_CFLAGS += -Wmissing-declarations +KBUILD_CFLAGS += $(call cc-option, -Wrestrict) KBUILD_CFLAGS += -Wmissing-format-attribute KBUILD_CFLAGS += -Wmissing-prototypes KBUILD_CFLAGS += -Wold-style-definition @@ -31,12 +104,10 @@ KBUILD_CFLAGS += -Wmissing-include-dirs KBUILD_CFLAGS += $(call cc-option, -Wunused-but-set-variable) KBUILD_CFLAGS += $(call cc-option, -Wunused-const-variable) KBUILD_CFLAGS += $(call cc-option, -Wpacked-not-aligned) +KBUILD_CFLAGS += $(call cc-option, -Wformat-overflow) +KBUILD_CFLAGS += $(call cc-option, -Wformat-truncation) +KBUILD_CFLAGS += $(call cc-option, -Wstringop-overflow) KBUILD_CFLAGS += $(call cc-option, -Wstringop-truncation) -# The following turn off the warnings enabled by -Wextra -KBUILD_CFLAGS += -Wno-missing-field-initializers -KBUILD_CFLAGS += -Wno-sign-compare -KBUILD_CFLAGS += -Wno-type-limits -KBUILD_CFLAGS += -Wno-shift-negative-value KBUILD_CPPFLAGS += -Wundef KBUILD_CPPFLAGS += -DKBUILD_EXTRA_WARN1 @@ -45,9 +116,16 @@ else # Some diagnostics enabled by default are noisy. # Suppress them by using -Wno... except for W=1. +KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) +KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable) +KBUILD_CFLAGS += $(call cc-disable-warning, restrict) +KBUILD_CFLAGS += $(call cc-disable-warning, packed-not-aligned) +KBUILD_CFLAGS += $(call cc-disable-warning, format-overflow) +KBUILD_CFLAGS += $(call cc-disable-warning, format-truncation) +KBUILD_CFLAGS += $(call cc-disable-warning, stringop-overflow) +KBUILD_CFLAGS += $(call cc-disable-warning, stringop-truncation) ifdef CONFIG_CC_IS_CLANG -KBUILD_CFLAGS += -Wno-initializer-overrides # Clang before clang-16 would warn on default argument promotions. ifneq ($(call clang-min-version, 160000),y) # Disable -Wformat @@ -61,7 +139,6 @@ ifeq ($(call clang-min-version, 120000),y) KBUILD_CFLAGS += -Wformat-insufficient-args endif endif -KBUILD_CFLAGS += -Wno-sign-compare KBUILD_CFLAGS += $(call cc-disable-warning, pointer-to-enum-cast) KBUILD_CFLAGS += -Wno-tautological-constant-out-of-range-compare KBUILD_CFLAGS += $(call cc-disable-warning, unaligned-access) @@ -83,8 +160,25 @@ KBUILD_CFLAGS += -Wtype-limits KBUILD_CFLAGS += $(call cc-option, -Wmaybe-uninitialized) KBUILD_CFLAGS += $(call cc-option, -Wunused-macros) +ifdef CONFIG_CC_IS_CLANG +KBUILD_CFLAGS += -Winitializer-overrides +endif + KBUILD_CPPFLAGS += -DKBUILD_EXTRA_WARN2 +else + +# The following turn off the warnings enabled by -Wextra +KBUILD_CFLAGS += -Wno-missing-field-initializers +KBUILD_CFLAGS += -Wno-type-limits +KBUILD_CFLAGS += -Wno-shift-negative-value + +ifdef CONFIG_CC_IS_CLANG +KBUILD_CFLAGS += -Wno-initializer-overrides +else +KBUILD_CFLAGS += -Wno-maybe-uninitialized +endif + endif # @@ -106,6 +200,11 @@ KBUILD_CFLAGS += $(call cc-option, -Wpacked-bitfield-compat) KBUILD_CPPFLAGS += -DKBUILD_EXTRA_WARN3 +else + +# The following turn off the warnings enabled by -Wextra +KBUILD_CFLAGS += -Wno-sign-compare + endif # diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index ab0c5bd1a60fe4fa997be22e35284c3070739ae9..0afd75472679f63d4cd60cac1feebe1c2a4f8c61 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -9,6 +9,35 @@ __modinst: include include/config/auto.conf include $(srctree)/scripts/Kbuild.include +install-y := + +ifeq ($(KBUILD_EXTMOD)$(sign-only),) + +# remove the old directory and symlink +$(shell rm -fr $(MODLIB)/kernel $(MODLIB)/build) + +install-$(CONFIG_MODULES) += $(addprefix $(MODLIB)/, build modules.order) + +$(MODLIB)/build: FORCE + $(call cmd,symlink) + +quiet_cmd_symlink = SYMLINK $@ + cmd_symlink = ln -s $(CURDIR) $@ + +$(MODLIB)/modules.order: modules.order FORCE + $(call cmd,install_modorder) + +quiet_cmd_install_modorder = INSTALL $@ + cmd_install_modorder = sed 's:^\(.*\)\.o$$:kernel/\1.ko:' $< > $@ + +# Install modules.builtin(.modinfo) even when CONFIG_MODULES is disabled. +install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo) + +$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo): $(MODLIB)/%: % FORCE + $(call cmd,install) + +endif + modules := $(call read-file, $(MODORDER)) ifeq ($(KBUILD_EXTMOD),) @@ -27,15 +56,16 @@ suffix-$(CONFIG_MODULE_COMPRESS_XZ) := .xz suffix-$(CONFIG_MODULE_COMPRESS_ZSTD) := .zst modules := $(patsubst $(extmod_prefix)%.o, $(dst)/%.ko$(suffix-y), $(modules)) +install-$(CONFIG_MODULES) += $(modules) -__modinst: $(modules) +__modinst: $(install-y) @: # # Installation # quiet_cmd_install = INSTALL $@ - cmd_install = mkdir -p $(dir $@); cp $< $@ + cmd_install = cp $< $@ # Strip # @@ -65,7 +95,6 @@ endif # Signing # Don't stop modules_install even if we can't sign external modules. # -ifeq ($(CONFIG_MODULE_SIG_ALL),y) ifeq ($(filter pkcs11:%, $(CONFIG_MODULE_SIG_KEY)),) sig-key := $(if $(wildcard $(CONFIG_MODULE_SIG_KEY)),,$(srctree)/)$(CONFIG_MODULE_SIG_KEY) else @@ -74,18 +103,34 @@ endif quiet_cmd_sign = SIGN $@ cmd_sign = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) "$(sig-key)" certs/signing_key.x509 $@ \ $(if $(KBUILD_EXTMOD),|| true) -else + +ifeq ($(sign-only),) + +# During modules_install, modules are signed only when CONFIG_MODULE_SIG_ALL=y. +ifndef CONFIG_MODULE_SIG_ALL quiet_cmd_sign := cmd_sign := : endif -ifeq ($(modules_sign_only),) +# Create necessary directories +$(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir))) $(dst)/%.ko: $(extmod_prefix)%.ko FORCE $(call cmd,install) $(call cmd,strip) $(call cmd,sign) +ifdef CONFIG_MODULES +__modinst: depmod + +PHONY += depmod +depmod: $(install-y) + $(call cmd,depmod) + +quiet_cmd_depmod = DEPMOD $(MODLIB) + cmd_depmod = $(srctree)/scripts/depmod.sh $(KERNELRELEASE) +endif + else $(dst)/%.ko: FORCE @@ -99,7 +144,7 @@ endif quiet_cmd_gzip = GZIP $@ cmd_gzip = $(KGZIP) -n -f $< quiet_cmd_xz = XZ $@ - cmd_xz = $(XZ) --lzma2=dict=2MiB -f $< + cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $< quiet_cmd_zstd = ZSTD $@ cmd_zstd = $(ZSTD) -T0 --rm -f -q $< diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 39472e834b634009f551dab7bcb72a8b0b70b37f..739402f4550979850d71dcdc31649f4daac9d44d 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -41,6 +41,7 @@ include $(srctree)/scripts/Kbuild.include MODPOST = scripts/mod/modpost modpost-args = \ + $(if $(CONFIG_MODULES),-M) \ $(if $(CONFIG_MODVERSIONS),-m) \ $(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \ $(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \ diff --git a/scripts/Makefile.package b/scripts/Makefile.package index 92dbc889bd7c0008abc10feb4528e1b8363f8d93..2bcab02da9653c2c0b5cec147dfffdc31378e5f1 100644 --- a/scripts/Makefile.package +++ b/scripts/Makefile.package @@ -11,7 +11,6 @@ TAR_CONTENT := Documentation LICENSES arch block certs crypto drivers fs \ samples scripts security sound tools usr virt \ .config Makefile \ Kbuild Kconfig COPYING $(wildcard localversion*) -MKSPEC := $(srctree)/scripts/package/mkspec quiet_cmd_src_tar = TAR $(2).tar.gz cmd_src_tar = \ @@ -66,30 +65,38 @@ $(linux-tarballs): archive-args = --prefix=linux/ $$(cat $<) $(linux-tarballs): .tmp_HEAD FORCE $(call if_changed,archive) -# rpm-pkg +# rpm-pkg srcrpm-pkg binrpm-pkg # --------------------------------------------------------------------------- -PHONY += rpm-pkg -rpm-pkg: srpm = $(shell rpmspec --srpm --query --queryformat='%{name}-%{VERSION}-%{RELEASE}.src.rpm' kernel.spec) -rpm-pkg: srcrpm-pkg - +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE)-linux -rb $(srpm) \ - --define='_smp_mflags %{nil}' -# srcrpm-pkg -# --------------------------------------------------------------------------- -PHONY += srcrpm-pkg -srcrpm-pkg: linux.tar.gz - $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec - +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE)-linux -bs kernel.spec \ - --define='_smp_mflags %{nil}' --define='_sourcedir rpmbuild/SOURCES' --define='_srcrpmdir .' +quiet_cmd_mkspec = GEN $@ + cmd_mkspec = $(srctree)/scripts/package/mkspec > $@ -# binrpm-pkg -# --------------------------------------------------------------------------- -PHONY += binrpm-pkg -binrpm-pkg: - $(MAKE) -f $(srctree)/Makefile - $(CONFIG_SHELL) $(MKSPEC) prebuilt > $(objtree)/binkernel.spec - +rpmbuild $(RPMOPTS) --define "_builddir $(objtree)" --target \ - $(UTS_MACHINE)-linux -bb $(objtree)/binkernel.spec +kernel.spec: FORCE + $(call cmd,mkspec) + +PHONY += rpm-sources +rpm-sources: linux.tar.gz + $(Q)mkdir -p rpmbuild/SOURCES + $(Q)ln -f linux.tar.gz rpmbuild/SOURCES/linux.tar.gz + $(Q)cp $(KCONFIG_CONFIG) rpmbuild/SOURCES/config + $(Q)$(srctree)/scripts/package/gen-diff-patch rpmbuild/SOURCES/diff.patch + +PHONY += rpm-pkg srcrpm-pkg binrpm-pkg + +rpm-pkg: private build-type := a +srcrpm-pkg: private build-type := s +binrpm-pkg: private build-type := b + +rpm-pkg srcrpm-pkg: rpm-sources +rpm-pkg srcrpm-pkg binrpm-pkg: kernel.spec + +$(strip rpmbuild -b$(build-type) kernel.spec \ + --define='_topdir $(abspath rpmbuild)' \ + $(if $(filter a b, $(build-type)), \ + --target $(UTS_MACHINE)-linux --build-in-place --noprep --define='_smp_mflags %{nil}' \ + $$(rpm -q rpm >/dev/null 2>&1 || echo --nodeps)) \ + $(if $(filter b, $(build-type)), \ + --without devel) \ + $(RPMOPTS)) # deb-pkg srcdeb-pkg bindeb-pkg # --------------------------------------------------------------------------- @@ -141,14 +148,10 @@ deb-pkg srcdeb-pkg bindeb-pkg: $(if $(findstring source, $(build-type)), \ --unsigned-source --compression=$(KDEB_SOURCE_COMPRESS)) \ $(if $(findstring binary, $(build-type)), \ - -r$(KBUILD_PKG_ROOTCMD) -a$$(cat debian/arch), \ + --rules-file='$(MAKE) -f debian/rules' --jobs=1 -r$(KBUILD_PKG_ROOTCMD) -a$$(cat debian/arch), \ --no-check-builddeps) \ $(DPKG_FLAGS)) -PHONY += intdeb-pkg -intdeb-pkg: - +$(CONFIG_SHELL) $(srctree)/scripts/package/builddeb - # snap-pkg # --------------------------------------------------------------------------- PHONY += snap-pkg diff --git a/scripts/atomic/gen-atomic-fallback.sh b/scripts/atomic/gen-atomic-fallback.sh index c0c8a85d7c81b56d8f8c66b96021898e482f7043..a45154cefa487a81ef87ba12a2f79c42f070df00 100755 --- a/scripts/atomic/gen-atomic-fallback.sh +++ b/scripts/atomic/gen-atomic-fallback.sh @@ -102,7 +102,7 @@ gen_proto_order_variant() fi # Allow ACQUIRE/RELEASE/RELAXED ops to be defined in terms of FULL ops - if [ ! -z "${order}" ]; then + if [ ! -z "${order}" ] && ! meta_is_implicitly_relaxed "${meta}"; then printf "#elif defined(arch_${basename})\n" printf "\t${retstmt}arch_${basename}(${args});\n" fi diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py index eaae2ce78381182463095c4e754816da3fc40678..61b7dddedc461e2ece91a7b25bcf14987fc98886 100755 --- a/scripts/bpf_doc.py +++ b/scripts/bpf_doc.py @@ -59,9 +59,9 @@ class Helper(APIElement): Break down helper function protocol into smaller chunks: return type, name, distincts arguments. """ - arg_re = re.compile('((\w+ )*?(\w+|...))( (\**)(\w+))?$') + arg_re = re.compile(r'((\w+ )*?(\w+|...))( (\**)(\w+))?$') res = {} - proto_re = re.compile('(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$') + proto_re = re.compile(r'(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$') capture = proto_re.match(self.proto) res['ret_type'] = capture.group(1) @@ -114,11 +114,11 @@ class HeaderParser(object): return Helper(proto=proto, desc=desc, ret=ret) def parse_symbol(self): - p = re.compile(' \* ?(BPF\w+)$') + p = re.compile(r' \* ?(BPF\w+)$') capture = p.match(self.line) if not capture: raise NoSyscallCommandFound - end_re = re.compile(' \* ?NOTES$') + end_re = re.compile(r' \* ?NOTES$') end = end_re.match(self.line) if end: raise NoSyscallCommandFound @@ -133,7 +133,7 @@ class HeaderParser(object): # - Same as above, with "const" and/or "struct" in front of type # - "..." (undefined number of arguments, for bpf_trace_printk()) # There is at least one term ("void"), and at most five arguments. - p = re.compile(' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$') + p = re.compile(r' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$') capture = p.match(self.line) if not capture: raise NoHelperFound @@ -141,7 +141,7 @@ class HeaderParser(object): return capture.group(1) def parse_desc(self, proto): - p = re.compile(' \* ?(?:\t| {5,8})Description$') + p = re.compile(r' \* ?(?:\t| {5,8})Description$') capture = p.match(self.line) if not capture: raise Exception("No description section found for " + proto) @@ -154,7 +154,7 @@ class HeaderParser(object): if self.line == ' *\n': desc += '\n' else: - p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') + p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') capture = p.match(self.line) if capture: desc_present = True @@ -167,7 +167,7 @@ class HeaderParser(object): return desc def parse_ret(self, proto): - p = re.compile(' \* ?(?:\t| {5,8})Return$') + p = re.compile(r' \* ?(?:\t| {5,8})Return$') capture = p.match(self.line) if not capture: raise Exception("No return section found for " + proto) @@ -180,7 +180,7 @@ class HeaderParser(object): if self.line == ' *\n': ret += '\n' else: - p = re.compile(' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') + p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') capture = p.match(self.line) if capture: ret_present = True @@ -219,12 +219,12 @@ class HeaderParser(object): self.seek_to('enum bpf_cmd {', 'Could not find start of bpf_cmd enum', 0) # Searches for either one or more BPF\w+ enums - bpf_p = re.compile('\s*(BPF\w+)+') + bpf_p = re.compile(r'\s*(BPF\w+)+') # Searches for an enum entry assigned to another entry, # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is # not documented hence should be skipped in check to # determine if the right number of syscalls are documented - assign_p = re.compile('\s*(BPF\w+)\s*=\s*(BPF\w+)') + assign_p = re.compile(r'\s*(BPF\w+)\s*=\s*(BPF\w+)') bpf_cmd_str = '' while True: capture = assign_p.match(self.line) @@ -239,7 +239,7 @@ class HeaderParser(object): break self.line = self.reader.readline() # Find the number of occurences of BPF\w+ - self.enum_syscalls = re.findall('(BPF\w+)+', bpf_cmd_str) + self.enum_syscalls = re.findall(r'(BPF\w+)+', bpf_cmd_str) def parse_desc_helpers(self): self.seek_to(helpersDocStart, @@ -263,7 +263,7 @@ class HeaderParser(object): self.seek_to('#define ___BPF_FUNC_MAPPER(FN, ctx...)', 'Could not find start of eBPF helper definition list') # Searches for one FN(\w+) define or a backslash for newline - p = re.compile('\s*FN\((\w+), (\d+), ##ctx\)|\\\\') + p = re.compile(r'\s*FN\((\w+), (\d+), ##ctx\)|\\\\') fn_defines_str = '' i = 0 while True: @@ -278,7 +278,7 @@ class HeaderParser(object): break self.line = self.reader.readline() # Find the number of occurences of FN(\w+) - self.define_unique_helpers = re.findall('FN\(\w+, \d+, ##ctx\)', fn_defines_str) + self.define_unique_helpers = re.findall(r'FN\(\w+, \d+, ##ctx\)', fn_defines_str) def validate_helpers(self): last_helper = '' @@ -425,7 +425,7 @@ class PrinterRST(Printer): try: cmd = ['git', 'log', '-1', '--pretty=format:%cs', '--no-patch', '-L', - '/{}/,/\*\//:include/uapi/linux/bpf.h'.format(delimiter)] + '/{}/,/\\*\\//:include/uapi/linux/bpf.h'.format(delimiter)] date = subprocess.run(cmd, cwd=linuxRoot, capture_output=True, check=True) return date.stdout.decode().rstrip() @@ -516,7 +516,7 @@ as "Dual BSD/GPL", may be used). Some helper functions are only accessible to programs that are compatible with the GNU Privacy License (GPL). In order to use such helpers, the eBPF program must be loaded with the correct -license string passed (via **attr**) to the **bpf**\ () system call, and this +license string passed (via **attr**) to the **bpf**\\ () system call, and this generally translates into the C source code of the program containing a line similar to the following: @@ -550,7 +550,7 @@ may be interested in: * The bpftool utility can be used to probe the availability of helper functions on the system (as well as supported program and map types, and a number of other parameters). To do so, run **bpftool feature probe** (see - **bpftool-feature**\ (8) for details). Add the **unprivileged** keyword to + **bpftool-feature**\\ (8) for details). Add the **unprivileged** keyword to list features available to unprivileged users. Compatibility between helper functions and program types can generally be found @@ -562,23 +562,23 @@ other functions, themselves allowing access to additional helpers. The requirement for GPL license is also in those **struct bpf_func_proto**. Compatibility between helper functions and map types can be found in the -**check_map_func_compatibility**\ () function in file *kernel/bpf/verifier.c*. +**check_map_func_compatibility**\\ () function in file *kernel/bpf/verifier.c*. Helper functions that invalidate the checks on **data** and **data_end** pointers for network processing are listed in function -**bpf_helper_changes_pkt_data**\ () in file *net/core/filter.c*. +**bpf_helper_changes_pkt_data**\\ () in file *net/core/filter.c*. SEE ALSO ======== -**bpf**\ (2), -**bpftool**\ (8), -**cgroups**\ (7), -**ip**\ (8), -**perf_event_open**\ (2), -**sendmsg**\ (2), -**socket**\ (7), -**tc-bpf**\ (8)''' +**bpf**\\ (2), +**bpftool**\\ (8), +**cgroups**\\ (7), +**ip**\\ (8), +**perf_event_open**\\ (2), +**sendmsg**\\ (2), +**socket**\\ (7), +**tc-bpf**\\ (8)''' print(footer) def print_proto(self, helper): @@ -598,7 +598,7 @@ SEE ALSO one_arg = '{}{}'.format(comma, a['type']) if a['name']: if a['star']: - one_arg += ' {}**\ '.format(a['star'].replace('*', '\\*')) + one_arg += ' {}**\\ '.format(a['star'].replace('*', '\\*')) else: one_arg += '** ' one_arg += '*{}*\\ **'.format(a['name']) diff --git a/scripts/depmod.sh b/scripts/depmod.sh index 3643b4f896eded47c9d8517ffc9585438bd62398..e22da27fe13eb35fccb3a34cfc90b558e3adc31f 100755 --- a/scripts/depmod.sh +++ b/scripts/depmod.sh @@ -1,14 +1,16 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # -# A depmod wrapper used by the toplevel Makefile +# A depmod wrapper -if test $# -ne 2; then - echo "Usage: $0 /sbin/depmod " >&2 +if test $# -ne 1; then + echo "Usage: $0 " >&2 exit 1 fi -DEPMOD=$1 -KERNELRELEASE=$2 + +KERNELRELEASE=$1 + +: ${DEPMOD:=depmod} if ! test -r System.map ; then echo "Warning: modules_install: missing 'System.map' file. Skipping depmod." >&2 @@ -23,33 +25,8 @@ if [ -z $(command -v $DEPMOD) ]; then exit 0 fi -# older versions of depmod require the version string to start with three -# numbers, so we cheat with a symlink here -depmod_hack_needed=true -tmp_dir=$(mktemp -d ${TMPDIR:-/tmp}/depmod.XXXXXX) -mkdir -p "$tmp_dir/lib/modules/$KERNELRELEASE" -if "$DEPMOD" -b "$tmp_dir" $KERNELRELEASE 2>/dev/null; then - if test -e "$tmp_dir/lib/modules/$KERNELRELEASE/modules.dep" -o \ - -e "$tmp_dir/lib/modules/$KERNELRELEASE/modules.dep.bin"; then - depmod_hack_needed=false - fi -fi -rm -rf "$tmp_dir" -if $depmod_hack_needed; then - symlink="$INSTALL_MOD_PATH/lib/modules/99.98.$KERNELRELEASE" - ln -s "$KERNELRELEASE" "$symlink" - KERNELRELEASE=99.98.$KERNELRELEASE -fi - set -- -ae -F System.map if test -n "$INSTALL_MOD_PATH"; then set -- "$@" -b "$INSTALL_MOD_PATH" fi -"$DEPMOD" "$@" "$KERNELRELEASE" -ret=$? - -if $depmod_hack_needed; then - rm -f "$symlink" -fi - -exit $ret +exec "$DEPMOD" "$@" "$KERNELRELEASE" diff --git a/scripts/dummy-tools/gcc b/scripts/dummy-tools/gcc index 1db1889f6d81e2947daf65cc9afc1b08f49a86d0..07f6dc4c5cf695c7c23a5ca9964c313953eb13a8 100755 --- a/scripts/dummy-tools/gcc +++ b/scripts/dummy-tools/gcc @@ -85,8 +85,7 @@ if arg_contain -S "$@"; then fi # For arch/powerpc/tools/gcc-check-mprofile-kernel.sh - if arg_contain -m64 "$@" && arg_contain -mlittle-endian "$@" && - arg_contain -mprofile-kernel "$@"; then + if arg_contain -m64 "$@" && arg_contain -mprofile-kernel "$@"; then if ! test -t 0 && ! grep -q notrace; then echo "_mcount" fi diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index 5179edd1b6271d676a43c2edff5e8ecc218ea6c6..c8047f4441e60ea944ae6fccedf0e4d0d7632dcd 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -111,12 +111,11 @@ lx-symbols command.""" return "{textaddr} {sections}".format( textaddr=textaddr, sections="".join(args)) - def load_module_symbols(self, module, module_file=None): + def load_module_symbols(self, module): module_name = module['name'].string() module_addr = str(module['mem'][constants.LX_MOD_TEXT]['base']).split()[0] - if not module_file: - module_file = self._get_module_file(module_name) + module_file = self._get_module_file(module_name) if not module_file and not self.module_files_updated: self._update_module_files() module_file = self._get_module_file(module_name) @@ -139,19 +138,6 @@ lx-symbols command.""" else: gdb.write("no module object found for '{0}'\n".format(module_name)) - def load_ko_symbols(self, mod_path): - self.loaded_modules = [] - module_list = modules.module_list() - - for module in module_list: - module_name = module['name'].string() - module_pattern = ".*/{0}\.ko(?:.debug)?$".format( - module_name.replace("_", r"[_\-]")) - if re.match(module_pattern, mod_path) and os.path.exists(mod_path): - self.load_module_symbols(module, mod_path) - return - raise gdb.GdbError("%s is not a valid .ko\n" % mod_path) - def load_all_symbols(self): gdb.write("loading vmlinux\n") @@ -190,11 +176,6 @@ lx-symbols command.""" self.module_files = [] self.module_files_updated = False - argv = gdb.string_to_argv(arg) - if len(argv) == 1: - self.load_ko_symbols(argv[0]) - return - self.load_all_symbols() if hasattr(gdb, 'Breakpoint'): diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index afdddc82f02b30c5bcbb4ffd65d6561c8ebbd3d0..56d3c338d91d70a96448e78cfa5ed508e44f8ec7 100755 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -81,7 +81,6 @@ arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_NO arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_SUPPORT arch/x86/include/uapi/asm/auxvec.h:CONFIG_IA32_EMULATION arch/x86/include/uapi/asm/auxvec.h:CONFIG_X86_64 -arch/x86/include/uapi/asm/mman.h:CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS " for c in $configs diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index af1c96198f4919c1af966e5fc06dcddb7c405fef..4eee155121a8b37c201e70c8327c3ea556d5cd94 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -93,11 +93,13 @@ endif %_defconfig: $(obj)/conf $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) -configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) +configfiles = $(wildcard $(srctree)/kernel/configs/$(1) $(srctree)/arch/$(SRCARCH)/configs/$(1)) +all-config-fragments = $(call configfiles,*.config) +config-fragments = $(call configfiles,$@) %.config: $(obj)/conf - $(if $(call configfiles),, $(error No configuration exists for this target on this architecture)) - $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) + $(if $(config-fragments),, $(error $@ fragment does not exists on this architecture)) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(config-fragments) $(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig PHONY += tinyconfig @@ -115,6 +117,7 @@ clean-files += tests/.cache # Help text used by make help help: + @echo 'Configuration targets:' @echo ' config - Update current config utilising a line-oriented program' @echo ' nconfig - Update current config utilising a ncurses menu based program' @echo ' menuconfig - Update current config utilising a menu based program' @@ -141,6 +144,12 @@ help: @echo ' default value without prompting' @echo ' tinyconfig - Configure the tiniest possible kernel' @echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)' + @echo '' + @echo 'Configuration topic targets:' + @$(foreach f, $(all-config-fragments), \ + if help=$$(grep -m1 '^# Help: ' $(f)); then \ + printf ' %-25s - %s\n' '$(notdir $(f))' "$${help#*: }"; \ + fi;) # =========================================================================== # object files used by all kconfig flavours diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 992575f1e97693af4f715bb66d6dfd7f3a93f58b..4a6811d77d182964d1ec73c34a87c28670dec692 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -349,7 +349,11 @@ int conf_read_simple(const char *name, int def) char *p, *p2; struct symbol *sym; int i, def_flags; + const char *warn_unknown; + const char *werror; + warn_unknown = getenv("KCONFIG_WARN_UNKNOWN_SYMBOLS"); + werror = getenv("KCONFIG_WERROR"); if (name) { in = zconf_fopen(name); } else { @@ -437,6 +441,10 @@ int conf_read_simple(const char *name, int def) if (def == S_DEF_USER) { sym = sym_find(line + 2 + strlen(CONFIG_)); if (!sym) { + if (warn_unknown) + conf_warning("unknown symbol: %s", + line + 2 + strlen(CONFIG_)); + conf_set_changed(true); continue; } @@ -471,7 +479,7 @@ int conf_read_simple(const char *name, int def) sym = sym_find(line + strlen(CONFIG_)); if (!sym) { - if (def == S_DEF_AUTO) + if (def == S_DEF_AUTO) { /* * Reading from include/config/auto.conf * If CONFIG_FOO previously existed in @@ -479,8 +487,13 @@ int conf_read_simple(const char *name, int def) * include/config/FOO must be touched. */ conf_touch_dep(line + strlen(CONFIG_)); - else + } else { + if (warn_unknown) + conf_warning("unknown symbol: %s", + line + strlen(CONFIG_)); + conf_set_changed(true); + } continue; } @@ -519,6 +532,10 @@ int conf_read_simple(const char *name, int def) } free(line); fclose(in); + + if (conf_warnings && werror) + exit(1); + return 0; } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 9c9caca5bd5f2ab595a525c6ee6b6d009a78fda2..4a9a23b1b7e1f91315f17675685d11dd97c4aa4d 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -275,7 +275,6 @@ struct jump_key { struct list_head entries; size_t offset; struct menu *target; - int index; }; extern struct file *file_list; diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index e7118d62a45fc68421913bd8d380b04e5ea26fda..471a59acecec61c3a8f7e141a7f6c6654aae21c6 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -101,6 +101,7 @@ const char *menu_get_prompt(struct menu *menu); struct menu *menu_get_parent_menu(struct menu *menu); bool menu_has_help(struct menu *menu); const char *menu_get_help(struct menu *menu); +int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h index 347daf25fdc8be922ff7a91ec0ce44959aa941df..a501abf9fa313b04faf379aff9244f7e8bb1d180 100644 --- a/scripts/kconfig/lxdialog/dialog.h +++ b/scripts/kconfig/lxdialog/dialog.h @@ -196,13 +196,9 @@ int first_alpha(const char *string, const char *exempt); int dialog_yesno(const char *title, const char *prompt, int height, int width); int dialog_msgbox(const char *title, const char *prompt, int height, int width, int pause); - - -typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void - *_data); -int dialog_textbox(const char *title, char *tbuf, int initial_height, - int initial_width, int *keys, int *_vscroll, int *_hscroll, - update_text_fn update_text, void *data); +int dialog_textbox(const char *title, const char *tbuf, int initial_height, + int initial_width, int *_vscroll, int *_hscroll, + int (*extra_key_cb)(int, size_t, size_t, void *), void *data); int dialog_menu(const char *title, const char *prompt, const void *selected, int *s_scroll); int dialog_checklist(const char *title, const char *prompt, int height, diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c index bc4d4fb1dc75008ded5a4e067955d514a92d0759..058ed0e5bbd545fe1c18baf339b5e71cc9b6702d 100644 --- a/scripts/kconfig/lxdialog/textbox.c +++ b/scripts/kconfig/lxdialog/textbox.c @@ -10,8 +10,8 @@ static int hscroll; static int begin_reached, end_reached, page_length; -static char *buf; -static char *page; +static const char *buf, *page; +static size_t start, end; /* * Go back 'n' lines in text. Called by dialog_textbox(). @@ -98,21 +98,10 @@ static void print_line(WINDOW *win, int row, int width) /* * Print a new page of text. */ -static void print_page(WINDOW *win, int height, int width, update_text_fn - update_text, void *data) +static void print_page(WINDOW *win, int height, int width) { int i, passed_end = 0; - if (update_text) { - char *end; - - for (i = 0; i < height; i++) - get_line(); - end = page; - back_lines(height); - update_text(buf, page - buf, end - buf, data); - } - page_length = 0; for (i = 0; i < height; i++) { print_line(win, i, width); @@ -142,24 +131,26 @@ static void print_position(WINDOW *win) * refresh window content */ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, - int cur_y, int cur_x, update_text_fn update_text, - void *data) + int cur_y, int cur_x) { - print_page(box, boxh, boxw, update_text, data); + start = page - buf; + + print_page(box, boxh, boxw); print_position(dialog); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); + + end = page - buf; } /* * Display text from a file in a dialog box. * * keys is a null-terminated array - * update_text() may not add or remove any '\n' or '\0' in tbuf */ -int dialog_textbox(const char *title, char *tbuf, int initial_height, - int initial_width, int *keys, int *_vscroll, int *_hscroll, - update_text_fn update_text, void *data) +int dialog_textbox(const char *title, const char *tbuf, int initial_height, + int initial_width, int *_vscroll, int *_hscroll, + int (*extra_key_cb)(int, size_t, size_t, void *), void *data) { int i, x, y, cur_x, cur_y, key = 0; int height, width, boxh, boxw; @@ -239,8 +230,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, /* Print first page of text */ attr_clear(box, boxh, boxw, dlg.dialog.atr); - refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text, - data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); while (!done) { key = wgetch(dialog); @@ -259,8 +249,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, begin_reached = 1; page = buf; refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x, update_text, - data); + cur_y, cur_x); } break; case 'G': /* Last page */ @@ -270,8 +259,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, /* point to last char in buf */ page = buf + strlen(buf); back_lines(boxh); - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case 'K': /* Previous line */ case 'k': @@ -280,8 +268,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, break; back_lines(page_length + 1); - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case 'B': /* Previous page */ case 'b': @@ -290,8 +277,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, if (begin_reached) break; back_lines(page_length + boxh); - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case 'J': /* Next line */ case 'j': @@ -300,8 +286,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, break; back_lines(page_length - 1); - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case KEY_NPAGE: /* Next page */ case ' ': @@ -310,8 +295,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, break; begin_reached = 0; - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case '0': /* Beginning of line */ case 'H': /* Scroll left */ @@ -326,8 +310,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, hscroll--; /* Reprint current page to scroll horizontally */ back_lines(page_length); - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case 'L': /* Scroll right */ case 'l': @@ -337,8 +320,7 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, hscroll++; /* Reprint current page to scroll horizontally */ back_lines(page_length); - refresh_text_box(dialog, box, boxh, boxw, cur_y, - cur_x, update_text, data); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); break; case KEY_ESC: if (on_key_esc(dialog) == KEY_ESC) @@ -351,11 +333,9 @@ int dialog_textbox(const char *title, char *tbuf, int initial_height, on_key_resize(); goto do_resize; default: - for (i = 0; keys[i]; i++) { - if (key == keys[i]) { - done = true; - break; - } + if (extra_key_cb && extra_key_cb(key, start, end, data)) { + done = true; + break; } } } diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 53d8834d12fe0ceb7458295d73546f6363ad4ac8..eccc87a441e713a8b9013e3b286a176688ece372 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -22,8 +22,6 @@ #include "lkc.h" #include "lxdialog/dialog.h" -#define JUMP_NB 9 - static const char mconf_readme[] = "Overview\n" "--------\n" @@ -288,6 +286,7 @@ static int single_menu_mode; static int show_all_options; static int save_and_exit; static int silent; +static int jump_key_char; static void conf(struct menu *menu, struct menu *active_menu); @@ -348,19 +347,19 @@ static void reset_subtitle(void) set_dialog_subtitles(subtitles); } -static int show_textbox_ext(const char *title, char *text, int r, int c, int - *keys, int *vscroll, int *hscroll, update_text_fn - update_text, void *data) +static int show_textbox_ext(const char *title, const char *text, int r, int c, + int *vscroll, int *hscroll, + int (*extra_key_cb)(int, size_t, size_t, void *), + void *data) { dialog_clear(); - return dialog_textbox(title, text, r, c, keys, vscroll, hscroll, - update_text, data); + return dialog_textbox(title, text, r, c, vscroll, hscroll, + extra_key_cb, data); } static void show_textbox(const char *title, const char *text, int r, int c) { - show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL, - NULL, NULL); + show_textbox_ext(title, text, r, c, NULL, NULL, NULL, NULL); } static void show_helptext(const char *title, const char *text) @@ -381,35 +380,54 @@ static void show_help(struct menu *menu) struct search_data { struct list_head *head; - struct menu **targets; - int *keys; + struct menu *target; }; -static void update_text(char *buf, size_t start, size_t end, void *_data) +static int next_jump_key(int key) +{ + if (key < '1' || key > '9') + return '1'; + + key++; + + if (key > '9') + key = '1'; + + return key; +} + +static int handle_search_keys(int key, size_t start, size_t end, void *_data) { struct search_data *data = _data; struct jump_key *pos; - int k = 0; + int index = 0; + + if (key < '1' || key > '9') + return 0; list_for_each_entry(pos, data->head, entries) { - if (pos->offset >= start && pos->offset < end) { - char header[4]; + index = next_jump_key(index); - if (k < JUMP_NB) { - int key = '0' + (pos->index % JUMP_NB) + 1; + if (pos->offset < start) + continue; - sprintf(header, "(%c)", key); - data->keys[k] = key; - data->targets[k] = pos->target; - k++; - } else { - sprintf(header, " "); - } + if (pos->offset >= end) + break; - memcpy(buf + pos->offset, header, sizeof(header) - 1); + if (key == index) { + data->target = pos->target; + return 1; } } - data->keys[k] = 0; + + return 0; +} + +int get_jump_key_char(void) +{ + jump_key_char = next_jump_key(jump_key_char); + + return jump_key_char; } static void search_conf(void) @@ -456,26 +474,23 @@ static void search_conf(void) sym_arr = sym_re_search(dialog_input); do { LIST_HEAD(head); - struct menu *targets[JUMP_NB]; - int keys[JUMP_NB + 1], i; struct search_data data = { .head = &head, - .targets = targets, - .keys = keys, }; struct jump_key *pos, *tmp; + jump_key_char = 0; res = get_relations_str(sym_arr, &head); set_subtitle(); dres = show_textbox_ext("Search Results", str_get(&res), 0, 0, - keys, &vscroll, &hscroll, &update_text, - &data); + &vscroll, &hscroll, + handle_search_keys, &data); again = false; - for (i = 0; i < JUMP_NB && keys[i]; i++) - if (dres == keys[i]) { - conf(targets[i]->parent, targets[i]); - again = true; - } + if (dres >= '1' && dres <= '9') { + assert(data.target != NULL); + conf(data.target->parent, data.target); + again = true; + } str_free(&res); list_for_each_entry_safe(pos, tmp, &head, entries) free(pos); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index b90fff833588a61703f04cd57546390a86f7a937..61c442d84aef4a0dd11a2512314fb2d5bea169d2 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -701,6 +701,11 @@ static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix) } } +int __attribute__((weak)) get_jump_key_char(void) +{ + return -1; +} + static void get_prompt_str(struct gstr *r, struct property *prop, struct list_head *head) { @@ -730,24 +735,27 @@ static void get_prompt_str(struct gstr *r, struct property *prop, } if (head && location) { jump = xmalloc(sizeof(struct jump_key)); - jump->target = location; - - if (list_empty(head)) - jump->index = 0; - else - jump->index = list_entry(head->prev, struct jump_key, - entries)->index + 1; - list_add_tail(&jump->entries, head); } str_printf(r, " Location:\n"); - for (j = 4; --i >= 0; j += 2) { + for (j = 0; --i >= 0; j++) { + int jk = -1; + int indent = 2 * j + 4; + menu = submenu[i]; - if (jump && menu == location) + if (jump && menu == location) { jump->offset = strlen(r->s); - str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu)); + jk = get_jump_key_char(); + } + + if (jk >= 0) { + str_printf(r, "(%c)", jk); + indent -= 3; + } + + str_printf(r, "%*c-> %s", indent, ' ', menu_get_prompt(menu)); if (menu->sym) { str_printf(r, " (%s [=%s])", menu->sym->name ? menu->sym->name : "", diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 3ba8b1af390fdc920606c4a70e7c55a43ee947ce..143a2c351d5764b5e9e1b175ad16bb40d3b5d2aa 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -220,7 +220,7 @@ search_help[] = "Location:\n" " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" " -> PCI support (PCI [ = y])\n" -" -> PCI access mode ( [ = y])\n" +"(1) -> PCI access mode ( [ = y])\n" "Selects: LIBCRC32\n" "Selected by: BAR\n" "-----------------------------------------------------------------\n" @@ -231,9 +231,13 @@ search_help[] = "o The 'Depends on:' line lists symbols that need to be defined for\n" " this symbol to be visible and selectable in the menu.\n" "o The 'Location:' lines tell, where in the menu structure this symbol\n" -" is located. A location followed by a [ = y] indicates that this is\n" -" a selectable menu item, and the current value is displayed inside\n" -" brackets.\n" +" is located.\n" +" A location followed by a [ = y] indicates that this is\n" +" a selectable menu item, and the current value is displayed inside\n" +" brackets.\n" +" Press the key in the (#) prefix to jump directly to that\n" +" location. You will be returned to the current search results\n" +" after exiting this new menu.\n" "o The 'Selects:' line tells, what symbol will be automatically selected\n" " if this symbol is selected (y or m).\n" "o The 'Selected by' line tells what symbol has selected this symbol.\n" @@ -275,7 +279,9 @@ static const char *current_instructions = menu_instructions; static char *dialog_input_result; static int dialog_input_result_len; +static int jump_key_char; +static void selected_conf(struct menu *menu, struct menu *active_menu); static void conf(struct menu *menu); static void conf_choice(struct menu *menu); static void conf_string(struct menu *menu); @@ -685,6 +691,57 @@ static int do_exit(void) return 0; } +struct search_data { + struct list_head *head; + struct menu *target; +}; + +static int next_jump_key(int key) +{ + if (key < '1' || key > '9') + return '1'; + + key++; + + if (key > '9') + key = '1'; + + return key; +} + +static int handle_search_keys(int key, size_t start, size_t end, void *_data) +{ + struct search_data *data = _data; + struct jump_key *pos; + int index = 0; + + if (key < '1' || key > '9') + return 0; + + list_for_each_entry(pos, data->head, entries) { + index = next_jump_key(index); + + if (pos->offset < start) + continue; + + if (pos->offset >= end) + break; + + if (key == index) { + data->target = pos->target; + return 1; + } + } + + return 0; +} + +int get_jump_key_char(void) +{ + jump_key_char = next_jump_key(jump_key_char); + + return jump_key_char; +} static void search_conf(void) { @@ -692,7 +749,8 @@ static void search_conf(void) struct gstr res; struct gstr title; char *dialog_input; - int dres; + int dres, vscroll = 0, hscroll = 0; + bool again; title = str_new(); str_printf( &title, "Enter (sub)string or regexp to search for " @@ -721,11 +779,28 @@ static void search_conf(void) dialog_input += strlen(CONFIG_); sym_arr = sym_re_search(dialog_input); - res = get_relations_str(sym_arr, NULL); + + do { + LIST_HEAD(head); + struct search_data data = { + .head = &head, + .target = NULL, + }; + jump_key_char = 0; + res = get_relations_str(sym_arr, &head); + dres = show_scroll_win_ext(main_window, + "Search Results", str_get(&res), + &vscroll, &hscroll, + handle_search_keys, &data); + again = false; + if (dres >= '1' && dres <= '9') { + assert(data.target != NULL); + selected_conf(data.target->parent, data.target); + again = true; + } + str_free(&res); + } while (again); free(sym_arr); - show_scroll_win(main_window, - "Search Results", str_get(&res)); - str_free(&res); str_free(&title); } @@ -1062,10 +1137,15 @@ static int do_match(int key, struct match_state *state, int *ans) } static void conf(struct menu *menu) +{ + selected_conf(menu, NULL); +} + +static void selected_conf(struct menu *menu, struct menu *active_menu) { struct menu *submenu = NULL; struct symbol *sym; - int res; + int i, res; int current_index = 0; int last_top_row = 0; struct match_state match_state = { @@ -1081,6 +1161,19 @@ static void conf(struct menu *menu) if (!child_count) break; + if (active_menu != NULL) { + for (i = 0; i < items_num; i++) { + struct mitem *mcur; + + mcur = (struct mitem *) item_userptr(curses_menu_items[i]); + if ((struct menu *) mcur->usrptr == active_menu) { + current_index = i; + break; + } + } + active_menu = NULL; + } + show_menu(menu_get_prompt(menu), menu_instructions, current_index, &last_top_row); keypad((menu_win(curses_menu)), TRUE); diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 9aedf40f1dc033552adb6e4c14e394f70283641e..25a7263ef3c8c586f0c199f0c5a0d3aef1292193 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -497,10 +497,17 @@ void refresh_all_windows(WINDOW *main_window) refresh(); } -/* layman's scrollable window... */ void show_scroll_win(WINDOW *main_window, const char *title, const char *text) +{ + (void)show_scroll_win_ext(main_window, title, (char *)text, NULL, NULL, NULL, NULL); +} + +/* layman's scrollable window... */ +int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text, + int *vscroll, int *hscroll, + extra_key_cb_fn extra_key_cb, void *data) { int res; int total_lines = get_line_no(text); @@ -514,6 +521,12 @@ void show_scroll_win(WINDOW *main_window, WINDOW *win; WINDOW *pad; PANEL *panel; + bool done = false; + + if (hscroll) + start_x = *hscroll; + if (vscroll) + start_y = *vscroll; getmaxyx(stdscr, lines, columns); @@ -549,8 +562,7 @@ void show_scroll_win(WINDOW *main_window, panel = new_panel(win); /* handle scrolling */ - do { - + while (!done) { copywin(pad, win, start_y, start_x, 2, 2, text_lines, text_cols, 0); print_in_middle(win, @@ -593,8 +605,18 @@ void show_scroll_win(WINDOW *main_window, case 'l': start_x++; break; + default: + if (extra_key_cb) { + size_t start = (get_line(text, start_y) - text); + size_t end = (get_line(text, start_y + text_lines) - text); + + if (extra_key_cb(res, start, end, data)) { + done = true; + break; + } + } } - if (res == 10 || res == 27 || res == 'q' || + if (res == 0 || res == 10 || res == 27 || res == 'q' || res == KEY_F(F_HELP) || res == KEY_F(F_BACK) || res == KEY_F(F_EXIT)) break; @@ -606,9 +628,14 @@ void show_scroll_win(WINDOW *main_window, start_x = 0; if (start_x >= total_cols-text_cols) start_x = total_cols-text_cols; - } while (res); + } + if (hscroll) + *hscroll = start_x; + if (vscroll) + *vscroll = start_y; del_panel(panel); delwin(win); refresh_all_windows(main_window); + return res; } diff --git a/scripts/kconfig/nconf.h b/scripts/kconfig/nconf.h index 6f925bc74eb3d0d05283f7bb4b9f9ee8eb25d033..ab836d5826648c49da4db6617c6f68d96e26e377 100644 --- a/scripts/kconfig/nconf.h +++ b/scripts/kconfig/nconf.h @@ -67,6 +67,8 @@ typedef enum { void set_colors(void); +typedef int (*extra_key_cb_fn)(int, size_t, size_t, void *); + /* this changes the windows attributes !!! */ void print_in_middle(WINDOW *win, int y, int width, const char *str, int attrs); int get_line_length(const char *line); @@ -78,6 +80,9 @@ int dialog_inputbox(WINDOW *main_window, const char *title, const char *prompt, const char *init, char **resultp, int *result_len); void refresh_all_windows(WINDOW *main_window); +int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text, + int *vscroll, int *hscroll, + extra_key_cb_fn extra_key_cb, void *data); void show_scroll_win(WINDOW *main_window, const char *title, const char *text); diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index 748da578b418c4acb53f454e17e2370d2050ceb6..d1f5bcff4b62d68d83e6134b0c883876619f07a2 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -396,6 +396,9 @@ static char *eval_clause(const char *str, size_t len, int argc, char *argv[]) p++; } + + if (new_argc >= FUNCTION_MAX_ARGS) + pperror("too many function arguments"); new_argv[new_argc++] = prev; /* diff --git a/scripts/kconfig/qconf-cfg.sh b/scripts/kconfig/qconf-cfg.sh index 117f36e568fc5e395f077db35c3be7cc19b3f6f3..0e113b0f24557b7987016aa329b224b607adc0c7 100755 --- a/scripts/kconfig/qconf-cfg.sh +++ b/scripts/kconfig/qconf-cfg.sh @@ -5,7 +5,8 @@ cflags=$1 libs=$2 bin=$3 -PKG="Qt5Core Qt5Gui Qt5Widgets" +PKG5="Qt5Core Qt5Gui Qt5Widgets" +PKG6="Qt6Core Qt6Gui Qt6Widgets" if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then echo >&2 "*" @@ -14,16 +15,26 @@ if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then exit 1 fi -if ${HOSTPKG_CONFIG} --exists $PKG; then - ${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags} - ${HOSTPKG_CONFIG} --libs ${PKG} > ${libs} +if ${HOSTPKG_CONFIG} --exists $PKG6; then + ${HOSTPKG_CONFIG} --cflags ${PKG6} > ${cflags} + # Qt6 requires C++17. + echo -std=c++17 >> ${cflags} + ${HOSTPKG_CONFIG} --libs ${PKG6} > ${libs} + ${HOSTPKG_CONFIG} --variable=libexecdir Qt6Core > ${bin} + exit 0 +fi + +if ${HOSTPKG_CONFIG} --exists $PKG5; then + ${HOSTPKG_CONFIG} --cflags ${PKG5} > ${cflags} + ${HOSTPKG_CONFIG} --libs ${PKG5} > ${libs} ${HOSTPKG_CONFIG} --variable=host_bins Qt5Core > ${bin} exit 0 fi echo >&2 "*" -echo >&2 "* Could not find Qt5 via ${HOSTPKG_CONFIG}." -echo >&2 "* Please install Qt5 and make sure it's in PKG_CONFIG_PATH" -echo >&2 "* You need $PKG" +echo >&2 "* Could not find Qt6 or Qt5 via ${HOSTPKG_CONFIG}." +echo >&2 "* Please install Qt6 or Qt5 and make sure it's in PKG_CONFIG_PATH" +echo >&2 "* You need $PKG6 for Qt6" +echo >&2 "* You need $PKG5 for Qt5" echo >&2 "*" exit 1 diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 78087b2d9ac6728dd573f87a8e55181c8529c159..620a3527c767af47e6cac0f36ee7533171626f2f 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -5,10 +5,10 @@ */ #include +#include #include #include #include -#include #include #include #include @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include @@ -1126,7 +1128,7 @@ QString ConfigInfoView::debug_info(struct symbol *sym) QString ConfigInfoView::print_filter(const QString &str) { - QRegExp re("[<>&\"\\n]"); + QRegularExpression re("[<>&\"\\n]"); QString res = str; for (int i = 0; (i = res.indexOf(re, i)) >= 0;) { switch (res[i].toLatin1()) { @@ -1322,15 +1324,15 @@ ConfigMainWindow::ConfigMainWindow(void) int width, height; char title[256]; - QDesktopWidget *d = configApp->desktop(); snprintf(title, sizeof(title), "%s%s", rootmenu.prompt->text, "" ); setWindowTitle(title); - width = configSettings->value("/window width", d->width() - 64).toInt(); - height = configSettings->value("/window height", d->height() - 64).toInt(); + QRect g = configApp->primaryScreen()->geometry(); + width = configSettings->value("/window width", g.width() - 64).toInt(); + height = configSettings->value("/window height", g.height() - 64).toInt(); resize(width, height); x = configSettings->value("/window x"); y = configSettings->value("/window y"); @@ -1379,17 +1381,17 @@ ConfigMainWindow::ConfigMainWindow(void) this, &ConfigMainWindow::goBack); QAction *quitAction = new QAction("&Quit", this); - quitAction->setShortcut(Qt::CTRL + Qt::Key_Q); + quitAction->setShortcut(Qt::CTRL | Qt::Key_Q); connect(quitAction, &QAction::triggered, this, &ConfigMainWindow::close); QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this); - loadAction->setShortcut(Qt::CTRL + Qt::Key_L); + loadAction->setShortcut(Qt::CTRL | Qt::Key_L); connect(loadAction, &QAction::triggered, this, &ConfigMainWindow::loadConfig); saveAction = new QAction(QPixmap(xpm_save), "&Save", this); - saveAction->setShortcut(Qt::CTRL + Qt::Key_S); + saveAction->setShortcut(Qt::CTRL | Qt::Key_S); connect(saveAction, &QAction::triggered, this, &ConfigMainWindow::saveConfig); @@ -1403,7 +1405,7 @@ ConfigMainWindow::ConfigMainWindow(void) connect(saveAsAction, &QAction::triggered, this, &ConfigMainWindow::saveConfigAs); QAction *searchAction = new QAction("&Find", this); - searchAction->setShortcut(Qt::CTRL + Qt::Key_F); + searchAction->setShortcut(Qt::CTRL | Qt::Key_F); connect(searchAction, &QAction::triggered, this, &ConfigMainWindow::searchConfig); singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this); @@ -1750,11 +1752,21 @@ void ConfigMainWindow::closeEvent(QCloseEvent* e) e->accept(); return; } - QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning, - QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape); - mb.setButtonText(QMessageBox::Yes, "&Save Changes"); - mb.setButtonText(QMessageBox::No, "&Discard Changes"); - mb.setButtonText(QMessageBox::Cancel, "Cancel Exit"); + + QMessageBox mb(QMessageBox::Icon::Warning, "qconf", + "Save configuration?"); + + QPushButton *yb = mb.addButton(QMessageBox::Yes); + QPushButton *db = mb.addButton(QMessageBox::No); + QPushButton *cb = mb.addButton(QMessageBox::Cancel); + + yb->setText("&Save Changes"); + db->setText("&Discard Changes"); + cb->setText("Cancel Exit"); + + mb.setDefaultButton(yb); + mb.setEscapeButton(cb); + switch (mb.exec()) { case QMessageBox::Yes: if (saveConfig()) diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 38120f932b0dc1c9dcca19865223c9bb3881a844..7056751c29b1fb7171f70b29c79026363bcabd6b 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1604,7 +1604,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, /* First handle the "special" cases */ if (sym_is(name, namelen, "usb")) do_usb_table(symval, sym->st_size, mod); - if (sym_is(name, namelen, "of")) + else if (sym_is(name, namelen, "of")) do_of_table(symval, sym->st_size, mod); else if (sym_is(name, namelen, "pnp")) do_pnp_device_entry(symval, sym->st_size, mod); diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index b29b29707f10462475e1ad8b258be08bf12eefe1..b3dee80497cb2b1fdfa488d42c14294def73749a 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -24,6 +24,7 @@ #include "../../include/linux/license.h" #include "../../include/linux/module_symbol.h" +static bool module_enabled; /* Are we using CONFIG_MODVERSIONS? */ static bool modversions; /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ @@ -761,6 +762,7 @@ static const char *const section_white_list[] = ".fmt_slot*", /* EZchip */ ".gnu.lto*", ".discard.*", + ".llvm.call-graph-profile", /* call graph */ NULL }; @@ -1013,9 +1015,20 @@ static int secref_whitelist(const char *fromsec, const char *fromsym, "*_console"))) return 0; - /* symbols in data sections that may refer to meminit/exit sections */ + /* symbols in data sections that may refer to meminit sections */ if (match(fromsec, PATTERNS(DATA_SECTIONS)) && - match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_EXIT_SECTIONS)) && + match(tosec, PATTERNS(ALL_XXXINIT_SECTIONS, ALL_XXXEXIT_SECTIONS)) && + match(fromsym, PATTERNS("*driver"))) + return 0; + + /* + * symbols in data sections must not refer to .exit.*, but there are + * quite a few offenders, so hide these unless for W=1 builds until + * these are fixed. + */ + if (!extra_warn && + match(fromsec, PATTERNS(DATA_SECTIONS)) && + match(tosec, PATTERNS(EXIT_SECTIONS)) && match(fromsym, PATTERNS("*driver"))) return 0; @@ -1226,6 +1239,15 @@ static void check_export_symbol(struct module *mod, struct elf_info *elf, */ s->is_func = (ELF_ST_TYPE(sym->st_info) == STT_FUNC); + /* + * For parisc64, symbols prefixed $$ from the library have the symbol type + * STT_LOPROC. They should be handled as functions too. + */ + if (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64 && + elf->hdr->e_machine == EM_PARISC && + ELF_ST_TYPE(sym->st_info) == STT_LOPROC) + s->is_func = true; + if (match(secname, PATTERNS(INIT_SECTIONS))) warn("%s: %s: EXPORT_SYMBOL used for init symbol. Remove __init or EXPORT_SYMBOL.\n", mod->name, name); @@ -1242,7 +1264,7 @@ static void check_section_mismatch(struct module *mod, struct elf_info *elf, const char *tosec = sec_name(elf, get_secindex(elf, sym)); const struct sectioncheck *mismatch; - if (elf->export_symbol_secndx == fsecndx) { + if (module_enabled && elf->export_symbol_secndx == fsecndx) { check_export_symbol(mod, elf, faddr, tosec, sym); return; } @@ -1256,21 +1278,16 @@ static void check_section_mismatch(struct module *mod, struct elf_info *elf, tosec, taddr); } -static int addend_386_rel(uint32_t *location, Elf_Rela *r) +static Elf_Addr addend_386_rel(uint32_t *location, unsigned int r_type) { - unsigned int r_typ = ELF_R_TYPE(r->r_info); - - switch (r_typ) { + switch (r_type) { case R_386_32: - r->r_addend = TO_NATIVE(*location); - break; + return TO_NATIVE(*location); case R_386_PC32: - r->r_addend = TO_NATIVE(*location) + 4; - break; - default: - r->r_addend = (Elf_Addr)(-1); + return TO_NATIVE(*location) + 4; } - return 0; + + return (Elf_Addr)(-1); } #ifndef R_ARM_CALL @@ -1314,32 +1331,28 @@ static int32_t sign_extend32(int32_t value, int index) return (int32_t)(value << shift) >> shift; } -static int addend_arm_rel(void *loc, Elf_Sym *sym, Elf_Rela *r) +static Elf_Addr addend_arm_rel(void *loc, Elf_Sym *sym, unsigned int r_type) { - unsigned int r_typ = ELF_R_TYPE(r->r_info); uint32_t inst, upper, lower, sign, j1, j2; int32_t offset; - switch (r_typ) { + switch (r_type) { case R_ARM_ABS32: case R_ARM_REL32: inst = TO_NATIVE(*(uint32_t *)loc); - r->r_addend = inst + sym->st_value; - break; + return inst + sym->st_value; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: inst = TO_NATIVE(*(uint32_t *)loc); offset = sign_extend32(((inst & 0xf0000) >> 4) | (inst & 0xfff), 15); - r->r_addend = offset + sym->st_value; - break; + return offset + sym->st_value; case R_ARM_PC24: case R_ARM_CALL: case R_ARM_JUMP24: inst = TO_NATIVE(*(uint32_t *)loc); offset = sign_extend32((inst & 0x00ffffff) << 2, 25); - r->r_addend = offset + sym->st_value + 8; - break; + return offset + sym->st_value + 8; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: upper = TO_NATIVE(*(uint16_t *)loc); @@ -1349,8 +1362,7 @@ static int addend_arm_rel(void *loc, Elf_Sym *sym, Elf_Rela *r) ((lower & 0x7000) >> 4) | (lower & 0x00ff), 15); - r->r_addend = offset + sym->st_value; - break; + return offset + sym->st_value; case R_ARM_THM_JUMP19: /* * Encoding T3: @@ -1371,8 +1383,7 @@ static int addend_arm_rel(void *loc, Elf_Sym *sym, Elf_Rela *r) ((upper & 0x03f) << 12) | ((lower & 0x07ff) << 1), 20); - r->r_addend = offset + sym->st_value + 4; - break; + return offset + sym->st_value + 4; case R_ARM_THM_CALL: case R_ARM_THM_JUMP24: /* @@ -1398,34 +1409,26 @@ static int addend_arm_rel(void *loc, Elf_Sym *sym, Elf_Rela *r) ((upper & 0x03ff) << 12) | ((lower & 0x07ff) << 1), 24); - r->r_addend = offset + sym->st_value + 4; - break; - default: - r->r_addend = (Elf_Addr)(-1); + return offset + sym->st_value + 4; } - return 0; + + return (Elf_Addr)(-1); } -static int addend_mips_rel(uint32_t *location, Elf_Rela *r) +static Elf_Addr addend_mips_rel(uint32_t *location, unsigned int r_type) { - unsigned int r_typ = ELF_R_TYPE(r->r_info); uint32_t inst; inst = TO_NATIVE(*location); - switch (r_typ) { + switch (r_type) { case R_MIPS_LO16: - r->r_addend = inst & 0xffff; - break; + return inst & 0xffff; case R_MIPS_26: - r->r_addend = (inst & 0x03ffffff) << 2; - break; + return (inst & 0x03ffffff) << 2; case R_MIPS_32: - r->r_addend = inst; - break; - default: - r->r_addend = (Elf_Addr)(-1); + return inst; } - return 0; + return (Elf_Addr)(-1); } #ifndef EM_RISCV @@ -1444,12 +1447,45 @@ static int addend_mips_rel(uint32_t *location, Elf_Rela *r) #define R_LARCH_SUB32 55 #endif +static void get_rel_type_and_sym(struct elf_info *elf, uint64_t r_info, + unsigned int *r_type, unsigned int *r_sym) +{ + typedef struct { + Elf64_Word r_sym; /* Symbol index */ + unsigned char r_ssym; /* Special symbol for 2nd relocation */ + unsigned char r_type3; /* 3rd relocation type */ + unsigned char r_type2; /* 2nd relocation type */ + unsigned char r_type; /* 1st relocation type */ + } Elf64_Mips_R_Info; + + bool is_64bit = (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64); + + if (elf->hdr->e_machine == EM_MIPS && is_64bit) { + Elf64_Mips_R_Info *mips64_r_info = (void *)&r_info; + + *r_type = mips64_r_info->r_type; + *r_sym = TO_NATIVE(mips64_r_info->r_sym); + return; + } + + if (is_64bit) { + Elf64_Xword r_info64 = r_info; + + r_info = TO_NATIVE(r_info64); + } else { + Elf32_Word r_info32 = r_info; + + r_info = TO_NATIVE(r_info32); + } + + *r_type = ELF_R_TYPE(r_info); + *r_sym = ELF_R_SYM(r_info); +} + static void section_rela(struct module *mod, struct elf_info *elf, Elf_Shdr *sechdr) { Elf_Rela *rela; - Elf_Rela r; - unsigned int r_sym; unsigned int fsecndx = sechdr->sh_info; const char *fromsec = sec_name(elf, fsecndx); Elf_Rela *start = (void *)elf->hdr + sechdr->sh_offset; @@ -1460,38 +1496,29 @@ static void section_rela(struct module *mod, struct elf_info *elf, return; for (rela = start; rela < stop; rela++) { - r.r_offset = TO_NATIVE(rela->r_offset); -#if KERNEL_ELFCLASS == ELFCLASS64 - if (elf->hdr->e_machine == EM_MIPS) { - unsigned int r_typ; - r_sym = ELF64_MIPS_R_SYM(rela->r_info); - r_sym = TO_NATIVE(r_sym); - r_typ = ELF64_MIPS_R_TYPE(rela->r_info); - r.r_info = ELF64_R_INFO(r_sym, r_typ); - } else { - r.r_info = TO_NATIVE(rela->r_info); - r_sym = ELF_R_SYM(r.r_info); - } -#else - r.r_info = TO_NATIVE(rela->r_info); - r_sym = ELF_R_SYM(r.r_info); -#endif - r.r_addend = TO_NATIVE(rela->r_addend); + Elf_Addr taddr, r_offset; + unsigned int r_type, r_sym; + + r_offset = TO_NATIVE(rela->r_offset); + get_rel_type_and_sym(elf, rela->r_info, &r_type, &r_sym); + + taddr = TO_NATIVE(rela->r_addend); + switch (elf->hdr->e_machine) { case EM_RISCV: if (!strcmp("__ex_table", fromsec) && - ELF_R_TYPE(r.r_info) == R_RISCV_SUB32) + r_type == R_RISCV_SUB32) continue; break; case EM_LOONGARCH: if (!strcmp("__ex_table", fromsec) && - ELF_R_TYPE(r.r_info) == R_LARCH_SUB32) + r_type == R_LARCH_SUB32) continue; break; } check_section_mismatch(mod, elf, elf->symtab_start + r_sym, - fsecndx, fromsec, r.r_offset, r.r_addend); + fsecndx, fromsec, r_offset, taddr); } } @@ -1499,8 +1526,6 @@ static void section_rel(struct module *mod, struct elf_info *elf, Elf_Shdr *sechdr) { Elf_Rel *rel; - Elf_Rela r; - unsigned int r_sym; unsigned int fsecndx = sechdr->sh_info; const char *fromsec = sec_name(elf, fsecndx); Elf_Rel *start = (void *)elf->hdr + sechdr->sh_offset; @@ -1512,45 +1537,32 @@ static void section_rel(struct module *mod, struct elf_info *elf, for (rel = start; rel < stop; rel++) { Elf_Sym *tsym; + Elf_Addr taddr = 0, r_offset; + unsigned int r_type, r_sym; void *loc; - r.r_offset = TO_NATIVE(rel->r_offset); -#if KERNEL_ELFCLASS == ELFCLASS64 - if (elf->hdr->e_machine == EM_MIPS) { - unsigned int r_typ; - r_sym = ELF64_MIPS_R_SYM(rel->r_info); - r_sym = TO_NATIVE(r_sym); - r_typ = ELF64_MIPS_R_TYPE(rel->r_info); - r.r_info = ELF64_R_INFO(r_sym, r_typ); - } else { - r.r_info = TO_NATIVE(rel->r_info); - r_sym = ELF_R_SYM(r.r_info); - } -#else - r.r_info = TO_NATIVE(rel->r_info); - r_sym = ELF_R_SYM(r.r_info); -#endif - r.r_addend = 0; + r_offset = TO_NATIVE(rel->r_offset); + get_rel_type_and_sym(elf, rel->r_info, &r_type, &r_sym); - loc = sym_get_data_by_offset(elf, fsecndx, r.r_offset); + loc = sym_get_data_by_offset(elf, fsecndx, r_offset); tsym = elf->symtab_start + r_sym; switch (elf->hdr->e_machine) { case EM_386: - addend_386_rel(loc, &r); + taddr = addend_386_rel(loc, r_type); break; case EM_ARM: - addend_arm_rel(loc, tsym, &r); + taddr = addend_arm_rel(loc, tsym, r_type); break; case EM_MIPS: - addend_mips_rel(loc, &r); + taddr = addend_mips_rel(loc, r_type); break; default: fatal("Please add code to calculate addend for this architecture\n"); } check_section_mismatch(mod, elf, tsym, - fsecndx, fromsec, r.r_offset, r.r_addend); + fsecndx, fromsec, r_offset, taddr); } } @@ -2272,7 +2284,7 @@ int main(int argc, char **argv) LIST_HEAD(dump_lists); struct dump_list *dl, *dl2; - while ((opt = getopt(argc, argv, "ei:mnT:to:au:WwENd:")) != -1) { + while ((opt = getopt(argc, argv, "ei:MmnT:to:au:WwENd:")) != -1) { switch (opt) { case 'e': external_module = true; @@ -2282,6 +2294,9 @@ int main(int argc, char **argv) dl->file = optarg; list_add_tail(&dl->list, &dump_lists); break; + case 'M': + module_enabled = true; + break; case 'm': modversions = true; break; diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index dfdb9484e32550dab2714ee74b5aa674a7c5fdb7..5f94c2c9f2d9502c3637ab7fd24f70fec672c3f7 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -50,28 +50,6 @@ #define ELF_R_TYPE ELF64_R_TYPE #endif -/* The 64-bit MIPS ELF ABI uses an unusual reloc format. */ -typedef struct -{ - Elf32_Word r_sym; /* Symbol index */ - unsigned char r_ssym; /* Special symbol for 2nd relocation */ - unsigned char r_type3; /* 3rd relocation type */ - unsigned char r_type2; /* 2nd relocation type */ - unsigned char r_type1; /* 1st relocation type */ -} _Elf64_Mips_R_Info; - -typedef union -{ - Elf64_Xword r_info_number; - _Elf64_Mips_R_Info r_info_fields; -} _Elf64_Mips_R_Info_union; - -#define ELF64_MIPS_R_SYM(i) \ - ((__extension__ (_Elf64_Mips_R_Info_union)(i)).r_info_fields.r_sym) - -#define ELF64_MIPS_R_TYPE(i) \ - ((__extension__ (_Elf64_Mips_R_Info_union)(i)).r_info_fields.r_type1) - #if KERNEL_ELFDATA != HOST_ELFDATA static inline void __endian(const void *src, void *dest, unsigned int size) diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 032774eb061e15598d381d6eed1055ff0f2b1245..d7dd0d04c70c9982bae9b86e54bf52dba295a2fd 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -64,7 +64,6 @@ install_linux_image () { ${MAKE} -f ${srctree}/Makefile INSTALL_MOD_PATH="${pdir}" modules_install rm -f "${pdir}/lib/modules/${KERNELRELEASE}/build" - rm -f "${pdir}/lib/modules/${KERNELRELEASE}/source" # Install the kernel if [ "${ARCH}" = um ] ; then @@ -162,34 +161,7 @@ install_kernel_headers () { rm -rf $pdir - ( - cd $srctree - find . arch/$SRCARCH -maxdepth 1 -name Makefile\* - find include scripts -type f -o -type l - find arch/$SRCARCH -name Kbuild.platforms -o -name Platform - find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f - ) > debian/hdrsrcfiles - - { - if is_enabled CONFIG_OBJTOOL; then - echo tools/objtool/objtool - fi - - find arch/$SRCARCH/include Module.symvers include scripts -type f - - if is_enabled CONFIG_GCC_PLUGINS; then - find scripts/gcc-plugins -name \*.so - fi - } > debian/hdrobjfiles - - destdir=$pdir/usr/src/linux-headers-$version - mkdir -p $destdir - tar -c -f - -C $srctree -T debian/hdrsrcfiles | tar -xf - -C $destdir - tar -c -f - -T debian/hdrobjfiles | tar -xf - -C $destdir - rm -f debian/hdrsrcfiles debian/hdrobjfiles - - # copy .config manually to be where it's expected to be - cp $KCONFIG_CONFIG $destdir/.config + "${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}" mkdir -p $pdir/lib/modules/$version/ ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build diff --git a/scripts/package/debian/rules b/scripts/package/debian/rules new file mode 100755 index 0000000000000000000000000000000000000000..3dafa9496c6366d727bb8b3249886a11b93ba2d0 --- /dev/null +++ b/scripts/package/debian/rules @@ -0,0 +1,33 @@ +#!/usr/bin/make -f +# SPDX-License-Identifier: GPL-2.0-only + +include debian/rules.vars + +srctree ?= . + +ifneq (,$(filter-out parallel=1,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))) + NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) + MAKEFLAGS += -j$(NUMJOBS) +endif + +.PHONY: binary binary-indep binary-arch +binary: binary-arch binary-indep +binary-indep: build-indep +binary-arch: build-arch + $(MAKE) -f $(srctree)/Makefile ARCH=$(ARCH) \ + KERNELRELEASE=$(KERNELRELEASE) \ + run-command KBUILD_RUN_COMMAND=+$(srctree)/scripts/package/builddeb + +.PHONY: build build-indep build-arch +build: build-arch build-indep +build-indep: +build-arch: + $(MAKE) -f $(srctree)/Makefile ARCH=$(ARCH) \ + KERNELRELEASE=$(KERNELRELEASE) \ + $(shell $(srctree)/scripts/package/deb-build-option) \ + olddefconfig all + +.PHONY: clean +clean: + rm -rf debian/files debian/linux-* + $(MAKE) -f $(srctree)/Makefile ARCH=$(ARCH) clean diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build new file mode 100755 index 0000000000000000000000000000000000000000..8a7051fad0878990cd4569a326fc7137a7db088d --- /dev/null +++ b/scripts/package/install-extmod-build @@ -0,0 +1,39 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only + +set -e + +destdir=${1} + +test -n "${srctree}" +test -n "${SRCARCH}" + +is_enabled() { + grep -q "^$1=y" include/config/auto.conf +} + +mkdir -p "${destdir}" + +( + cd "${srctree}" + echo Makefile + find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*' + find include scripts -type f -o -type l + find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform + find "arch/${SRCARCH}" -name include -o -name scripts -type d +) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}" + +{ + if is_enabled CONFIG_OBJTOOL; then + echo tools/objtool/objtool + fi + + find "arch/${SRCARCH}/include" Module.symvers include scripts -type f + + if is_enabled CONFIG_GCC_PLUGINS; then + find scripts/gcc-plugins -name '*.so' + fi +} | tar -c -f - -T - | tar -xf - -C "${destdir}" + +# copy .config manually to be where it's expected to be +cp "${KCONFIG_CONFIG}" "${destdir}/.config" diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec new file mode 100644 index 0000000000000000000000000000000000000000..3eee0143e0c5cc7671e640aad2368446e94805e0 --- /dev/null +++ b/scripts/package/kernel.spec @@ -0,0 +1,114 @@ +# _arch is undefined if /usr/lib/rpm/platform/*/macros was not included. +%{!?_arch: %define _arch dummy} +%{!?make: %define make make} +%define makeflags %{?_smp_mflags} ARCH=%{ARCH} +%define __spec_install_post /usr/lib/rpm/brp-compress || : +%define debug_package %{nil} + +Name: kernel +Summary: The Linux Kernel +Version: %(echo %{KERNELRELEASE} | sed -e 's/-/_/g') +Release: %{pkg_release} +License: GPL +Group: System Environment/Kernel +Vendor: The Linux Community +URL: https://www.kernel.org +Source0: linux.tar.gz +Source1: config +Source2: diff.patch +Provides: kernel-%{KERNELRELEASE} +BuildRequires: bc binutils bison dwarves +BuildRequires: (elfutils-libelf-devel or libelf-devel) flex +BuildRequires: gcc make openssl openssl-devel perl python3 rsync + +%description +The Linux Kernel, the operating system core itself + +%package headers +Summary: Header files for the Linux kernel for use by glibc +Group: Development/System +Obsoletes: kernel-headers +Provides: kernel-headers = %{version} +%description headers +Kernel-headers includes the C header files that specify the interface +between the Linux kernel and userspace libraries and programs. The +header files define structures and constants that are needed for +building most standard programs and are also needed for rebuilding the +glibc package. + +%if %{with_devel} +%package devel +Summary: Development package for building kernel modules to match the %{version} kernel +Group: System Environment/Kernel +AutoReqProv: no +%description -n kernel-devel +This package provides kernel headers and makefiles sufficient to build modules +against the %{version} kernel package. +%endif + +%prep +%setup -q -n linux +cp %{SOURCE1} .config +patch -p1 < %{SOURCE2} + +%build +%{make} %{makeflags} KERNELRELEASE=%{KERNELRELEASE} KBUILD_BUILD_VERSION=%{release} + +%install +mkdir -p %{buildroot}/boot +%ifarch ia64 +mkdir -p %{buildroot}/boot/efi +cp $(%{make} %{makeflags} -s image_name) %{buildroot}/boot/efi/vmlinuz-%{KERNELRELEASE} +ln -s efi/vmlinuz-%{KERNELRELEASE} %{buildroot}/boot/ +%else +cp $(%{make} %{makeflags} -s image_name) %{buildroot}/boot/vmlinuz-%{KERNELRELEASE} +%endif +%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} modules_install +%{make} %{makeflags} INSTALL_HDR_PATH=%{buildroot}/usr headers_install +cp System.map %{buildroot}/boot/System.map-%{KERNELRELEASE} +cp .config %{buildroot}/boot/config-%{KERNELRELEASE} +ln -fns /usr/src/kernels/%{KERNELRELEASE} %{buildroot}/lib/modules/%{KERNELRELEASE}/build +%if %{with_devel} +%{make} %{makeflags} run-command KBUILD_RUN_COMMAND='${srctree}/scripts/package/install-extmod-build %{buildroot}/usr/src/kernels/%{KERNELRELEASE}' +%endif + +%clean +rm -rf %{buildroot} + +%post +if [ -x /sbin/installkernel -a -r /boot/vmlinuz-%{KERNELRELEASE} -a -r /boot/System.map-%{KERNELRELEASE} ]; then +cp /boot/vmlinuz-%{KERNELRELEASE} /boot/.vmlinuz-%{KERNELRELEASE}-rpm +cp /boot/System.map-%{KERNELRELEASE} /boot/.System.map-%{KERNELRELEASE}-rpm +rm -f /boot/vmlinuz-%{KERNELRELEASE} /boot/System.map-%{KERNELRELEASE} +/sbin/installkernel %{KERNELRELEASE} /boot/.vmlinuz-%{KERNELRELEASE}-rpm /boot/.System.map-%{KERNELRELEASE}-rpm +rm -f /boot/.vmlinuz-%{KERNELRELEASE}-rpm /boot/.System.map-%{KERNELRELEASE}-rpm +fi + +%preun +if [ -x /sbin/new-kernel-pkg ]; then +new-kernel-pkg --remove %{KERNELRELEASE} --rminitrd --initrdfile=/boot/initramfs-%{KERNELRELEASE}.img +elif [ -x /usr/bin/kernel-install ]; then +kernel-install remove %{KERNELRELEASE} +fi + +%postun +if [ -x /sbin/update-bootloader ]; then +/sbin/update-bootloader --remove %{KERNELRELEASE} +fi + +%files +%defattr (-, root, root) +/lib/modules/%{KERNELRELEASE} +%exclude /lib/modules/%{KERNELRELEASE}/build +/boot/* + +%files headers +%defattr (-, root, root) +/usr/include + +%if %{with_devel} +%files devel +%defattr (-, root, root) +/usr/src/kernels/%{KERNELRELEASE} +/lib/modules/%{KERNELRELEASE}/build +%endif diff --git a/scripts/package/mkdebian b/scripts/package/mkdebian index ba2453e08d408d1ebf8946c64b583b1fb9f336b3..5044224cf6714b3e5738f1e6d30dda05c589e3ff 100755 --- a/scripts/package/mkdebian +++ b/scripts/package/mkdebian @@ -263,34 +263,11 @@ Description: Linux kernel debugging symbols for $version EOF fi -cat < debian/rules -#!$(command -v $MAKE) -f - -srctree ?= . -KERNELRELEASE = ${KERNELRELEASE} - -.PHONY: clean build build-arch build-indep binary binary-arch binary-indep - -build-indep: -build-arch: - \$(MAKE) -f \$(srctree)/Makefile ARCH=${ARCH} \ - KERNELRELEASE=\$(KERNELRELEASE) \ - \$(shell \$(srctree)/scripts/package/deb-build-option) \ - olddefconfig all - -build: build-arch - -binary-indep: -binary-arch: build-arch - \$(MAKE) -f \$(srctree)/Makefile ARCH=${ARCH} \ - KERNELRELEASE=\$(KERNELRELEASE) intdeb-pkg - -clean: - rm -rf debian/files debian/linux-* - \$(MAKE) -f \$(srctree)/Makefile ARCH=${ARCH} clean - -binary: binary-arch +cat < debian/rules.vars +ARCH := ${ARCH} +KERNELRELEASE := ${KERNELRELEASE} EOF -chmod +x debian/rules + +cp "${srctree}/scripts/package/debian/rules" debian/ exit 0 diff --git a/scripts/package/mkspec b/scripts/package/mkspec index 8049f0e2c110fb8ca696cb36f70d0e150060574f..d41608efb74742f1811e3ece6ccfbce1f5289751 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -9,148 +9,16 @@ # Patched for non-x86 by Opencon (L) 2002 # -# how we were called determines which rpms we build and how we build them -if [ "$1" = prebuilt ]; then - S=DEL - MAKE="$MAKE -f $srctree/Makefile" -else - S= - - mkdir -p rpmbuild/SOURCES - cp linux.tar.gz rpmbuild/SOURCES - cp "${KCONFIG_CONFIG}" rpmbuild/SOURCES/config - "${srctree}/scripts/package/gen-diff-patch" rpmbuild/SOURCES/diff.patch -fi - if grep -q CONFIG_MODULES=y include/config/auto.conf; then - M= +echo '%define with_devel %{?_without_devel: 0} %{?!_without_devel: 1}' else - M=DEL +echo '%define with_devel 0' fi -__KERNELRELEASE=$(echo $KERNELRELEASE | sed -e "s/-/_/g") -EXCLUDES="$RCS_TAR_IGNORE --exclude=*vmlinux* --exclude=*.mod \ ---exclude=*.o --exclude=*.ko --exclude=*.cmd --exclude=Documentation \ ---exclude=.config.old --exclude=.missing-syscalls.d --exclude=*.s" - -# We can label the here-doc lines for conditional output to the spec file -# -# Labels: -# $S: this line is enabled only when building source package -# $M: this line is enabled only when CONFIG_MODULES is enabled -sed -e '/^DEL/d' -e 's/^\t*//' </dev/null || echo 1) - License: GPL - Group: System Environment/Kernel - Vendor: The Linux Community - URL: https://www.kernel.org -$S Source0: linux.tar.gz -$S Source1: config -$S Source2: diff.patch - Provides: kernel-$KERNELRELEASE -$S BuildRequires: bc binutils bison dwarves -$S BuildRequires: (elfutils-libelf-devel or libelf-devel) flex -$S BuildRequires: gcc make openssl openssl-devel perl python3 rsync - - # $UTS_MACHINE as a fallback of _arch in case - # /usr/lib/rpm/platform/*/macros was not included. - %define _arch %{?_arch:$UTS_MACHINE} - %define __spec_install_post /usr/lib/rpm/brp-compress || : - %define debug_package %{nil} - - %description - The Linux Kernel, the operating system core itself - - %package headers - Summary: Header files for the Linux kernel for use by glibc - Group: Development/System - Obsoletes: kernel-headers - Provides: kernel-headers = %{version} - %description headers - Kernel-headers includes the C header files that specify the interface - between the Linux kernel and userspace libraries and programs. The - header files define structures and constants that are needed for - building most standard programs and are also needed for rebuilding the - glibc package. - -$S$M %package devel -$S$M Summary: Development package for building kernel modules to match the $__KERNELRELEASE kernel -$S$M Group: System Environment/Kernel -$S$M AutoReqProv: no -$S$M %description -n kernel-devel -$S$M This package provides kernel headers and makefiles sufficient to build modules -$S$M against the $__KERNELRELEASE kernel package. -$S$M -$S %prep -$S %setup -q -n linux -$S cp %{SOURCE1} .config -$S patch -p1 < %{SOURCE2} -$S -$S %build -$S $MAKE %{?_smp_mflags} KERNELRELEASE=$KERNELRELEASE KBUILD_BUILD_VERSION=%{release} -$S - %install - mkdir -p %{buildroot}/boot - %ifarch ia64 - mkdir -p %{buildroot}/boot/efi - cp \$($MAKE -s image_name) %{buildroot}/boot/efi/vmlinuz-$KERNELRELEASE - ln -s efi/vmlinuz-$KERNELRELEASE %{buildroot}/boot/ - %else - cp \$($MAKE -s image_name) %{buildroot}/boot/vmlinuz-$KERNELRELEASE - %endif -$M $MAKE %{?_smp_mflags} INSTALL_MOD_PATH=%{buildroot} modules_install - $MAKE %{?_smp_mflags} INSTALL_HDR_PATH=%{buildroot}/usr headers_install - cp System.map %{buildroot}/boot/System.map-$KERNELRELEASE - cp .config %{buildroot}/boot/config-$KERNELRELEASE -$S$M rm -f %{buildroot}/lib/modules/$KERNELRELEASE/build -$S$M rm -f %{buildroot}/lib/modules/$KERNELRELEASE/source -$S$M mkdir -p %{buildroot}/usr/src/kernels/$KERNELRELEASE -$S$M tar cf - $EXCLUDES . | tar xf - -C %{buildroot}/usr/src/kernels/$KERNELRELEASE -$S$M cd %{buildroot}/lib/modules/$KERNELRELEASE -$S$M ln -sf /usr/src/kernels/$KERNELRELEASE build -$S$M ln -sf /usr/src/kernels/$KERNELRELEASE source - - %clean - rm -rf %{buildroot} - - %post - if [ -x /sbin/installkernel -a -r /boot/vmlinuz-$KERNELRELEASE -a -r /boot/System.map-$KERNELRELEASE ]; then - cp /boot/vmlinuz-$KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm - cp /boot/System.map-$KERNELRELEASE /boot/.System.map-$KERNELRELEASE-rpm - rm -f /boot/vmlinuz-$KERNELRELEASE /boot/System.map-$KERNELRELEASE - /sbin/installkernel $KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm - rm -f /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm - fi - - %preun - if [ -x /sbin/new-kernel-pkg ]; then - new-kernel-pkg --remove $KERNELRELEASE --rminitrd --initrdfile=/boot/initramfs-$KERNELRELEASE.img - elif [ -x /usr/bin/kernel-install ]; then - kernel-install remove $KERNELRELEASE - fi - - %postun - if [ -x /sbin/update-bootloader ]; then - /sbin/update-bootloader --remove $KERNELRELEASE - fi - - %files - %defattr (-, root, root) -$M /lib/modules/$KERNELRELEASE -$M %exclude /lib/modules/$KERNELRELEASE/build -$M %exclude /lib/modules/$KERNELRELEASE/source - /boot/* - - %files headers - %defattr (-, root, root) - /usr/include -$S$M -$S$M %files devel -$S$M %defattr (-, root, root) -$S$M /usr/src/kernels/$KERNELRELEASE -$S$M /lib/modules/$KERNELRELEASE/build -$S$M /lib/modules/$KERNELRELEASE/source +cat< v6.2-rc5 + # stable kernel: 6.1.7 -> v6.1.7 + version_tag=v$(echo "${KERNELVERSION}" | sed -E 's/^([0-9]+\.[0-9]+)\.0(.*)$/\1\2/') + + # If a localversion* file exists, and the corresponding + # annotated tag exists and is an ancestor of HEAD, use + # it. This is the case in linux-next. tag=${file_localversion#-} - tag=$(git describe --exact-match --match=$tag $tag 2>/dev/null) + desc= + if [ -n "${tag}" ]; then + desc=$(git describe --match=$tag 2>/dev/null) + fi + + # Otherwise, if a localversion* file exists, and the tag + # obtained by appending it to the tag derived from + # KERNELVERSION exists and is an ancestor of HEAD, use + # it. This is e.g. the case in linux-rt. + if [ -z "${desc}" ] && [ -n "${file_localversion}" ]; then + tag="${version_tag}${file_localversion}" + desc=$(git describe --match=$tag 2>/dev/null) + fi # Otherwise, default to the annotated tag derived from KERNELVERSION. - # mainline kernel: 6.2.0-rc5 -> v6.2-rc5 - # stable kernel: 6.1.7 -> v6.1.7 - if [ -z "${tag}" ]; then - tag=v$(echo "${KERNELVERSION}" | sed -E 's/^([0-9]+\.[0-9]+)\.0(.*)$/\1\2/') + if [ -z "${desc}" ]; then + tag="${version_tag}" + desc=$(git describe --match=$tag 2>/dev/null) fi # If we are at the tagged commit, we ignore it because the version is # well-defined. - if [ -z "$(git describe --exact-match --match=$tag 2>/dev/null)" ]; then + if [ "${tag}" != "${desc}" ]; then # If only the short version is requested, don't bother # running further git commands @@ -81,8 +97,8 @@ scm_version() fi # If we are past the tagged commit, we pretty print it. # (like 6.1.0-14595-g292a089d78d3) - if atag="$(git describe --match=$tag 2>/dev/null)"; then - echo "$atag" | awk -F- '{printf("-%05d", $(NF-1))}' + if [ -n "${desc}" ]; then + echo "${desc}" | awk -F- '{printf("-%05d", $(NF-1))}' fi # Add -g and exactly 12 hex chars. diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index ecddc807c536189ef281d542b86124cfb1c21666..a6bd817efc1a699b2500a555f2f1d9c901407aa5 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -29,9 +29,11 @@ config IMA to learn more about IMA. If unsure, say N. +if IMA + config IMA_KEXEC bool "Enable carrying the IMA measurement list across a soft boot" - depends on IMA && TCG_TPM && HAVE_IMA_KEXEC + depends on TCG_TPM && HAVE_IMA_KEXEC default n help TPM PCRs are only reset on a hard reboot. In order to validate @@ -43,7 +45,6 @@ config IMA_KEXEC config IMA_MEASURE_PCR_IDX int - depends on IMA range 8 14 default 10 help @@ -53,7 +54,7 @@ config IMA_MEASURE_PCR_IDX config IMA_LSM_RULES bool - depends on IMA && AUDIT && (SECURITY_SELINUX || SECURITY_SMACK || SECURITY_APPARMOR) + depends on AUDIT && (SECURITY_SELINUX || SECURITY_SMACK || SECURITY_APPARMOR) default y help Disabling this option will disregard LSM based policy rules. @@ -61,7 +62,6 @@ config IMA_LSM_RULES choice prompt "Default template" default IMA_NG_TEMPLATE - depends on IMA help Select the default IMA measurement template. @@ -80,14 +80,12 @@ endchoice config IMA_DEFAULT_TEMPLATE string - depends on IMA default "ima-ng" if IMA_NG_TEMPLATE default "ima-sig" if IMA_SIG_TEMPLATE choice prompt "Default integrity hash algorithm" default IMA_DEFAULT_HASH_SHA1 - depends on IMA help Select the default hash algorithm used for the measurement list, integrity appraisal and audit log. The compiled default @@ -117,7 +115,6 @@ endchoice config IMA_DEFAULT_HASH string - depends on IMA default "sha1" if IMA_DEFAULT_HASH_SHA1 default "sha256" if IMA_DEFAULT_HASH_SHA256 default "sha512" if IMA_DEFAULT_HASH_SHA512 @@ -126,7 +123,6 @@ config IMA_DEFAULT_HASH config IMA_WRITE_POLICY bool "Enable multiple writes to the IMA policy" - depends on IMA default n help IMA policy can now be updated multiple times. The new rules get @@ -137,7 +133,6 @@ config IMA_WRITE_POLICY config IMA_READ_POLICY bool "Enable reading back the current IMA policy" - depends on IMA default y if IMA_WRITE_POLICY default n if !IMA_WRITE_POLICY help @@ -147,7 +142,6 @@ config IMA_READ_POLICY config IMA_APPRAISE bool "Appraise integrity measurements" - depends on IMA default n help This option enables local measurement integrity appraisal. @@ -269,7 +263,7 @@ config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY config IMA_BLACKLIST_KEYRING bool "Create IMA machine owner blacklist keyrings (EXPERIMENTAL)" depends on SYSTEM_TRUSTED_KEYRING - depends on IMA_TRUSTED_KEYRING + depends on INTEGRITY_TRUSTED_KEYRING default n help This option creates an IMA blacklist keyring, which contains all @@ -279,7 +273,7 @@ config IMA_BLACKLIST_KEYRING config IMA_LOAD_X509 bool "Load X509 certificate onto the '.ima' trusted keyring" - depends on IMA_TRUSTED_KEYRING + depends on INTEGRITY_TRUSTED_KEYRING default n help File signature verification is based on the public keys @@ -304,7 +298,6 @@ config IMA_APPRAISE_SIGNED_INIT config IMA_MEASURE_ASYMMETRIC_KEYS bool - depends on IMA depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y default y @@ -323,7 +316,8 @@ config IMA_SECURE_AND_OR_TRUSTED_BOOT config IMA_DISABLE_HTABLE bool "Disable htable to allow measurement of duplicate records" - depends on IMA default n help This option disables htable to allow measurement of duplicate records. + +endif diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c index c6fc50d67214c401f95f3e5aff3f17032eb8c950..85fb5c22529a761d9cc4509c74ea01cc790113e0 100644 --- a/security/keys/trusted-keys/trusted_core.c +++ b/security/keys/trusted-keys/trusted_core.c @@ -44,13 +44,12 @@ static const struct trusted_key_source trusted_key_sources[] = { #endif }; -DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init); DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal); DEFINE_STATIC_CALL_NULL(trusted_key_unseal, *trusted_key_sources[0].ops->unseal); DEFINE_STATIC_CALL_NULL(trusted_key_get_random, *trusted_key_sources[0].ops->get_random); -DEFINE_STATIC_CALL_NULL(trusted_key_exit, *trusted_key_sources[0].ops->exit); +static void (*trusted_key_exit)(void); static unsigned char migratable; enum { @@ -359,19 +358,16 @@ static int __init init_trusted(void) if (!get_random) get_random = kernel_get_random; - static_call_update(trusted_key_init, - trusted_key_sources[i].ops->init); static_call_update(trusted_key_seal, trusted_key_sources[i].ops->seal); static_call_update(trusted_key_unseal, trusted_key_sources[i].ops->unseal); static_call_update(trusted_key_get_random, get_random); - static_call_update(trusted_key_exit, - trusted_key_sources[i].ops->exit); + trusted_key_exit = trusted_key_sources[i].ops->exit; migratable = trusted_key_sources[i].ops->migratable; - ret = static_call(trusted_key_init)(); + ret = trusted_key_sources[i].ops->init(); if (!ret) break; } @@ -388,7 +384,8 @@ static int __init init_trusted(void) static void __exit cleanup_trusted(void) { - static_call_cond(trusted_key_exit)(); + if (trusted_key_exit) + (*trusted_key_exit)(); } late_initcall(init_trusted); diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h index d43231b783e4fbb422935bff0a9da074ed765d98..55b1df8f66a8cc6ace8573c5a8d7200e62ed69d0 100644 --- a/security/landlock/ruleset.h +++ b/security/landlock/ruleset.h @@ -67,7 +67,7 @@ struct landlock_rule { * @layers: Stack of layers, from the latest to the newest, implemented * as a flexible array member (FAM). */ - struct landlock_layer layers[]; + struct landlock_layer layers[] __counted_by(num_layers); }; /** diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 10350534de6d65b3a4103ae9babe42fac3682261..2aa0e219d72177799b915322d73e0af1698901f6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2775,14 +2775,20 @@ static int selinux_umount(struct vfsmount *mnt, int flags) static int selinux_fs_context_submount(struct fs_context *fc, struct super_block *reference) { - const struct superblock_security_struct *sbsec; + const struct superblock_security_struct *sbsec = selinux_superblock(reference); struct selinux_mnt_opts *opts; + /* + * Ensure that fc->security remains NULL when no options are set + * as expected by selinux_set_mnt_opts(). + */ + if (!(sbsec->flags & (FSCONTEXT_MNT|CONTEXT_MNT|DEFCONTEXT_MNT))) + return 0; + opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return -ENOMEM; - sbsec = selinux_superblock(reference); if (sbsec->flags & FSCONTEXT_MNT) opts->fscontext_sid = sbsec->sid; if (sbsec->flags & CONTEXT_MNT) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 969d4aa6fd556f38f09ae7cb573973fd5a209479..57ee70ae50f24ac771a7bd74d224c17b1ff03d25 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -184,6 +184,7 @@ static bool tomoyo_manage_by_non_root; * * Returns nothing. */ +__printf(3, 4) static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) { va_list args; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index a539b2cbb5c45a036b1a508d132bd0e1ba91c3db..0e8e2e959aef4ec0a96c1bd98aa53c2a87da4e69 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -954,7 +954,7 @@ bool tomoyo_str_starts(char **src, const char *find); char *tomoyo_encode(const char *str); char *tomoyo_encode2(const char *str, int str_len); char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, - va_list args); + va_list args) __printf(3, 0); char *tomoyo_read_token(struct tomoyo_acl_param *param); char *tomoyo_realpath_from_path(const struct path *path); char *tomoyo_realpath_nofollow(const char *pathname); @@ -1037,8 +1037,6 @@ struct tomoyo_policy_namespace *tomoyo_assign_namespace (const char *domainname); struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, const u8 profile); -unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, - const u8 index); u8 tomoyo_parse_ulong(unsigned long *result, char **str); void *tomoyo_commit_ok(void *data, const unsigned int size); void __init tomoyo_load_builtin_policy(void); @@ -1067,7 +1065,7 @@ void tomoyo_warn_oom(const char *function); void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) __printf(2, 3); void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, - va_list args); + va_list args) __printf(3, 0); /********** External variable definitions. **********/ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index ac20c0bdff9dc1194d6e6c437be7fc26c007c264..90b53500a236bdcd5cb98c10fd63dd9844a2253c 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -784,13 +784,12 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (!strcmp(domainname, "parent")) { char *cp; - strncpy(ee->tmp, old_domain->domainname->name, - TOMOYO_EXEC_TMPSIZE - 1); + strscpy(ee->tmp, old_domain->domainname->name, TOMOYO_EXEC_TMPSIZE); cp = strrchr(ee->tmp, ' '); if (cp) *cp = '\0'; } else if (*domainname == '<') - strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1); + strscpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE); else snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", old_domain->domainname->name, domainname); diff --git a/sound/Kconfig b/sound/Kconfig index f0e15822e858c8bc4d8b8948babceb865c7ca39e..4c036a9a420ab5d7487813bec7f58d977a10091d 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig SOUND tristate "Sound card support" - depends on HAS_IOMEM + depends on HAS_IOMEM || UML help If you have a sound card in your computer, i.e. if it can say more than an occasional beep, say Y. diff --git a/sound/core/init.c b/sound/core/init.c index d61bde1225f23a362aa4c2edd64ec9ec32fc1987..22c0d217b8608f04b4c9c1803379b9546f12ed14 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -278,9 +278,6 @@ static int snd_card_init(struct snd_card *card, struct device *parent, size_t extra_size) { int err; -#ifdef CONFIG_SND_DEBUG - char name[8]; -#endif if (extra_size > 0) card->private_data = (char *)card + sizeof(struct snd_card); @@ -364,8 +361,8 @@ static int snd_card_init(struct snd_card *card, struct device *parent, } #ifdef CONFIG_SND_DEBUG - sprintf(name, "card%d", idx); - card->debugfs_root = debugfs_create_dir(name, sound_debugfs_root); + card->debugfs_root = debugfs_create_dir(dev_name(&card->card_dev), + sound_debugfs_root); #endif return 0; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 4859fb1caec9c58245f0042904b269bd7958c56d..a11cd7d6295ff7f865b10e80ff664703d4cff7b7 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1992,8 +1992,8 @@ static int default_write_copy(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, struct iov_iter *iter, unsigned long bytes) { - if (!copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff), - bytes, iter)) + if (copy_from_iter(get_dma_ptr(substream->runtime, channel, hwoff), + bytes, iter) != bytes) return -EFAULT; return 0; } @@ -2025,8 +2025,8 @@ static int default_read_copy(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, struct iov_iter *iter, unsigned long bytes) { - if (!copy_to_iter(get_dma_ptr(substream->runtime, channel, hwoff), - bytes, iter)) + if (copy_to_iter(get_dma_ptr(substream->runtime, channel, hwoff), + bytes, iter) != bytes) return -EFAULT; return 0; } diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index ba06484ac4aa9b6f7f05be7c63187cb22341df7b..1431cb997808d071d44efadba2b8a1d49117ee25 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1770,7 +1770,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, if (IS_ENABLED(CONFIG_SND_UMP)) snd_iprintf(buffer, "Type: %s\n", rawmidi_is_ump(rmidi) ? "UMP" : "Legacy"); - if (rmidi->ops->proc_read) + if (rmidi->ops && rmidi->ops->proc_read) rmidi->ops->proc_read(entry, buffer); mutex_lock(&rmidi->open_mutex); if (rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT) { diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 174585bf59d29ebc57ef0e489742a0d9a27f4af3..b603bb93f896030130504e84eb29ff311ad393d1 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -187,8 +187,13 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char err = expand_var_event(event, 0, len, buf, in_kernel); if (err < 0) return err; - if (len != newlen) - memset(buf + len, 0, newlen - len); + if (len != newlen) { + if (in_kernel) + memset(buf + len, 0, newlen - len); + else if (clear_user((__force void __user *)buf + len, + newlen - len)) + return -EFAULT; + } return newlen; } EXPORT_SYMBOL(snd_seq_expand_var_event); diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 44302d98950e89aca5de30ca166b5e3ba6de9c7c..18320a248aa7daa080df9499668c400f4da22066 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -349,9 +349,9 @@ snd_seq_midisynth_probe(struct device *_dev) if (! port->name[0]) { if (info->name[0]) { if (ports > 1) - snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p); + scnprintf(port->name, sizeof(port->name), "%s-%u", info->name, p); else - snprintf(port->name, sizeof(port->name), "%s", info->name); + scnprintf(port->name, sizeof(port->name), "%s", info->name); } else { /* last resort */ if (ports > 1) diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index f26a1812dfa73e564aaf77f8197045c8e9a3a933..2db371d79930d0f2af7630f50901a3d69006e538 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -207,7 +207,7 @@ static void fill_port_info(struct snd_seq_port_info *port, SNDRV_SEQ_PORT_TYPE_PORT; port->midi_channels = 16; if (*group->name) - snprintf(port->name, sizeof(port->name), "Group %d (%s)", + snprintf(port->name, sizeof(port->name), "Group %d (%.53s)", group->group + 1, group->name); else sprintf(port->name, "Group %d", group->group + 1); @@ -416,6 +416,25 @@ static void setup_client_midi_version(struct seq_ump_client *client) snd_seq_kernel_client_put(cptr); } +/* set up client's group_filter bitmap */ +static void setup_client_group_filter(struct seq_ump_client *client) +{ + struct snd_seq_client *cptr; + unsigned int filter; + int p; + + cptr = snd_seq_kernel_client_get(client->seq_client); + if (!cptr) + return; + filter = ~(1U << 0); /* always allow groupless messages */ + for (p = 0; p < SNDRV_UMP_MAX_GROUPS; p++) { + if (client->groups[p].active) + filter &= ~(1U << (p + 1)); + } + cptr->group_filter = filter; + snd_seq_kernel_client_put(cptr); +} + /* UMP group change notification */ static void handle_group_notify(struct work_struct *work) { @@ -424,6 +443,7 @@ static void handle_group_notify(struct work_struct *work) update_group_attrs(client); update_port_infos(client); + setup_client_group_filter(client); } /* UMP FB change notification */ @@ -492,6 +512,8 @@ static int snd_seq_ump_probe(struct device *_dev) goto error; } + setup_client_group_filter(client); + err = create_ump_endpoint_port(client); if (err < 0) goto error; diff --git a/sound/core/seq/seq_ump_convert.c b/sound/core/seq/seq_ump_convert.c index 7cc84e137999cd8e5a38ac6913e4a9cbc58fc4d0..b141024830ecc831430c697858ce409a56c711af 100644 --- a/sound/core/seq/seq_ump_convert.c +++ b/sound/core/seq/seq_ump_convert.c @@ -1197,6 +1197,8 @@ int snd_seq_deliver_to_ump(struct snd_seq_client *source, struct snd_seq_event *event, int atomic, int hop) { + if (dest->group_filter & (1U << dest_port->ump_group)) + return 0; /* group filtered - skip the event */ if (event->type == SNDRV_SEQ_EVENT_SYSEX) return cvt_sysex_to_ump(dest, dest_port, event, atomic, hop); else if (snd_seq_client_is_midi2(dest)) diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c index 6f597d03e7c157ae45476cfd888e64720d1604b0..b1425bf98c3befd7237c6b19920c86667dabe16e 100644 --- a/sound/firewire/bebob/bebob_midi.c +++ b/sound/firewire/bebob/bebob_midi.c @@ -84,9 +84,9 @@ static void set_midi_substream_names(struct snd_bebob *bebob, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - bebob->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + bebob->card->shortname, subs->number + 1); } } diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c index 4c2998034313587ac8a53f18ad708db6fb32e4a7..78988e44b8bcd23329dfd929e144c13163e8602b 100644 --- a/sound/firewire/dice/dice-midi.c +++ b/sound/firewire/dice/dice-midi.c @@ -88,8 +88,8 @@ static void set_midi_substream_names(struct snd_dice *dice, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", dice->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", dice->card->shortname, subs->number + 1); } } diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c index 68eb8c39afa6c58ea7994172ea1773b5ac654aed..8f4bace16050a16248c9c117af9cc0888bbf0463 100644 --- a/sound/firewire/digi00x/digi00x-midi.c +++ b/sound/firewire/digi00x/digi00x-midi.c @@ -100,14 +100,14 @@ static void set_substream_names(struct snd_dg00x *dg00x, list_for_each_entry(subs, &str->substreams, list) { if (!is_console) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - dg00x->card->shortname, - subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + dg00x->card->shortname, + subs->number + 1); } else { - snprintf(subs->name, sizeof(subs->name), - "%s control", - dg00x->card->shortname); + scnprintf(subs->name, sizeof(subs->name), + "%s control", + dg00x->card->shortname); } } } diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c index 25821d186b878f67a389c474ff3d0caf855be3da..da3054fdcc7d66050f51abc09cb2dd6ac309597e 100644 --- a/sound/firewire/fireface/ff-midi.c +++ b/sound/firewire/fireface/ff-midi.c @@ -79,8 +79,8 @@ static void set_midi_substream_names(struct snd_rawmidi_str *stream, struct snd_rawmidi_substream *substream; list_for_each_entry(substream, &stream->substreams, list) { - snprintf(substream->name, sizeof(substream->name), - "%s MIDI %d", name, substream->number + 1); + scnprintf(substream->name, sizeof(substream->name), + "%s MIDI %d", name, substream->number + 1); } } diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c index dd4298876ac087954ae37e3f20ceea93b3362ed6..e3ed4e094ccd0e8311a053878cb9d5e7c429a46b 100644 --- a/sound/firewire/fireworks/fireworks.c +++ b/sound/firewire/fireworks/fireworks.c @@ -93,11 +93,11 @@ get_hardware_info(struct snd_efw *efw) strcpy(efw->card->driver, "Fireworks"); strcpy(efw->card->shortname, hwinfo->model_name); strcpy(efw->card->mixername, hwinfo->model_name); - snprintf(efw->card->longname, sizeof(efw->card->longname), - "%s %s v%s, GUID %08x%08x at %s, S%d", - hwinfo->vendor_name, hwinfo->model_name, version, - hwinfo->guid_hi, hwinfo->guid_lo, - dev_name(&efw->unit->device), 100 << fw_dev->max_speed); + scnprintf(efw->card->longname, sizeof(efw->card->longname), + "%s %s v%s, GUID %08x%08x at %s, S%d", + hwinfo->vendor_name, hwinfo->model_name, version, + hwinfo->guid_hi, hwinfo->guid_lo, + dev_name(&efw->unit->device), 100 << fw_dev->max_speed); if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE)) efw->resp_addr_changable = true; diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c index 84621e3568482431d4334a5d4afb773dfb4a63a7..350bf4d299c2009571427b75b0378314c2965f05 100644 --- a/sound/firewire/fireworks/fireworks_midi.c +++ b/sound/firewire/fireworks/fireworks_midi.c @@ -84,8 +84,8 @@ static void set_midi_substream_names(struct snd_efw *efw, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", efw->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", efw->card->shortname, subs->number + 1); } } diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c index 2365f7dfde2664dfa3990e2e8ab848b2de5ada4d..eebc7e790ee2ab682ea024d6b82723252f995bd4 100644 --- a/sound/firewire/motu/motu-midi.c +++ b/sound/firewire/motu/motu-midi.c @@ -88,8 +88,8 @@ static void set_midi_substream_names(struct snd_motu *motu, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", motu->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", motu->card->shortname, subs->number + 1); } } diff --git a/sound/firewire/oxfw/oxfw-midi.c b/sound/firewire/oxfw/oxfw-midi.c index 775cba3f1f028a03853394443ddc92e9aecad912..c215fa6f7a03713e946b276016fc80f5585d4d9e 100644 --- a/sound/firewire/oxfw/oxfw-midi.c +++ b/sound/firewire/oxfw/oxfw-midi.c @@ -129,9 +129,9 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw, struct snd_rawmidi_substream *subs; list_for_each_entry(subs, &str->substreams, list) { - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - oxfw->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + oxfw->card->shortname, subs->number + 1); } } diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c index 63d40f1a914f8c710bcdc2e57cf3794102d475b7..241a697ce26b2585f7271ef6e9ae0cbcadce879b 100644 --- a/sound/firewire/oxfw/oxfw.c +++ b/sound/firewire/oxfw/oxfw.c @@ -108,11 +108,11 @@ static int name_card(struct snd_oxfw *oxfw, const struct ieee1394_device_id *ent strcpy(oxfw->card->mixername, m); strcpy(oxfw->card->shortname, m); - snprintf(oxfw->card->longname, sizeof(oxfw->card->longname), - "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d", - v, m, firmware >> 20, firmware & 0xffff, - fw_dev->config_rom[3], fw_dev->config_rom[4], - dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed); + scnprintf(oxfw->card->longname, sizeof(oxfw->card->longname), + "%s %s (OXFW%x %04x), GUID %08x%08x at %s, S%d", + v, m, firmware >> 20, firmware & 0xffff, + fw_dev->config_rom[3], fw_dev->config_rom[4], + dev_name(&oxfw->unit->device), 100 << fw_dev->max_speed); end: return err; } diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c index 02eed2dce435f24e41f2f5a5b06104d52c2f10e0..c57fac4f196819462aac2d0ebc11f03c16ce36fc 100644 --- a/sound/firewire/tascam/tascam-midi.c +++ b/sound/firewire/tascam/tascam-midi.c @@ -108,9 +108,9 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm) /* TODO: support virtual MIDI ports. */ if (subs->number < tscm->spec->midi_capture_ports) { /* Hardware MIDI ports. */ - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - tscm->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + tscm->card->shortname, subs->number + 1); } } @@ -123,9 +123,9 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm) list_for_each_entry(subs, &stream->substreams, list) { if (subs->number < tscm->spec->midi_playback_ports) { /* Hardware MIDI ports only. */ - snprintf(subs->name, sizeof(subs->name), - "%s MIDI %d", - tscm->card->shortname, subs->number + 1); + scnprintf(subs->name, sizeof(subs->name), + "%s MIDI %d", + tscm->card->shortname, subs->number + 1); } } diff --git a/sound/hda/intel-sdw-acpi.c b/sound/hda/intel-sdw-acpi.c index 5cb92f7ccbcac991f95aa1fd2ecea60486fe9248..b57d72ea4503fa7504757eb6542b51e5b2e7b5e9 100644 --- a/sound/hda/intel-sdw-acpi.c +++ b/sound/hda/intel-sdw-acpi.c @@ -23,7 +23,7 @@ static int ctrl_link_mask; module_param_named(sdw_link_mask, ctrl_link_mask, int, 0444); MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); -static bool is_link_enabled(struct fwnode_handle *fw_node, int i) +static bool is_link_enabled(struct fwnode_handle *fw_node, u8 idx) { struct fwnode_handle *link; char name[32]; @@ -31,7 +31,7 @@ static bool is_link_enabled(struct fwnode_handle *fw_node, int i) /* Find master handle */ snprintf(name, sizeof(name), - "mipi-sdw-link-%d-subproperties", i); + "mipi-sdw-link-%hhu-subproperties", idx); link = fwnode_get_named_child_node(fw_node, name); if (!link) @@ -51,8 +51,8 @@ static int sdw_intel_scan_controller(struct sdw_intel_acpi_info *info) { struct acpi_device *adev = acpi_fetch_acpi_dev(info->handle); - int ret, i; - u8 count; + u8 count, i; + int ret; if (!adev) return -EINVAL; diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c index c471ac2aa450a1a4ee65d60ca5fad3e47d3d053f..401d8df28d8767a8b1159494d30ab497e6958ffc 100644 --- a/sound/isa/ad1848/ad1848.c +++ b/sound/isa/ad1848/ad1848.c @@ -96,13 +96,13 @@ static int snd_ad1848_probe(struct device *dev, unsigned int n) strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); if (!thinkpad[n]) - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %d, dma %d", - chip->pcm->name, chip->port, irq[n], dma1[n]); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %d, dma %d", + chip->pcm->name, chip->port, irq[n], dma1[n]); else - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %d, dma %d [Thinkpad]", - chip->pcm->name, chip->port, irq[n], dma1[n]); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %d, dma %d [Thinkpad]", + chip->pcm->name, chip->port, irq[n], dma1[n]); error = snd_card_register(card); if (error < 0) diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c index 1e89233853662046f4161a767877ad87f67a8967..c87be4be6df1d4afeb9c62936a57de4a02297bed 100644 --- a/sound/isa/cs423x/cs4231.c +++ b/sound/isa/cs423x/cs4231.c @@ -98,13 +98,13 @@ static int snd_cs4231_probe(struct device *dev, unsigned int n) strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); if (dma2[n] < 0) - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %d, dma %d", - chip->pcm->name, chip->port, irq[n], dma1[n]); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %d, dma %d", + chip->pcm->name, chip->port, irq[n], dma1[n]); else - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %d, dma %d&%d", - chip->pcm->name, chip->port, irq[n], dma1[n], dma2[n]); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %d, dma %d&%d", + chip->pcm->name, chip->port, irq[n], dma1[n], dma2[n]); error = snd_wss_mixer(chip); if (error < 0) diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index 10112e1bb25dc2373c77ae3ef0d120d328a91030..7226cbf2d7de4c5736cbd31858c727bd7b4e2917 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -367,14 +367,14 @@ static int snd_cs423x_probe(struct snd_card *card, int dev) strscpy(card->driver, chip->pcm->name, sizeof(card->driver)); strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); if (dma2[dev] < 0) - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %i, dma %i", - chip->pcm->name, chip->port, irq[dev], dma1[dev]); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %i, dma %i", + chip->pcm->name, chip->port, irq[dev], dma1[dev]); else - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %i, dma %i&%d", - chip->pcm->name, chip->port, irq[dev], dma1[dev], - dma2[dev]); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %i, dma %i&%d", + chip->pcm->name, chip->port, irq[dev], dma1[dev], + dma2[dev]); err = snd_wss_timer(chip, 0); if (err < 0) diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index f935b56eeec7bfc1baceeb4285c49c3dadeb1c35..97728bf45474238d500aaead219a8d5e3396ae00 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -130,9 +130,9 @@ static int snd_es1688_probe(struct snd_card *card, unsigned int n) strscpy(card->driver, "ES1688", sizeof(card->driver)); strscpy(card->shortname, chip->pcm->name, sizeof(card->shortname)); - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port, - chip->irq, chip->dma8); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %i, dma %i", chip->pcm->name, chip->port, + chip->irq, chip->dma8); if (fm_port[n] == SNDRV_AUTO_PORT) fm_port[n] = port[n]; /* share the same port */ diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index 59242baed576410d5a2d82d854e79901f1e95842..59792f2fada1d578154c25e11d61ef8153b5689e 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -1344,10 +1344,10 @@ static int snd_miro_probe(struct snd_card *card) } strcpy(card->driver, "miro"); - snprintf(card->longname, sizeof(card->longname), - "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", - card->shortname, miro->name, codec->pcm->name, - miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2); + scnprintf(card->longname, sizeof(card->longname), + "%s: OPTi%s, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, miro->name, codec->pcm->name, + miro->wss_base + 4, miro->irq, miro->dma1, miro->dma2); if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) rmidi = NULL; diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index 4beeb32fe2a7a9d0bbfb999bbbd6c9020bf3a58a..c33f67dd51331acf457d74b02926dc8392e8a673 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -859,15 +859,15 @@ static int snd_opti9xx_probe(struct snd_card *card) strcpy(card->driver, chip->name); sprintf(card->shortname, "OPTi %s", card->driver); #if defined(CS4231) || defined(OPTi93X) - snprintf(card->longname, sizeof(card->longname), - "%s, %s at 0x%lx, irq %d, dma %d&%d", - card->shortname, codec->pcm->name, - chip->wss_base + 4, irq, dma1, xdma2); + scnprintf(card->longname, sizeof(card->longname), + "%s, %s at 0x%lx, irq %d, dma %d&%d", + card->shortname, codec->pcm->name, + chip->wss_base + 4, irq, dma1, xdma2); #else - snprintf(card->longname, sizeof(card->longname), - "%s, %s at 0x%lx, irq %d, dma %d", - card->shortname, codec->pcm->name, chip->wss_base + 4, irq, - dma1); + scnprintf(card->longname, sizeof(card->longname), + "%s, %s at 0x%lx, irq %d, dma %d", + card->shortname, codec->pcm->name, chip->wss_base + 4, irq, + dma1); #endif /* CS4231 || OPTi93X */ if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT) diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index c05935c2edc48e1a813a9bb7e1e9674d17a86b39..9234d4fe8ada8ea1368e6e7811ea9af648821f47 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -456,7 +456,7 @@ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, /* convert to word unit */ pos = (pos << 1) + rec->loop_start[voice]; count <<= 1; - LOOP_WRITE(rec, pos, USER_SOCKPTR(NULL), count); + LOOP_WRITE(rec, pos, NULL, count); return 0; } diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index 0bc0025f7c199a6c678e2bf8c949b33fe882ff28..cc56fafd27b1253e301d58f493b89d17fbdaba08 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -557,7 +557,7 @@ static int sscape_upload_microcode(struct snd_card *card, int version) char name[14]; int err; - snprintf(name, sizeof(name), "sndscape.co%d", version); + scnprintf(name, sizeof(name), "sndscape.co%d", version); err = request_firmware(&init_fw, name, card->dev); if (err < 0) { diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 1415baac9c361349932e2c67d6a97e9a18513527..08e34b184780187f66922448c40761e6281d84a2 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -3102,11 +3102,13 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci, } sprintf(card->shortname, "C-Media CMI%d", val); if (cm->chip_version < 68) - sprintf(modelstr, " (model %d)", cm->chip_version); + scnprintf(modelstr, sizeof(modelstr), + " (model %d)", cm->chip_version); else modelstr[0] = '\0'; - sprintf(card->longname, "%s%s at %#lx, irq %i", - card->shortname, modelstr, cm->iobase, cm->irq); + scnprintf(card->longname, sizeof(card->longname), + "%s%s at %#lx, irq %i", + card->shortname, modelstr, cm->iobase, cm->irq); if (cm->chip_version >= 39) { val = snd_cmipci_read_b(cm, CM_REG_MPU_PCI + 1); diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c index f9b77353c2660e0242e30938f8837427e55585fd..c6031f74409964ebf4b10d0dffadc2ed46c29250 100644 --- a/sound/pci/hda/cs35l41_hda.c +++ b/sound/pci/hda/cs35l41_hda.c @@ -185,10 +185,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->speaker_id, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); + if (ret) + goto coeff_err; + + return 0; } /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ @@ -197,10 +201,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->amp_name, -1, "wmfw"); if (!ret) { /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ - return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, cs35l41->amp_name, - cs35l41->speaker_id, "bin"); + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, cs35l41->amp_name, + cs35l41->speaker_id, "bin"); + if (ret) + goto coeff_err; + + return 0; } /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ @@ -215,10 +223,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->amp_name, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - return cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, NULL, - cs35l41->speaker_id, "bin"); + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, + coeff_filename, CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, + cs35l41->speaker_id, "bin"); + if (ret) + goto coeff_err; + + return 0; } /* try cirrus/part-dspN-fwtype-sub.wmfw */ @@ -233,12 +245,50 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, cs35l41->speaker_id, "bin"); if (ret) /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ - return cs35l41_request_firmware_file(cs35l41, coeff_firmware, - coeff_filename, CS35L41_FIRMWARE_ROOT, - cs35l41->acpi_subsystem_id, NULL, - cs35l41->speaker_id, "bin"); + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, + coeff_filename, CS35L41_FIRMWARE_ROOT, + cs35l41->acpi_subsystem_id, NULL, + cs35l41->speaker_id, "bin"); + if (ret) + goto coeff_err; + } + + return ret; +coeff_err: + release_firmware(*wmfw_firmware); + kfree(*wmfw_filename); + return ret; +} + +static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41, + const struct firmware **wmfw_firmware, + char **wmfw_filename, + const struct firmware **coeff_firmware, + char **coeff_filename) +{ + int ret; + + /* Handle fallback */ + dev_warn(cs35l41->dev, "Falling back to default firmware.\n"); + + /* fallback try cirrus/part-dspN-fwtype.wmfw */ + ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); + if (ret) + goto err; + + /* fallback try cirrus/part-dspN-fwtype.bin */ + ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, + CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); + if (ret) { + release_firmware(*wmfw_firmware); + kfree(*wmfw_filename); + goto err; } + return 0; +err: + dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n"); return ret; } @@ -254,7 +304,6 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename); goto out; - } /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ @@ -267,6 +316,9 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, cs35l41->amp_name, -1, "bin"); + if (ret) + goto coeff_err; + goto out; } @@ -286,32 +338,23 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id, NULL, -1, "bin"); + if (ret) + goto coeff_err; } out: - if (!ret) - return 0; + if (ret) + /* if all attempts at finding firmware fail, try fallback */ + goto fallback; - /* Handle fallback */ - dev_warn(cs35l41->dev, "Falling back to default firmware.\n"); + return 0; +coeff_err: release_firmware(*wmfw_firmware); kfree(*wmfw_filename); - - /* fallback try cirrus/part-dspN-fwtype.wmfw */ - ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw"); - if (!ret) - /* fallback try cirrus/part-dspN-fwtype.bin */ - ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, - CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin"); - - if (ret) { - release_firmware(*wmfw_firmware); - kfree(*wmfw_filename); - dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n"); - } - return ret; +fallback: + return cs35l41_fallback_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, + coeff_firmware, coeff_filename); } #if IS_ENABLED(CONFIG_EFI) diff --git a/sound/pci/hda/cs35l56_hda.c b/sound/pci/hda/cs35l56_hda.c index 76b9c685560bf078ec856843f0db1f42969c7508..7adc1d373d65c377cf2db29f4a6bb63b57db0bb4 100644 --- a/sound/pci/hda/cs35l56_hda.c +++ b/sound/pci/hda/cs35l56_hda.c @@ -105,7 +105,7 @@ static void cs35l56_hda_playback_hook(struct device *dev, int action) } } -static int __maybe_unused cs35l56_hda_runtime_suspend(struct device *dev) +static int cs35l56_hda_runtime_suspend(struct device *dev) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); @@ -115,7 +115,7 @@ static int __maybe_unused cs35l56_hda_runtime_suspend(struct device *dev) return cs35l56_runtime_suspend_common(&cs35l56->base); } -static int __maybe_unused cs35l56_hda_runtime_resume(struct device *dev) +static int cs35l56_hda_runtime_resume(struct device *dev) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); int ret; @@ -218,7 +218,7 @@ static int cs35l56_hda_posture_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = pos; - return ret; + return 0; } static int cs35l56_hda_posture_put(struct snd_kcontrol *kcontrol, @@ -865,15 +865,13 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int id) sub = acpi_get_subsystem_id(ACPI_HANDLE(cs35l56->base.dev)); if (IS_ERR(sub)) { - /* If no ACPI SUB, return 0 and fallback to legacy firmware path, otherwise fail */ - if (PTR_ERR(sub) == -ENODATA) - return 0; - else - return PTR_ERR(sub); + dev_info(cs35l56->base.dev, + "Read ACPI _SUB failed(%ld): fallback to generic firmware\n", + PTR_ERR(sub)); + } else { + cs35l56->system_name = sub; } - cs35l56->system_name = sub; - cs35l56->base.reset_gpio = devm_gpiod_get_index_optional(cs35l56->base.dev, "reset", cs35l56->index, @@ -1003,6 +1001,7 @@ void cs35l56_hda_remove(struct device *dev) { struct cs35l56_hda *cs35l56 = dev_get_drvdata(dev); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_get_sync(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); @@ -1016,7 +1015,7 @@ void cs35l56_hda_remove(struct device *dev) EXPORT_SYMBOL_NS_GPL(cs35l56_hda_remove, SND_HDA_SCODEC_CS35L56); const struct dev_pm_ops cs35l56_hda_pm_ops = { - SET_RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL) + RUNTIME_PM_OPS(cs35l56_hda_runtime_suspend, cs35l56_hda_runtime_resume, NULL) SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend, cs35l56_hda_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_hda_system_suspend_late, cs35l56_hda_system_resume_early) diff --git a/sound/pci/hda/cs35l56_hda_i2c.c b/sound/pci/hda/cs35l56_hda_i2c.c index 83e4acdd89aceddb9d8671728b752d3611f2f2d8..757a4d193e0fb5919778847693a3353f2825265b 100644 --- a/sound/pci/hda/cs35l56_hda_i2c.c +++ b/sound/pci/hda/cs35l56_hda_i2c.c @@ -21,7 +21,6 @@ static int cs35l56_hda_i2c_probe(struct i2c_client *clt) return -ENOMEM; cs35l56->base.dev = &clt->dev; - cs35l56->base.can_hibernate = true; cs35l56->base.regmap = devm_regmap_init_i2c(clt, &cs35l56_regmap_i2c); if (IS_ERR(cs35l56->base.regmap)) { ret = PTR_ERR(cs35l56->base.regmap); diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index dbf7aa88e0e31a48a751e91761cfa644f98ab1c1..bf685d01259d30070aaf3ac7f3ed3204bc30c5bd 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -998,7 +998,11 @@ static int add_control_with_pfx(struct hda_gen_spec *spec, int type, const char *sfx, int cidx, unsigned long val) { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; - snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + int len; + + len = snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + if (snd_BUG_ON(len >= sizeof(name))) + return -EINVAL; if (!add_control(spec, type, name, cidx, val)) return -ENOMEM; return 0; diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 765d95e7986171d2d2d3fed9ef2509db975f7074..ca765ac4765f4a27b77f81f466b647a9699f068d 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2211,6 +2211,7 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x8086, 0x2068, "Intel NUC7i3BNB", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=198611 */ SND_PCI_QUIRK(0x17aa, 0x2227, "Lenovo X1 Carbon 3rd Gen", 0), + SND_PCI_QUIRK(0x17aa, 0x316e, "Lenovo ThinkCentre M70q", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1689623 */ SND_PCI_QUIRK(0x17aa, 0x367b, "Lenovo IdeaCentre B550", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1572975 */ diff --git a/sound/pci/hda/patch_cs8409.c b/sound/pci/hda/patch_cs8409.c index 0ba1fbcbb21e47508ba3d685f01162366e5ba6d6..627899959ffe8c34c76211d51824e1d1e93d33a8 100644 --- a/sound/pci/hda/patch_cs8409.c +++ b/sound/pci/hda/patch_cs8409.c @@ -888,7 +888,7 @@ static void cs42l42_resume(struct sub_codec *cs42l42) /* Initialize CS42L42 companion codec */ cs8409_i2c_bulk_write(cs42l42, cs42l42->init_seq, cs42l42->init_seq_num); - usleep_range(30000, 35000); + msleep(CS42L42_INIT_TIMEOUT_MS); /* Clear interrupts, by reading interrupt status registers */ cs8409_i2c_bulk_read(cs42l42, irq_regs, ARRAY_SIZE(irq_regs)); diff --git a/sound/pci/hda/patch_cs8409.h b/sound/pci/hda/patch_cs8409.h index 2a8dfb4ff046b0918fa3d21e91255d8b98417f00..937e9387abdc7a8d65c3178eb0b810f2690077e8 100644 --- a/sound/pci/hda/patch_cs8409.h +++ b/sound/pci/hda/patch_cs8409.h @@ -229,6 +229,7 @@ enum cs8409_coefficient_index_registers { #define CS42L42_I2C_SLEEP_US (2000) #define CS42L42_PDN_TIMEOUT_US (250000) #define CS42L42_PDN_SLEEP_US (2000) +#define CS42L42_INIT_TIMEOUT_MS (45) #define CS42L42_FULL_SCALE_VOL_MASK (2) #define CS42L42_FULL_SCALE_VOL_0DB (1) #define CS42L42_FULL_SCALE_VOL_MINUS6DB (0) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a07df6f9296007fc1addf3b3abf327912278db44..3eeecf67c17b177e6d16fa7854fdf913cb43f95a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7057,6 +7057,29 @@ static void alc295_fixup_dell_inspiron_top_speakers(struct hda_codec *codec, } } +/* Forcibly assign NID 0x03 to HP while NID 0x02 to SPK */ +static void alc287_fixup_bind_dacs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + static const hda_nid_t conn[] = { 0x02, 0x03 }; /* exclude 0x06 */ + static const hda_nid_t preferred_pairs[] = { + 0x17, 0x02, 0x21, 0x03, 0 + }; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + snd_hda_override_conn_list(codec, 0x17, ARRAY_SIZE(conn), conn); + spec->gen.preferred_dacs = preferred_pairs; + spec->gen.auto_mute_via_amp = 1; + if (spec->gen.autocfg.speaker_pins[0] != 0x14) { + snd_hda_codec_write_cache(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + 0x0); /* Make sure 0x14 was disable */ + } +} + + enum { ALC269_FIXUP_GPIO2, ALC269_FIXUP_SONY_VAIO, @@ -7319,6 +7342,8 @@ enum { ALC287_FIXUP_TAS2781_I2C, ALC245_FIXUP_HP_MUTE_LED_COEFBIT, ALC245_FIXUP_HP_X360_MUTE_LEDS, + ALC287_FIXUP_THINKPAD_I2S_SPK, + ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -9413,6 +9438,16 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC245_FIXUP_HP_GPIO_LED }, + [ALC287_FIXUP_THINKPAD_I2S_SPK] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_bind_dacs, + }, + [ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc287_fixup_bind_dacs, + .chained = true, + .chain_id = ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -9786,7 +9821,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x1d1f, "ASUS ROG Strix G17 2023 (G713PV)", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1d42, "ASUS Zephyrus G14 2022", ALC289_FIXUP_ASUS_GA401), SND_PCI_QUIRK(0x1043, 0x1d4e, "ASUS TM420", ALC256_FIXUP_ASUS_HPE), - SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x1e02, "ASUS UX3402ZA", ALC245_FIXUP_CS35L41_SPI_2), + SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x1e11, "ASUS Zephyrus G15", ALC289_FIXUP_ASUS_GA502), SND_PCI_QUIRK(0x1043, 0x1e12, "ASUS UM3402", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x1e51, "ASUS Zephyrus M15", ALC294_FIXUP_ASUS_GU502_PINS), @@ -9825,7 +9861,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x10ec, 0x124c, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), - SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC225_FIXUP_HEADSET_JACK), + SND_PCI_QUIRK(0x10ec, 0x12cc, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_AMP), @@ -9959,14 +9995,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x22be, "Thinkpad X1 Carbon 8th", ALC285_FIXUP_THINKPAD_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c1, "Thinkpad P1 Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), SND_PCI_QUIRK(0x17aa, 0x22c2, "Thinkpad X1 Extreme Gen 3", ALC285_FIXUP_THINKPAD_NO_BASS_SPK_HEADSET_JACK), - SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), - SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI), + SND_PCI_QUIRK(0x17aa, 0x22f1, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x22f2, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x22f3, "Thinkpad", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2316, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2317, "Thinkpad P1 Gen 6", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2318, "Thinkpad Z13 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x2319, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), + SND_PCI_QUIRK(0x17aa, 0x231a, "Thinkpad Z16 Gen2", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY), SND_PCI_QUIRK(0x17aa, 0x310c, "ThinkCentre Station", ALC294_FIXUP_LENOVO_MIC_LOCATION), @@ -10062,7 +10098,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x8086, 0x2074, "Intel NUC 8", ALC233_FIXUP_INTEL_NUC8_DMIC), SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10), - SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC225_FIXUP_HEADSET_JACK), + SND_PCI_QUIRK(0x8086, 0x3038, "Intel NUC 13", ALC295_FIXUP_CHROME_BOOK), SND_PCI_QUIRK(0xf111, 0x0001, "Framework Laptop", ALC295_FIXUP_FRAMEWORK_LAPTOP_MIC_NO_PRESENCE), #if 0 @@ -10544,6 +10580,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x17, 0x90170111}, {0x19, 0x03a11030}, {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, + {0x17, 0x90170110}, + {0x19, 0x03a11030}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0287, 0x17aa, "Lenovo", ALC287_FIXUP_THINKPAD_I2S_SPK, + {0x17, 0x90170110}, /* 0x231f with RTK I2S AMP */ + {0x19, 0x04a11040}, + {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0286, 0x1025, "Acer", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE, {0x12, 0x90a60130}, {0x17, 0x90170110}, diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c index 37114fd61a3856173414c089dd7be56cda5fa7c5..fb802802939e170f73e87c85ec223ebf40fab4e8 100644 --- a/sound/pci/hda/tas2781_hda_i2c.c +++ b/sound/pci/hda/tas2781_hda_i2c.c @@ -173,16 +173,6 @@ static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, return 0; } -static int tasdevice_hda_clamp(int val, int max) -{ - if (val > max) - val = max; - - if (val < 0) - val = 0; - return val; -} - static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -191,7 +181,7 @@ static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, int max = tas_priv->rcabin.ncfgs - 1; int val, ret = 0; - val = tasdevice_hda_clamp(nr_profile, max); + val = clamp(nr_profile, 0, max); if (tas_priv->rcabin.profile_cfg_id != val) { tas_priv->rcabin.profile_cfg_id = val; @@ -248,7 +238,7 @@ static int tasdevice_program_put(struct snd_kcontrol *kcontrol, int max = tas_fw->nr_programs - 1; int val, ret = 0; - val = tasdevice_hda_clamp(nr_program, max); + val = clamp(nr_program, 0, max); if (tas_priv->cur_prog != val) { tas_priv->cur_prog = val; @@ -277,7 +267,7 @@ static int tasdevice_config_put(struct snd_kcontrol *kcontrol, int max = tas_fw->nr_configurations - 1; int val, ret = 0; - val = tasdevice_hda_clamp(nr_config, max); + val = clamp(nr_config, 0, max); if (tas_priv->cur_conf != val) { tas_priv->cur_conf = val; diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index b37c877c2c16057505fae4b206d74f96657c66f3..9dee0345f22cf1e550d38f1c3b3c7918e2f116c5 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -2105,15 +2105,15 @@ __snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id strcpy(card->driver, "RIPTIDE"); strcpy(card->shortname, "Riptide"); #ifdef SUPPORT_JOYSTICK - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x", - card->shortname, chip->port, chip->irq, chip->mpuaddr, - chip->opladdr, chip->gameaddr); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x gameport 0x%x", + card->shortname, chip->port, chip->irq, chip->mpuaddr, + chip->opladdr, chip->gameaddr); #else - snprintf(card->longname, sizeof(card->longname), - "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x", - card->shortname, chip->port, chip->irq, chip->mpuaddr, - chip->opladdr); + scnprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %i mpu 0x%x opl3 0x%x", + card->shortname, chip->port, chip->irq, chip->mpuaddr, + chip->opladdr); #endif snd_riptide_proc_init(chip); err = snd_card_register(card); diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index b304b3562c82bce54b5c0ad0fd91df9638580c5b..15a864dcd7bd3a526ccd5a568c9dc46ad2e394c8 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -213,6 +213,27 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "21J6"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82QF"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82TL"), + } + }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82UG"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -220,6 +241,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82V2"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "82YM"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -255,6 +283,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "M6500RC"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 15 B7ED"), + } + }, { .driver_data = &acp6x_card, .matches = { @@ -325,6 +360,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "8A22"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + DMI_MATCH(DMI_BOARD_NAME, "8A3E"), + } + }, { .driver_data = &acp6x_card, .matches = { diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c index afe213a71212fc1fd227d41d4620553e6507cf15..dcc4e14b3dde275551688e9eacb85639b4fbd974 100644 --- a/sound/soc/atmel/mchp-pdmc.c +++ b/sound/soc/atmel/mchp-pdmc.c @@ -954,7 +954,7 @@ static int mchp_pdmc_dt_init(struct mchp_pdmc *dd) /* used to clean the channel index found on RHR's MSB */ static int mchp_pdmc_process(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - struct iov_iter *buf, unsigned long bytes) + unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; u8 *dma_ptr = runtime->dma_area + hwoff + diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 95b5bd883215e7f576852e35c3c96c0d8ea82777..f1e1dbc509f6e4cd9e6ab09c2daa886d0610dba6 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1968,11 +1968,15 @@ config SND_SOC_UDA1380 tristate depends on I2C +config SND_SOC_WCD_CLASSH + tristate + config SND_SOC_WCD9335 tristate "WCD9335 Codec" depends on SLIMBUS select REGMAP_SLIMBUS select REGMAP_IRQ + select SND_SOC_WCD_CLASSH help The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports Qualcomm Technologies, Inc. (QTI) multimedia solutions, @@ -1987,6 +1991,7 @@ config SND_SOC_WCD934X depends on SLIMBUS select REGMAP_IRQ select REGMAP_SLIMBUS + select SND_SOC_WCD_CLASSH select SND_SOC_WCD_MBHC depends on MFD_WCD934X || COMPILE_TEST help @@ -1997,6 +2002,7 @@ config SND_SOC_WCD938X depends on SND_SOC_WCD938X_SDW tristate depends on SOUNDWIRE || !SOUNDWIRE + select SND_SOC_WCD_CLASSH config SND_SOC_WCD938X_SDW tristate "WCD9380/WCD9385 Codec - SDW" diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index c8502a49b40afcb56f45fe654eb8383638202149..a87e56938ce58887036537de1fa5329db3bd25f5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -303,10 +303,11 @@ snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda1334-objs := uda1334.o snd-soc-uda1380-objs := uda1380.o +snd-soc-wcd-classh-objs := wcd-clsh-v2.o snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o -snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o -snd-soc-wcd934x-objs := wcd-clsh-v2.o wcd934x.o -snd-soc-wcd938x-objs := wcd938x.o wcd-clsh-v2.o +snd-soc-wcd9335-objs := wcd9335.o +snd-soc-wcd934x-objs := wcd934x.o +snd-soc-wcd938x-objs := wcd938x.o snd-soc-wcd938x-sdw-objs := wcd938x-sdw.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o @@ -685,6 +686,7 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o +obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c index 8ee1baa0326911d2ae39a5fa047e73a5e7aa7212..87dd0ccade4ce754b9962a4f931e2a80c2dc0ee8 100644 --- a/sound/soc/codecs/aw88395/aw88395_lib.c +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -452,11 +452,13 @@ static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, if ((aw_bin->all_bin_parse_num != 1) || (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) { dev_err(aw_dev->dev, "bin num or type error"); + ret = -EINVAL; goto parse_bin_failed; } if (aw_bin->header_info[0].valid_data_len % 4) { dev_err(aw_dev->dev, "bin data len get error!"); + ret = -EINVAL; goto parse_bin_failed; } diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index d1edb9876c10f18c1f7bc22dba03a04baf522934..be4f4229576c4bbd222dd0e518829791ccc68598 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -279,7 +279,7 @@ static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = { }; static const struct snd_kcontrol_new cs35l45_dac_muxes[] = { - SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]), + SOC_DAPM_ENUM("DACPCM Source", cs35l45_dacpcm_enums[0]), }; static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { @@ -333,7 +333,7 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = { SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]), SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]), - SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]), + SND_SOC_DAPM_MUX("DACPCM Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]), SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -403,7 +403,7 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { { "ASP_RX1", NULL, "ASP_EN" }, { "ASP_RX2", NULL, "ASP_EN" }, - { "AMP", NULL, "DACPCM1 Source"}, + { "AMP", NULL, "DACPCM Source"}, { "AMP", NULL, "GLOBAL_EN"}, CS35L45_DSP_MUX_ROUTE("DSP_RX1"), @@ -427,7 +427,7 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = { {"DSP1 Preload", NULL, "DSP1 Preloader"}, {"DSP1", NULL, "DSP1 Preloader"}, - CS35L45_DAC_MUX_ROUTE("DACPCM1"), + CS35L45_DAC_MUX_ROUTE("DACPCM"), { "SPK", NULL, "AMP"}, }; @@ -969,7 +969,7 @@ static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data) ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val); if (!ret && mbox_val) - ret = cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK, + cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK, (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT); /* Handle DSP trace log IRQ */ @@ -1078,6 +1078,7 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45) switch (dev_id[0]) { case 0x35A450: + case 0x35A460: break; default: dev_err(cs35l45->dev, "Bad DEVID 0x%x\n", dev_id[0]); diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 9f4f2f4f23f5668c846f2a051eee64c533deb38f..d10e0e2380e852a5ba3b59f749fa685d923634c0 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -27,7 +27,6 @@ static int cs35l56_i2c_probe(struct i2c_client *client) return -ENOMEM; cs35l56->base.dev = dev; - cs35l56->base.can_hibernate = true; i2c_set_clientdata(client, cs35l56); cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config); diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index ae373f335ea8dbdf5c2c321b96def7dfb0f0e7b0..98b1e63360aeb3a5cbd277a328fd28b7393ea01b 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -243,26 +243,27 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base) { unsigned int reg; unsigned int val; - int ret; + int read_ret, poll_ret; if (cs35l56_base->rev < CS35L56_REVID_B0) reg = CS35L56_DSP1_HALO_STATE_A1; else reg = CS35L56_DSP1_HALO_STATE; - ret = regmap_read_poll_timeout(cs35l56_base->regmap, reg, - val, - (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), - CS35L56_HALO_STATE_POLL_US, - CS35L56_HALO_STATE_TIMEOUT_US); - - if ((ret < 0) && (ret != -ETIMEDOUT)) { - dev_err(cs35l56_base->dev, "Failed to read HALO_STATE: %d\n", ret); - return ret; - } - - if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) { - dev_err(cs35l56_base->dev, "Firmware boot fail: HALO_STATE=%#x\n", val); + /* + * This can't be a regmap_read_poll_timeout() because cs35l56 will NAK + * I2C until it has booted which would terminate the poll + */ + poll_ret = read_poll_timeout(regmap_read, read_ret, + (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE), + CS35L56_HALO_STATE_POLL_US, + CS35L56_HALO_STATE_TIMEOUT_US, + false, + cs35l56_base->regmap, reg, &val); + + if (poll_ret) { + dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n", + read_ret, val); return -EIO; } diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 600b79c62ec48a5dec9d843cb036477c7867f20a..f2e7c6d0be46c8b57e3701505196e7862d8f05a8 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1207,6 +1207,7 @@ void cs35l56_remove(struct cs35l56_private *cs35l56) flush_workqueue(cs35l56->dsp_wq); destroy_workqueue(cs35l56->dsp_wq); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_suspend(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c index eeab07c850f9582e918815b44c2698fb5d3995f1..974bae4abfad1cfb8339174c1d861db541380b23 100644 --- a/sound/soc/codecs/cs42l42-sdw.c +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -344,6 +344,16 @@ static int cs42l42_sdw_update_status(struct sdw_slave *peripheral, switch (status) { case SDW_SLAVE_ATTACHED: dev_dbg(cs42l42->dev, "ATTACHED\n"); + + /* + * The SoundWire core can report stale ATTACH notifications + * if we hard-reset CS42L42 in probe() but it had already been + * enumerated. Reject the ATTACH if we haven't yet seen an + * UNATTACH report for the device being in reset. + */ + if (cs42l42->sdw_waiting_first_unattach) + break; + /* * Initialise codec, this only needs to be done once. * When resuming from suspend, resume callback will handle re-init of codec, @@ -354,6 +364,16 @@ static int cs42l42_sdw_update_status(struct sdw_slave *peripheral, break; case SDW_SLAVE_UNATTACHED: dev_dbg(cs42l42->dev, "UNATTACHED\n"); + + if (cs42l42->sdw_waiting_first_unattach) { + /* + * SoundWire core has seen that CS42L42 is not on + * the bus so release RESET and wait for ATTACH. + */ + cs42l42->sdw_waiting_first_unattach = false; + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + } + break; default: break; diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index a0de0329406a1ff2ea3dc8a29f4ec510335553dd..2961340f15e2ed05d7c551c8d5f68fd557181bdc 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -2320,7 +2320,26 @@ int cs42l42_common_probe(struct cs42l42_private *cs42l42, if (cs42l42->reset_gpio) { dev_dbg(cs42l42->dev, "Found reset GPIO\n"); - gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); + + /* + * ACPI can override the default GPIO state we requested + * so ensure that we start with RESET low. + */ + gpiod_set_value_cansleep(cs42l42->reset_gpio, 0); + + /* Ensure minimum reset pulse width */ + usleep_range(10, 500); + + /* + * On SoundWire keep the chip in reset until we get an UNATTACH + * notification from the SoundWire core. This acts as a + * synchronization point to reject stale ATTACH notifications + * if the chip was already enumerated before we reset it. + */ + if (cs42l42->sdw_peripheral) + cs42l42->sdw_waiting_first_unattach = true; + else + gpiod_set_value_cansleep(cs42l42->reset_gpio, 1); } usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 4bd7b85a5747158ecf808c41d970eaa4c15066ab..7785125b73ab9342f70bb3438d69693b6710688b 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -53,6 +53,7 @@ struct cs42l42_private { u8 stream_use; bool hp_adc_up_pending; bool suspended; + bool sdw_waiting_first_unattach; bool init_done; }; diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index 24e718e5117468b9767cf3cc5be7ef89bdd02ef2..5643c666d7d04bf2e507126a7ba4d28430b6925f 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -2077,7 +2077,8 @@ static const struct cs42l43_irq cs42l43_irqs[] = { static int cs42l43_request_irq(struct cs42l43_codec *priv, struct irq_domain *dom, const char * const name, - unsigned int irq, irq_handler_t handler) + unsigned int irq, irq_handler_t handler, + unsigned long flags) { int ret; @@ -2087,8 +2088,8 @@ static int cs42l43_request_irq(struct cs42l43_codec *priv, dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name); - ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler, IRQF_ONESHOT, - name, priv); + ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler, + IRQF_ONESHOT | flags, name, priv); if (ret) return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name); @@ -2124,11 +2125,11 @@ static int cs42l43_shutter_irq(struct cs42l43_codec *priv, return 0; } - ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler); + ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler, IRQF_SHARED); if (ret) return ret; - return cs42l43_request_irq(priv, dom, open_name, open_irq, handler); + return cs42l43_request_irq(priv, dom, open_name, open_irq, handler, IRQF_SHARED); } static int cs42l43_codec_probe(struct platform_device *pdev) @@ -2178,7 +2179,8 @@ static int cs42l43_codec_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) { ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name, - cs42l43_irqs[i].irq, cs42l43_irqs[i].handler); + cs42l43_irqs[i].irq, + cs42l43_irqs[i].handler, 0); if (ret) goto err_pm; } @@ -2205,7 +2207,8 @@ static int cs42l43_codec_probe(struct platform_device *pdev) // Don't use devm as we need to get against the MFD device priv->mclk = clk_get_optional(cs42l43->dev, "mclk"); if (IS_ERR(priv->mclk)) { - dev_err_probe(priv->dev, PTR_ERR(priv->mclk), "Failed to get mclk\n"); + ret = PTR_ERR(priv->mclk); + dev_err_probe(priv->dev, ret, "Failed to get mclk\n"); goto err_pm; } diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 13689e718d36fee41152fe688ea043f7aa333a31..09eef6042aad6d53ad91fcef1cfe365f91c768be 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -531,7 +531,10 @@ static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, hp->sample_rate = sample_rate; hp->channels = channels; - hcp->chmap_idx = idx; + if (pcm_audio) + hcp->chmap_idx = ca_id; + else + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; return 0; } diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 15e1a62b9e571053284e3a2e8e8466fd5faf290f..e8cdc166bdaa99a948b03966d8cf67dbf49e7440 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2403,13 +2403,11 @@ static irqreturn_t rt5640_irq(int irq, void *data) struct rt5640_priv *rt5640 = data; int delay = 0; - if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { - cancel_delayed_work_sync(&rt5640->jack_work); + if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) delay = 100; - } if (rt5640->jack) - queue_delayed_work(system_long_wq, &rt5640->jack_work, delay); + mod_delayed_work(system_long_wq, &rt5640->jack_work, delay); return IRQ_HANDLED; } @@ -2565,10 +2563,9 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, if (jack_data && jack_data->use_platform_clock) rt5640->use_platform_clock = jack_data->use_platform_clock; - ret = devm_request_threaded_irq(component->dev, rt5640->irq, - NULL, rt5640_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "rt5640", rt5640); + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640_disable_jack_detect(component); @@ -2621,14 +2618,14 @@ static void rt5640_enable_hda_jack_detect( rt5640->jack = jack; - ret = devm_request_threaded_irq(component->dev, rt5640->irq, - NULL, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "rt5640", rt5640); + ret = request_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5640", rt5640); if (ret) { dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); - rt5640->irq = -ENXIO; + rt5640->jack = NULL; return; } + rt5640->irq_requested = true; /* sync initial jack state */ queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); @@ -2801,12 +2798,12 @@ static int rt5640_suspend(struct snd_soc_component *component) { struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component); - if (rt5640->irq) { + if (rt5640->jack) { /* disable jack interrupts during system suspend */ disable_irq(rt5640->irq); + rt5640_cancel_work(rt5640); } - rt5640_cancel_work(rt5640); snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); rt5640_reset(component); regcache_cache_only(rt5640->regmap, true); @@ -2829,9 +2826,6 @@ static int rt5640_resume(struct snd_soc_component *component) regcache_cache_only(rt5640->regmap, false); regcache_sync(rt5640->regmap); - if (rt5640->irq) - enable_irq(rt5640->irq); - if (rt5640->jack) { if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) { snd_soc_component_update_bits(component, @@ -2859,6 +2853,7 @@ static int rt5640_resume(struct snd_soc_component *component) } } + enable_irq(rt5640->irq); queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); } diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 038d93e208838014a4158f45e49b7b6b603aa8e7..1a137ca3f496473b34cd26f688d2c22bb042b821 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3269,13 +3269,17 @@ static int rt5645_component_set_jack(struct snd_soc_component *component, { struct snd_soc_jack *mic_jack = NULL; struct snd_soc_jack *btn_jack = NULL; - int *type = (int *)data; + int type; - if (*type & SND_JACK_MICROPHONE) - mic_jack = hs_jack; - if (*type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3)) - btn_jack = hs_jack; + if (hs_jack) { + type = *(int *)data; + + if (type & SND_JACK_MICROPHONE) + mic_jack = hs_jack; + if (type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3)) + btn_jack = hs_jack; + } return rt5645_set_jack_detect(component, hs_jack, mic_jack, btn_jack); } diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index b05b4f73d8aa7311fc1f82043564727f579c3bfc..fbad1ed066260194285d2151769d0533527a0b65 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -157,11 +157,6 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) return ret; } - ret = devm_add_action_or_reset(&i2c->dev, rt5682_i2c_disable_regulators, - rt5682); - if (ret) - return ret; - ret = regulator_bulk_enable(ARRAY_SIZE(rt5682->supplies), rt5682->supplies); if (ret) { @@ -169,6 +164,11 @@ static int rt5682_i2c_probe(struct i2c_client *i2c) return ret; } + ret = devm_add_action_or_reset(&i2c->dev, rt5682_i2c_disable_regulators, + rt5682); + if (ret) + return ret; + ret = rt5682_get_ldo1(rt5682, &i2c->dev); if (ret) return ret; diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index b976c1946286a9c07f65b048c683373a7112b12a..420bbf588efeaf1c0cc1d7700fa3d572ad6e816e 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -293,7 +293,7 @@ #define ADC3XXX_BYPASS_RPGA 0x80 /* MICBIAS control bits */ -#define ADC3XXX_MICBIAS_MASK 0x2 +#define ADC3XXX_MICBIAS_MASK 0x3 #define ADC3XXX_MICBIAS1_SHIFT 5 #define ADC3XXX_MICBIAS2_SHIFT 3 @@ -1099,7 +1099,7 @@ static int adc3xxx_parse_dt_micbias(struct adc3xxx *adc3xxx, unsigned int val; if (!of_property_read_u32(np, propname, &val)) { - if (val >= ADC3XXX_MICBIAS_AVDD) { + if (val > ADC3XXX_MICBIAS_AVDD) { dev_err(dev, "Invalid property value for '%s'\n", propname); return -EINVAL; } diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c index a75db27e52055c45b031450bd4dce0857d5836f4..d96e23ec43d4ca851f757001de5af8f45cc84bf7 100644 --- a/sound/soc/codecs/wcd-clsh-v2.c +++ b/sound/soc/codecs/wcd-clsh-v2.c @@ -355,6 +355,7 @@ void wcd_clsh_set_hph_mode(struct wcd_clsh_ctrl *ctrl, int mode) wcd_clsh_v2_set_hph_mode(comp, mode); } +EXPORT_SYMBOL_GPL(wcd_clsh_set_hph_mode); static void wcd_clsh_set_flyback_current(struct snd_soc_component *comp, int mode) @@ -869,11 +870,13 @@ int wcd_clsh_ctrl_set_state(struct wcd_clsh_ctrl *ctrl, return 0; } +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_set_state); int wcd_clsh_ctrl_get_state(struct wcd_clsh_ctrl *ctrl) { return ctrl->state; } +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_get_state); struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, int version) @@ -890,8 +893,13 @@ struct wcd_clsh_ctrl *wcd_clsh_ctrl_alloc(struct snd_soc_component *comp, return ctrl; } +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_alloc); void wcd_clsh_ctrl_free(struct wcd_clsh_ctrl *ctrl) { kfree(ctrl); } +EXPORT_SYMBOL_GPL(wcd_clsh_ctrl_free); + +MODULE_DESCRIPTION("WCD93XX Class-H driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0a50180750e8815c42040b4e433cd2f0381d3148..7689fe3cc86d657e332b820c94cb87396344ab9d 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1468,8 +1468,10 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) } wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); - if (IS_ERR(wm8960->regmap)) - return PTR_ERR(wm8960->regmap); + if (IS_ERR(wm8960->regmap)) { + ret = PTR_ERR(wm8960->regmap); + goto bulk_disable; + } if (pdata) memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data)); @@ -1479,13 +1481,14 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) ret = i2c_master_recv(i2c, &val, sizeof(val)); if (ret >= 0) { dev_err(&i2c->dev, "Not wm8960, wm8960 reg can not read by i2c\n"); - return -EINVAL; + ret = -EINVAL; + goto bulk_disable; } ret = wm8960_reset(wm8960->regmap); if (ret != 0) { dev_err(&i2c->dev, "Failed to issue reset\n"); - return ret; + goto bulk_disable; } if (wm8960->pdata.shared_lrclk) { @@ -1494,7 +1497,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) if (ret != 0) { dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", ret); - return ret; + goto bulk_disable; } } @@ -1528,7 +1531,13 @@ static int wm8960_i2c_probe(struct i2c_client *i2c) ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_wm8960, &wm8960_dai, 1); + if (ret) + goto bulk_disable; + return 0; + +bulk_disable: + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); return ret; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 6fc34f41b1758094acb4ba653eeb1bb023712399..d1b9238d391e81c089e43a85534d77846d77e440 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -687,7 +687,10 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, struct wm_coeff_ctl *ctl; int ret; + mutex_lock(&dsp->cs_dsp.pwr_lock); ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); + mutex_unlock(&dsp->cs_dsp.pwr_lock); + if (ret < 0) return ret; @@ -703,8 +706,14 @@ EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type, unsigned int alg, void *buf, size_t len) { - return cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), - 0, buf, len); + int ret; + + mutex_lock(&dsp->cs_dsp.pwr_lock); + ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg), + 0, buf, len); + mutex_unlock(&dsp->cs_dsp.pwr_lock); + + return ret; } EXPORT_SYMBOL_GPL(wm_adsp_read_ctl); diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 76b5bfc288fde069e956ce74989fca71cbb1d246..bab7d34cf585bfed41bee48c8e6bce74ddd36e65 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -52,8 +52,8 @@ struct codec_priv { unsigned long mclk_freq; unsigned long free_freq; u32 mclk_id; - u32 fll_id; - u32 pll_id; + int fll_id; + int pll_id; }; /** @@ -206,7 +206,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream, } /* Specific configuration for PLL */ - if (codec_priv->pll_id && codec_priv->fll_id) { + if (codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE) pll_out = priv->sample_rate * 384; else @@ -248,7 +248,7 @@ static int fsl_asoc_card_hw_free(struct snd_pcm_substream *substream) priv->streams &= ~BIT(substream->stream); - if (!priv->streams && codec_priv->pll_id && codec_priv->fll_id) { + if (!priv->streams && codec_priv->pll_id >= 0 && codec_priv->fll_id >= 0) { /* Force freq to be free_freq to avoid error message in codec */ ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(rtd, 0), codec_priv->mclk_id, @@ -621,6 +621,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) priv->card.dapm_routes = audio_map; priv->card.num_dapm_routes = ARRAY_SIZE(audio_map); priv->card.driver_name = DRIVER_NAME; + + priv->codec_priv.fll_id = -1; + priv->codec_priv.pll_id = -1; + /* Diversify the card configurations */ if (of_device_is_compatible(np, "fsl,imx-audio-cs42888")) { codec_dai_name = "cs42888"; diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 1e4020fae05abbbb55c403d261a50ba857b31e3e..8a9a30dd31e208d697b67c1a6788bc66e853fc38 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -710,10 +710,15 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir) { unsigned int ofs = sai->soc_data->reg_offset; bool tx = dir == TX; - u32 xcsr, count = 100; + u32 xcsr, count = 100, mask; + + if (sai->soc_data->mclk_with_tere && sai->mclk_direction_output) + mask = FSL_SAI_CSR_TERE; + else + mask = FSL_SAI_CSR_TERE | FSL_SAI_CSR_BCE; regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), - FSL_SAI_CSR_TERE | FSL_SAI_CSR_BCE, 0); + mask, 0); /* TERE will remain set till the end of current frame */ do { diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 0b58df56f4daa831baffc5dd294bfbc458add70e..aeb81aa61184fbc0889765ac1573a0a23ef3c2c6 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -315,7 +315,7 @@ static int imx_audmix_probe(struct platform_device *pdev) if (IS_ERR(priv->cpu_mclk)) { ret = PTR_ERR(priv->cpu_mclk); dev_err(&cpu_pdev->dev, "failed to get DAI mclk1: %d\n", ret); - return -EINVAL; + return ret; } priv->audmix_pdev = audmix_pdev; diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c index d63782b8bdef8f8b27d9669dabdc550cd0445f5a..bb736d45c9e04e5de7de46698d55b1b16b5a8718 100644 --- a/sound/soc/fsl/imx-pcm-rpmsg.c +++ b/sound/soc/fsl/imx-pcm-rpmsg.c @@ -19,6 +19,7 @@ static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c index 3c7b95db2eacc182b9d56a71c0847e76a5c910e0..b578f9a32d7f1cd62e9bdf4d84b92063a74c334d 100644 --- a/sound/soc/fsl/imx-rpmsg.c +++ b/sound/soc/fsl/imx-rpmsg.c @@ -89,6 +89,14 @@ static int imx_rpmsg_probe(struct platform_device *pdev) SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC; + /* + * i.MX rpmsg sound cards work on codec slave mode. MCLK will be + * disabled by CPU DAI driver in hw_free(). Some codec requires MCLK + * present at power up/down sequence. So need to set ignore_pmdown_time + * to power down codec immediately before MCLK is turned off. + */ + data->dai.ignore_pmdown_time = 1; + /* Optional codec node */ ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args); if (ret) { diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 5b18a4af022f19a1c14cf734f1df0f1f8609f64d..2588ec735dbdf2b2da39d85a6aeb7bc1c40b5c53 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -310,7 +310,8 @@ int asoc_simple_startup(struct snd_pcm_substream *substream) if (fixed_sysclk % props->mclk_fs) { dev_err(rtd->dev, "fixed sysclk %u not divisible by mclk_fs %u\n", fixed_sysclk, props->mclk_fs); - return -EINVAL; + ret = -EINVAL; + goto codec_err; } ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, fixed_rate, fixed_rate); diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 190f11366e84e0075625604f341d633f4b0075dd..274417e39e7de0972e0132846d944094843e650c 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -759,10 +759,12 @@ static int asoc_simple_probe(struct platform_device *pdev) struct snd_soc_dai_link *dai_link = priv->dai_link; struct simple_dai_props *dai_props = priv->dai_props; + ret = -EINVAL; + cinfo = dev->platform_data; if (!cinfo) { dev_err(dev, "no info for asoc-simple-card\n"); - return -EINVAL; + goto err; } if (!cinfo->name || @@ -771,7 +773,7 @@ static int asoc_simple_probe(struct platform_device *pdev) !cinfo->platform || !cinfo->cpu_dai.name) { dev_err(dev, "insufficient asoc_simple_card_info settings\n"); - return -EINVAL; + goto err; } cpus = dai_link->cpus; diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c index cb00bc86ac949a5a5592da5a40768271d88e87ba..8876558f19a1bacfd4b7d5b575b25602d885b68a 100644 --- a/sound/soc/intel/avs/boards/hdaudio.c +++ b/sound/soc/intel/avs/boards/hdaudio.c @@ -55,6 +55,9 @@ static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int return -ENOMEM; dl[i].codecs->name = devm_kstrdup(dev, cname, GFP_KERNEL); + if (!dl[i].codecs->name) + return -ENOMEM; + dl[i].codecs->dai_name = pcm->name; dl[i].num_codecs = 1; dl[i].num_cpus = 1; diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 1fbb2c2fadb55368c9c20aa5f9d039ae1054fde3..8565a530706d004159b002d46c1a2a13dd4154a2 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -796,6 +796,28 @@ static int avs_component_probe(struct snd_soc_component *component) ret = avs_load_topology(component, filename); kfree(filename); + if (ret == -ENOENT && !strncmp(mach->tplg_filename, "hda-", 4)) { + unsigned int vendor_id; + + if (sscanf(mach->tplg_filename, "hda-%08x-tplg.bin", &vendor_id) != 1) + return ret; + + if (((vendor_id >> 16) & 0xFFFF) == 0x8086) + mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, + "hda-8086-generic-tplg.bin"); + else + mach->tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, + "hda-generic-tplg.bin"); + + filename = kasprintf(GFP_KERNEL, "%s/%s", component->driver->topology_name_prefix, + mach->tplg_filename); + if (!filename) + return -ENOMEM; + + dev_info(card->dev, "trying to load fallback topology %s\n", mach->tplg_filename); + ret = avs_load_topology(component, filename); + kfree(filename); + } if (ret < 0) return ret; diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index f8a3e8a91761aaf6903c4f1f50fcc1b1c00a463d..9904a9e33cccbec5f257e0d4678b8e85ba668b28 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -808,6 +808,16 @@ static const struct platform_device_id board_ids[] = { SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | SOF_ES8336_JD_INVERTED), }, + { + .name = "mtl_es83x6_c1_h02", + .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) | + SOF_NO_OF_HDMI_CAPTURE_SSP(2) | + SOF_HDMI_CAPTURE_1_SSP(0) | + SOF_HDMI_CAPTURE_2_SSP(2) | + SOF_SSP_HDMI_CAPTURE_PRESENT | + SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK | + SOF_ES8336_JD_INVERTED), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 5a1c750e6ae6aeb368b53c7d6f801119960502b0..842649501e303afd0b078c1dd721065bc7244747 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -376,6 +376,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { /* No Jack */ .driver_data = (void *)SOF_SDW_TGL_HDMI, }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B14"), + }, + /* No Jack */ + .driver_data = (void *)SOF_SDW_TGL_HDMI, + }, + { .callback = sof_sdw_quirk_cb, .matches = { diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c index 8e995edf4c10da3e5328009eb1c027fb29e03f77..5103e75ac8309c898eb3449f414f262b400ff065 100644 --- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c @@ -655,18 +655,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_sdw_machines[] = { .drv_name = "sof_sdw", .sof_tplg_filename = "sof-adl-rt1316-l2-mono-rt714-l3.tplg", }, - { - .link_mask = 0x3, /* rt1316 on link1 & rt714 on link0 */ - .links = adl_sdw_rt1316_link1_rt714_link0, - .drv_name = "sof_sdw", - .sof_tplg_filename = "sof-adl-rt1316-l1-mono-rt714-l0.tplg", - }, { .link_mask = 0x7, /* rt714 on link0 & two rt1316s on link1 and link2 */ .links = adl_sdw_rt1316_link12_rt714_link0, .drv_name = "sof_sdw", .sof_tplg_filename = "sof-adl-rt1316-l12-rt714-l0.tplg", }, + { + .link_mask = 0x3, /* rt1316 on link1 & rt714 on link0 */ + .links = adl_sdw_rt1316_link1_rt714_link0, + .drv_name = "sof_sdw", + .sof_tplg_filename = "sof-adl-rt1316-l1-mono-rt714-l0.tplg", + }, { .link_mask = 0x5, /* 2 active links required */ .links = adl_sdw_rt1316_link2_rt714_link0, diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c index 0304246d292238323d917902e241013662a4c44a..92498d1d6c8d761fdca339f65d7af2f0ad441609 100644 --- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c @@ -30,6 +30,16 @@ static const struct snd_soc_acpi_codecs mtl_rt5682_rt5682s_hp = { .codecs = {"10EC5682", "RTL5682"}, }; +static const struct snd_soc_acpi_codecs mtl_essx_83x6 = { + .num_codecs = 3, + .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, +}; + +static const struct snd_soc_acpi_codecs mtl_lt6911_hdmi = { + .num_codecs = 1, + .codecs = {"INTC10B0"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { { .comp_ids = &mtl_rt5682_rt5682s_hp, @@ -52,6 +62,21 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_machines[] = { .quirk_data = &mtl_rt1019p_amp, .sof_tplg_filename = "sof-mtl-rt1019-rt5682.tplg", }, + { + .comp_ids = &mtl_essx_83x6, + .drv_name = "mtl_es83x6_c1_h02", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &mtl_lt6911_hdmi, + .sof_tplg_filename = "sof-mtl-es83x6-ssp1-hdmi-ssp02.tplg", + }, + { + .comp_ids = &mtl_essx_83x6, + .drv_name = "sof-essx8336", + .sof_tplg_filename = "sof-mtl-es8336", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | + SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_machines); diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index d8688016907525eb80e6dd014e12d2916237b118..bc2f2849ecfbce1f5e12d8cdc7d56858b6c56798 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -112,34 +112,6 @@ static int axg_spdifin_prepare(struct snd_pcm_substream *substream, return 0; } -static int axg_spdifin_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); - int ret; - - ret = clk_prepare_enable(priv->refclk); - if (ret) { - dev_err(dai->dev, - "failed to enable spdifin reference clock\n"); - return ret; - } - - regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, - SPDIFIN_CTRL0_EN); - - return 0; -} - -static void axg_spdifin_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); - - regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, 0); - clk_disable_unprepare(priv->refclk); -} - static void axg_spdifin_write_mode_param(struct regmap *map, int mode, unsigned int val, unsigned int num_per_reg, @@ -251,17 +223,32 @@ static int axg_spdifin_dai_probe(struct snd_soc_dai *dai) ret = axg_spdifin_sample_mode_config(dai, priv); if (ret) { dev_err(dai->dev, "mode configuration failed\n"); - clk_disable_unprepare(priv->pclk); - return ret; + goto pclk_err; } + ret = clk_prepare_enable(priv->refclk); + if (ret) { + dev_err(dai->dev, + "failed to enable spdifin reference clock\n"); + goto pclk_err; + } + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, + SPDIFIN_CTRL0_EN); + return 0; + +pclk_err: + clk_disable_unprepare(priv->pclk); + return ret; } static int axg_spdifin_dai_remove(struct snd_soc_dai *dai) { struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, 0); + clk_disable_unprepare(priv->refclk); clk_disable_unprepare(priv->pclk); return 0; } @@ -270,8 +257,6 @@ static const struct snd_soc_dai_ops axg_spdifin_ops = { .probe = axg_spdifin_dai_probe, .remove = axg_spdifin_dai_remove, .prepare = axg_spdifin_prepare, - .startup = axg_spdifin_startup, - .shutdown = axg_spdifin_shutdown, }; static int axg_spdifin_iec958_info(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index e29c2fee9521943bd961b5725b30a9024a00e72b..1bd7114c472a8bb8c9032a599b0da385710ee820 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1303,6 +1303,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph) if (i >= RSND_MAX_COMPONENT) { dev_info(dev, "reach to max component\n"); of_node_put(node); + of_node_put(ports); break; } } diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index f18406dfa1e432b81a1f64932e7b41d222ca4df4..ba7c0ae82e0079ba5531307bfed98cab66d9e357 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -1054,7 +1054,7 @@ int snd_soc_pcm_component_sync_stop(struct snd_pcm_substream *substream) int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream, int channel, unsigned long pos, - struct iov_iter *buf, unsigned long bytes) + struct iov_iter *iter, unsigned long bytes) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; @@ -1065,7 +1065,7 @@ int snd_soc_pcm_component_copy(struct snd_pcm_substream *substream, if (component->driver->copy) return soc_component_ret(component, component->driver->copy(component, substream, - channel, pos, buf, bytes)); + channel, pos, iter, bytes)); return -EINVAL; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index cc442c52cdea959f9ddf9cddc0e84330effc61d6..9de98c01d815175f772f0e150fc710bad68edddb 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1347,7 +1347,7 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, snd_soc_runtime_get_dai_fmt(rtd); ret = snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); if (ret) - return ret; + goto err; /* add DPCM sysfs entries */ soc_dpcm_debugfs_add(rtd); @@ -1372,17 +1372,26 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, /* create compress_device if possible */ ret = snd_soc_dai_compress_new(cpu_dai, rtd, num); if (ret != -ENOTSUPP) - return ret; + goto err; /* create the pcm */ ret = soc_new_pcm(rtd, num); if (ret < 0) { dev_err(card->dev, "ASoC: can't create pcm %s :%d\n", dai_link->stream_name, ret); - return ret; + goto err; } - return snd_soc_pcm_dai_new(rtd); + ret = snd_soc_pcm_dai_new(rtd); + if (ret < 0) + goto err; + + rtd->initialized = true; + + return 0; +err: + snd_soc_link_exit(rtd); + return ret; } static void soc_set_name_prefix(struct snd_soc_card *card, @@ -1445,8 +1454,8 @@ static int soc_probe_component(struct snd_soc_card *card, if (component->card) { if (component->card != card) { dev_err(component->dev, - "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n", - card->name, component->card->name); + "Trying to bind component \"%s\" to card \"%s\" but is already bound to card \"%s\"\n", + component->name, card->name, component->card->name); return -ENODEV; } return 0; @@ -1980,7 +1989,8 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card) /* release machine specific resources */ for_each_card_rtds(card, rtd) - snd_soc_link_exit(rtd); + if (rtd->initialized) + snd_soc_link_exit(rtd); /* remove and free each DAI */ soc_remove_link_dais(card); soc_remove_link_components(card); diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index ff2166525dbce83c64117722b7482e89b9444e35..cad222eb9a293a6dfb9fad3d1aeb5e68ddb7fd2f 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -44,8 +44,8 @@ static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm, * platforms which make use of the snd_dmaengine_dai_dma_data struct for their * DAI DMA data. Internally the function will first call * snd_hwparams_to_dma_slave_config to fill in the slave config based on the - * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the - * remaining fields based on the DAI DMA data. + * hw_params, followed by snd_dmaengine_pcm_set_config_from_dai_data to fill in + * the remaining fields based on the DAI DMA data. */ int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config) @@ -290,29 +290,29 @@ static snd_pcm_uframes_t dmaengine_pcm_pointer( static int dmaengine_copy(struct snd_soc_component *component, struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - struct iov_iter *buf, unsigned long bytes) + struct iov_iter *iter, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; struct dmaengine_pcm *pcm = soc_component_to_pcm(component); int (*process)(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - struct iov_iter *buf, unsigned long bytes) = pcm->config->process; + unsigned long bytes) = pcm->config->process; bool is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; void *dma_ptr = runtime->dma_area + hwoff + channel * (runtime->dma_bytes / runtime->channels); if (is_playback) - if (copy_from_iter(dma_ptr, bytes, buf) != bytes) + if (copy_from_iter(dma_ptr, bytes, iter) != bytes) return -EFAULT; if (process) { - int ret = process(substream, channel, hwoff, buf, bytes); + int ret = process(substream, channel, hwoff, bytes); if (ret < 0) return ret; } if (!is_playback) - if (copy_to_iter(dma_ptr, bytes, buf) != bytes) + if (copy_to_iter(dma_ptr, bytes, iter) != bytes) return -EFAULT; return 0; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index eb07238768511a7c7478a6a2556e5a72f0b52ec0..54704250c0a2c43e42ad09ce631953233c444b1e 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -985,6 +985,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, { struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + struct snd_pcm_hw_params tmp_params; int i, ret = 0; snd_soc_dpcm_mutex_assert_held(rtd); @@ -998,7 +999,6 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, goto out; for_each_rtd_codec_dais(rtd, i, codec_dai) { - struct snd_pcm_hw_params codec_params; unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); /* @@ -1019,23 +1019,22 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, continue; /* copy params for each codec */ - codec_params = *params; + tmp_params = *params; /* fixup params based on TDM slot masks */ if (tdm_mask) - soc_pcm_codec_params_fixup(&codec_params, tdm_mask); + soc_pcm_codec_params_fixup(&tmp_params, tdm_mask); ret = snd_soc_dai_hw_params(codec_dai, substream, - &codec_params); + &tmp_params); if(ret < 0) goto out; - soc_pcm_set_dai_params(codec_dai, &codec_params); - snd_soc_dapm_update_dai(substream, &codec_params, codec_dai); + soc_pcm_set_dai_params(codec_dai, &tmp_params); + snd_soc_dapm_update_dai(substream, &tmp_params, codec_dai); } for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - struct snd_pcm_hw_params cpu_params; unsigned int ch_mask = 0; int j; @@ -1047,7 +1046,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, continue; /* copy params for each cpu */ - cpu_params = *params; + tmp_params = *params; if (!rtd->dai_link->codec_ch_maps) goto hw_params; @@ -1062,16 +1061,16 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, /* fixup cpu channel number */ if (ch_mask) - soc_pcm_codec_params_fixup(&cpu_params, ch_mask); + soc_pcm_codec_params_fixup(&tmp_params, ch_mask); hw_params: - ret = snd_soc_dai_hw_params(cpu_dai, substream, &cpu_params); + ret = snd_soc_dai_hw_params(cpu_dai, substream, &tmp_params); if (ret < 0) goto out; /* store the parameters for each DAI */ - soc_pcm_set_dai_params(cpu_dai, &cpu_params); - snd_soc_dapm_update_dai(substream, &cpu_params, cpu_dai); + soc_pcm_set_dai_params(cpu_dai, &tmp_params); + snd_soc_dapm_update_dai(substream, &tmp_params, cpu_dai); } ret = snd_soc_pcm_component_hw_params(substream, params); diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index 11607c5f5d5a82f4c8c97abb7e6582cfca36677c..9c746e4edef71c2e9cfa94b8ed4fc61b888c8c54 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -217,6 +217,7 @@ int snd_soc_dai_is_dummy(struct snd_soc_dai *dai) return 1; return 0; } +EXPORT_SYMBOL_GPL(snd_soc_dai_is_dummy); int snd_soc_component_is_dummy(struct snd_soc_component *component) { diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c index 9935e457b467b02b1609e31c40ab3a0e480719af..a7ae76efc2dddb480c0415bf02b75dab20878c24 100644 --- a/sound/soc/sof/amd/pci-rmb.c +++ b/sound/soc/sof/amd/pci-rmb.c @@ -35,7 +35,6 @@ static const struct sof_amd_acp_desc rembrandt_chip_info = { .dsp_intr_base = ACP6X_DSP_SW_INTR_BASE, .sram_pte_offset = ACP6X_SRAM_PTE_OFFSET, .hw_semaphore_offset = ACP6X_AXI2DAGB_SEM_0, - .acp_clkmux_sel = ACP6X_CLKMUX_SEL, .fusion_dsp_offset = ACP6X_DSP_FUSION_RUNSTALL, .probe_reg_offset = ACP6X_FUTURE_REG_ACLK_0, }; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 30db685cc5f4b99ae47fe95e9367abc330c2b349..2d1616b81485c53b0f436ef57bcbabaebb03c184 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -486,10 +486,9 @@ int snd_sof_device_remove(struct device *dev) snd_sof_ipc_free(sdev); snd_sof_free_debug(sdev); snd_sof_remove(sdev); + sof_ops_free(sdev); } - sof_ops_free(sdev); - /* release firmware */ snd_sof_fw_unload(sdev); diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index b84ca58da9d5da72d4158dabd51564f046630fe7..f9412517eaf29be8345c00ad574ed643dd2066d5 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -460,7 +460,7 @@ int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) /* step 3: wait for IPC DONE bit from ROM */ ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, chip->ipc_ack, status, ((status & chip->ipc_ack_mask) == chip->ipc_ack_mask), - HDA_DSP_REG_POLL_INTERVAL_US, MTL_DSP_PURGE_TIMEOUT_US); + HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_INIT_TIMEOUT_US); if (ret < 0) { if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "timeout waiting for purge IPC done\n"); diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 02181490f12a6f1b550e0463dcae039161a0993b..95696b3d7c4cfbadb5a3307954a0c9ace19e07cd 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -62,7 +62,6 @@ #define MTL_DSP_IRQSTS_IPC BIT(0) #define MTL_DSP_IRQSTS_SDW BIT(6) -#define MTL_DSP_PURGE_TIMEOUT_US 20000000 /* 20s */ #define MTL_DSP_REG_POLL_INTERVAL_US 10 /* 10 us */ /* Memory windows */ diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index f2a30cd3137875b69a3f6248d50267dcff24d193..7cb63e6b24dc96f59eea968ae1f718990dedbe18 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -231,7 +231,7 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, ret = sof_update_ipc_object(scomp, available_fmt, SOF_AUDIO_FMT_NUM_TOKENS, swidget->tuples, - swidget->num_tuples, sizeof(available_fmt), 1); + swidget->num_tuples, sizeof(*available_fmt), 1); if (ret) { dev_err(scomp->dev, "Failed to parse audio format token count\n"); return ret; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index e7ef77012c3586649f364d538c42ab4c89496c48..e5405f854a910c279596b6803c73d9d21b9170c1 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -212,7 +212,8 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, sof_widget_free_unlocked(sdev, swidget); use_count_decremented = true; core_put: - snd_sof_dsp_core_put(sdev, swidget->core); + if (!use_count_decremented) + snd_sof_dsp_core_put(sdev, swidget->core); pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index f9b5d59691550f036ca6467c90035f0551a9647c..0acc848c1f004d7218ba0d42496003591074245b 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -1246,7 +1246,7 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = { static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, int channel, unsigned long hwoff, - struct iov_iter *buf, unsigned long bytes) + unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c index 1f2c5018bf5aceded1267d64d5974dc8e3495ec5..4737e776d3837d60135523f64e1dc89f298e23e0 100644 --- a/sound/soc/tegra/tegra_audio_graph_card.c +++ b/sound/soc/tegra/tegra_audio_graph_card.c @@ -10,6 +10,7 @@ #include #include #include +#include #define MAX_PLLA_OUT0_DIV 128 @@ -44,6 +45,21 @@ struct tegra_audio_cdata { unsigned int plla_out0_rates[NUM_RATE_TYPE]; }; +static bool need_clk_update(struct snd_soc_dai *dai) +{ + if (snd_soc_dai_is_dummy(dai) || + !dai->driver->ops || + !dai->driver->name) + return false; + + if (strstr(dai->driver->name, "I2S") || + strstr(dai->driver->name, "DMIC") || + strstr(dai->driver->name, "DSPK")) + return true; + + return false; +} + /* Setup PLL clock as per the given sample rate */ static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -140,19 +156,7 @@ static int tegra_audio_graph_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); int err; - /* - * This gets called for each DAI link (FE or BE) when DPCM is used. - * We may not want to update PLLA rate for each call. So PLLA update - * must be restricted to external I/O links (I2S, DMIC or DSPK) since - * they actually depend on it. I/O modules update their clocks in - * hw_param() of their respective component driver and PLLA rate - * update here helps them to derive appropriate rates. - * - * TODO: When more HW accelerators get added (like sample rate - * converter, volume gain controller etc., which don't really - * depend on PLLA) we need a better way to filter here. - */ - if (cpu_dai->driver->ops && rtd->dai_link->no_pcm) { + if (need_clk_update(cpu_dai)) { err = tegra_audio_graph_update_pll(substream, params); if (err) return err; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 49f63f878e6fe8b7acbaa363dfebf3b2114dd753..b5cbf1f195c48cbe16c0da96488880fcad450b10 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -485,7 +485,7 @@ static int init_card(struct snd_usb_caiaqdev *cdev) } usb_make_path(usb_dev, usbpath, sizeof(usbpath)); - snprintf(card->longname, sizeof(card->longname), "%s %s (%s)", + scnprintf(card->longname, sizeof(card->longname), "%s %s (%s)", cdev->vendor_name, cdev->product_name, usbpath); setup_card(cdev); diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index a27e244650c8ad7889c80f5e4325693dfa9ee73e..1ec177fe284eddd7eb431d56083e82886c858550 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -265,7 +265,7 @@ static void free_midi_urbs(struct snd_usb_midi2_endpoint *ep) if (!ep) return; - for (i = 0; i < ep->num_urbs; ++i) { + for (i = 0; i < NUM_URBS; ++i) { ctx = &ep->urbs[i]; if (!ctx->urb) break; @@ -279,6 +279,7 @@ static void free_midi_urbs(struct snd_usb_midi2_endpoint *ep) } /* allocate URBs for an EP */ +/* the callers should handle allocation errors via free_midi_urbs() */ static int alloc_midi_urbs(struct snd_usb_midi2_endpoint *ep) { struct snd_usb_midi2_urb *ctx; @@ -351,8 +352,10 @@ static int snd_usb_midi_v2_open(struct snd_ump_endpoint *ump, int dir) return -EIO; if (ep->direction == STR_OUT) { err = alloc_midi_urbs(ep); - if (err) + if (err) { + free_midi_urbs(ep); return err; + } } return 0; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 9105ec623120a964802af82502b247bd9df47051..409fc11646948e40ec6b03be6840c150c04db8b7 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -1204,6 +1204,13 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, cval->res = 16; } break; + case USB_ID(0x1bcf, 0x2283): /* NexiGo N930AF FHD Webcam */ + if (!strcmp(kctl->id.name, "Mic Capture Volume")) { + usb_audio_info(chip, + "set resolution quirk: cval->res = 16\n"); + cval->res = 16; + } + break; } } @@ -1929,7 +1936,6 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, struct uac_clock_source_descriptor *hdr = _ftr; struct usb_mixer_elem_info *cval; struct snd_kcontrol *kctl; - char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ret; if (state->mixer->protocol != UAC_VERSION_2) @@ -1966,10 +1972,9 @@ static int parse_clock_source_unit(struct mixer_build *state, int unitid, kctl->private_free = snd_usb_mixer_elem_free; ret = snd_usb_copy_string_desc(state->chip, hdr->iClockSource, - name, sizeof(name)); + kctl->id.name, sizeof(kctl->id.name)); if (ret > 0) - snprintf(kctl->id.name, sizeof(kctl->id.name), - "%s Validity", name); + append_ctl_name(kctl, " Validity"); else snprintf(kctl->id.name, sizeof(kctl->id.name), "Clock Source %d Validity", hdr->bClockID); diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 9d11bb08667e7a664017a5ec8530e5ff4ade91f9..d260be8cb6bc03e8c5c2b2a3dc5ff5759ff2588d 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -3205,8 +3205,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) /* Add input phantom controls */ if (info->inputs_per_phantom == 1) { for (i = 0; i < info->phantom_count; i++) { - snprintf(s, sizeof(s), fmt, i + 1, - "Phantom Power", "Switch"); + scnprintf(s, sizeof(s), fmt, i + 1, + "Phantom Power", "Switch"); err = scarlett2_add_new_ctl( mixer, &scarlett2_phantom_ctl, i, 1, s, &private->phantom_ctls[i]); @@ -3218,8 +3218,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) int from = i * info->inputs_per_phantom + 1; int to = (i + 1) * info->inputs_per_phantom; - snprintf(s, sizeof(s), fmt2, from, to, - "Phantom Power", "Switch"); + scnprintf(s, sizeof(s), fmt2, from, to, + "Phantom Power", "Switch"); err = scarlett2_add_new_ctl( mixer, &scarlett2_phantom_ctl, i, 1, s, &private->phantom_ctls[i]); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 598659d761cc94a419b7df7960dcd502bf4188e4..4e64842245e1900f160ef0687ed1e8d7da3dcaac 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1994,7 +1994,11 @@ void snd_usb_audioformat_attributes_quirk(struct snd_usb_audio *chip, /* mic works only when ep packet size is set to wMaxPacketSize */ fp->attributes |= UAC_EP_CS_ATTR_FILL_MAX; break; - + case USB_ID(0x3511, 0x2b1e): /* Opencomm2 UC USB Bluetooth dongle */ + /* mic works only when ep pitch control is not set */ + if (stream == SNDRV_PCM_STREAM_CAPTURE) + fp->attributes &= ~UAC_EP_CS_ATTR_PITCH_CONTROL; + break; } } @@ -2173,6 +2177,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FIXED_RATE), DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ QUIRK_FLAG_FIXED_RATE), + DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */ + QUIRK_FLAG_GET_SAMPLE_RATE), /* Vendor matches */ VENDOR_FLG(0x045e, /* MS Lifecam */ diff --git a/sound/xen/xen_snd_front_cfg.c b/sound/xen/xen_snd_front_cfg.c index 63b0398c327656b543cca5962bb8991ee8b287cd..55ecf766ca6780447d1ed9103328366bfa7f57b2 100644 --- a/sound/xen/xen_snd_front_cfg.c +++ b/sound/xen/xen_snd_front_cfg.c @@ -483,7 +483,7 @@ int xen_snd_front_cfg_card(struct xen_snd_front_info *front_info, *stream_cnt = 0; num_devices = 0; do { - snprintf(node, sizeof(node), "%d", num_devices); + scnprintf(node, sizeof(node), "%d", num_devices); if (!xenbus_exists(XBT_NIL, xb_dev->nodename, node)) break; diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 1f6d904c6481dd4414d67be8e0acec46ed337c57..798e60b5454b7e108a07550cb354a8ee27c0de8f 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h @@ -434,6 +434,7 @@ #define X86_FEATURE_SEV_ES (19*32+ 3) /* AMD Secure Encrypted Virtualization - Encrypted State */ #define X86_FEATURE_V_TSC_AUX (19*32+ 9) /* "" Virtual TSC_AUX */ #define X86_FEATURE_SME_COHERENT (19*32+10) /* "" AMD hardware-enforced cache coherency */ +#define X86_FEATURE_DEBUG_SWAP (19*32+14) /* AMD SEV-ES full debug state swap support */ /* AMD-defined Extended Feature 2 EAX, CPUID level 0x80000021 (EAX), word 20 */ #define X86_FEATURE_NO_NESTED_DATA_BP (20*32+ 0) /* "" No Nested Data Breakpoints */ diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h index a00a53e15ab73883e2d289aabe06db84b3208e8a..1d111350197f3169a8eec402d77980dd617c6b95 100644 --- a/tools/arch/x86/include/asm/msr-index.h +++ b/tools/arch/x86/include/asm/msr-index.h @@ -57,6 +57,7 @@ #define MSR_IA32_PRED_CMD 0x00000049 /* Prediction Command */ #define PRED_CMD_IBPB BIT(0) /* Indirect Branch Prediction Barrier */ +#define PRED_CMD_SBPB BIT(7) /* Selective Branch Prediction Barrier */ #define MSR_PPIN_CTL 0x0000004e #define MSR_PPIN 0x0000004f @@ -155,6 +156,15 @@ * Not susceptible to Post-Barrier * Return Stack Buffer Predictions. */ +#define ARCH_CAP_GDS_CTRL BIT(25) /* + * CPU is vulnerable to Gather + * Data Sampling (GDS) and + * has controls for mitigation. + */ +#define ARCH_CAP_GDS_NO BIT(26) /* + * CPU is not vulnerable to Gather + * Data Sampling (GDS). + */ #define ARCH_CAP_XAPIC_DISABLE BIT(21) /* * IA32_XAPIC_DISABLE_STATUS MSR @@ -178,6 +188,8 @@ #define RNGDS_MITG_DIS BIT(0) /* SRBDS support */ #define RTM_ALLOW BIT(1) /* TSX development mode */ #define FB_CLEAR_DIS BIT(3) /* CPU Fill buffer clear disable */ +#define GDS_MITG_DIS BIT(4) /* Disable GDS mitigation */ +#define GDS_MITG_LOCKED BIT(5) /* GDS mitigation locked */ #define MSR_IA32_SYSENTER_CS 0x00000174 #define MSR_IA32_SYSENTER_ESP 0x00000175 diff --git a/tools/bpf/bpftool/link.c b/tools/bpf/bpftool/link.c index 0b214f6ab5c83447dfc1714f5094c6c6d749129d..2e5c231e08ac795d73dad72eb968f1b4ece1611d 100644 --- a/tools/bpf/bpftool/link.c +++ b/tools/bpf/bpftool/link.c @@ -83,7 +83,7 @@ const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] = { #define perf_event_name(array, id) ({ \ const char *event_str = NULL; \ \ - if ((id) >= 0 && (id) < ARRAY_SIZE(array)) \ + if ((id) < ARRAY_SIZE(array)) \ event_str = array[id]; \ event_str; \ }) diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build index 89430338a3d92f8eb8311056e6f889b30a723c58..fac42486a8cf0bbfa60b97cc92db7ee4852cb737 100644 --- a/tools/build/Makefile.build +++ b/tools/build/Makefile.build @@ -117,6 +117,16 @@ $(OUTPUT)%.s: %.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_s_c) +# bison and flex files are generated in the OUTPUT directory +# so it needs a separate rule to depend on them properly +$(OUTPUT)%-bison.o: $(OUTPUT)%-bison.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,$(host)cc_o_c) + +$(OUTPUT)%-flex.o: $(OUTPUT)%-flex.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,$(host)cc_o_c) + # Gather build data: # obj-y - list of build objects # subdir-y - list of directories to nest diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index f0c5de018a956a3bcf77032431021fa355aab1e9..dad79ede4e0ae0030ee401a1daf5d59ff871dcb6 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -340,7 +340,7 @@ $(OUTPUT)test-jvmti-cmlr.bin: $(BUILD) $(OUTPUT)test-llvm.bin: - $(BUILDXX) -std=gnu++14 \ + $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ -L$(shell $(LLVM_CONFIG) --libdir) \ $(shell $(LLVM_CONFIG) --libs Core BPF) \ @@ -348,17 +348,15 @@ $(OUTPUT)test-llvm.bin: > $(@:.bin=.make.output) 2>&1 $(OUTPUT)test-llvm-version.bin: - $(BUILDXX) -std=gnu++14 \ + $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ > $(@:.bin=.make.output) 2>&1 $(OUTPUT)test-clang.bin: - $(BUILDXX) -std=gnu++14 \ + $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ -L$(shell $(LLVM_CONFIG) --libdir) \ - -Wl,--start-group -lclangBasic -lclangDriver \ - -lclangFrontend -lclangEdit -lclangLex \ - -lclangAST -Wl,--end-group \ + -Wl,--start-group -lclang-cpp -Wl,--end-group \ $(shell $(LLVM_CONFIG) --libs Core option) \ $(shell $(LLVM_CONFIG) --system-libs) \ > $(@:.bin=.make.output) 2>&1 diff --git a/tools/build/feature/test-clang.cpp b/tools/build/feature/test-clang.cpp deleted file mode 100644 index 7d87075cd1c5dd6e3e0964a9c428aa6af53ce6ed..0000000000000000000000000000000000000000 --- a/tools/build/feature/test-clang.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "clang/Basic/Version.h" -#if CLANG_VERSION_MAJOR < 8 -#include "clang/Basic/VirtualFileSystem.h" -#endif -#include "clang/Driver/Driver.h" -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "llvm/ADT/IntrusiveRefCntPtr.h" -#include "llvm/Support/ManagedStatic.h" -#if CLANG_VERSION_MAJOR >= 8 -#include "llvm/Support/VirtualFileSystem.h" -#endif -#include "llvm/Support/raw_ostream.h" - -using namespace clang; -using namespace clang::driver; - -int main() -{ - IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); - IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); - - DiagnosticsEngine Diags(DiagID, &*DiagOpts); - Driver TheDriver("test", "bpf-pc-linux", Diags); - - llvm::llvm_shutdown(); - return 0; -} diff --git a/tools/build/feature/test-cxx.cpp b/tools/build/feature/test-cxx.cpp deleted file mode 100644 index 396aaedd2418dd650ab8ad33c845855db9806694..0000000000000000000000000000000000000000 --- a/tools/build/feature/test-cxx.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -static void print_str(std::string s) -{ - std::cout << s << std::endl; -} - -int main() -{ - std::string s("Hello World!"); - print_str(std::move(s)); - std::cout << "|" << s << "|" << std::endl; - return 0; -} diff --git a/tools/build/feature/test-llvm-version.cpp b/tools/build/feature/test-llvm-version.cpp deleted file mode 100644 index 8a091625446af985a7a7f7ec4b764d348c264f01..0000000000000000000000000000000000000000 --- a/tools/build/feature/test-llvm-version.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include "llvm/Config/llvm-config.h" - -#define NUM_VERSION (((LLVM_VERSION_MAJOR) << 16) + (LLVM_VERSION_MINOR << 8) + LLVM_VERSION_PATCH) -#define pass int main() {printf("%x\n", NUM_VERSION); return 0;} - -#if NUM_VERSION >= 0x030900 -pass -#else -# error This LLVM is not tested yet. -#endif diff --git a/tools/build/feature/test-llvm.cpp b/tools/build/feature/test-llvm.cpp deleted file mode 100644 index 88a3d1bdd9f6978e998eb7e480b78aced875f841..0000000000000000000000000000000000000000 --- a/tools/build/feature/test-llvm.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/raw_ostream.h" -#define NUM_VERSION (((LLVM_VERSION_MAJOR) << 16) + (LLVM_VERSION_MINOR << 8) + LLVM_VERSION_PATCH) - -#if NUM_VERSION < 0x030900 -# error "LLVM version too low" -#endif -int main() -{ - llvm::errs() << "Hello World!\n"; - llvm::llvm_shutdown(); - return 0; -} diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 27f5e7dfc2f761ad2d2ec9ff8fba996ef046d319..264eeb9c46a9f5ea9264829b41eda164e72d0d2e 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c @@ -1171,12 +1171,79 @@ static int process_ip_string(FILE *f, char *ip_string, int type) return 0; } +/* + * Only IPv4 subnet strings needs to be converted to plen + * For IPv6 the subnet is already privided in plen format + */ +static int kvp_subnet_to_plen(char *subnet_addr_str) +{ + int plen = 0; + struct in_addr subnet_addr4; + + /* + * Convert subnet address to binary representation + */ + if (inet_pton(AF_INET, subnet_addr_str, &subnet_addr4) == 1) { + uint32_t subnet_mask = ntohl(subnet_addr4.s_addr); + + while (subnet_mask & 0x80000000) { + plen++; + subnet_mask <<= 1; + } + } else { + return -1; + } + + return plen; +} + +static int process_ip_string_nm(FILE *f, char *ip_string, char *subnet, + int is_ipv6) +{ + char addr[INET6_ADDRSTRLEN]; + char subnet_addr[INET6_ADDRSTRLEN]; + int error, i = 0; + int ip_offset = 0, subnet_offset = 0; + int plen; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); + + while (parse_ip_val_buffer(ip_string, &ip_offset, addr, + (MAX_IP_ADDR_SIZE * 2)) && + parse_ip_val_buffer(subnet, + &subnet_offset, + subnet_addr, + (MAX_IP_ADDR_SIZE * + 2))) { + if (!is_ipv6) + plen = kvp_subnet_to_plen((char *)subnet_addr); + else + plen = atoi(subnet_addr); + + if (plen < 0) + return plen; + + error = fprintf(f, "address%d=%s/%d\n", ++i, (char *)addr, + plen); + if (error < 0) + return error; + + memset(addr, 0, sizeof(addr)); + memset(subnet_addr, 0, sizeof(subnet_addr)); + } + + return 0; +} + static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) { int error = 0; - char if_file[PATH_MAX]; - FILE *file; + char if_filename[PATH_MAX]; + char nm_filename[PATH_MAX]; + FILE *ifcfg_file, *nmfile; char cmd[PATH_MAX]; + int is_ipv6 = 0; char *mac_addr; int str_len; @@ -1197,7 +1264,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * in a given distro to configure the interface and so are free * ignore information that may not be relevant. * - * Here is the format of the ip configuration file: + * Here is the ifcfg format of the ip configuration file: * * HWADDR=macaddr * DEVICE=interface name @@ -1220,6 +1287,32 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as * IPV6NETMASK. * + * Here is the keyfile format of the ip configuration file: + * + * [ethernet] + * mac-address=macaddr + * [connection] + * interface-name=interface name + * + * [ipv4] + * method= (where is "auto" if DHCP is configured + * or "manual" if no boot-time protocol should be used) + * + * address1=ipaddr1/plen + * address2=ipaddr2/plen + * + * gateway=gateway1;gateway2 + * + * dns=dns1;dns2 + * + * [ipv6] + * address1=ipaddr1/plen + * address2=ipaddr2/plen + * + * gateway=gateway1;gateway2 + * + * dns=dns1;dns2 + * * The host can specify multiple ipv4 and ipv6 addresses to be * configured for the interface. Furthermore, the configuration * needs to be persistent. A subsequent GET call on the interface @@ -1227,14 +1320,29 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * call. */ - snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, - "/ifcfg-", if_name); + /* + * We are populating both ifcfg and nmconnection files + */ + snprintf(if_filename, sizeof(if_filename), "%s%s%s", KVP_CONFIG_LOC, + "/ifcfg-", if_name); - file = fopen(if_file, "w"); + ifcfg_file = fopen(if_filename, "w"); - if (file == NULL) { + if (!ifcfg_file) { syslog(LOG_ERR, "Failed to open config file; error: %d %s", - errno, strerror(errno)); + errno, strerror(errno)); + return HV_E_FAIL; + } + + snprintf(nm_filename, sizeof(nm_filename), "%s%s%s%s", KVP_CONFIG_LOC, + "/", if_name, ".nmconnection"); + + nmfile = fopen(nm_filename, "w"); + + if (!nmfile) { + syslog(LOG_ERR, "Failed to open config file; error: %d %s", + errno, strerror(errno)); + fclose(ifcfg_file); return HV_E_FAIL; } @@ -1248,14 +1356,31 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) goto setval_error; } - error = kvp_write_file(file, "HWADDR", "", mac_addr); - free(mac_addr); + error = kvp_write_file(ifcfg_file, "HWADDR", "", mac_addr); + if (error < 0) + goto setmac_error; + + error = kvp_write_file(ifcfg_file, "DEVICE", "", if_name); + if (error < 0) + goto setmac_error; + + error = fprintf(nmfile, "\n[connection]\n"); + if (error < 0) + goto setmac_error; + + error = kvp_write_file(nmfile, "interface-name", "", if_name); if (error) - goto setval_error; + goto setmac_error; - error = kvp_write_file(file, "DEVICE", "", if_name); + error = fprintf(nmfile, "\n[ethernet]\n"); + if (error < 0) + goto setmac_error; + + error = kvp_write_file(nmfile, "mac-address", "", mac_addr); if (error) - goto setval_error; + goto setmac_error; + + free(mac_addr); /* * The dhcp_enabled flag is only for IPv4. In the case the host only @@ -1263,47 +1388,91 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * proceed to parse and pass the IPv6 information to the * disto-specific script hv_set_ifconfig. */ + + /* + * First populate the ifcfg file format + */ if (new_val->dhcp_enabled) { - error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); + error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "dhcp"); if (error) goto setval_error; - } else { - error = kvp_write_file(file, "BOOTPROTO", "", "none"); + error = kvp_write_file(ifcfg_file, "BOOTPROTO", "", "none"); if (error) goto setval_error; } - /* - * Write the configuration for ipaddress, netmask, gateway and - * name servers. - */ - - error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR); + error = process_ip_string(ifcfg_file, (char *)new_val->ip_addr, + IPADDR); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->sub_net, NETMASK); + error = process_ip_string(ifcfg_file, (char *)new_val->sub_net, + NETMASK); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY); + error = process_ip_string(ifcfg_file, (char *)new_val->gate_way, + GATEWAY); if (error) goto setval_error; - error = process_ip_string(file, (char *)new_val->dns_addr, DNS); + error = process_ip_string(ifcfg_file, (char *)new_val->dns_addr, DNS); if (error) goto setval_error; - fclose(file); + if (new_val->addr_family == ADDR_FAMILY_IPV6) { + error = fprintf(nmfile, "\n[ipv6]\n"); + if (error < 0) + goto setval_error; + is_ipv6 = 1; + } else { + error = fprintf(nmfile, "\n[ipv4]\n"); + if (error < 0) + goto setval_error; + } + + /* + * Now we populate the keyfile format + */ + + if (new_val->dhcp_enabled) { + error = kvp_write_file(nmfile, "method", "", "auto"); + if (error < 0) + goto setval_error; + } else { + error = kvp_write_file(nmfile, "method", "", "manual"); + if (error < 0) + goto setval_error; + } + + /* + * Write the configuration for ipaddress, netmask, gateway and + * name services + */ + error = process_ip_string_nm(nmfile, (char *)new_val->ip_addr, + (char *)new_val->sub_net, is_ipv6); + if (error < 0) + goto setval_error; + + error = fprintf(nmfile, "gateway=%s\n", (char *)new_val->gate_way); + if (error < 0) + goto setval_error; + + error = fprintf(nmfile, "dns=%s\n", (char *)new_val->dns_addr); + if (error < 0) + goto setval_error; + + fclose(nmfile); + fclose(ifcfg_file); /* * Now that we have populated the configuration file, * invoke the external script to do its magic. */ - str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s", - "hv_set_ifconfig", if_file); + str_len = snprintf(cmd, sizeof(cmd), KVP_SCRIPTS_PATH "%s %s %s", + "hv_set_ifconfig", if_filename, nm_filename); /* * This is a little overcautious, but it's necessary to suppress some * false warnings from gcc 8.0.1. @@ -1316,14 +1485,16 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) if (system(cmd)) { syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s", - cmd, errno, strerror(errno)); + cmd, errno, strerror(errno)); return HV_E_FAIL; } return 0; - +setmac_error: + free(mac_addr); setval_error: syslog(LOG_ERR, "Failed to write config file"); - fclose(file); + fclose(ifcfg_file); + fclose(nmfile); return error; } diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh index d10fe35b7f259d4928816626d72d7986fde6dcba..ae5a7a8249a208cf5782e8627ba4354959d38e2b 100755 --- a/tools/hv/hv_set_ifconfig.sh +++ b/tools/hv/hv_set_ifconfig.sh @@ -18,12 +18,12 @@ # # This example script is based on a RHEL environment. # -# Here is the format of the ip configuration file: +# Here is the ifcfg format of the ip configuration file: # # HWADDR=macaddr # DEVICE=interface name # BOOTPROTO= (where is "dhcp" if DHCP is configured -# or "none" if no boot-time protocol should be used) +# or "none" if no boot-time protocol should be used) # # IPADDR0=ipaddr1 # IPADDR1=ipaddr2 @@ -41,6 +41,32 @@ # tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as # IPV6NETMASK. # +# Here is the keyfile format of the ip configuration file: +# +# [ethernet] +# mac-address=macaddr +# [connection] +# interface-name=interface name +# +# [ipv4] +# method= (where is "auto" if DHCP is configured +# or "manual" if no boot-time protocol should be used) +# +# address1=ipaddr1/plen +# address=ipaddr2/plen +# +# gateway=gateway1;gateway2 +# +# dns=dns1; +# +# [ipv6] +# address1=ipaddr1/plen +# address2=ipaddr1/plen +# +# gateway=gateway1;gateway2 +# +# dns=dns1;dns2 +# # The host can specify multiple ipv4 and ipv6 addresses to be # configured for the interface. Furthermore, the configuration # needs to be persistent. A subsequent GET call on the interface @@ -48,18 +74,19 @@ # call. # - - echo "IPV6INIT=yes" >> $1 echo "NM_CONTROLLED=no" >> $1 echo "PEERDNS=yes" >> $1 echo "ONBOOT=yes" >> $1 - cp $1 /etc/sysconfig/network-scripts/ +chmod 600 $2 +interface=$(echo $2 | awk -F - '{ print $2 }') +filename="${2##*/}" + +sed '/\[connection\]/a autoconnect=true' $2 > /etc/NetworkManager/system-connections/${filename} -interface=$(echo $1 | awk -F - '{ print $2 }') /sbin/ifdown $interface 2>/dev/null /sbin/ifup $interface 2>/dev/null diff --git a/tools/include/linux/btf_ids.h b/tools/include/linux/btf_ids.h index 71e54b1e379645fd8ce2233801f3e2c619699ee6..2f882d5cb30f5c014af5deec2738f8460dcc23ad 100644 --- a/tools/include/linux/btf_ids.h +++ b/tools/include/linux/btf_ids.h @@ -38,7 +38,7 @@ asm( \ ____BTF_ID(symbol) #define __ID(prefix) \ - __PASTE(prefix, __COUNTER__) + __PASTE(__PASTE(prefix, __COUNTER__), __LINE__) /* * The BTF_ID defines unique symbol for each ID pointing diff --git a/tools/include/linux/mm.h b/tools/include/linux/mm.h index a03d9bba515148a72192cf2b9cddab32d047858d..f3c82ab5b14cd77819030096b81e0b67cba0df1d 100644 --- a/tools/include/linux/mm.h +++ b/tools/include/linux/mm.h @@ -11,8 +11,6 @@ #define PHYS_ADDR_MAX (~(phys_addr_t)0) -#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) -#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a)) @@ -29,7 +27,7 @@ static inline void *phys_to_virt(unsigned long address) return __va(address); } -void reserve_bootmem_region(phys_addr_t start, phys_addr_t end); +void reserve_bootmem_region(phys_addr_t start, phys_addr_t end, int nid); static inline void totalram_pages_inc(void) { diff --git a/tools/include/linux/seq_file.h b/tools/include/linux/seq_file.h index 102fd9217f1f9b96438f14e7d9ef643d123ebc43..f6bc226af0c1d1c4d26c1c2d3f7a963c34c728eb 100644 --- a/tools/include/linux/seq_file.h +++ b/tools/include/linux/seq_file.h @@ -1,4 +1,6 @@ #ifndef _TOOLS_INCLUDE_LINUX_SEQ_FILE_H #define _TOOLS_INCLUDE_LINUX_SEQ_FILE_H +struct seq_file; + #endif /* _TOOLS_INCLUDE_LINUX_SEQ_FILE_H */ diff --git a/tools/include/uapi/asm-generic/unistd.h b/tools/include/uapi/asm-generic/unistd.h index fd6c1cb585db43c4e9bbbcffc8ee19a34325debe..abe087c53b4b04348431716c1cb638b99d5a788a 100644 --- a/tools/include/uapi/asm-generic/unistd.h +++ b/tools/include/uapi/asm-generic/unistd.h @@ -820,8 +820,11 @@ __SYSCALL(__NR_set_mempolicy_home_node, sys_set_mempolicy_home_node) #define __NR_cachestat 451 __SYSCALL(__NR_cachestat, sys_cachestat) +#define __NR_fchmodat2 452 +__SYSCALL(__NR_fchmodat2, sys_fchmodat2) + #undef __NR_syscalls -#define __NR_syscalls 452 +#define __NR_syscalls 453 /* * 32 bit systems traditionally used different diff --git a/tools/include/uapi/drm/drm.h b/tools/include/uapi/drm/drm.h index a87bbbbca2d48ada91efcc8aaab469ba51f0224a..794c1d857677d9b9b2e20222f671f15fbb86c4aa 100644 --- a/tools/include/uapi/drm/drm.h +++ b/tools/include/uapi/drm/drm.h @@ -673,8 +673,11 @@ struct drm_gem_open { * Bitfield of supported PRIME sharing capabilities. See &DRM_PRIME_CAP_IMPORT * and &DRM_PRIME_CAP_EXPORT. * - * PRIME buffers are exposed as dma-buf file descriptors. See - * Documentation/gpu/drm-mm.rst, section "PRIME Buffer Sharing". + * Starting from kernel version 6.6, both &DRM_PRIME_CAP_IMPORT and + * &DRM_PRIME_CAP_EXPORT are always advertised. + * + * PRIME buffers are exposed as dma-buf file descriptors. + * See :ref:`prime_buffer_sharing`. */ #define DRM_CAP_PRIME 0x5 /** @@ -682,6 +685,8 @@ struct drm_gem_open { * * If this bit is set in &DRM_CAP_PRIME, the driver supports importing PRIME * buffers via the &DRM_IOCTL_PRIME_FD_TO_HANDLE ioctl. + * + * Starting from kernel version 6.6, this bit is always set in &DRM_CAP_PRIME. */ #define DRM_PRIME_CAP_IMPORT 0x1 /** @@ -689,6 +694,8 @@ struct drm_gem_open { * * If this bit is set in &DRM_CAP_PRIME, the driver supports exporting PRIME * buffers via the &DRM_IOCTL_PRIME_HANDLE_TO_FD ioctl. + * + * Starting from kernel version 6.6, this bit is always set in &DRM_CAP_PRIME. */ #define DRM_PRIME_CAP_EXPORT 0x2 /** @@ -756,15 +763,14 @@ struct drm_gem_open { /** * DRM_CAP_SYNCOBJ * - * If set to 1, the driver supports sync objects. See - * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". + * If set to 1, the driver supports sync objects. See :ref:`drm_sync_objects`. */ #define DRM_CAP_SYNCOBJ 0x13 /** * DRM_CAP_SYNCOBJ_TIMELINE * * If set to 1, the driver supports timeline operations on sync objects. See - * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". + * :ref:`drm_sync_objects`. */ #define DRM_CAP_SYNCOBJ_TIMELINE 0x14 @@ -909,6 +915,27 @@ struct drm_syncobj_timeline_wait { __u32 pad; }; +/** + * struct drm_syncobj_eventfd + * @handle: syncobj handle. + * @flags: Zero to wait for the point to be signalled, or + * &DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE to wait for a fence to be + * available for the point. + * @point: syncobj timeline point (set to zero for binary syncobjs). + * @fd: Existing eventfd to sent events to. + * @pad: Must be zero. + * + * Register an eventfd to be signalled by a syncobj. The eventfd counter will + * be incremented by one. + */ +struct drm_syncobj_eventfd { + __u32 handle; + __u32 flags; + __u64 point; + __s32 fd; + __u32 pad; +}; + struct drm_syncobj_array { __u64 handles; @@ -1169,6 +1196,8 @@ extern "C" { */ #define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2) +#define DRM_IOCTL_SYNCOBJ_EVENTFD DRM_IOWR(0xCF, struct drm_syncobj_eventfd) + /* * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x9f. @@ -1180,25 +1209,50 @@ extern "C" { #define DRM_COMMAND_BASE 0x40 #define DRM_COMMAND_END 0xA0 -/* - * Header for events written back to userspace on the drm fd. The - * type defines the type of event, the length specifies the total - * length of the event (including the header), and user_data is - * typically a 64 bit value passed with the ioctl that triggered the - * event. A read on the drm fd will always only return complete - * events, that is, if for example the read buffer is 100 bytes, and - * there are two 64 byte events pending, only one will be returned. +/** + * struct drm_event - Header for DRM events + * @type: event type. + * @length: total number of payload bytes (including header). * - * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and - * up are chipset specific. + * This struct is a header for events written back to user-space on the DRM FD. + * A read on the DRM FD will always only return complete events: e.g. if the + * read buffer is 100 bytes large and there are two 64 byte events pending, + * only one will be returned. + * + * Event types 0 - 0x7fffffff are generic DRM events, 0x80000000 and + * up are chipset specific. Generic DRM events include &DRM_EVENT_VBLANK, + * &DRM_EVENT_FLIP_COMPLETE and &DRM_EVENT_CRTC_SEQUENCE. */ struct drm_event { __u32 type; __u32 length; }; +/** + * DRM_EVENT_VBLANK - vertical blanking event + * + * This event is sent in response to &DRM_IOCTL_WAIT_VBLANK with the + * &_DRM_VBLANK_EVENT flag set. + * + * The event payload is a struct drm_event_vblank. + */ #define DRM_EVENT_VBLANK 0x01 +/** + * DRM_EVENT_FLIP_COMPLETE - page-flip completion event + * + * This event is sent in response to an atomic commit or legacy page-flip with + * the &DRM_MODE_PAGE_FLIP_EVENT flag set. + * + * The event payload is a struct drm_event_vblank. + */ #define DRM_EVENT_FLIP_COMPLETE 0x02 +/** + * DRM_EVENT_CRTC_SEQUENCE - CRTC sequence event + * + * This event is sent in response to &DRM_IOCTL_CRTC_QUEUE_SEQUENCE. + * + * The event payload is a struct drm_event_crtc_sequence. + */ #define DRM_EVENT_CRTC_SEQUENCE 0x03 struct drm_event_vblank { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 8790b3962e4b85f22593f8f9c9f8d94eaf6981f3..0448700890f77d9a4fffa6fed5c17aa02b9f718b 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1962,7 +1962,9 @@ union bpf_attr { * performed again, if the helper is used in combination with * direct packet access. * Return - * 0 on success, or a negative error in case of failure. + * 0 on success, or a negative error in case of failure. Positive + * error indicates a potential drop or congestion in the target + * device. The particular positive error codes are not defined. * * u64 bpf_get_current_pid_tgid(void) * Description diff --git a/tools/include/uapi/linux/seccomp.h b/tools/include/uapi/linux/seccomp.h new file mode 100644 index 0000000000000000000000000000000000000000..dbfc9b37fcaee47e0d9c0431abd32797a4a52d1d --- /dev/null +++ b/tools/include/uapi/linux/seccomp.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_SECCOMP_H +#define _UAPI_LINUX_SECCOMP_H + +#include +#include + + +/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, ) */ +#define SECCOMP_MODE_DISABLED 0 /* seccomp is not in use. */ +#define SECCOMP_MODE_STRICT 1 /* uses hard-coded filter. */ +#define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ + +/* Valid operations for seccomp syscall. */ +#define SECCOMP_SET_MODE_STRICT 0 +#define SECCOMP_SET_MODE_FILTER 1 +#define SECCOMP_GET_ACTION_AVAIL 2 +#define SECCOMP_GET_NOTIF_SIZES 3 + +/* Valid flags for SECCOMP_SET_MODE_FILTER */ +#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0) +#define SECCOMP_FILTER_FLAG_LOG (1UL << 1) +#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2) +#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3) +#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4) +/* Received notifications wait in killable state (only respond to fatal signals) */ +#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5) + +/* + * All BPF programs must return a 32-bit value. + * The bottom 16-bits are for optional return data. + * The upper 16-bits are ordered from least permissive values to most, + * as a signed value (so 0x8000000 is negative). + * + * The ordering ensures that a min_t() over composed return values always + * selects the least permissive choice. + */ +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */ +#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */ +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ +#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ +#define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */ +#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ +#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ +#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ + +/* Masks for the return value sections. */ +#define SECCOMP_RET_ACTION_FULL 0xffff0000U +#define SECCOMP_RET_ACTION 0x7fff0000U +#define SECCOMP_RET_DATA 0x0000ffffU + +/** + * struct seccomp_data - the format the BPF program executes over. + * @nr: the system call number + * @arch: indicates system call convention as an AUDIT_ARCH_* value + * as defined in . + * @instruction_pointer: at the time of the system call. + * @args: up to 6 system call arguments always stored as 64-bit values + * regardless of the architecture. + */ +struct seccomp_data { + int nr; + __u32 arch; + __u64 instruction_pointer; + __u64 args[6]; +}; + +struct seccomp_notif_sizes { + __u16 seccomp_notif; + __u16 seccomp_notif_resp; + __u16 seccomp_data; +}; + +struct seccomp_notif { + __u64 id; + __u32 pid; + __u32 flags; + struct seccomp_data data; +}; + +/* + * Valid flags for struct seccomp_notif_resp + * + * Note, the SECCOMP_USER_NOTIF_FLAG_CONTINUE flag must be used with caution! + * If set by the process supervising the syscalls of another process the + * syscall will continue. This is problematic because of an inherent TOCTOU. + * An attacker can exploit the time while the supervised process is waiting on + * a response from the supervising process to rewrite syscall arguments which + * are passed as pointers of the intercepted syscall. + * It should be absolutely clear that this means that the seccomp notifier + * _cannot_ be used to implement a security policy! It should only ever be used + * in scenarios where a more privileged process supervises the syscalls of a + * lesser privileged process to get around kernel-enforced security + * restrictions when the privileged process deems this safe. In other words, + * in order to continue a syscall the supervising process should be sure that + * another security mechanism or the kernel itself will sufficiently block + * syscalls if arguments are rewritten to something unsafe. + * + * Similar precautions should be applied when stacking SECCOMP_RET_USER_NOTIF + * or SECCOMP_RET_TRACE. For SECCOMP_RET_USER_NOTIF filters acting on the + * same syscall, the most recently added filter takes precedence. This means + * that the new SECCOMP_RET_USER_NOTIF filter can override any + * SECCOMP_IOCTL_NOTIF_SEND from earlier filters, essentially allowing all + * such filtered syscalls to be executed by sending the response + * SECCOMP_USER_NOTIF_FLAG_CONTINUE. Note that SECCOMP_RET_TRACE can equally + * be overriden by SECCOMP_USER_NOTIF_FLAG_CONTINUE. + */ +#define SECCOMP_USER_NOTIF_FLAG_CONTINUE (1UL << 0) + +struct seccomp_notif_resp { + __u64 id; + __s64 val; + __s32 error; + __u32 flags; +}; + +#define SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP (1UL << 0) + +/* valid flags for seccomp_notif_addfd */ +#define SECCOMP_ADDFD_FLAG_SETFD (1UL << 0) /* Specify remote fd */ +#define SECCOMP_ADDFD_FLAG_SEND (1UL << 1) /* Addfd and return it, atomically */ + +/** + * struct seccomp_notif_addfd + * @id: The ID of the seccomp notification + * @flags: SECCOMP_ADDFD_FLAG_* + * @srcfd: The local fd number + * @newfd: Optional remote FD number if SETFD option is set, otherwise 0. + * @newfd_flags: The O_* flags the remote FD should have applied + */ +struct seccomp_notif_addfd { + __u64 id; + __u32 flags; + __u32 srcfd; + __u32 newfd; + __u32 newfd_flags; +}; + +#define SECCOMP_IOC_MAGIC '!' +#define SECCOMP_IO(nr) _IO(SECCOMP_IOC_MAGIC, nr) +#define SECCOMP_IOR(nr, type) _IOR(SECCOMP_IOC_MAGIC, nr, type) +#define SECCOMP_IOW(nr, type) _IOW(SECCOMP_IOC_MAGIC, nr, type) +#define SECCOMP_IOWR(nr, type) _IOWR(SECCOMP_IOC_MAGIC, nr, type) + +/* Flags for seccomp notification fd ioctl. */ +#define SECCOMP_IOCTL_NOTIF_RECV SECCOMP_IOWR(0, struct seccomp_notif) +#define SECCOMP_IOCTL_NOTIF_SEND SECCOMP_IOWR(1, \ + struct seccomp_notif_resp) +#define SECCOMP_IOCTL_NOTIF_ID_VALID SECCOMP_IOW(2, __u64) +/* On success, the return value is the remote process's added fd number */ +#define SECCOMP_IOCTL_NOTIF_ADDFD SECCOMP_IOW(3, \ + struct seccomp_notif_addfd) + +#define SECCOMP_IOCTL_NOTIF_SET_FLAGS SECCOMP_IOW(4, __u64) + +#endif /* _UAPI_LINUX_SECCOMP_H */ diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h index ba2dcf64f4e6399caad96f11f1fcb679e8e80abf..ae64090184d328f3089832139d8d385f4bd934af 100644 --- a/tools/lib/perf/include/perf/event.h +++ b/tools/lib/perf/include/perf/event.h @@ -148,8 +148,18 @@ struct perf_record_switch { struct perf_record_header_attr { struct perf_event_header header; struct perf_event_attr attr; - __u64 id[]; -}; + /* + * Array of u64 id follows here but we cannot use a flexible array + * because size of attr in the data can be different then current + * version. Please use perf_record_header_attr_id() below. + * + * __u64 id[]; // do not use this + */ +}; + +/* Returns the pointer to id array based on the actual attr size. */ +#define perf_record_header_attr_id(evt) \ + ((void *)&(evt)->attr.attr + (evt)->attr.attr.size) enum { PERF_CPU_MAP__CPUS = 0, diff --git a/tools/mm/Makefile b/tools/mm/Makefile index 6c1da51f4177c42a190ca7d48d85c729d9155ca8..1c5606cc33346bd29b523deb466cc739a0c6bb33 100644 --- a/tools/mm/Makefile +++ b/tools/mm/Makefile @@ -8,8 +8,8 @@ TARGETS=page-types slabinfo page_owner_sort LIB_DIR = ../lib/api LIBS = $(LIB_DIR)/libapi.a -CFLAGS += -Wall -Wextra -I../lib/ -LDFLAGS += $(LIBS) +CFLAGS += -Wall -Wextra -I../lib/ -pthread +LDFLAGS += $(LIBS) -pthread all: $(TARGETS) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 1384090530dbe3e7e0c018c833aeacdf2f95b720..e308d1ba664ef9366edc5f1412dac8659ad3a9bb 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4333,7 +4333,8 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn continue; } - if (insn_func(dest) && insn_func(dest) == insn_func(insn)) { + if (insn_func(dest) && insn_func(insn) && + insn_func(dest)->pfunc == insn_func(insn)->pfunc) { /* * Anything from->to self is either _THIS_IP_ or * IRET-to-self. diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt index f04f0eaded985fc8ba4ea121d2667a5bf3250649..ca5789625cd2b8e562f242dc2db57687f25d64ca 100644 --- a/tools/perf/Documentation/perf-bench.txt +++ b/tools/perf/Documentation/perf-bench.txt @@ -67,6 +67,9 @@ SUBSYSTEM 'internals':: Benchmark internal perf functionality. +'uprobe':: + Benchmark overhead of uprobe + BPF. + 'all':: All benchmark subsystems. diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt index 1478068ad5dd491b6a13721d895ebd68b9d050fd..0b4e79dbd3f689942d4e734eb4e48161634ae063 100644 --- a/tools/perf/Documentation/perf-config.txt +++ b/tools/perf/Documentation/perf-config.txt @@ -125,9 +125,6 @@ Given a $HOME/.perfconfig like this: group = true skip-empty = true - [llvm] - dump-obj = true - clang-opt = -g You can hide source code of annotate feature setting the config to false with @@ -657,36 +654,6 @@ ftrace.*:: -F option is not specified. Possible values are 'function' and 'function_graph'. -llvm.*:: - llvm.clang-path:: - Path to clang. If omit, search it from $PATH. - - llvm.clang-bpf-cmd-template:: - Cmdline template. Below lines show its default value. Environment - variable is used to pass options. - "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ - "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ - "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ - "-Wno-unused-value -Wno-pointer-sign " \ - "-working-directory $WORKING_DIR " \ - "-c \"$CLANG_SOURCE\" --target=bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" - - llvm.clang-opt:: - Options passed to clang. - - llvm.kbuild-dir:: - kbuild directory. If not set, use /lib/modules/`uname -r`/build. - If set to "" deliberately, skip kernel header auto-detector. - - llvm.kbuild-opts:: - Options passed to 'make' when detecting kernel header options. - - llvm.dump-obj:: - Enable perf dump BPF object files compiled by LLVM. - - llvm.opts:: - Options passed to llc. - samples.*:: samples.context:: diff --git a/tools/perf/Documentation/perf-dlfilter.txt b/tools/perf/Documentation/perf-dlfilter.txt index fb22e3b31dc5c17bfad896d216dd5f5e008bc0db..8887cc20a809ecf3b96692a88ee4c44136dbf15b 100644 --- a/tools/perf/Documentation/perf-dlfilter.txt +++ b/tools/perf/Documentation/perf-dlfilter.txt @@ -64,6 +64,12 @@ internal filtering. If implemented, 'filter_description' should return a one-line description of the filter, and optionally a longer description. +Do not assume the 'sample' argument is valid (dereferenceable) +after 'filter_event' and 'filter_event_early' return. + +Do not assume data referenced by pointers in struct perf_dlfilter_sample +is valid (dereferenceable) after 'filter_event' and 'filter_event_early' return. + The perf_dlfilter_sample structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -150,7 +156,8 @@ struct perf_dlfilter_fns { const char *(*srcline)(void *ctx, __u32 *line_number); struct perf_event_attr *(*attr)(void *ctx); __s32 (*object_code)(void *ctx, __u64 ip, void *buf, __u32 len); - void *(*reserved[120])(void *); + void (*al_cleanup)(void *ctx, struct perf_dlfilter_al *al); + void *(*reserved[119])(void *); }; ---- @@ -161,7 +168,8 @@ struct perf_dlfilter_fns { 'args' returns arguments from --dlarg options. 'resolve_address' provides information about 'address'. al->size must be set -before calling. Returns 0 on success, -1 otherwise. +before calling. Returns 0 on success, -1 otherwise. Call al_cleanup() (if present, +see below) when 'al' data is no longer needed. 'insn' returns instruction bytes and length. @@ -171,6 +179,12 @@ before calling. Returns 0 on success, -1 otherwise. 'object_code' reads object code and returns the number of bytes read. +'al_cleanup' must be called (if present, so check perf_dlfilter_fns.al_cleanup != NULL) +after resolve_address() to free any associated resources. + +Do not assume pointers obtained via perf_dlfilter_fns are valid (dereferenceable) +after 'filter_event' and 'filter_event_early' return. + The perf_dlfilter_al structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -197,9 +211,13 @@ struct perf_dlfilter_al { /* Below members are only populated by resolve_ip() */ __u8 filtered; /* true if this sample event will be filtered out */ const char *comm; + void *priv; /* Private data. Do not change */ }; ---- +Do not assume data referenced by pointers in struct perf_dlfilter_al +is valid (dereferenceable) after 'filter_event' and 'filter_event_early' return. + perf_dlfilter_sample flags ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt index df4595563801e23bc5ce87e5527c02eeeb832263..d780b93fcf87131b7267092c53f52a155b613a0e 100644 --- a/tools/perf/Documentation/perf-ftrace.txt +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -96,8 +96,9 @@ OPTIONS for 'perf ftrace trace' --func-opts:: List of options allowed to set: - call-graph - Display kernel stack trace for function tracer. - irq-info - Display irq context info for function tracer. + + - call-graph - Display kernel stack trace for function tracer. + - irq-info - Display irq context info for function tracer. -G:: --graph-funcs=:: @@ -118,11 +119,12 @@ OPTIONS for 'perf ftrace trace' --graph-opts:: List of options allowed to set: - nosleep-time - Measure on-CPU time only for function_graph tracer. - noirqs - Ignore functions that happen inside interrupt. - verbose - Show process names, PIDs, timestamps, etc. - thresh= - Setup trace duration threshold in microseconds. - depth= - Set max depth for function graph tracer to follow. + + - nosleep-time - Measure on-CPU time only for function_graph tracer. + - noirqs - Ignore functions that happen inside interrupt. + - verbose - Show process names, PIDs, timestamps, etc. + - thresh= - Setup trace duration threshold in microseconds. + - depth= - Set max depth for function graph tracer to follow. OPTIONS for 'perf ftrace latency' diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 680396c56bd10f7c2a98f4dfbf0a9a0d0df8db54..d5217be012d79492efdf34868b2db49e3d955294 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -99,20 +99,6 @@ OPTIONS If you want to profile write accesses in [0x1000~1008), just set 'mem:0x1000/8:w'. - - a BPF source file (ending in .c) or a precompiled object file (ending - in .o) selects one or more BPF events. - The BPF program can attach to various perf events based on the ELF section - names. - - When processing a '.c' file, perf searches an installed LLVM to compile it - into an object file first. Optional clang options can be passed via the - '--clang-opt' command line option, e.g.: - - perf record --clang-opt "-DLINUX_VERSION_CODE=0x50000" \ - -e tests/bpf-script-example.c - - Note: '--clang-opt' must be placed before '--event/-e'. - - a group of events surrounded by a pair of brace ("{event1,event2,...}"). Each event is separated by commas and the group should be quoted to prevent the shell interpretation. You also need to use --group on @@ -523,9 +509,10 @@ CLOCK_BOOTTIME, CLOCK_REALTIME and CLOCK_TAI. Select AUX area tracing Snapshot Mode. This option is valid only with an AUX area tracing event. Optionally, certain snapshot capturing parameters can be specified in a string that follows this option: - 'e': take one last snapshot on exit; guarantees that there is at least one + + - 'e': take one last snapshot on exit; guarantees that there is at least one snapshot in the output file; - : if the PMU supports this, specify the desired snapshot size. + - : if the PMU supports this, specify the desired snapshot size. In Snapshot Mode trace data is captured only when signal SIGUSR2 is received and on exit if the above 'e' option is given. @@ -547,14 +534,6 @@ PERF_RECORD_SWITCH_CPU_WIDE. In some cases (e.g. Intel PT, CoreSight or Arm SPE) switch events will be enabled automatically, which can be suppressed by by the option --no-switch-events. ---clang-path=PATH:: -Path to clang binary to use for compiling BPF scriptlets. -(enabled when BPF support is on) - ---clang-opt=OPTIONS:: -Options passed to clang when compiling BPF scriptlets. -(enabled when BPF support is on) - --vmlinux=PATH:: Specify vmlinux path which has debuginfo. (enabled when BPF prologue is on) @@ -572,8 +551,9 @@ providing implementation for Posix AIO API. --affinity=mode:: Set affinity mask of trace reading thread according to the policy defined by 'mode' value: - node - thread affinity mask is set to NUMA node cpu mask of the processed mmap buffer - cpu - thread affinity mask is set to cpu of the processed mmap buffer + + - node - thread affinity mask is set to NUMA node cpu mask of the processed mmap buffer + - cpu - thread affinity mask is set to cpu of the processed mmap buffer --mmap-flush=number:: @@ -625,16 +605,17 @@ Record timestamp boundary (time of first/last samples). --switch-output[=mode]:: Generate multiple perf.data files, timestamp prefixed, switching to a new one based on 'mode' value: - "signal" - when receiving a SIGUSR2 (default value) or - - when reaching the size threshold, size is expected to - be a number with appended unit character - B/K/M/G -